ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

机器人视觉引导焊接:点云配准与强化学习纠偏实战指南

机器人视觉引导焊接:点云配准与强化学习纠偏实战指南

简介:工业视觉引导焊接是智能制造中的关键应用场景,这份PDF资料面向机器人轨迹规划相关的算法工程师、研究生与竞赛学习者,系统讲解如何利用PyTorch框架将三维点云配准与强化学习相结合,解决焊接机器人运动轨迹规划问题。全篇30页,十个章节递进展开,从工业视觉系统组成、焊接机器人运动控制基础,到PyTorch张量操作、自动求导与模型训练,再到三维点云数据获取、预处理和常见配准算法;随后引入强化学习的马尔可夫决策过程与深度Q网络实现,并详细说明点云配准网络设计、状态信息整合、奖励函数设计、实验环境搭建、数据集准备、代码实现与结果分析,覆盖理论、设计、编码、验证全流程,帮助读者厘清技术脉络。资源包仅包含1个PDF文档,约1.92MB,目录支持章节跳转和阅读器大纲定位,便于快速查阅点云配准、DQN训练、实验对比等关键模块;已有69人学习,适合希望借助PyTorch开展三维点云与机器人智能规划研究的中高级读者参考。

1. 视觉引导焊接为什么要同时用点云配准与强化学习

一条焊接工位上来料位置总是有偏差,工件摆歪个 5 毫米,示教路径就会错过坡口,焊缝成形立刻翻车。工业视觉引导焊接要解决的,就是「工件到底在哪、轨迹该怎么跟着工件走」这两件事。点云配准负责从三维传感器数据里算出工件的精确位姿,强化学习负责在示教轨迹被热变形、装夹误差和机器人关节间隙干扰之后,把末端姿态一点点拉回坡口中心。适合谁来读:做机器人焊接集成、想把手动示教改成视觉引导,或者已经在用静态拍照定位但被弧光、飞溅、工件一致性搞得焦头烂额的工程师。这篇按配准→轨迹映射→RL纠偏→联调验证的链路走一遍,把参数和坑都摆出来。

2. 三维点云配准:从FPFH粗配准到PyTorch精配准的落地

2.1 为什么不能上来就ICP:初值敏感是配准的头号敌人

常见做法是先用粗配准拿到一个大致位姿,再用精配准收敛到亚毫米级。直接对原始点云跑ICP,目标函数是非凸的,起始位姿偏差超过一定范围就会陷进局部最优,配准结果偏移个十几毫米,后端的焊接轨迹自然全错。

粗配准的主流方案是FPFH特征 + SAC-IA采样一致性配准:先分别计算源点云和目标点云的快速点特征直方图,再在特征空间里找对应点对,用RANSAC思路筛选出使距离误差最小的刚体变换。这一步能把位姿误差压到毫米级左右,给后续ICP提供一个不跑飞的初值。

精配准阶段的常规实现是ICP及其变体:把源点云每个点按当前变换投影过去,用最近邻找目标点,然后最小化点对距离求新的刚体变换,迭代直到收敛。工程现场一般不用默认的逐点ICP,而是用点到平面ICP或GICP。点到平面能让平面区域的收敛更快,GICP则对噪声更稳。如果非要用PyTorch重写一遍ICP,核心计算可以全部在张量上完成,中间不落地到CPU,推理速度在嵌入式工控机上也能接受。

2.2 用PyTorch实现ICP精配准的最小闭环

以下是一个可以直接跑的PyTorch ICP精配准实现。它适合做焊件这类刚性工件的位姿细化,输入是N×3的源点云和目标点云,输出是4×4刚体变换矩阵。

