简介:围绕西门子S7-1215C PLC与信捷视觉系统构建工业机器人分拣系统的期刊论文PDF,面向自动化、机电一体化专业技术人员以及职业院校技能大赛选手与指导教师,重点解决分拣机器人软硬件集成、通讯调试与故障排查等实践问题。资源为1个pdf文件,压缩包约374KB,内容对应2015年全国职业院校技能大赛“工业机器人技术应用”赛项设备载体。内容系统梳理工业机器人、托盘与工件盒生产线、视觉系统、立体仓库及AGV机器人等六大部分,详述汇博HR.20-1700-C10机器人、信捷X-sight视觉与S7-1200系列1215C DC/DC/DC的选型配置,并展开PLC与视觉系统、机器人之间的通讯、视觉检测软件流程编辑、系统调试步骤与典型问题现象、原因及解决办法。读者可据此理解柔性分拣产线的控制逻辑、模块分工与联调思路,对参赛备赛和工程实践均有直接参考价值。已有282人学习。
1. 为什么分拣线要选 S7-1215C 加视觉系统这套组合
一条小型分拣线最怕的不是机器人本体贵,而是视觉给了坐标、PLC 没接住,或者机器人动作和视觉触发差半拍。S7-1215C 出现在这类项目里,通常不是因为它算力最强,而是它把 PROFINET 口、本体 IO、SCL 编程和紧凑体积凑在了一个能落地的价位。视觉系统负责把工件的位置、角度、类别变成数字,S7-1215C 负责把这些数字变成机器人能执行的取放节拍。适合做设备集成的电气工程师、自动化方向的毕设学生,以及要给产线加视觉分拣但预算有限的团队。一个反直觉的地方:视觉分辨率从 500 万升到 1200 万,分拣节拍未必变快,PLC 与视觉的通讯协议、数据结构和超时处理才是真正的瓶颈。
2. S7-1215C 与视觉系统通讯链路和数据结构设计
2.1 三种视觉与 PLC 通讯方式选型对比
视觉系统输出给 PLC 的东西一般就几类:像素坐标、物理坐标、角度、类别、状态字。通讯方式决定了这些数据怎么进 S7-1215C。常见做法有三种:硬线 IO、PROFINET IO、开放式用户通讯 TCP。硬线 IO 最稳,但只能传“有料/无料/合格/不合格”,坐标和角度传不了。PROFINET IO 适合智能相机支持 GSD 文件的情况,周期稳定,但改一次数据长度要重新组态。TCP socket 最灵活,视觉工控机当客户端,S7-1215C 当服务器,传 JSON 或自定义二进制帧,调试方便,代价是要自己处理粘包和超时。
| 通讯方式 | 典型周期 | 开发量 | 适合场景 |
|---|---|---|---|
| 硬线 IO | 1~5 ms | 低 | 仅分合格/不合格,无坐标 |
| PROFINET IO | 1~4 ms | 中 | 智能相机带 GSD,数据固定 |
| TCP 开放式用户通讯 | 5~50 ms | 中高 | 工控机视觉,数据可变,需解析 |
S7-1215C 的 PROFINET 接口支持开放式用户通讯,指令库里有 TCON、TSEND、TRCV、TDISCON。如果视觉软件跑在 PC 上,我一般会选 TCP,因为改协议不用动博途硬件组态。如果视觉是康耐视或基恩士的智能相机,且支持 PROFINET,优先走 PROFINET IO,节拍更稳。
2.2 用 TCP 开放式用户通讯建立视觉链路的最小实现
下面这段 SCL 写的是 S7-1215C 作为 TCP 服务器,视觉客户端主动连过来。放在 OB1 或循环中断里调用,背景 DB 用全局库。代码只保留关键逻辑:
// 在 OB1 中调用,背景 DB 为 "TCON_DB" // 连接参数需要提前在 DB 里定义 TCON_Param "TCON_DB"(REQ := "StartConnect", ID := 1, CONNECT := "ConnParam", DONE => "ConnectDone", BUSY => "ConnectBusy", ERROR => "ConnectError", STATUS => "ConnectStatus"); // 接收视觉数据,假设最大 256 字节 "TRCV_DB"(EN_R := "ConnectDone", ID := 1, DATA := "RecvBuffer", LEN := 256, DONE => "RecvDone", NDR => "RecvNewData", ERROR => "RecvError", STATUS => "RecvStatus", RCVD_LEN => "RecvLen");逻辑说明:TCON 建立被动连接,参数里本地端口设为 2000,远程 IP 可以不填,因为服务器等客户端连入。TRCV 每扫描周期尝试收数据,NDR 为真表示收到新帧。参数说明:ID 是连接号,同一个连接后续收发都用同一个 ID;DATA 是接收缓冲区,建议用Array[0..255] of Byte;LEN 是缓冲区长度,不要小于实际最大帧长;RCVD_LEN 告诉你实际收到多少字节。注意:TRCV 不是每周期都产生新数据,必须用 NDR 触发解析,否则会把同一帧反复处理。
视觉端我常用 Python 写一个测试客户端,确认 PLC 能收到再上产线:
import socket import struct # PLC 的 IP 和端口,端口要和 TCON 中一致 PLC_IP = "192.168.0.10" PLC_PORT = 2000 # 帧格式:X坐标(float) Y坐标(float) 角度(float) 类别(uint8) 校验和(uint8) payload = struct.pack("<fffBB", 125.4, 230.8, 15.2, 1, 0) checksum = sum(payload) & 0xFF frame = b"\xAA\x55" + payload + bytes([checksum]) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect((PLC_IP, PLC_PORT)) s.sendall(frame) print("sent", len(frame), "bytes")这段 Python 只用于验证链路,发一帧固定坐标,在博途里监控 RecvBuffer 就能看到字节。参数说明:<fffBB表示小端浮点、浮点、浮点、无符号字节、无符号字节;帧头AA55用来做帧同步;校验和用累加和。实际项目里可以把帧头、长度、校验做成固定协议,PLC 收到后先找 AA55,再按长度取一帧。
2.3 分拣结果的数据结构:从字节数组到 UDT
视觉给的是字节流,PLC 内部最好马上转成 UDT。在博途里新建一个数据类型typCargo:
TYPE "typCargo" VERSION : 0.1 STRUCT X : Real; // 机器人基坐标 X,单位 mm Y : Real; // 机器人基坐标 Y,单位 mm Angle : Real; // 工件角度,单位度 Class : Int; // 类别编号,1=A,2=B,3=C Status : Int; // 0=空,1=待取,2=已取,3=异常 Timestamp : DInt; // 入队时的系统时间,ms END_STRUCT; END_TYPE然后在全局 DB 里声明CargoQueue : Array[1..20] of typCargo;。解析函数把 RecvBuffer 里的浮点数按偏移量搬到 UDT 中。参数说明:偏移量 2 开始是因为前两个字节是帧头;Real 在 S7-1200 中占 4 字节,大端存储,但 TCP 收到的是小端,所以要用SWAP或自定义字节交换函数。我一般写一个DWordSwapFC 把 4 字节反过来再DWORD_TO_REAL。这个结构决定了后面 FIFO 和机器人取件的接口是否清晰,不要用一堆 M 点硬拼。
3. 机器人分拣动作的 PLC 状态机与 FIFO 队列实现
3.1 分拣工位状态划分与 IO 握手时序
工业机器人分拣不是“给坐标就动”,PLC 和机器人之间要有明确的握手。常见做法是 PLC 把目标位姿写入机器人输入区,然后置位DataReady,机器人回Busy;取件完成回Done,PLC 复位DataReady。如果走 PROFINET,这些信号可以打包成 16 字节的输出区,机器人侧映射成系统变量。如果走硬线 IO,至少需要 5 根线:DataReady、Busy、Done、Error、Ack。状态划分我通常用这几个:Idle、WaitVision、DataReady、RobotMoving、PickDone、Fault。每个状态超时都要有计时器,比如 DataReady 超过 3 秒没收到 Busy,就进 Fault。
状态迁移表可以写在 DB 里,但代码里用 CASE 更直接。注意 PLC 扫描周期是毫秒级,机器人 Busy 信号可能抖动,所以输入信号要加 50 ms 滤波,或者用TON延时确认。
3.2 在博途里用 SCL 写一个可复位的状态机
下面是一个简化状态机,放在 FB 里,背景 DB 保存状态和计时器:
CASE #State OF 0: // Idle IF #Start THEN #State := 10; END_IF; 10: // WaitVision IF #VisionDataReady THEN #State := 20; ELSIF #VisionTimeout.Q THEN #State := 90; END_IF; 20: // DataReady,把坐标发给机器人 #Robot_X := #Cargo.X; #Robot_Y := #Cargo.Y; #Robot_Angle := #Cargo.Angle; #DataReady := TRUE; IF #Robot_Busy THEN #State := 30; ELSIF #HandshakeTimeout.Q THEN #State := 90; END_IF; 30: // RobotMoving IF #Robot_Done THEN #DataReady := FALSE; #State := 40; ELSIF #Robot_Error THEN #State := 90; END_IF; 40: // PickDone,出队并回到等待 #Dequeue := TRUE; #State := 10; 90: // Fault #DataReady := FALSE; #FaultCode := #LastError; IF #Reset THEN #State := 0; END_IF; END_CASE;逻辑说明:State 是 Int,每个分支只做一件事。VisionDataReady来自视觉解析完成,HandshakeTimeout和VisionTimeout是 TON 实例的背景数据。参数说明:State 编号留间隔,以后插入新状态不用改所有数字;Robot_X、Robot_Y 等是输出给机器人的 REAL;FaultCode 记录错误来源,方便在 HMI 上显示。注意:DataReady 置位后不要马上复位,要等机器人 Busy 或 Done 再复位,否则机器人可能丢触发。
3.3 FIFO 队列:视觉结果入队与机器人取件出队
视觉可能一次给多个工件,机器人一个一个取,所以需要一个 FIFO。S7-1215C 没有现成的 FIFO 指令,自己用环形缓冲写一个 FC。下面用 SCL 实现入队:
// 入队:把 #NewCargo 写入队列,头指针前移 IF #Count < 20 THEN #Tail := #Tail + 1; IF #Tail > 20 THEN #Tail := 1; END_IF; #CargoQueue[#Tail] := #NewCargo; #Count := #Count + 1; #EnqueueOK := TRUE; ELSE #EnqueueOK := FALSE; // 队列满,报警 END_IF;出队逻辑相反,从头指针取,取完 Head 前移,Count 减一。参数说明:Head 和 Tail 是 Int,范围 1~20;Count 是当前数量;CargoQueue 是Array[1..20] of typCargo。注意:数组下标从 1 开始,所以判断>20后回绕到 1。入队前要检查 Count,防止覆盖未取数据。实际项目中还要加队列溢出报警,让视觉暂停发送。这个结构比用 M 点数组清晰得多,机器人取件时只需要读CargoQueue[Head]。
4. 视觉标定、坐标转换与分拣精度调参
4.1 手眼标定:相机像素坐标到机器人基坐标的映射
视觉给的是像素坐标,机器人要的是基坐标,中间差一个标定矩阵。平面分拣常用九点标定:在机器人工作平面上取 9 个点,记录每个点的像素坐标和机器人坐标,解出仿射变换。仿射变换公式:
x_robot = a * u + b * v + c y_robot = d * u + e * v + f
其中 (u,v) 是像素坐标,(x_robot,y_robot) 是机器人坐标。a、b、c、d、e、f 六个参数通过最小二乘解出。如果相机有镜头畸变,先做畸变校正,否则边缘点误差会到几毫米。标定板要覆盖整个分拣区域,不要只取中间一小块。
在 S7-1215C 里做浮点矩阵运算不是强项,我一般把标定参数算好后下载到 DB,PLC 只做一次线性计算:
// 像素坐标转机器人坐标,参数存在 "CalibDB" 中 #x_robot := "CalibDB".a * #u + "CalibDB".b * #v + "CalibDB".c; #y_robot := "CalibDB".d * #u + "CalibDB".e * #v + "CalibDB".f;参数说明:a、b、c、d、e、f 都是 REAL;u、v 来自视觉解析后的像素坐标;x_robot、y_robot 写入 typCargo。注意:如果传送带在动,还要加编码器补偿,x_robot 要减去传送带位移。这个补偿值可以用高速计数器读编码器,每毫秒更新。
4.2 九点标定与仿射变换在 PLC 中的实现
九点标定的数据采集可以在 HMI 上做:机器人依次走到 9 个点,视觉记录像素,PLC 记录机器人坐标。然后在上位机用 Python 算最小二乘,把结果写回 PLC。下面是一段 Python 计算示例:
import numpy as np # 9 组像素坐标和机器人坐标 pixel = np.array([[100,200],[300,200],[500,200], [100,400],[300,400],[500,400], [100,600],[300,600],[500,600]], dtype=float) robot = np.array([[10.0,20.0],[110.0,20.0],[210.0,20.0], [10.0,120.0],[110.0,120.0],[210.0,120.0], [10.0,220.0],[110.0,220.0],[210.0,220.0]]) # 构造 A 矩阵,解仿射参数 A = np.hstack([pixel, np.ones((9,1))]) coeff_x, _, _, _ = np.linalg.lstsq(A, robot[:,0], rcond=None) coeff_y, _, _, _ = np.linalg.lstsq(A, robot[:,1], rcond=None) print("a,b,c =", coeff_x) print("d,e,f =", coeff_y)逻辑说明:np.linalg.lstsq解超定方程,得到 6 个参数。参数说明:pixel 是视觉输出的像素坐标,robot 是机器人示教器上读到的基坐标;A 矩阵每行是[u, v, 1]。算完后把 6 个数填到博途的 CalibDB。注意:如果机器人坐标系和相机坐标系有旋转,仿射变换能吸收旋转,但前提是九点覆盖范围够大。
4.3 影响分拣精度的 4 个参数与调试顺序
分拣精度不是单一因素,按影响大小排:标定质量、触发延时、传送带速度、光源稳定性。调试顺序建议先静态后动态:静态下放一个工件,看视觉坐标和机器人取件点差多少,调整标定参数;然后开传送带低速,看动态偏差;最后升到生产速度,调触发延时补偿。
| 参数 | 典型值 | 影响 | 调整方法 |
|---|---|---|---|
| 标定残差 | < 0.5 mm | 整体偏移 | 重新采九点,检查标定板 |
| 触发延时 | 20~80 ms | 动态 X 偏差 | 用编码器补偿或调相机延时 |
| 传送带速度 | 100~300 mm/s | 节拍和偏差 | 低速验证后逐步提高 |
| 光源亮度 | 70%~85% | 识别率 | 固定光源,避免环境光变化 |
注意:如果视觉识别率突然下降,先看光源和镜头,不要先改 PLC 程序。分拣精度调好后,把标定参数和补偿参数保存在 DB 中,断电保持,换产线时重新标定。
5. 分拣系统节拍优化与故障排查:从博途资源监视到通讯超时处理
5.1 用博途资源监视定位 PLC 扫描周期瓶颈
S7-1215C 的扫描周期在博途里可以实时看。打开“在线与诊断”里的“循环时间”,如果扫描周期从 2 ms 涨到 10 ms,先看程序里有没有大数组循环搬运。视觉解析如果用 FOR 循环逐字节处理 256 字节,每周期都跑,就会拖慢。常见做法是把解析放到接收完成中断里,或者用NDR触发只跑一次。另一个检查点是通讯指令的背景 DB,TCON、TRCV 如果每周期都调用且 EN_R 一直为真,会占用扫描时间。我一般把 TRCV 放在循环中断 OB30 里,周期 5 ms,避免主程序被拖慢。博途的“资源”标签页可以看内存占用,队列数组不要开太大,20 个 typCargo 大约几百字节,没问题;如果开 1000 个,M 区会紧张。
5.2 视觉通讯超时与丢帧的排查清单
通讯超时先分三层查:物理层、协议层、应用层。物理层看网口灯和交换机,S7-1215C 的 PROFINET 口和视觉 PC 要在同一网段,不要经过路由器。协议层用 Wireshark 抓包,看视觉有没有发帧、PLC 有没有 ACK。如果视觉发了但 PLC 的 RCVD_LEN 一直是 0,检查 TCON 的本地端口是否被占用,或者防火墙。应用层看帧头 AA55 有没有对齐,粘包时 TRCV 可能一次收到两帧,解析函数要循环找帧头,直到剩余长度不足一帧。丢帧的另一个原因是视觉触发太快,PLC 队列满后拒绝入队,视觉没有收到确认还在发。可以在协议里加一个 ACK 字节,PLC 入队成功回 1,失败回 0,视觉收到 0 就暂停 100 ms。最后,如果机器人取件节拍是 2 秒,视觉每 200 ms 给一个坐标,队列很快满,这时候要么降低视觉频率,要么让机器人一次取多个。节拍匹配比单点优化更重要。
本文还有配套的精品资源,点击获取