简介:面向机器人视觉伺服与机械臂控制方向的工程资源,整合YOLOv5目标检测、MoveIt运动规划与Gazebo物理仿真,解决eye-in-hand构型下基于图像的视觉伺服(IBVS)应用问题。适用于ROS/机器人方向研究者及具备Python与Linux基础的进阶学习者,可作为仿真到实机迁移的参考工程。压缩包共105个文件,体积8.02MB,涵盖20个launch启动配置、19个xml描述、14个yaml参数、13个stl三维模型、8个xacro模型、6个py脚本及rviz可视化配置等,便于按模块调用与二次开发。已有89人学习浏览。通过源码可直接了解YOLOv5识别结果如何映射为机械臂视觉伺服误差,掌握MoveIt规划与Gazebo仿真的衔接流程,以及目标识别—轨迹规划—视觉反馈闭环的搭建思路。
1. YOLOv5、MoveIt 与 Gazebo 的 eye-in-hand 视觉伺服:这套闭环资源到底能省多少事
很多人以为这套项目最难的是 YOLOv5 目标识别,毕竟要训练自己的数据集,调超参数,动辄几十轮 epoch。真正做下来你会发现,模型训练顶多算个热身,最难的是把 YOLOv5 输出的像素坐标、Image-Based 视觉伺服算出来的速度指令、MoveIt 的动作规划、Gazebo 里的物理仿真这四件事串成同一个闭环。任何一个环节的坐标系对不上,整个机械臂就会像喝了假酒一样乱晃。这套 eye-in-hand 视觉伺服资源就是把这个闭环完整跑通的工程包——机械臂末端装相机,YOLOv5 识别目标,算出目标在图像里的位置偏差,IBVS 控制律把偏差转成机械臂末端速度,MoveIt 负责规划并下发关节指令,Gazebo 做物理仿真。适合正在搭视觉伺服实验环境、做毕业设计或想从纯仿真跨到真实机械臂的开发者。
2. Image-Based 视觉伺服的误差链路:从像素偏差到关节角速度
视觉伺服的核心不是“看见目标”,而是“看见偏差”。目标在图像里偏离期望位置多少像素,这个像素偏差怎么变成机械臂该动的速度,是这套系统真正的中间环节。这一章先把这条链路讲清楚,后面两章的代码和参数才有地方落。
2.1 为什么选 Image-Based 而不是 Position-Based
先放一个最容易被误解的点:eye-in-hand 视觉伺服分两派,Image-Based(IBVS)直接在图像平面定义误差,Position-Based(PBVS)则先估计目标的三维位姿再做伺服。选 IBVS 不是因为它在数学上更优雅,而是因为在这套 YOLOv5 + Gazebo 的配置里,PBVS 有一个绕不开的坑:它需要把 YOLOv5 的 2D 检测结果反投影成 3D 坐标,这一步既依赖相机内参的精度,又依赖目标 3D 模型和深度估计。Gazebo 里的仿真相机虽然内外参都写在模型文件里,但一旦换目标、换光照、换摆放角度,PBVS 的三维重建精度马上开始抖动。
IBVS 的误差定义在图像平面,比如目标是茶壶,期望特征是“茶壶中心在图像正中心”,当前特征是“YOLOv5 检测框中心在 (u, v)”,那个 2D 偏差就是误差。这个误差的导数与相机运动速度之间是图像雅可比关系,不需要估计目标在笛卡尔空间里的精确位姿。对仿真环境来说,这意味着少一个黑匣子,多一份可控性。两种方案的取舍可以用下表概括:
| 对比项 | Image-Based (IBVS) | Position-Based (PBVS) |
|---|---|---|
| 误差定义 | 图像平面特征差(像素) | 笛卡尔空间位姿差(米/弧度) |
| 对深度信息的依赖 | 间接依赖,通过增益缩放 | 强依赖,需要准确估计 |
| 抗标定误差能力 | 较强(部分误差被闭环抑制) | 较弱(标定误差直接进反馈) |
| 系统实现复杂度 | 低,导出雅可比即可 | 高,需要 3D 重建或深度相机 |
| 应用场景 | 目标特征清晰、实时要求高的视觉伺服 | 有稳定深度源、需要直接笛卡尔路径 |
我一般会建议:如果目标是硬抓取、路径必须走直线,PBVS 有优势;但这套资源的目标是“把目标稳定控制在图像中心并逼近”,IBVS 的误差链路更短、更容易在仿真里调试。而且在大范围位姿变化下,IBVS 的鲁棒性比 PBVS 好——这句话在仿真里体验不明显,但换到真实相机上非常关键。
2.2 IBVS 控制律与特征误差的映射关系
IBVS 的控制律在教科书里的写法是 v = -λ · L⁺ · e,其中 e 是图像特征误差,L 是图像雅可比(交互矩阵),λ 是伺服增益,L⁺ 是 L 的伪逆,v 是相机坐标系下的速度向量,包含线速度 (vx, vy, vz) 和角速度 (ωx, ωy, ωz)。在 eye-in-hand 配置下,机械臂末端和相机固连,所以相机速度就是末端速度(差一个固定的手眼标定变换)。
这个公式落到代码上并不复杂,真正的难度在于:深度 Z 藏在 L 矩阵的分母里,机械臂靠近目标时,同样的像素误差对应的速度增益会被自动放大,这是 IBVS 在近端容易震荡的根源。另一个难点是雅可比伪逆,特征点数量少于 3 时,L 是行数不足的矩阵,伪逆对噪声非常敏感。实际项目里我会选至少 3 个特征喂控制律,只用 YOLOv5 检测框中心单点时,要用更保守的增益去压。
一个最小可跑的 IBVS 控制律示意代码如下:
import numpy as np def ibvs_controller(feature_current, feature_desired, depth, fx, fy, gain=0.01): """ 计算相机坐标系下的伺服速度指令 参数: feature_current: 当前特征点集, shape=(n,2), 单位像素 feature_desired: 期望特征点集, shape=(n,2), 单位像素 depth: 特征点深度估计值, 单位米 fx, fy: 相机焦距, 单位像素 gain: 伺服增益 lambda, 太大会震荡, 太小收敛慢 返回: velocity: 相机速度向量 [vx, vy, vz, wx, wy, wz] """ n = len(feature_current) if n == 0: return np.zeros(6) # 特征误差按列展开: 每个特征点有两行误差 e = (feature_current - feature_desired).reshape(-1, 1) # 构造图像雅可比 L, 形状为 (2*n, 6) L = np.zeros((2 * n, 6)) for i in range(n): u, v = feature_current[i] z = depth if depth > 0.01 else 0.01 # 深度至少给 1cm, 防除零 L[2*i:2*i+2, :] = np.array([ [-fx/z, 0, u/z, u*v/fx, -(fx + u*u/fx), v], [ 0, -fy/z, v/z, fy + v*v/fy, -u*v/fx, -u] ]) # 伪逆 + 比例控制, 负号是让机械臂朝误差减小的方向运动 L_pinv = np.linalg.pinv(L) velocity = -gain * L_pinv.dot(e) return velocity.flatten()逻辑说明:这个函数做了三步,先把“当前特征点减去期望特征点”得到误差向量,再用交互矩阵把像素误差映射到相机速度的含义,最后用伪逆求最小范数速度。每一步都有具体度量单位,u、v 是像素,fx、fy 是像素焦距,depth 是米,所以输出的 velocity 混合了米每秒和弧度每秒两种单位,MoveIt 那边接收时要注意单位区分。
参数说明:fx、fy 从相机内参拿,Gazebo 仿真相机模型里直接写在 sensor 配置里;depth 是最脆弱的参数,在纯 IBVS 里可以给一个固定常数(目标放在桌上、相机高度不变时这么做没问题),但想让靠近目标时更稳,就得用第 3 章里 YOLOv5 检测框面积去估一个实时缩放趋势。gain 是最值得调的参数,我一般从 0.003 起步,每次乘以 3 倍观察是否出现“折返振荡”,一旦出现就退回上一档。代码里的交互矩阵右上角是 u*v/fx,右下角是 -(fx + u²/fx),这是标准形式,别改错符号。
3. YOLOv5 目标检测改造成伺服感知前端:中心点、面积与置信度处理
YOLOv5 本身是通用的检测模型,直接搬到视觉伺服里会出现两个典型问题:一是它返回的是检测框而不是伺服特征,需要一层“特征提取”逻辑;二是它的推理延迟高、置信度波动大,直接当误差源来用会导致机械臂乱颤。这一章做两件事:把检测结果转成伺服能用的特征,再讲清楚模型和超参数在伺服场景下的取舍。
3.1 从检测框到伺服特征:YOLOv5 后处理函数的写法
视觉伺服用检测框,一般不直接要四个角点,而是从框里提取两个量:中心点坐标 (u, v) 和框面积 Area。中心点进 IBVS 误差,面积用来做深度方向的粗估计——目标越近,面积越大。YOLOv5 的原生推理输出是 xyxy 格式的框,坐标是归一化后的 [x1, y1, x2, y2] 乘上图像宽高得到绝对值。
def detection_to_servo_features(pred, img_w, img_h, target_class=0, conf_thres=0.3): """ 从 YOLOv5 推理结果提取伺服特征 参数: pred: YOLOv5 输出的检测结果, shape=(N,6), 列依次是 x1, y1, x2, y2, confidence, class_id (像素坐标, 已缩放到图像尺寸) img_w, img_h: 图像宽高, 单位像素 target_class: 要伺服的类别 id conf_thres: 置信度阈值, 过滤低置信度框 返回: center_u, center_v: 检测框中心像素坐标 area_norm: 检测框面积占图像总面积的比例, 用于深度趋近判断 ok: 是否存在有效目标 """ if pred is None or len(pred) == 0: return 0.0, 0.0, 0.0, False # 过滤类别与置信度, 避开误检框 mask = (pred[:, 5] == target_class) & (pred[:, 4] >= conf_thres) pred = pred[mask] if len(pred) == 0: return 0.0, 0.0, 0.0, False # 多个目标时优先选置信度最高的框作为伺服锚点 best = pred[pred[:, 4].argmax()] x1, y1, x2, y2 = best[:4] center_u = (x1 + x2) / 2.0 center_v = (y1 + y2) / 2.0 area_norm = (x2 - x1) * (y2 - y1) / (img_w * img_h) return center_u, center_v, area_norm, True逻辑说明:第一步先做类别和置信度掩码,避免把不相干的目标或闪过的误检框当成伺服对象;第二步在多目标场景下取置信度最高的框。area_norm是归一化面积,取值在 0 到 1 之间,用它做的深度判断不受输入分辨率影响,比原始像素面积更通用。
一个容易被忽视的细节:YOLOv5 的置信度在遮挡、运动模糊时会掉得很厉害。伺服场景下目标在画面边缘时置信度往往低于 0.5,如果阈值设成 0.5,目标还没居中就先丢了。我的习惯是把置信度阈值放到 0.25 到 0.3,但代价是误检变多,所以后面要么加一个“连续 N 帧有效才更新目标”的滤波,要么在 Gazebo 场景里把干扰物体清干净。
3.2 模型选择与超参数:伺服场景下 YOLOv5 的工程取舍
讲到 YOLOv5 超参数,大部分教程都在聊训练阶段的 lr、momentum、anchor 缩放,但视觉伺服里真正影响闭环质量的超参数在推理侧:imgsz、conf_thres、iou_thres,以及是否开启半精度推理。伺服闭环是实时系统,推理延迟直接进反馈路径。YOLOv5 的模型从轻到重有 n/s/m/l/x 五档,在 Gazebo 仿真里 CPU 推理 m 以上容易出现一帧 200ms 以上的延迟,机械臂的控制周期早就过完了。仿真项目一般推荐 yolov5s,分辨率设 640,显存占用小、速度足够。
import torch # 加载模型时直接用半精度, CUDA 上显存占用减半, 推理速度提升明显 model = torch.hub.load('ultralytics/yolov5', 'yolov5s', pretrained=True) model.half() model.conf = 0.3 # 伺服场景常用的置信度阈值, 太高目标未居中就丢帧 model.iou = 0.45 # NMS 的 IoU 阈值, 目标重叠明显时拉低到 0.4 model.max_det = 1 # 只保留置信度最高的一个检测框, 省去多目标管理 # 推理时将输入强制缩放到 640, 与训练分辨率保持一致 results = model(frame, size=640)逻辑说明:model.half()把权重切到 FP16,在显存受限场景下是性价比最高的提速手段;max_det = 1对视觉伺服有额外价值——伺服只需要一个锚定目标,多余检测不仅浪费算力,还会让多目标切换和目标跳变问题同时冒出来;size=640与训练分辨率一致,输入缩放不一致会拉低 mAP。
参数说明:conf 阈值是误检和漏检的旋钮,伺服场景建议 0.25 到 0.35,太高目标没居中就丢帧,太低会在目标附近抖动;iou 阈值只影响 NMS 合并框的数量,默认 0.45 够用,场景里物体重叠多就降;max_det 设成 1 是单目标视觉伺服省心的关键。如果你的控制周期要求 50ms 以内,还可以配合 3.1 节的后处理把推理丢到独立线程,主线程只消费最新一帧结果。
到这一步,感知前端已经能把 YOLOv5 的检测框转换成伺服特征和控制输入。但要让机械臂真的按 IBVS 命令运动,还有一半工程问题在机械臂侧——MoveIt 和 Gazebo 的同步、手眼标定、控制器选型。下一章就是这条链路的下半段。
4. MoveIt + Gazebo 闭环搭建:手眼标定、控制器同步与仿真参数
视觉伺服闭环的最后一步是把 IBVS 输出的速度指令送到机械臂,由 MoveIt 规划出关节轨迹,再由 Gazebo 里的物理仿真模型执行。这一步拆开看每个工具都能跑,合在一起就让无数人翻车:rviz 里规划好好的,Gazebo 里机械臂不动;机械臂末端动了,但相机算出来的误差反而变大。这一章按“先把坐标系立住,再把控制器对上”的顺序讲。
4.1 坐标系与手眼标定:把相机固定在机械臂末端的第一步
eye-in-hand 系统的坐标系链是固定的:world → base_link → … → ee_link → camera_link。YOLOv5 识别出来的目标点是在 camera_link 坐标系下的图像平面坐标,而 IBVS 输出的速度指令是在相机坐标系下定义的;想让机械臂执行这个速度,就得把相机坐标系的指令先变到末端坐标系,再通过机器人运动学变到基座坐标系。中间的桥梁就是 camera_link 到 ee_link 的静态变换。
在 Gazebo 里做这个变换有两个位置可以写:一种是写在 URDF 里,把相机模型直接挂到末端连杆下面;另一种是用static_transform_publisher在 launch 时发布手眼外参。URDF 方式适合相机相对末端固定不动的场景,而这恰好是 eye-in-hand 的标准假设。
<!-- 在 URDF 的 ee_link 下挂相机, 假设相机光轴与末端 Z 轴有 0.02m 的偏移 --> <link name="camera_link"> <visual> <origin xyz="0 0 0.02" rpy="0 0 0"/> <geometry> <mesh filename="package://robot_description/meshes/camera.dae" scale="0.01 0.01 0.01"/> </geometry> </visual> <inertial> <mass value="0.05"/> <inertia ixx="0.0001" iyy="0.0001" izz="0.0001" ixy="0" ixz="0" iyz="0"/> </inertial> </link> <joint name="ee_camera_joint" type="fixed"> <parent link="ee_link"/> <child link="camera_link"/> <origin xyz="0 0 0.02" rpy="0 0 0"/> </joint>逻辑说明:ee_camera_joint是固定关节,位置和姿态都写在<origin>里。在这个例子里相机放在末端坐标系沿 Z 轴偏移 0.02m 处,没有旋转。实际 Gazebo 模型里,相机通常还要配一个<sensor>标签定义内参、图像分辨率和噪声模型。
参数说明:xyz的偏移量必须和你 Gazebo 里相机模型的视觉位置一致,否则视觉伺服会引入一个恒定的手眼误差;rpy如果填了非零值,要特别注意 IBVS 的速度指令在相机坐标系和末端坐标系之间的旋转变换,旋转矩阵差一个符号,机械臂就会朝着完全相反的方向运动,这是第 5 章避坑的第一条。
4.2 三层控制结构与仿真参数:MoveIt 与 Gazebo 不同步的根本解法
MoveIt 和 Gazebo 不同步,绝大多数情况是控制器的连接出了问题。MoveIt 规划出来的轨迹最终靠 controller 发给 Gazebo 的 joint 指令执行。ROS 生态里,MoveIt 默认调度的是JointTrajectoryController,而 Gazebo 里机械臂仿真往往只配了一个JointPositionController或JointEffortController,两边话题对不上,轨迹就发不出去,表现为“规划成功但机械臂不动”。
在这套视觉伺服资源里,我建议按三层结构来搭:
第一层是视觉伺服层,用第 2 章的 IBVS 算出一个速度指令;第二层是 MoveIt 层,把速度指令转成机械臂末端位姿目标或笛卡尔速度目标,交给 MoveIt 的规划接口;第三层是 Gazebo 物理层,接收关节位置/速度指令,实际驱动模型运动。中间任意一层的话题没接对,伺服都会断。panda 机械臂常见配置的 controller 如下:
arm_controller: type: position_controllers/JointTrajectoryController joints: - panda_joint1 - panda_joint2 - panda_joint3 - panda_joint4 - panda_joint5 - panda_joint6 - panda_joint7 state_publish_rate: 100 action_monitor_rate: 30 constraints: goal_time: 0.5 stopped_velocity_tolerance: 0.02# 一个完整闭环的最小启动流程 # 1. 启动 Gazebo 仿真环境与机械臂 URDF roslaunch robot_gazebo gazebo_panda.launch # 2. 启动 MoveIt 运动规划 roslaunch panda_moveit_config move_group.launch # 3. 启动视觉伺服节点, 内部完成 YOLOv5 推理 + IBVS 控制律 roslaunch visual_servoing ibvs_node.launch逻辑说明:JointTrajectoryController接收 MoveIt 的 trajectory action,驱动 Gazebo 关节。要注意action_monitor_rate: 30是控制节奏,如果希望响应更实时,可以把它提到 50 到 100;stopped_velocity_tolerance是判定轨迹执行结束的阈值,设太大会提前结束轨迹,太小会一直卡在执行中状态。
参数说明:state_publish_rate影响关节状态反馈的实时性,Gazebo 仿真里设 100Hz 足够,太高 CPU 占用大幅上升;关节名字一定要和 URDF 里的实际名称完全一致,多一个空格都会匹配失败。如果视觉伺服节点直接发速度指令到 MoveIt,建议把 MoveIt 那一层调成笛卡尔速度控制,规划周期 20 到 50ms 才能跟得上 IBVS 的更新频率。
5. 常见问题与避坑:手眼标定、同步漂移、伺服震荡的五个坑
这一章写的是我在复现这套 eye-in-hand 视觉伺服项目时遇到过的五个具体坑。每个坑都按“现象 → 原因 → 解决”来写,你可以直接对照自己卡住的那个位置。
5.1 tf 树缺了末端的 static_transform:伺服方向反了
现象:Gazebo 里目标明明在图像左边,机械臂末端却往右偏;有时候整个机械臂绕着一个点疯狂转圈,看起来完全没有伺服逻辑。
原因:camera_link没有挂到ee_link下面,MoveIt 和 Gazebo 拿到的相机位置不一致。常见做法是只加载了机械臂的 URDF,但 launch 文件里忘了发布camera_link到ee_link的静态变换,或者发布了但 rpy 的符号填反了。IBVS 的速度指令在相机坐标系下定义,如果相机坐标系和末端坐标系的相对旋转是错的,反馈回路直接变成正反馈。
解决:启动后先看 tf 树,确认ee_link → camera_link的变换数值与 URDF 里一致。随后用rosrun tf tf_echo camera_link ee_link看输出,如果旋转矩阵与预期差 90 度,把 URDF 里相机的 rpy 改成对应姿态再验证。一个取巧的做法是在 Gazebo 可视化界面里临时放一个目标在相机正前方,如果目标在画面正中心,说明坐标系没歪。
5.2 MoveIt 规划正常但 Gazebo 执行卡死:控制器类型不匹配
现象:rviz 里点 Plan 能看到规划的轨迹,但点 Execute,Gazebo 里的机械臂纹丝不动,控制台没有明显的 error,只是轨迹一直停在执行中状态。
原因:MoveIt 的连接接口默认走 action,期望的是FollowJointTrajectory类型的 controller。而 Gazebo 模型里常常只配了position_controllers/JointPositionController,它只能接收 topic 形式的 joint 指令。规划发不进去,Gazebo 自然不动。
解决:在 launch 文件里加载真正支持 trajectory 的 controller,把上面 4.2 节里的position_controllers/JointTrajectoryController配上,确认 controller 名字和 move_group 配置文件里引用的名称对得上。我踩坑那次最后发现问题在 controller 名字尾部多了一个不可见字符,删掉重写就通了。
5.3 IBVS 增益 λ 过大:目标附近剧烈震荡
现象:目标离图像中心较远时机械臂快速靠近,挺好;但一旦靠近,机械臂就在目标附近来回抖,图像里能看到检测框左右乱跳。
原因:深度 Z 变小后,同样的像素误差映射出的速度增量被放大——交互矩阵里 -fx/Z 这一项在 Z 变小时绝对值变大。固定增益会让系统在近端变成高增益闭环,进而震荡。
解决:把增益从固定值改成自适应。最简单版本是让 λ 随归一化检测框面积递减:lambda_eff = lambda_base / (1 + k * (area_norm / area_ref))。更稳一点的做法是给伺服速度加一个饱和限幅,比如线速度上限 0.05 m/s,角速度上限 0.2 rad/s,先限幅再输出。
5.4 推理延迟导致画面跳变:YOLOv5 伺服侧延迟优化
现象:机械臂运动过程中图像明显卡顿,目标中心点做台阶式跳跃,伺服误差不是连续曲线,机械臂走走停停。
原因:推理是在主线程里同步做的,上一帧还没推理完,这一帧就错过了,控制周期变成随时变长的不可控值。CPU 推理 640 分辨率下通常 80 到 200ms,远高于机械臂控制周期 20ms。
解决:把 YOLOv5 推理丢到单独的线程,用队列缓存最近一帧结果,IBVS 控制律只消费最新结果;同时开启model.half(),如果设备支持 TensorRT,可以再做一次加速。还有一个折中方案是imgsz降到 320,检测精度掉一点但推理延迟能压到 40ms 左右,目标在图像里比较小的话不太推荐,容易漏检。
5.5 Gazebo 光照与材质差异:仿真里漏检率上升
现象:同一个模型的权重,在真实图像上检测很好,Gazebo 渲染环境下漏检一堆;把目标换一个材质颜色,置信度瞬间跌破 0.3。
原因:Gazebo 的光照模型、阴影、反射和真实相机采集的图像分布差异很大,YOLOv5 在训练数据里没见过的渲染风格会导致特征响应下降。常见做法是闭着眼调置信度阈值,其实方向反了,根子在域差异。
解决:在 Gazebo 里给视觉传感器加一个 noise 参数,模拟真实相机的镜头噪声;再把目标物体的材质颜色做一些扰动,用三种不同纹理跑一轮仿真数据采集,把这些仿真图像按 20% 的比例混入原训练集重新微调 30 轮。我在这个项目里用这个办法把漏检率压到了可接受范围。
6. 让伺服结果可量化:四个验证技巧与我的固定检查习惯
闭环搭建好之后,最怕的不是“跑不动”,而是“跑起来了但不知道跑得好不好”。伺服系统的结果是运动行为,不用数据验证,很容易把一段本来就摇摆的运动误判成“能抓到”。这一章给你四个我自己常用的验证技巧,最后收在一个执行习惯上。
技巧一:在 IBVS 节点里把特征误差范数记录下来,打印成一行 CSV,再做滚动窗口画图。合格的系统,误差范数应该大致指数衰减,而不是上下震荡。我一般会在 5 秒内看误差范数是否落到初始值的 10% 以下。
技巧二:把 YOLOv5 检测框的归一化面积变化率当成第二个指标。视觉伺服成功后,面积应当单调增长或保持平稳;如果面积剧烈抖动,说明机械臂在来回接近和后退,这时候回查增益。
技巧三:在 Gazebo 里加一个障碍物,看 MoveIt 规划是否能在伺服趋近的过程中自动避开。这个验证放在最后做,因为它可能暴露手眼标定的隐藏问题——避障路径一变,原来没显现的坐标系偏差会被放大。
技巧四:固定起始位置后,连续跑 20 次伺服收敛测试,记录每次收敛时间、末态像素误差、是否出现瞬时失控。20 次里至少 17 次收敛我才会认为这套参数可用。
从单目标扩展到多目标,最稳的做法不是改伺服逻辑,而是改目标管理——维护一个优先级队列,让 IBVS 只对队列头部目标输出速度。每帧更新队列的置信度和面积,如果一个目标连续丢失超过 3 帧再切换到下一个,这个习惯在目标遮挡频繁的场景里特别管用。
如果后续想从 eye-in-hand 换到 eye-to-hand,也就是相机固定在外部,IBVS 的误差定义和控制律不变,变的只有两件事:速度指令从相机坐标系到机械臂基座坐标系的变换需要重算;手眼标定从固定关节下的静态变换变成外部标定。别小看这个变换,我在切换时曾经因为少乘了一次旋转矩阵,导致机械臂往目标的反方向狂奔。后来强制自己每换一次配置,就先用零输入测一遍系统是否保持静止,确认机械臂不自主漂移再放开闭环。从那以后我每次调视觉伺服项目,都会强制走一遍同样的流程:先核对坐标系变换,再检查 controller 匹配度,然后把增益从小往大调,最后才放开闭环做 20 次收敛统计。这套流程帮我挡掉了大部分低级翻车,希望也能帮到你。
本文还有配套的精品资源,点击获取