做机器人系统集成的,大概率都经历过这种场面:视觉引导抓取的 Demo 在实验室跑得好好的,搬到产线第一天就开始掉件,重复定位差个两三毫米,工件换一批料就识别不到。现场第一反应通常是"标定没做好",重新标一遍,标完还是那样。我在几个类似的项目里踩过这个循环,最后发现问题往往不在标定本身,而在整条视觉链路的某一环从来没被单独拆开验证过——相机分辨率够不够、镜头畸变校没校、光照会不会随班次漂移、视觉结果送到运动控制时时间戳对不对得上。机器视觉这四个字听着像是给机器人系统装一双眼睛,实际做下来更像是在搭一条从光子到电机指令的完整链路,任何一环松动,整条链路都会把误差放大到肉眼可见的程度。这篇内容主要面向已经上手或即将上手机器人视觉项目的工程师,也适合做上位机、PLC 联动的朋友参考,讲的是选型、标定、坐标变换、时间同步和现场排查这几件事里真正决定成败的细节。
1. 机器视觉在机器人系统里到底解决什么问题
1.1 从"给机械臂装眼睛"这个误解说起
很多人(包括刚入行时的我)对机器视觉的理解停留在一个很朴素的层面:装个相机,拍一张照,找到工件位置,把坐标发给机器人,让它去抓。这个描述在逻辑上没错,但它把一个连续的物理过程压缩成了一个瞬时事件,而恰恰是那些被压缩掉的中间过程,构成了现场 90% 的故障来源。
机器人系统里引入视觉,本质上是用一个外部传感器补齐机器人自身的两个短板:一是绝对定位能力,二是环境感知能力。工业机械臂的重复定位精度通常能到 0.02mm 这个量级,但它的绝对精度往往差一个数量级,也就是说你让它去一个"理论上"的坐标,它到了那个点,但那个点和工件实际位置之间可能差 1mm。工件在料框里摆放的位置又有随机性,靠示教点硬编码根本不可能。所以视觉的第一价值不是"看见",是建立工件坐标系与机器人基坐标系之间的可靠映射。
第二个短板是环境感知。冲压上下料、拆垛码垛、无序分拣这些场景里,工件姿态是未知的,机器人需要知道的不只是 XYZ,还有绕三个轴的旋转量,也就是完整的 6D 位姿。这时候视觉系统输出的就不该是一个点,而是一个变换矩阵。
理解了这个定位,你就明白为什么很多视觉项目做不下去:团队把精力全放在"识别率"上,训练模型调到 99%,结果抓取成功率只有 70%,因为剩下的误差来自坐标映射,跟识别算法一点关系都没有。
1.2 视觉任务的三层拆分:定位、识别、测量
机器人视觉的任务可以拆成三层,每一层对硬件和算法的要求完全不同,混着谈最容易出问题。
定位(Locate):回答"工件在哪、朝向如何"。这是机器人视觉的刚需,输出通常是 2D 位姿(x, y, θ)或 3D 位姿(x, y, z, rx, ry, rz)。平面抓取只需要 2D,配合固定的抓取高度就够了;立体抓取、装配、打磨才需要 3D。定位任务的核心指标是重复性,不是精确度——同样的工件在不同时间拍照,结果的一致程度决定了机器人能不能稳定抓到位。
识别(Identify):回答"这是什么"。多品种共线、混料、缺陷分类这些场景才需要。识别通常和定位绑在一起做,先分类再定位,或者用同一个模型输出类别和位置。
测量(Measure):回答"尺寸/位置是否符合要求"。这是精度要求最高的一层,往往需要专门的标定手段(比如远心镜头加背光),并且对温度、振动、镜头畸变都极度敏感。机器人系统里做在线测量的项目,我一般会建议先确认清楚公差要求,如果公差小于 0.1mm,就要认真评估是不是该用专用量具而不是机器人视觉。
把这三个任务分清之后,选型就有依据了:定位任务看重帧率和重复性,测量任务看重分辨率和畸变控制,识别任务看重样本和鲁棒性。很多项目失败是因为用做识别的方案去干测量的活,或者用测量的成本去解决定位的问题。
1.3 什么场景其实不该上视觉
这句话可能有点反直觉,但在实际项目里我确实劝退过好几次视觉方案。以下这几种情况,视觉未必是最优解:
- 工件位置完全固定且品种单一。这种情况下用机械定位工装(V 型块、定位销、气动夹具)的成本可能只有视觉方案的十分之一,而且更稳定、更好维护。视觉的价值在于应对不确定性,没有不确定性就不需要它。
- 精度要求进入微米级且是绝对测量。机器人在这个精度段本身就吃力,热变形、反向间隙、刚性变形都会引入误差,视觉再准也没用,不如用专用测量设备。
- 节拍极紧(比如 200ms 以内一次抓取)且环境振动大。视觉处理时间加大曝光时间再加机器人响应,很容易超节拍,强行压节拍就要牺牲图像质量,最后变成两边都不达标。
- 现场没有条件做光照控制。视觉的稳定性很大程度上取决于光照,如果现场粉尘大、有强环境光直射、或者工件反光不可控,识别结果就会剧烈抖动。这种场景要么改工装遮挡环境光,要么就别勉强上视觉。
判断标准其实很简单:先假设没有视觉,用纯机械方案能不能解决?如果能,先做机械方案;如果不能,再评估视觉需要承担多大的不确定性,以及现场是否具备支撑这种不确定性的条件。
2. 视觉硬件选型的取舍逻辑
2.1 相机:全局快门与卷帘快门的差别在机器人场景会被放大
相机选型的第一道坎不是分辨率,是快门类型。卷帘快门逐行曝光,成本低、噪声小,静态拍摄完全够用;全局快门整帧同时曝光,成本高一些,但在机器人场景里几乎是必须的。
原因在于运动。机器人视觉的相机要么装在机器人末端(随机器人运动),要么固定在工作台上看着运动的工件。无论哪种,拍摄瞬间相对运动都存在。卷帘快门的行间曝光时间差会造成"果冻效应",一条直线在图上被拍成斜线,位姿求解出来的角度直接偏掉。我见过一个案例,相机随机械臂移动过程中拍照,用的是卷帘快门,图上标定板的方格子明显是梯形,标定误差怎么都降不下来,换成全局快门后一次就过了。
除了快门类型,还要看几个参数在机器人场景下的实际影响:
| 参数 | 机器人场景的关注点 | 常见误判 |
|---|---|---|
| 分辨率 | 决定像素精度,需按 FOV 反算 | 只看总像素数,忽略靶面尺寸导致镜头配不上 |
| 靶面尺寸 | 决定镜头选型,1/1.8" 和 2/3" 差很多 | 买了大靶面相机的机身,结果镜头像场覆盖不够,四角发黑 |
| 帧率 | 决定最大检测节拍 | 标称帧率是满分辨率下的,开 ROI 才能提速 |
| 曝光时间下限 | 决定运动模糊 | 实际可用曝光受光源亮度限制,标称值参考意义有限 |
| 接口 | 影响带宽和线缆长度 | USB3 线超过 3 米容易掉线,GigE 需要独立网卡和巨帧配置 |
分辨率的反算方法是这样:先确定视场范围 FOV(比如 300mm × 250mm),再确定要求的位置精度(比如 0.1mm),然后考虑亚像素定位能力。一个经验值是亚像素算法能达到 1/4 到 1/10 像素的定位精度,保守起见按 1/4 算,那么所需像素精度是 0.4mm,横向像素数 = 300 / 0.4 = 750。这个数字看起来很低,但别忘了这是理论下限,实际还要考虑视野内可能有多个工件、需要留余量、需要考虑镜头边缘畸变——边缘的畸变区域定位精度会明显下降。我的习惯是再留 3 到 5 倍余量,也就是横向 2500 到 4000 像素,对应的就是 500 万到 1200 万像素的相机。
2.2 镜头与工作距离的换算
镜头选型最常见的错误是靠感觉配,配完发现工作距离装不下,或者视场覆盖不了。其实有公式可以算:
f = (靶面尺寸 × 工作距离) / FOV举个例子,相机靶面是 1/1.8"(约 8.8mm × 6.6mm),要求工作距离 500mm,视场 400mm 宽,那么水平方向焦距 f = 8.8 × 500 / 400 = 11mm。选 12mm 的定焦镜头,实际视场会是 8.8 × 500 / 12 = 366mm,比要求的窄一点。这时候可以二选一:换 8mm 镜头(视场 550mm,但要考虑畸变),或者接受 366mm 的视场,把工作距离缩到 410mm 左右。
除了焦距,还要看两个容易被忽略的参数。
畸变。短焦镜头的桶形畸变可以到 3% 以上,长焦镜头好一些。做定位任务时畸变可以由软件校正(OpenCV 的undistort流程),但校正会引入插值误差,而且校正后的像素精度会降低。做测量任务时,1% 以上的畸变就很难接受了,宁可上远心镜头。
远心镜头值不值得上。远心镜头的核心特性是没有透视误差——工件离镜头远近不影响成像尺寸。这在测量场景是刚需,在定位场景其实也有用:如果工件有厚度,普通镜头的透视会让顶面和底面的位置错开,抓取点就会偏。远心镜头的代价是体积大、工作距离固定、价格高,而且视场通常不大。选型时可以这样判断:如果工件厚度超过视野宽度的 1/20,或者抓取精度要求优于 0.2mm,就认真考虑远心。
2.3 光源往往比相机更决定成败
这条经验是我踩过最深的坑之后总结的:光源选错了,再贵的相机也救不回来;光源选对了,便宜的相机也能稳定工作。视觉的稳定性本质上是"目标与背景的对比度是否稳定",而这几乎完全由光源决定。
常见光源和适用场景:
- 环形光:通用性最好,适合大多数平面工件的表面特征照明,安装方便,缺点是反光面容易出现中心亮斑。
- 条形光:可以大角度斜射,突出表面纹理和划痕,也常用来做背光时的大面积均匀照明。
- 背光(平行背光):从工件背面打光,工件轮廓变成剪影,对比度极高,是做轮廓定位和尺寸测量的最佳选择。缺点是只能得到轮廓信息,表面特征全丢。
- 同轴光:光线与镜头同轴,适合镜面、金属加工面这些反光强烈的工件,能把表面刻印、微小划痕清晰地打出来。
- 穹顶光(漫射光):内壁漫反射形成一个均匀的光罩,适合曲面、球面、强反光件的整体均匀照明,几乎是消除高光斑的唯一有效手段。
选光源的逻辑是先问"我关心工件的什么特征"。如果关心轮廓,用背光;关心表面字符,用同轴或低角度环形光;关心反光曲面的均匀性,用穹顶光。定了类型之后,再考虑波长——单色光(比如红色 630nm、蓝色 470nm)能提高特定颜色工件的对比度,红外光能穿透某些表面涂层,这些都属于调优手段,可以在主方案定了之后再试。
还有一个几乎所有人都会忽略的点:光源的频闪同步。要做短曝光高速拍摄,就必须用频闪光源在曝光窗口内瞬间拉满亮度。这时候控制器要能接收相机的触发输出,并且延时抖动要小。我见过用普通恒亮光源硬压曝光的项目,曝光压到 500μs 图像全黑,最后只能降节拍。频闪控制器的成本远低于升级相机和镜头,但它解决的是同一类问题。
3. 手眼标定:机器人视觉项目里最容易翻车的一环
3.1 眼在手上与眼在手外的坐标系差异
手眼标定是所有机器人视觉项目绕不开的一步,它要解决的问题是:相机看到的位置,怎么换算成机器人能用的位置。两种安装方式的数学形式完全不同,搞混了后面全乱。
眼在手上(eye-in-hand):相机固定在机器人末端法兰或腕部,随机器人一起动。标定要解的是"相机坐标系到末端法兰坐标系"的固定变换。特点是相机视野会跟着机器人走,适合大范围作业,但每换一个拍照位置都要重新算,而且相机线缆要走拖链,长期弯折容易出问题。
眼在手外(eye-to-hand):相机固定在工作台或支架上,机器人动相机不动。标定要解的是"相机坐标系到机器人基坐标系"的固定变换。特点是标定一次长期有效,视野固定,适合工件位置相对集中的场景,缺点是有遮挡和视野盲区。
两种方式的标定方程形式不同。eye-in-hand 的经典形式是 AX = XB,eye-to-hand 是 AX = ZB,其中 A 是机器人末端的相邻位姿变换,B 是相机观测到的标定板相邻位姿变换,X 或 Z 是待求的手眼矩阵。OpenCV 的calibrateHandEye函数直接支持这两种输入结构,内部实现了 Tsai-Lenz、Park、Horaud、Daniilidis、Andreff 几种解法,选哪种在实际项目里差别不大,关键在数据质量。
用 OpenCV 的时候注意一个坑:eye-to-hand 场景下传入的机器人位姿需要做逆变换处理。官方文档给的输入约定是R_gripper2base(末端到基座),而 eye-to-hand 的方程推导里用的是相反方向。我见过不少人在这一步直接把位姿塞进去,标定出来误差大得离谱,还以为是算法问题。稳妥的做法是自己先写一个小验证:用标定结果把标定板上某个已知点从相机系变换到基座系,和机器人正运动学算出来的位置对比,看差多少。
3.2 标定流程与样本采集的实操细节
标定算法本身是成熟的,翻车基本都翻在样本采集上。下面是我现在固定用的流程,可以直接抄。
第一步:准备标定板。棋盘格或圆点阵列都行,棋盘格更常用。尺寸选择的原则是让标定板在图像中占比不低于 1/3,太小的话角点检测误差会被放大。标定板本身要保证平整度,亚克力或陶瓷基板都行,别用打印纸贴在铝板上——纸的翘曲在 0.1mm 量级,足以毁掉精度。
第二步:确定采集位姿。至少 3 个位姿在数学上就能解,但实际至少要 15 到 20 组。位姿的多样性比数量更重要,要求是:
- 绕不同轴都有明显旋转,单轴旋转角度建议覆盖 ±30° 以上;
- 标定板在图像中的位置要覆盖视野的四个角和中心;
- 标定板相对相机的距离要有变化,覆盖工作距离范围;
- 避免所有位姿都集中在某个小区域内,那会导致解出的矩阵在某些方向上约束不足。
第三步:采集与记录。每个位姿下,机器人停稳(等余振衰减),同时记录两件事:机器人当前的末端位姿(从控制器读,六个数或者齐次矩阵),和相机拍到的标定板图像。两个数据必须严格对应同一个位姿,所以要么在同一个程序循环里一次做完,要么加严格的同步标记。我习惯是在机器人程序里发一个 IO 信号,视觉侧收到信号后拍照,同时上位机从机器人读位姿,用同一个信号做触发源。
第四步:求解与验证。求解完必须做验证,不能看函数返回了就直接用。验证方法是:用标定结果反算每个采集位姿下标定板原点在机器人基座系下的坐标,和机器人正运动学算出来的坐标做差,看残差的均值和最大值。如果最大残差超过你的精度要求的 2 倍,就回去检查数据。
import cv2 import numpy as np # eye-in-hand: 末端到基座、标定板到相机 R_g2b, t_g2b = [], [] # 机器人末端在基座系下的旋转/平移 R_t2c, t_t2c = [], [] # 标定板在相机系下的旋转/平移 for pose, img in samples: R, t = pose_to_matrix(pose) # 末端位姿转旋转矩阵和平移 R_g2b.append(R); t_g2b.append(t) ok, corners = cv2.findChessboardCorners(img, (9, 6), None) gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) corners = cv2.cornerSubPix(gray, corners, (11, 11), (-1, -1), (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)) ok, rvec, tvec = cv2.solvePnP(objp, corners, K, dist) Rc, _ = cv2.Rodrigues(rvec) R_t2c.append(Rc); t_t2c.append(tvec.reshape(3, 1)) R_c2g, t_c2g = cv2.calibrateHandEye( R_g2b, t_g2b, R_t2c, t_t2c, method=cv2.CALIB_HAND_EYE_PARK)这段代码里K和dist是相机的内参和畸变系数,必须先单独标定好,内参不准的话外参一定不准。内参标定用同样的棋盘格,采集 20 张左右不同位姿的图像,用calibrateCamera求,重点看重投影误差,一般要压到 0.2 像素以内。
3.3 标定误差的验证方法
标定完必须验证,而且要分层验证,不能只做一个整体误差。
分层验证的第一步,验证内参。用calibrateCamera返回的总重投影误差,加上逐张图像的重投影误差。如果某几张图的误差明显高于其他,说明那几张的角点检测有问题(可能是模糊、过曝、或者标定板边缘被裁掉),应该剔除重拍。
第二步,验证手眼矩阵。用一个独立的验证集:重新采 5 到 10 个位姿(不参与求解),用标定结果反算,看残差。这一步能抓出"求解用了过拟合数据"的问题。
第三步,验证实际抓取。拿一个实际工件,让机器人按视觉结果去抓,看抓完之后工件相对夹爪的偏移量。这是最终验收标准,也是最容易被跳过的一步。有个细节要注意:验证抓取时要用和实际生产相同的夹爪、相同的抓取高度、相同的工件,换成别的工件或者换个夹持深度,误差会变,因为你验证的其实是整条链路的综合精度。
一个常见的误区是把残差当成精度。残差反映的是标定模型的拟合程度,它小不代表绝对精度高——如果所有采集位姿都集中在一个小区域,模型在那个区域拟合得很好,但换到视野边缘就会偏。所以验证集一定要覆盖实际工作区域。
4. 视觉结果怎么喂给运动控制
4.1 坐标变换链路的完整推导
视觉算法输出的通常是一个相对相机的位姿,机器人需要的是相对基座的位姿。中间的变换链路必须写清楚,一步错了后面全错。以 eye-in-hand 为例:
T_base_target = T_base_flange × T_flange_cam × T_cam_targetT_base_flange:机器人正运动学,从控制器实时读;T_flange_cam:手眼标定结果,固定值;T_cam_target:视觉算法输出的目标位姿。
三个矩阵依次相乘,得到的T_base_target就是机器人要去的位姿。如果夹爪有工具坐标系(TCP),还要再右乘或左乘一个工具偏移,具体方向取决于你的定义,这一点最容易搞反。
我建议在代码里用一个统一的 4×4 齐次变换类来管理所有位姿,禁止手写矩阵乘法,也禁止把欧拉角、四元数、旋转矩阵混着用。旋转表示法混乱是坐标变换 bug 的头号来源。统一用旋转矩阵存,输入输出时才转换,能省掉大量的调试时间。
还有一个高频错误:抓取点和识别点的分离。视觉识别出来的往往是工件的某个特征点(比如中心孔、角点),而机器人实际要去的抓取点可能在工件表面上另一个位置。这两个点之间有一个固定的工件坐标系变换,必须显式建模,不能靠现场示教一个偏移量糊过去——换个工件型号就失效了。
4.2 时间戳与延迟补偿
这是机器人视觉里最隐蔽的一类误差,而且它看起来不像是误差,像是"偶发的抓偏"。
问题的本质是:视觉拍照的那一刻,机器人处于位姿 A;视觉处理完把结果发出去,机器人已经运动到位姿 B;如果上位机用位姿 B 去乘以手眼矩阵,坐标就错了。当机器人静止时这个错误为零,一旦机器人在运动中拍照(不管是相机装在机器人上,还是工件在传送带上),误差就出现了,而且和速度成正比。
解决办法有两条路。
第一条,拍照时冻结位姿。相机触发的同时,用一个高优先级任务把机器人当前位姿锁存下来,后续计算一律用这个锁存值。这是最干净的做法,前提是控制系统支持在指定时刻采样位姿,并且采样延迟可控。很多控制器提供"位置锁存"功能,由外部 IO 触发,精度可以到微秒级,强烈建议用这个。
第二条,时间戳补偿。如果拍照时刻的位姿拿不到,只能在事后按时间戳推算。这需要:相机曝光时刻的精确时间戳(相机支持硬件时间戳最好,否则至少要有触发信号的时间)、机器人的位姿历史缓冲(按固定周期记录位姿和时间戳)、以及一个插值算法。这条路的误差取决于时间戳精度和机器人速度,在 500mm/s 的带速下,1ms 的时间误差就是 0.5mm 的位置误差。
传送带跟踪场景还要额外处理编码器。通常的做法是用编码器脉冲触发相机,同时记录脉冲计数,视觉结果与脉冲计数绑定。机器人侧读同一个编码器,按脉冲数换算位置,两边用一个共同的参考原点对齐。这样可以完全绕开时间戳同步问题,是带传动场景的首选方案。
4.3 通信接口的选择
视觉系统和机器人控制器之间的通信,选型要考虑三件事:延迟、可靠性、调试便利性。
| 接口 | 典型延迟 | 适用场景 | 注意点 |
|---|---|---|---|
| TCP/IP Socket | 1-10ms | 上位机做视觉、下发单次抓取指令 | 要做心跳和粘包处理,断线重连必须实现 |
| Modbus TCP | 2-20ms | 简单坐标下发、参数交互 | 寄存器数量有限,大矩阵要拆多个周期传 |
| EtherCAT | <1ms | 高节拍、需要硬实时的场景 | 集成成本高,需要控制器支持主站 |
| 数字 IO + 并行总线 | <1ms | 简单的触发和结果标志 | 只能传少量数据,坐标要靠共享内存 |
| 专用机器人接口 | 1-5ms | 主流控制器的原生接口 | 各家协议不同,移植性差 |
实际项目里最常见的是 Socket 加 IO 触发组合:IO 负责硬触发的时序,Socket 负责传数据。这样时序靠硬件保证,数据走软链路,各司其职。要注意的是 Socket 消息必须带序号和校验,因为网络抖动会丢包或者乱序,视觉侧发了一个坐标但机器人没收到,机器人就会一直等。我一般会在协议里加"请求-确认"机制,机器人收到并执行后回一个 ACK,超时没收到就重发。
5. 精度不达标时的排查链路
5.1 先分清是视觉误差还是机械误差
精度出问题的时候,最容易犯的错误是直接怀疑算法,一头扎进视觉代码里。正确的第一步是把两类误差分开测量,这个动作能把排查范围立刻砍掉一半。
具体做法是做一个"固定靶验证":把标定板(或一个已知尺寸的工件)固定在机器人末端,让机器人走一系列位姿,每个位姿下视觉测量标定板的位置,同时用机器人正运动学计算标定板应该在的位置,两者做差。
- 如果差值随位姿变化很大、且没有规律,问题多半在标定或坐标变换;
- 如果差值在某个方向上系统性偏大(比如 Z 方向总是差 0.8mm),问题可能是工具坐标系或者标定板厚度补偿没做;
- 如果差值很小但实际抓取还是偏,那问题就不在视觉链路,而在夹爪、工件变形、或者机器人绝对精度。
这个验证我一般会在项目开始阶段就做一次,作为基线。后面每次改完参数都重做一次,看基线有没有漂。它相当于整个系统的"体温计",有它在,排查效率会高很多。
5.2 常见现象与根因对照
下面这张表是我这几年攒下来的,基本覆盖了现场遇到的大部分情况。看到现象可以先查对应的一栏,能省不少时间。
| 现象 | 优先怀疑的根因 | 验证方法 |
|---|---|---|
| 静止抓取准,运动中抓偏 | 时间戳不同步、位姿未锁存 | 把机器人速度降到 10%,看误差是否同比减小 |
| 视野中心准,边缘偏 | 镜头畸变未校正、内参标定不准 | 在视野不同位置放同一标定板,比较测量值 |
| 上午准,下午偏 | 温度漂移、光照随日照变化 | 记录每小时误差,对比车间温度曲线 |
| 换一批料就不准 | 工件表面反光特性变了、颜色变了 | 换单色光或加偏振片试试 |
| 抓取高度总差一个固定值 | 工具坐标系定义错、工件厚度未补偿 | 用不同厚度工件测试,看差值是否等于厚度差 |
| 偶尔一次偏得很离谱 | 识别到了错误目标、误匹配 | 保存历史图像,回放看识别结果 |
| 重启后精度变化 | 标定数据未持久化、加载了默认参数 | 检查配置文件加载路径和版本 |
| 机器人走到某些位姿时误差大 | 机器人绝对精度不足、关节接近奇异位形 | 避开大臂伸展位形,或改用在视觉中补偿 |
举一个具体的排查例子。有个项目反馈"抓取成功率 85%,失败的都偏 2-3mm"。我先做了固定靶验证,视觉链路误差在 0.3mm 以内,说明视觉没问题。然后看失败样本的图像,发现失败的那几次图像里,工件上有明显的反光斑,位置随环境光变化。根因是车间一侧有窗户,下午阳光斜射进来,工件表面的高光把边缘检测带偏了。解决方案是加一个遮光罩加换成带偏振的环形光,成功率上到 99%。整个过程没改一行算法代码。
这个例子说明一个道理:视觉项目的排查顺序应该是环境 → 硬件 → 标定 → 算法,从物理层往上走。大多数人习惯反过来,从算法往下查,往往是查了半天发现是光照问题。
5.3 现场环境带来的隐性误差
实验室里跑得通的东西,到现场失效,很大一部分原因是环境变量从来没被当成变量对待。以下几个是最常见的。
温度。工业相机的传感器在温度变化时会有暗电流漂移,机械结构会有热膨胀。一个 1 米长的铝支架,温度变化 10℃ 会产生约 0.23mm 的伸缩(铝的线膨胀系数约 23×10⁻⁶/℃)。如果相机装在这个支架上,标定的基准就跟着动了。解决方案是把相机支架做成低膨胀材料(比如铸铁或钢),或者把标定周期缩短,每天开机重新验证一次。
振动。机器人加减速时的振动会传到相机支架上,如果支架刚性不足,拍照瞬间图像会模糊。判断方法是看图像的边缘锐度,或者在支架上加一个加速度计做频谱分析,看机器人的激励频率有没有引起共振。改善方法很简单:加筋、加厚、加阻尼,比换相机便宜得多。
环境光。车间照明、天窗、隔壁工位的焊光,都会在图像上叠加变化。最彻底的办法是用遮光罩把工作区域罩起来,只留人工光源。次优方案是用窄带滤光片,把光源波长之外的光滤掉,这个方案成本很低但效果显著,尤其是在有环境光干扰的场合。
粉尘和油污。镜头前玻璃积灰会降低对比度和锐度,长时间运行后图像质量会缓慢劣化,表现为"以前能识别,现在识别率下降"。解决办法是加气幕或正压保护罩,并在维护规程里加入定期清洁。这一点在铸造、打磨、机加工场景里尤其重要。
6. 从 Demo 到稳定运行的工程化处理
6.1 光照与工件的稳定性管理
一个视觉系统能不能长期稳定,很大程度上取决于你有没有把"稳定性"当成一个独立的设计目标。Demo 阶段只要求"能识别",工程化阶段要求"连续 30 天识别率不低于 99.5%"。这两件事的差距全在细节里。
光照的稳定性管理,我建议至少做三件事。第一,固定光源的亮度并做闭环监测,很多光源控制器支持亮度反馈,或者简单点,在视野角落放一个白色参考块,每次拍照测量它的灰度值,超出范围就报警。第二,光源要有寿命管理,LED 长时间工作会光衰,一般 2 到 3 万小时后亮度下降明显,把光源列入定期更换清单。第三,对环境光做遮蔽,能加罩就加罩,加不了就上窄带滤光片。
工件的稳定性管理,核心是承认工件会有变化。同一批料里,表面粗糙度、颜色、反光特性都可能不同;不同批次的料,模具磨损会导致尺寸微小变化;供应商换了,外观可能完全不同。工程化的做法是:在视觉系统里加入置信度输出,识别结果的置信度低于阈值时不直接抓取,而是走异常处理流程(报警、人工确认、或者换一个拍照位姿重拍)。
还有一点值得提:建立图像档案。每次识别都把原图和结果存下来,保留至少两周。出问题的时候可以回溯,看是识别错了还是识别对了但坐标算错了。没有图像档案的排查基本靠猜,有档案的话几分钟就能定位。存储成本在现在的硬盘价格下完全不值一提。
6.2 异常处理与失败重试策略
视觉系统一定会失败,问题是失败之后怎么办。没有异常处理的系统,一次识别失败就可能导致整线停机,或者更糟——机器人抓空之后继续执行后续动作,撞到工装。
我的做法是把异常分成三类,分别处理。
可重试类:识别置信度低、图像质量不佳、目标被部分遮挡。处理方式是换一个拍照位姿重拍(如果相机能移动),或者调整光源亮度重拍,最多重试 2 到 3 次,仍失败则升级。
需人工介入类:工件姿态超出可抓取范围、料框空了、连续多次识别失败。处理方式是报警并暂停当前工位,等人来处理。这时候要给出明确的信息,比如"3 号料位连续 5 次识别失败,最后一张图像已保存至 xxx",让操作工能快速判断。
系统故障类:相机掉线、通信中断、标定数据异常。处理方式应该是立即安全停机,并且不允许自动恢复,必须人工确认后手动复位。相机掉线这种情况我见过不少,USB 接口的相机长时间运行后掉线是常见问题,解决方案是换成 GigE 接口、加独立网卡、开启巨帧,并且在软件里做心跳检测。
重试策略里有个细节值得注意:重试不能是简单重复同一个动作。如果第一次拍照失败是因为光照或位姿问题,第二次用同样的条件大概率还是失败。有效的重试必须改变条件——换位姿、换曝光、换光源组合。这一点在设计重试逻辑的时候就要想清楚,而不是等到现场再临时加。
6.3 参数管理与版本追溯
这一条听起来像是软件工程的范畴,但在实际的机器人视觉项目里,它是决定"能不能维护"的分水岭。
一个视觉系统里有多少参数?相机内参、畸变系数、手眼矩阵、工具坐标系、光源亮度、曝光时间、增益、识别阈值、ROI 区域、模板文件、通信地址……加起来轻松过百。这些参数如果散落在代码、配置文件、机器人程序、视觉软件工程文件里,那基本等于给自己埋雷。出问题的时候没人知道哪个参数被改过、什么时候改的、改成什么了。
我的做法是把所有参数收拢到一个统一的配置中心,用一个带版本号的配置文件管理,每次修改都记录变更人、时间、原因。配置文件本身纳入版本管理,出现问题时可以回滚到上一个已知良好的版本。这一步在项目初期做,成本很低;等系统上线后再补,就要停机改造。
配置里要特别注意区分两类参数:标定类参数和工艺类参数。标定类参数(内参、手眼矩阵、工具坐标系)是物理系统的描述,改动必须走严格的验证流程,改完必须重新做精度验证;工艺类参数(阈值、曝光、ROI)是应用层的调优,可以相对自由地改。把这两类混在一起管理,很容易出现"调阈值的时候不小心改了标定文件"这种事故。
最后分享一个我在实际操作中的体会:每次现场调试结束、系统交付之前,我都会做一次"冷启动验证"——把整个系统断电、重启、按操作手册从零开始走一遍流程,看能不能正常跑起来。这一步能抓出一堆隐藏问题:某个参数是调试时临时写在内存里的、某个文件路径是绝对路径换个用户就找不到、某个服务没有设置开机自启、某个权限没配好。这些问题在调试状态下永远不会暴露,但交付之后一定会出。花半天做这个验证,能省掉后面无数个深夜的电话。