import torch def icp_pytorch(src, tgt, init_pose=None, max_iter=30, dist_thresh=0.005): """ src: [N, 3] 源点云(工件坐标系点云) tgt: [M, 3] 目标点云(现场传感器点云) init_pose: [4, 4] 粗配准给的初始变换,没有则用单位阵 dist_thresh: 最近邻距离阈值,超过的当作外点剔除,单位:米 """ device = src.device if init_pose is None: init_pose = torch.eye(4, device=device) # 把源点云先按初始变换转过去,后续迭代只优化残差 src_h = torch.cat([src, torch.ones(src.shape[0], 1, device=device)], dim=1) src_t = (init_pose @ src_h.T).T[:, :3] pose = init_pose.clone() for i in range(max_iter): # 最近邻:torch.cdist 直接算两两距离,M比较大时这里是主要瓶颈 dist = torch.cdist(src_t, tgt) nn_idx = dist.argmin(dim=1) nn_dist = dist.gather(1, nn_idx.unsqueeze(1)).squeeze(1) # 外点剔除:弧焊场景里飞溅和反光产生的离群点经常占5%~10% valid = nn_dist < dist_thresh if valid.sum() < 50: print(f"iter {i}: 有效点太少({valid.sum()}),停止迭代") break A = src_t[valid] B = tgt[nn_idx[valid]] # 去质心后SVD求旋转,再加回平移 centroid_s = A.mean(dim=0) centroid_t = B.mean(dim=0) A_c = A - centroid_s B_c = B - centroid_t H = A_c.T @ B_c U, _, Vh = torch.linalg.svd(H) R = Vh.T @ U.T # 避免反射矩阵,保证是纯旋转 if torch.det(R) < 0: Vh[-1, :] *= -1 R = Vh.T @ U.T t = centroid_t - R @ centroid_s # 更新位姿:注意右乘还是左乘,这里累积的是“源→目标”的增量 delta = torch.eye(4, device=device) delta[:3, :3] = R delta[:3, 3] = t pose = delta @ pose src_t = (delta @ torch.cat([src_t, torch.ones(src_t.shape[0], 1, device=device)], dim=1).T).T[:, :3] # 收敛判断用平均最近邻距离 if torch.norm(t) < 1e-6: break return pose.detach().cpu().numpy()

关键参数有三个。dist_thresh在焊缝场景建议用0.5到1毫米,太大会把坡口边缘的杂点吸进来,太小会让有效点对数不够导致矩阵奇异。max_iter设30到50次足够,ICP是线性收敛,后面十几代基本只在小数点后第五位变化。去质心必须做,不做的话SVD解出的旋转会被平移分量污染,这是我刚做配准时踩过的一坑。

还有一个隐藏问题:torch.cdist在点云规模超过十万时占显存很凶,一整张1280×1024的结构光点云约130万点,直接cdist会卡死。一般做法是先对目标点云体素降采样到6000到15000点,源点云裁剪到焊缝附近的ROI区域。把输入缩到这个量级,上面的代码在150次迭代内能稳定跑在30毫秒左右。

2.3 工业点云的前处理流程与参数经验

拿到手的三维点云不能直接喂给配准,业界标准流程是「ROI裁剪 → 体素降采样 → 离群点剔除 → 配准」。ROI裁剪的目的是把夹具、工件背面、背景的干扰点直接去掉,只保留焊缝区域附近的点云,这样做还能减少后续配准的计算量。

体素降采样的体素尺寸需要按传感器精度和工作尺寸定:焊缝坡口宽度通常2到4毫米,体素尺寸取1到3毫米不会把坡口细节抹平。我一般用2毫米体素,既能压掉约70%的点数,又不至于让坡口边缘变成一条模糊带。离群点剔除用统计滤波,邻域点数为20到30,标准差阈值1.0到2.0。焊接现场的金属飞溅在点云里表现为孤立的亮点,统计滤波对这类点很有效。

前处理的PyTorch实现可以直接借用torch.unique做整数化聚类,但工程上更常见的做法是调Open3D完成,只有精配准这一步落到PyTorch里,这样能在部署时避开Open3D的版本依赖问题。记住一个原则:前处理用成熟库,核心配准用自己的PyTorch实现,两者之间用numpy数组传递,部署时的灵活度最大。

3. 从配准到轨迹:把模板路径映射到机器人基坐标系

3.1 离线示教在线映射:一条路径反复用的关键

焊接轨迹规划的主流方法是离线示教模板路径,现场再根据配准结果做刚体变换,而不是每次焊接都重新规划完整轨迹。模板路径在工件坐标系里定义,包含一系列路径点的空间位置、姿态和焊接工艺参数。现场点云配准求出的是工件在传感器坐标系下的位姿,传感器到机器人基坐标系的变换在安装时标定好,于是模板路径就能映射到机器人基坐标系。

坐标变换链写成向量形式就是:p_robot = T_sensor_to_robot × T_object_to_sensor × p_template。用矩阵乘法串联时,顺序反了结果完全是另一个点,越远偏差越大。常见的顺序错误是把T_sensor_to_robot和T_object_to_sensor写反,结果是路径在近点处看起来对,焊到远端时偏掉几十毫米。这个链条要在仿真里先验证再上真机。

