简介:一份聚焦ABB机器人通过Socket与视觉系统通讯的完整实现方案,适合工业机器人集成工程师、自动化项目调试人员及学习RAPID编程的进阶用户。文档以图文方式拆解通讯建立、数据收发与解析、关键信息提取到机器人点位转换的全流程,并配有具体指令说明与参数设置示例,可解决相机定位数据与机器人运动对接的常见问题。压缩包内为1个docx格式文档,整体约403KB,携带方便,能直接在示教器编程或虚拟控制器调试时对照参考;已有2362人学习下载,属于实操性强、踩坑点明确的技术笔记。内容覆盖SocketCreate/SocketSend/SocketReceive使用、strfind与StrPart解析字符串、StrToVal类型转换、四元数与欧拉角互相转化等核心知识,尤其对视觉坐标偏移量如何叠加至robtarget姿态进行了逐步演示,便于快速移植到实际抓取或装配工序中。
1. 先看清ABB视觉通讯的四种主流方案:硬件IO、串口、总线、Socket——选哪种取决于你的现场
做ABB机器人视觉对接,最常被问的一句话是“能不能直接用一根网线把相机拍到的坐标发给机器人”。很多项目方以为视觉通讯就该是TCP/IP、就该传坐标,结果现场一看,一个拧螺丝的工位根本没有PLC,却要机器人自己跟相机打交道。我的经验是:选通讯方式不是看哪个先进,而是看数据量、有没有中间控制器、以及现场电工熟悉哪种接线。ABB机器人的视觉通讯,本质就四类——硬件IO只传触发和OK/NG信号,串口传文本结果,现场总线走PLC底下挂的相机,Socket则直接由机器人RAPID程序跟相机网口对话。绝大多数引导抓取和定位装配,最终都会落在这几种方案的组合上。这篇就按“选型、接线、编程、标定、踩坑、验证”的顺序,把每个环节能直接照着用的细节讲透。
2. 先定通讯骨架:从IO、总线到Socket,各在什么场景下当主角
2.1 硬件IO:信号最直接,但传不了坐标,适合做触发握手
硬件IO是视觉通讯里最“保守”也最可靠的一层。它的作用不是传数据,而是传“状态”——机器人发一个触发拍照的信号给相机,相机拍完算完再回一个完成信号给机器人。很多二值化的视觉检测,比如有无、对错、颜色判定,结果只是一个OK/NG,完全可以用一路IO完成,不需要任何协议解析。
我一般会在任何视觉项目里都保留IO握手,哪怕最后走Socket传坐标,IO也用来做“相机已就绪”“拍照完成”的状态互锁。接线时注意:ABB机器人侧DI/DO是24V直流信号,相机侧的IO输出口多数是PNP型,电压范围也在这个量级,但接线时必须确认信号参考地跟机器人IO板的地是同一个。曾经在调试时遇到过信号乱跳的情况,查了半天是现场电工把相机外壳地跟IO地混在一起,导致共模电压不稳。
用IO触发拍照的RAPID代码非常简单,核心是给相机一个上升沿,然后等待完成信号。
PROC VisionTrigger() SetDO doCamTrigger, 0; WaitTime 0.1; SetDO doCamTrigger, 1; ! 给一个上升沿触发拍照 WaitTime 0.1; SetDO doCamTrigger, 0; IF WaitDI(diCamDone, 1, 5) = FALSE THEN TPWrite "Vision timeout!"; Stop; ENDIF ENDPROC这段代码的要点是:触发前先把DO置0,延时后再置1,目的是产生一个干净的上升沿,防止相机因为电平保持时间不够而漏触发。WaitDI的第三个参数是超时秒数,这里的5表示等5秒还没收到完成信号就返回FALSE并停机,避免机器人一直死等。实际项目中,相机算图时间可能在几十毫秒到几百毫秒之间,超时设得太短会导致误报,设得太长又会掩盖相机处理异常,常见做法是先跑几十次测出最大耗时,再留30%~50%余量设超时。
2.2 串口与现场总线:PLC在场时的稳妥选择
如果产线已经有PLC,视觉通讯可以走串口或者总线,让PLC做协调者,机器人直接跟PLC握手。RS232串口在老旧设备里很常见,数据是一行ASCII文本,格式可以自定义,很多老款视觉控制器就只提供了串口输出。串口的问题有两个:一是波特率、停止位、校验位这些参数必须跟相机侧逐项对上,二是发送间隔要留够,否则相机串口缓冲容易溢出丢数据。
现场总线的方式则以Profinet、EtherNet/IP为主。ABB机器人扩展总线通讯需要对应的总线选项,比如Profinet需要配置好模块名称和IP,然后在RAPID里通过系统输入输出映射信号。这种方式的好处是数据映射很稳定,PLC和机器人都有诊断画面。但代价是调试链路多了PLC一层,视觉返回的数据要先在PLC里组装和转发,任何一方重启都要重新检查映射关系,排查起来比直连麻烦。
2.3 Socket直连:视觉坐标进机器人的主流路径,RAPID端四个函数就能跑通
真正要传像素坐标或者已经换算好的机器人基座坐标时,Socket是最活泼的方案。ABB机器人从RWS 6.x开始,RAPID语言里就内置了完整socket指令支持,不需要额外硬件,只要机器人控制柜能跟相机网口互通就行。常见的做法是机器人作为客户端,视觉系统作为服务端——相机开机后一直监听端口,机器人需要结果时主动发请求。
RAPID侧的核心代码四个函数就能覆盖:SocketCreate建立连接,SocketConnect连到相机IP和端口,SocketSend发送触发请求,SocketReceive接收返回结果。这里有个细节:机器人做客户端,连接是主动发起的,视觉程序每次检测到有客户端连进来就处理一次请求然后关闭连接;也可以保持长连接,但现场好坏参半,长连接少了握手时间,却会在视觉程序意外断掉时让机器人卡在发送超时里。
VAR socketdev clientSocket; VAR string recvData; VAR string request; PROC VisionGetResult() ! 建立并连接socket SocketCreate clientSocket; SocketConnect clientSocket, "192.168.0.10", 5000; ! 发送触发请求,相机收到后拍照并返回结果 request := "TRIGGER#1"; SocketSend clientSocket \Str := request; ! 接收返回数据,最长接收200个字符 SocketReceive clientSocket \Str := recvData \Time := 5; TPWrite "Raw data: " + recvData; SocketClose clientSocket; ENDPROC这里SocketConnect的第二个参数是相机IP字符串,第三个参数是端口号。SocketReceive里的\Time是超时时间,单位是秒,这里设的5秒是上限,实际应配合视觉算法耗时来定。\Str表示接收的数据按字符串处理,ABB机器人没有原生字节数组,所有socket数据最终都要转成字符串或数值。注意SocketReceive会阻塞机器人任务,如果相机久久不回数据,机器人就停在那里,所以超时参数一定要设置,并且超时后应有异常处理,而不是直接往下执行。
另外,socket方案里一个典型的坑是端口占用的:视觉程序重启后,旧连接没有正常关闭,新连接时机器人端会报“socket already connected”类错误。解决方法是机器人每次连接前先SocketClose,或者让视觉程序启动时强制清理监听队列。调试阶段我习惯在电脑上用网络调试工具先模拟服务端,机器人发来触发后手动回一条文本,确认机器人端解析没问题再连真相机。
2.4 通讯协议约定:发什么、收什么、分隔符是什么,先写进文档再写代码
Socket通了,真正决定项目顺利程度的是数据格式。视觉给机器人返回的无非几类:OK/NG、坐标X/Y/Z、角度、还有置信度。常见的数据协议是ASCII字符串加分隔符,比如“OK,123.4,56.7,89.0”。解析时用RAPID的字符串函数StrPart按逗号切分,再用StrToVal转成数值。协议的坑集中在三类:小数位不固定、负数带负号、结果行结尾带换行符。
我建议在项目启动第一天就把协议写成一张表,包括字段顺序、字段类型、分隔符、小数点位数、异常值表示法、超时阈值。视觉方很多软件工程师未必懂RAPID对字符串的挑剔程度,比如RAPID里字符串长度有限制,一行数据太长会被截断;又比如StrToVal遇到空字符串或者“#”这种字符会直接报错。双方把格式提前谈死,后面联调能省一半时间。
3. 从像素到机器人坐标:手眼标定与坐标变换是通讯的“翻译官”
3.1 眼在手上 vs 眼在手外,标定方法和流程完全不同
视觉算出的是像素坐标,机器人要的是自己坐标系下的位置,中间必须做一次标定。这个标定谁来做、怎么做,决定了引导精度。先装好相机再谈通讯,顺序不能反。
固定安装的相机叫“眼在手外”,相机架在工位上方,视野覆盖机器人要去抓的区域。标定时让机器人带着针尖或者吸盘走几个固定的参考点,记下这些点在机器人基座坐标系下的坐标,同时相机识别同一批点得到像素坐标,然后算出一组映射关系。
装在机器人手上的相机叫“眼在手上”,相机跟着机器人动,标定时要用一块固定标定板,机器人走多个位置,每个位置相机拍一次标定板,记录对应的法兰盘位姿。这里用到的是手眼标定矩阵求解,比固定相机多了一个旋转分量,数学上复杂一些,但如果现场需要做“边走边找料”或“料在运动”,只能选它。我的建议是:能固定就不装手上,固定相机标定简单,精度也更容易保证,手眼标定一旦机器人撞过机或换过相机,几乎都要重新做。
3.2 固定相机场景:仿射变换求解,六个参数走遍全场
固定安装的相机,像素坐标到机器人基座坐标的映射,绝大多数情况下用一个仿射变换就够。它假设视野内的形变是线性的,公式只涉及缩放、旋转和平移,六个参数就能表达。
import numpy as np # 像素坐标 (u, v) 与机器人基座坐标 (x, y) 的样本 # 标定时取至少3个点,推荐9个点以上 pixel = np.array([ [100.0, 120.0], [300.0, 150.0], [500.0, 480.0], [150.0, 400.0], [350.0, 300.0], [450.0, 200.0], ], dtype=np.float64) robot = np.array([ [500.0, 100.0], [520.0, 110.0], [550.0, 140.0], [510.0, 130.0], [530.0, 120.0], [540.0, 105.0], ], dtype=np.float64) # 构造仿射变换方程: robot = pixel @ A + b # 解法:用最小二乘拟合 A 和 b A, _, _, _ = np.linalg.lstsq( np.hstack([pixel, np.ones((pixel.shape[0], 1))]), robot, rcond=None ) print("变换矩阵: ", A)代码里把样本点拼成了齐次坐标形式,用np.linalg.lstsq最小二乘求解六参数矩阵。用了9个样本点时,映射精度会比3个点好很多,因为相机镜头或多或少存在畸变,点数多一点能平均掉一部分误差。A矩阵的前两列对应旋转和缩放,第三列对应平移偏移。实际使用时,像素坐标带入公式直接得到机器人基座坐标,但这里算出来的坐标是平面映射,只适合料在同一个高度平面内的场景。如果料高度有变化,需要加上Z轴补偿测量,或者引入多高度标定的插值策略。
3.3 手眼标定的实操细节:记录位姿时不要只拍一次
手眼标定的常见做法是让机器人带相机运动到一系列位姿,在每个位姿下拍照并记录当前法兰盘在基座坐标系下的X、Y、Z坐标和四元数。标定工具一般由视觉软件方提供,机器人端要做的就是把位姿数据准确送进标定界面。这里容易出问题的是手动示教时操作员拍的位置不够分散,所有点都挤在视野中间,标定出来的外参在视野边缘会急剧发飘。
所以标定取点时要让参考点覆盖整个视野的九个区域——中心、四角、四边中点,每个点位姿尽量控制在相机标定板能被完整拍到且没有被强反光干扰的角度。取点时相机和机器人姿态都固定,只平移,能得到一个纯平移方向的标定结果;如果需要考虑旋转,必须在多个姿态角度下采集。完成后务必做一次验证:让机器人移动到某个预置点位,相机识别该点的像素坐标,换算成机器人坐标后手动移动机器人对上去看偏差。偏差在1~2毫米以内属于正常,大于5毫米就需要重做标定。
4. RAPID端把视觉数据吃进来:字符串解析、异常处理和重连策略
4.1 收发函数与字符串解析:StrPart、StrToVal的协作套路
视觉返回的数据如果只是简单OK/NG,用IO就够,但传坐标和角度时就要做字符串解析。RAPID解析字符串有三个高频率函数:StrPart取某一段,StrToVal把文本转数值,StrMatch找子串位置。下面的代码演示如何从“OK,123.45,67.89,30.0”里面拆出三个数。
FUNC bool VisionParse(String rawData, INOUT num xPos, INOUT num yPos, INOUT num angleVal) VAR num commaPos1; VAR num commaPos2; VAR num commaPos3; VAR string strX; VAR string strY; VAR string strA; ! 检查数据头是否为OK IF StrMatch(rawData, 1, "OK,") = 0 THEN RETURN FALSE; ENDIF ! 找三个逗号的位置 commaPos1 := StrMatch(rawData, 1, ","); commaPos2 := StrMatch(rawData, commaPos1 + 1, ","); commaPos3 := StrMatch(rawData, commaPos2 + 1, ","); ! 按逗号切出三个字段 strX := StrPart(rawData, commaPos1 + 1, commaPos2 - commaPos1 - 1); strY := StrPart(rawData, commaPos2 + 1, commaPos3 - commaPos2 - 1); strA := StrPart(rawData, commaPos3 + 1, StrLen(rawData) - commaPos3); ! 文本转数值 IF StrToVal(strX, xPos) = FALSE THEN RETURN FALSE; ENDIF IF StrToVal(strY, yPos) = FALSE THEN RETURN FALSE; ENDIF IF StrToVal(strA, angleVal) = FALSE THEN RETURN FALSE; ENDIF RETURN TRUE; ENDPROCStrMatch返回的是子串出现的起始位置,找不到就返回0,所以用返回值判断数据的合法性。StrPart的第二个参数是起始位置,第三个参数是截取长度,这里通过相邻逗号位置相减,把长度精确算出来。StrToVal转数值时最怕数据里带了回车符或空格,视觉软件返回的字符串很多时候在末尾带一个\n换行,这会让最后一个字段转数值失败。我一般会在SocketReceive之后用一个循环把尾部的\r和\n都清掉再做解析。
4.2 通讯异常与重连:死等是最差策略,超时重试才是现场活下来的前提
前面代码一直是同步执行,这在实际现场是最容易翻车的地方。一个Socket连接建立后,如果相机端程序崩溃、网线被踢掉、或者视觉软件正在初始化而机器人恰好在这时发了请求,机器人会卡在SocketReceive里干等。因此必须给整个通讯过程套上一层任务保护。
常见的做法是单独用一个后台任务轮询通讯状态,主任务只发请求然后等待结果信号,超时没等到就重新发起连接。用RAPID写的话,就是在带\Time参数的SocketReceive返回值上做强判,如果超时返回,就SocketClose后重连并重发请求。数据是机器人主动去“取”的,重发不会有什么副作用,代价只是多几百毫秒的周期。重连次数做个上限,比如连续5次失败就报错停机,让操作员去看相机侧,而不是让机器人自己无限重试。
VAR num retryCount := 0; VAR bool gotResult := FALSE; WHILE retryCount < 5 AND gotResult = FALSE DO gotResult := VisionRequestOnce(); IF gotResult = FALSE THEN retryCount := retryCount + 1; WaitTime 0.5; TPWrite "Vision retry " + NumToStr(retryCount, 0); ENDIF ENDWHILE IF gotResult = FALSE THEN TPWrite "Vision failed after 5 retries"; Stop; ENDIF这里的VisionRequestOnce是用户自定义函数,封装了建连、发送、接收、解析的完整过程,返回TRUE表示成功拿到有效数据。重试的间隔0.5秒考虑到相机可能正在恢复,太短会连不上,太长相机会错过机器人的节拍。调试时可以通过窗口里的TPWrite看到每次重试的次数,方便判断是瞬断还是相机彻底不可用。这个套路看起来笨,但现场99%的偶发通讯故障都能被它兜住。
4.3 数据格式不用标准JSON还是用字符串,关键看解析成本和失败率
有人问为什么RAPID不直接用JSON,视觉那边也方便。原因很现实:RAPID没有原生的JSON库,要解析JSON就得自己写状态机,字段一多非常容易崩。而视觉软件工程师也是拿代码生成字符串,大家用最朴素的逗号分隔,双方都能一眼看懂。简单格式的代价是不能一次传嵌套结构。但现实里一次传X、Y、A三个数就是全部需求,没必要上复杂协议。
唯一要注意的是,如果现场要在一条产线上跑多种产品型号,字段含义会随型号变化,这时候可以在字符串开头约定一个两位型号码,比如“P1,OK,123.4,56.7”,RAPID端先判断型号码再决定怎么解析。这样比每一种型号写一个socket通道清爽得多。
5. 避坑清单:视觉通讯调试中反复踩的6个现场问题
5.1 现象:机器人偶发卡死,停在哪一次都说不清
通讯偶发卡死是视觉对接里最让人头疼的。现象是机器人程序走到了SocketReceive就不再往下走,人工复位重启又能跑几十次。原因通常是视觉程序处理超时、相机重启、网线松动等导致某一次请求没有返回数据,而机器人没有任何超时兜底。解决方向是把超时参数全部加上,并且超时后进入重连流程,而不是直接报错。另外要在发送请求前把机器人侧自动清除旧数据的机制做好,避免上一次残留数据被当成本次结果。
5.2 现象:坐标解析出来整体偏移,越到视野边缘偏得越狠
如果视觉返回的坐标在视野中心区域比较准,越往四周偏差越大,那基本不是通讯问题,而是标定模型不够。固定相机如果镜头畸变明显,纯仿射变换无法拟合边缘的桶形畸变。解决方法是:检查相机内参标定,让视觉软件输出畸变校正后的像素坐标;或者标定采集的点分布得不够均匀,重新采集让样本点铺满视野。还有一个很容易被忽略的是安装工装不稳定,相机固定支架在机器人动作时振动,导致标定外参失效。
5.3 现象:IO信号对不上,机器人触发了但相机没拍
现象是偶尔拍不到,或者每次都差一拍。原因大多是触发信号脉宽太短。相机IO触发需要一定的有效电平时间,有的相机要求至少1ms到几毫秒,机器人DO的输出如果只是瞬时SetDO再Set回,实际脉宽取决于扫描周期,可能只有零点几毫秒。解决方法是把触发DO置1后加至少20ms延时再置0。另一个常见坑是机器人已经把完成信号收走了,单片机的IO状态没释放,导致下一次握手提前误判。处理方式是在相机侧把完成信号做成脉冲,保持时间控制住,并让机器人侧在读取数据后主动清掉这个DI。
5.4 现象:Socket显示连上了,但收到的是乱数据
收到乱码或者半截数据,首先排除两端波特率/编码不一致。ABB socket收发的字符串在机器人内部是UTF-8或者本地代码页,视觉软件如果按GBK输出中文字符,机器人端就会看到乱码。解决方式是让视觉端只输出纯数字和半角逗号,不要带中文标签。另一个是视觉端字符串末尾带了换行符,接收后解析时务必先trim。还有一个隐蔽问题:视觉端分两次发送了一段数据,机器人第一次Receive只收到半截,第二次又收到另外半截,这需要在协议里约定一次发送必须在一个包内完成,或者接收端做缓冲拼接。
5.5 现象:手眼标定做了,换了料盘上的隔板位置就偏了
标定本身没问题,但没有考虑料盘定位误差。如果料盘每次由人工摆放或者其他机构推入,它的实际位置和理想位置存在偏差,视觉就得先找到料盘本身的定位基准,否则像素坐标映射到基座坐标后,抓取位置整体偏移。解决方式是在模板里增加一个基准点识别,用基准点的像素坐标动态修正料盘的平移偏量,再把这个修正量叠加到视觉输出坐标上。
5.6 现象:机器人重启后Socket连不上
现象是视觉软件还在跑,但机器人冷启动后第一次连接就失败,需要手动重启视觉软件才能恢复。原因是视觉端没有处理好“客户端断开后残留连接”的情况,特别是机器人断电瞬间TCP连接没有正常四步挥手,服务端还保留着旧的连接状态。解决方式是让视觉软件在服务端设置连接超时自动释放,或者采用“每连接一次、处理完即关闭”的短连接模式。我碰到过一次是控制柜IP改了但视觉端配置文件里写死了旧IP,这类属于基础配置纠纷,排查时第一件事就是通ping看两边网络是否真的通。
6. 上线前花两小时做一套通讯自检:假服务端回环、模拟数据注入、慢速真机跑
视觉通讯项目最怕的不是调试时跑不通,而是刚上线时偶发暴雷。我习惯在正式联调前先做三层自检,每一层都能把一类问题隔离出来。第一层是在电脑上跑一个假服务端,模拟相机的IP和端口,机器人正常触发请求,假服务端回固定数据,验证机器人端的socket连接、发送、接收、解析整条链路,这一层能找出RAPID程序自己的问题。第二层是让视觉软件输出模拟数据,也就是不启动相机硬件,直接用图库图片或者预设坐标走完视觉处理流程,验证视觉软件与机器人之间的协议格式是否兼容。第三层才是真机慢速跑,机器人用最低速度执行几次抓取,观察通讯周期和坐标波动。
假服务端我用Python写过几十次,代码非常固定,起一个TCP服务监听线程,收到请求后返回预设字符串。
import socket import threading # 模拟视觉服务端,监听5000端口,收到TRIGGER就返回一组坐标 def handle_client(conn): data = conn.recv(256) print("Received:", data) # 协议约定:OK, X, Y, A response = b"OK,123.45,67.89,30.0\n" conn.sendall(response) conn.close() server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(("0.0.0.0", 5000)) server.listen(5) print("Fake vision server listening on 5000") while True: conn, addr = server.accept() thread = threading.Thread(target=handle_client, args=(conn,)) thread.start()SO_REUSEADDR这个选项必须在绑端口前设置,否则机器人端反复快速重连后,服务端端口会处于TIME_WAIT状态,导致下一次绑定失败重新等待。用线程处理每个连接是为了支持机器人断线重连多次。这个假服务端跑起来后,机器人端就可以单独测试超时、重试、解析失败这些异常路径,不用真相机反复拍图。我在这个阶段会故意让假服务端延迟回包或者不回包,验证机器人端超时能触发、重试计数能清零,一切符合预期才上真相机。
慢速真机跑的时候,我习惯盯着两个数据:通讯周期和坐标重复性。通讯周期用机器人端每次从发请求到收到结果之间的时间差衡量,应保持稳定;坐标重复性让机器人停在同一个拍照位置拍十次,视觉返回的坐标波动越小越好。如果波动超过系统允许的定位公差,说明问题在视觉本身,而不是通讯。
把这三层自检走一遍,基本上就能把“通讯问题”和“标定问题”“视觉算法问题”切开。我自己绕过的最大弯路就是直接连真相机联调,结果通讯偶发断一下、视觉算法偶发误检一下,两边混在一起完全找不到根因。后来固定了这套流程,每个项目上线前都做一遍,通讯这块基本稳定。如果你只记住一个习惯,那就是动手前把协议约定写成文档,双方照着执行;动手后先用假服务端把机器人侧的功能全测完,再碰真相机。希望帮到你。
本文还有配套的精品资源,点击获取