3.2 焊缝特征点提取与路径离散化

模板路径不是每个工件都要重新标定,但焊缝的起点和终点一般要现场确认。直线焊缝只需要两个端点,圆弧焊缝需要圆心、半径和起止角。从配准后的点云里找焊缝的核心技巧是:焊缝坡口在点云上表现为一条深度凹槽,用平面拟合把工件表面点去掉,剩下凹槽里的点就是焊缝区域。对凹槽点做PCA,主方向就是焊缝走向,端点取投影极值。

路径离散化间距和焊接速度强相关:机器人焊接常用速度是5到15毫米每秒,控制周期8到16毫秒,一个周期走0.04到0.24毫米。出于安全余量,轨迹点间距取1到2毫米足够平滑。点太密会加大机器人轨迹缓存压力,太稀则焊缝成形呈锯齿状。这节没有花哨的算法,间距就是按控制周期和速度算出来的。

映射模板路径到机器人基坐标系的PyTorch实现非常简单,矩阵乘法一次完成:

import torch def map_template_to_robot(template_points, T_obj_to_sensor, T_sensor_to_robot): """ template_points: [N, 3] 工件坐标系下的模板路径点 T_obj_to_sensor: [4, 4] 配准得到的工件位姿 T_sensor_to_robot: [4, 4] 手眼标定得到的外部参数 """ # 先扩展齐次坐标方便做矩阵变换 N = template_points.shape[0] pts_h = torch.cat([ torch.as_tensor(template_points, dtype=torch.float32), torch.ones(N, 1) ], dim=1) # 左边乘传感器到机器人,再左乘目标到传感器 T_obj_to_robot = T_sensor_to_robot @ T_obj_to_sensor robot_pts_h = (T_obj_to_robot @ pts_h.T).T return robot_pts_h[:, :3].numpy()

这里的参数顺序要特别注意:T_obj_to_sensor来自点云配准,T_sensor_to_robot来自手眼标定,它们都是4×4齐次矩阵。右乘顺序错了,路径就不是绕工件转,而是绕机器人基座转,远端点能偏出几百毫米。映射完后,建议对路径的首末点做一次直线光顺,避免工件边缘的毛刺让端点跳动。

3.3 轨迹映射的正确性验证:仿真里先查三件事

上真机之前先在机器人仿真环境里做离线验证,这是我坚持的底线。验证时把同一套模板路径用到三个不同位姿的工件上,检查三件事:一是路径点是否落在焊缝坡口中心线的容差范围内(±1毫米);二是首末点是否在工件边缘以内,有没有悬空或扎进工件;三是姿态是否平滑,有没有相邻路径点姿态突变超过5度的位置。

仿真环境可以用RobotStudio这类商业软件,也可以用PyBullet加载URDF做运动学验证。仿真通过后再空跑真机,不送电、不送丝,让机器人按映射后的路径走一遍,用激光跟踪仪或千分表抽查几个关键点的实际位置。大部分坐标系错误和矩阵串联顺序问题在这一步暴露,而不是等到焊完一条试板才被发现。

4. 强化学习做轨迹纠偏:TD3在焊接场景的实践

4.1 为什么选TD3而不是PID或PPO

焊接过程中,热变形会让工件在焊接过程中缓慢移动,坡口宽度不均匀,机器人关节摩擦也会让理论轨迹和实际轨迹之间出现偏差。PID纠偏的问题是参数要针对不同焊缝调,而且对坡口宽度突变这类非对称误差反应迟钝。RL的思路是学习一个修正策略:输入当前偏差状态,输出末端姿态修正量,让机器人在执行过程中不断把焊枪拉回坡口中心。

动作空间是连续的三维姿态修正量(Δrx,Δry,Δz),动作维度低但要求平滑。这个场景用TD3比PPO更合适:TD3是确定性策略,输出动作噪声小,不会出现PPO那种策略分布过宽导致的抖动。TD3的三个关键机制——双Q网络、延迟策略更新、目标策略平滑——正好压住连续控制里常见的Q值过估计问题。PyTorch实现TD3时主要维护Actor网络一个、Critic网络两个、对应目标网络各一个,共六个网络,结构清楚不复杂。

4.2 Actor-Critic网络的PyTorch实现

import torch import torch.nn as nn class Actor(nn.Module): """ 输入状态:当前位姿偏差(dx, dy, dz)、姿态偏差(drx, dry, drz)、焊接速度v 输出动作:末端姿态修正量(a_rx, a_ry, a_rz),单位弧度/毫米 """ def __init__(self, state_dim=7, action_dim=3, hidden=256): super().__init__() self.net = nn.Sequential( nn.Linear(state_dim, hidden), nn.ReLU(), nn.Linear(hidden, hidden), nn.ReLU(), nn.Linear(hidden, action_dim), nn.Tanh() # 输出归一化到[-1, 1],再缩放成实际修正量 ) def forward(self, s): return self.net(s) class Critic(nn.Module): # 输入状态和动作的拼接,输出Q值 def __init__(self, state_dim=7, action_dim=3, hidden=256): super().__init__() self.net = nn.Sequential( nn.Linear(state_dim + action_dim, hidden), nn.ReLU(), nn.Linear(hidden, hidden), nn.ReLU(), nn.Linear(hidden, 1) ) def forward(self, s, a): return self.net(torch.cat([s, a], dim=1))

这里有两个值得注意的参数。一是Tanh输出层的缩放系数:焊接末端修正量不需要很大,常见范围是±0.02弧度角度和±5毫米位移,所以必须在Tanh输出之后乘一个缩放向量,把动作限制在物理合理区间。二是隐藏层256已经够用,堆到1024除了增加推理延迟,没有明显收益。如果想快速验证训练流程,可以用一个更小的64隐藏层网络,但实际焊接环境里的状态噪声较大,256层的稳定性更好。

4.3 奖励函数设计与训练策略

奖励函数是RL落在焊接场景里最容易被低估的部分。常见做法是稀疏奖励:只在焊完一条试板后给奖励,但这种方法在真机上训练成本极高,仿真里收敛也慢。更推荐的做法是密集奖励,每一步根据实时焊缝偏差计算:

reward = -w1 × (偏差距离)² - w2 × (修正量变化率)² - w3 × (姿态偏差)²

三个权重系数需要按物理单位调。偏差距离以毫米为单位,w1取1.0到2.0;修正量变化率以弧度每秒为单位,w2取0.1到0.5,它惩罚抖动,防止网络学到高频颤抖动作;姿态偏差以弧度为单位的,w3取0.5。如果发现训练出来的动作一直在边界值来回跳动,先看w2是不是太小。

一个实用的训练策略是先用模仿学习预热:把PID或人工示教过程中记录的状态-动作对拿出来,先让Actor网络做行为克隆,再切到RL去微调。这个做法在机械臂强化学习实战里普遍被验证能大幅减少探索时间。我一般用5000到10000条人类示教数据做预热,之后RL只需要几万步就能收敛到稳定策略,比纯RL从零开始少花一个数量级的采样。

4.4 从仿真到真机:摩擦力是那张难念的经

仿真里训练好的策略往真机迁移时,最容易翻车的不是视觉,是机器人关节摩擦。仿真里假设的理想驱动器没有非线性摩擦,真机低速焊接时关节摩擦补偿不足会导致末端出现周期性的卡顿,表现在焊缝上就是鱼鳞纹不均匀。

两条应对路径:一是把摩擦项建到仿真里,在PyTorch仿真环境的状态更新中加一个与速度方向相反的库仑摩擦项,摩擦系数按真机负载工况估计;二是用域随机化,每次训练迭代随机化摩擦系数的正负20%范围,策略学到的是对摩擦变化不敏感的控制规律。两条我都试过,域随机化更省事,效果也更好。

5. 配准与RL落地的常见坑:坐标系、弧光干扰与训练不收敛

5.1 点云配准突然偏到工件背面:ROI裁剪把参照物全裁掉了

现象是FPFH粗配准在某个工件上突然给出一个完全反方向的位姿,ICP在这个初值上直接收敛到背面。原因分析下来是ROI裁剪过狠,点云里只剩下工件局部平面,特征太少,SAC-IA在对称或弱特征点云上找到了错误的对应关系。解决办法是改两处:ROI裁剪保留一个非对称的参照特征,比如工件上的工艺孔或台阶面;粗配准后增加一步离散度检查,旋转矩阵的行列式必须为正值,且变换后源点云的重心与目标点云重心的距离不能超过工件尺度的30%。这两条在代码里就是几行的事,但能挡掉大部分粗配准异常。

5.2 手眼标定矩阵的误差放大了远端偏移

现象是路径的近点对得很准,焊到远端时偏移达到5毫米以上。原因是手眼标定(T_sensor_to_robot)的旋转误差在距相机远的工件区域被放大。焊接工作站里相机到工件的距离常有1米以上,标定矩阵哪怕只有0.1度的旋转误差,远端就会偏掉接近2毫米。解决办法是:标定时采集不少于15个位姿,姿态差异要大;每次换夹具后重新标定;定期用球杆或标准工件做精度复核——把标准工件放在工作空间边缘和中间各测一次,比较配准出来的位姿差异。

5.3 弧光一出现,点云里全是噪点

现象是焊接起弧瞬间,配准结果跳变,轨迹跟着突变。原因是电弧和飞溅在传感器点云里产生大量离群点,统计滤波处理不了的密集飞溅甚至会把点云局部密度颠倒过来。解决手段是硬性的:视觉拍照和焊接在时间上错开,利用机器人的IO信号在焊接时暂停触发,只保留起弧前的点云做配准。如果工艺要求边焊边跟踪,就要换抗弧光的专用激光视觉传感器,同时在预处理里加上针对弧光干扰的自适应阈值——把亮度值高于某个分位数的点全部剔除,这个分位数根据现场实测标定。

5.4 RL训练不收敛:奖励函数给了网络钻空子的机会

现象是训练几万步之后策略仍然乱抖,有时候甚至出现焊枪原地转圈这种「偷懒」行为。原因基本可以锁定在奖励函数被钻了空子:密集奖励里如果有哪个负项可以被动作绕过,网络就会找到这条捷径。比如只惩罚横向偏差,不惩罚纵向偏差,网络就把焊枪抬高离开工件表面,偏差根本测不到。解决办法是把奖励项补完整:纵向偏差、姿态偏差、末端抖动全都要进奖励;再给动作空间加物理边界,比如末端修正量的变化率上限,从源头上限制高频抖动。

5.5 PyTorch模型部署到控制器时的算子兼容问题

现象是RL策略在工控机上用PyTorch推理正常,但转成ONNX后某些层直接报不支持,或者推理速度反而变慢。原因是Torch的某些高级算子(比如SVD的自动微分变体)在ONNX导出时没有对等实现。点云配准里用torch.linalg.svd的时候,推理阶段要用torch.linalg.svdvals或提前把SVD换成闭式解,绕开自动微分。另一个常见做法是配准部分保持PyTorch原生推理,不导出ONNX,只有RL策略网络导出ONNX。部署时用TensorRT或ONNX Runtime做加速,实测在工控机上能把3层256宽度的Q网络推理压到2毫秒以内,完全满足8到16毫秒的控制周期要求。

6. 验证进阶:焊一条试板之前,先做这三件事

先把配准精度单独验证了,不要带着RL一起测。用激光跟踪仪测出工件上三个基准孔的实际坐标,把配准结果投影到同一个坐标系做对比,三个点的平均距离误差小于0.8毫米才算过关。这个环节用一个简单脚本记录每次配准的RMSE、迭代次数和点云有效比例,连续测20次,看这些指标有没有超出范围的跳变。

再验证轨迹映射的正确性。把同一模板路径映射到三个不同位姿的仿真工件上,检查路径与坡口中心线的最大偏差。仿真过了,上真机空跑,不送丝不送电,用手持千分表抽查路径上的五个关键点。这一步能区分清楚偏差到底来自配准、标定还是机器人本身。

最后才验证RL策略。先在仿真里做焊接跟踪,统计跟踪精度和动作平滑度;再上真机用废弃板试焊,观察焊缝成形的鱼鳞纹是否均匀。这里有一个我从多次试焊里总结出来的习惯:每一轮试焊都记录焊接速度、送丝速度、姿态修正量曲线和焊缝照片,按日期归档。强化学习的训练曲线会被这些现场数据反复验证,没有记录就等于没有调参依据。

回想起来,这条流水线里最容易让人掉以轻心的是坐标系矩阵的顺序,其次是ROI裁剪的尺度,RL的坑反而干净,基本都出在奖励函数设计上。希望这些具体到一个距离阈值、一个矩阵顺序的踩坑记录能帮到你少走几步弯路。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进