ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

川崎机器人AS语言实现多客户端TCP服务器完整指南

川崎机器人AS语言实现多客户端TCP服务器完整指南 前阵子给一条视觉分拣产线调试程序碰到一个很典型的现场需求3台相机、1套HMI都要实时读机器人当前位置还要往机器人上下发启动信号。按很多人的第一反应会安排机器人去连上位机再由上位机中转转发给各设备。可这种方案在现场反而绕了一大圈增加一台中转机不说多一层转发就多一处故障点。后来我干脆让川崎机器人控制器自己当一个TCP服务器用AS语言直接处理多客户端的连接和通信整个架构清爽了很多。这篇就把这段经验完整摊开为什么让机器人做TCP服务器、多客户端怎么设计、AS语言怎么实现、如何部署到控制器、以及最后那些不踩一遍根本不知道的坑。全程按保姆级标准来写代码也直接给出针对的是有川崎机器人基础、但对网络通信不太熟的朋友如果你本身就是做上位机的也可以反过来通过这篇搞清楚机器人侧到底要准备什么接口。1. 为什么需要让川崎机器人直接当TCP服务器1.1 三种典型的现场需求先说第一个场景视觉引导多工位抓取。一条产线上好几个相机同时盯不同工位每个工位拍完照之后都要问机器人“你当前位置在哪”“你现在能不能抓”。如果机器人只作为TCP客户端去连某一台电脑其他相机根本没法和它直接对话所有请求都得挤到那台电脑上排队转发。让机器人直接开一个TCP服务器端口每个相机各自连上来各发各的指令谁也不用等谁。第二个场景是HMI和MES并发采集。HMI要看机器人的实时坐标、报警状态MES系统要周期性地记录机器人当前的工作模式和产量数据。两个系统如果走同一个连接一个要把数据拉走另一个也要拉往往会相互干扰。更合理的做法是机器人开多个连接HMI连一个会话MES连另一个会话互不影响。第三个场景是远程下发配方或运动参数。现在很多设备调试过程中工艺员会拿着笔记本到现场改配方或者直接在控制室里通过局域网发送一份新的速度参数表。这种操作如果走串口或者只能通过U盘拷贝低效而且容易拷错。有一个稳定的TCP服务器在机器人侧远程下发文件或者参数都只是发一串字符串的事。1.2 直接上AS语言 vs 外挂网关也有人问我为什么不加一个协议转换网关之类的东西比如买一个工业网关把机器人串口或EtherNet/IP转成标准TCP再让其他设备都去连网关。确实有人这么做而且网关产品本身很成熟。但代价也很直接多一个硬件设备就要多配一个IP多维护一份配置多承担一层故障。网关挂了所有依赖机器人数据的上位机一起挂。而且网关通讯通常还有延迟一般会比直连多出几十毫秒到几百毫秒不等视觉引导这类对时序敏感的场景就很吃亏。相比之下川崎机器人自身的AS语言本身提供了Socket相关的网络指令让控制器直接开启TCP服务端并不需要额外硬件。在数据实时性、可靠性上直连永远是最直接的方案。当然前提是你愿意花一点时间把AS语言的网络通信逻辑搞清楚。1.3 AS网络指令与C语言Socket的相似之处只要写过C语言的socket服务端程序再看AS语言的TCP指令会非常亲切。C语言里要做的事情是socket()建立套接字、bind()绑定端口、listen()监听、accept()接受新连接、recv()和send()收发数据、close()关闭套接字。AS语言的思路完全一样只是指令名称和调用方式换了一套壳。比如打开监听端口AS里大约是OPEN_TCPSRV这类指令返回一个句柄有客户端连入时需要用一个接受连接的步骤拿到这个客户端专属的句柄接下来收发数据用RECV和SEND断开时用CLOSE关掉对应句柄。本质就是那套Socket模型读、写、关、多路复用。所以后面写的代码你用C语言或Python的人都能一眼看懂只是关键字不一样。这也是我为什么总说网络编程的基本功在哪个语言里都通用。2. 多客户端服务器的整体设计2.1 单连接模型的局限很多刚开始接触川崎网络通信的人习惯写一个很简单的单连接服务器程序打开端口然后死等一个客户端接入进入RECV等待收到一条指令处理一条回一条。这套流程调试起来没问题但现场一用就露馅。因为只要那个客户端一直占着连接不断开其他客户端就永远连不进来。而且当服务器卡在RECV等待时连新客户端接入的探测都顾不上表现上就是“第一个设备好好的第二个设备死活连不上”。这种情况我见过不止一次有人还误以为是控制器网络硬件有问题其实是程序模型不对。单连接模型的本质是阻塞式串行处理一条连接把整个程序堵死。要支持多客户端必须改成并行处理或者轮询模型。对AS语言这种偏顺序执行的机器人语言来说最友好的方案就是轮询。2.2 连接句柄表 非阻塞轮询最简单的多客户端思路多客户端服务器设计上绕不开三板斧监听一个公共端口、用一张表记录所有已经连上来的客户端、循环去检查每一条连接是否有新数据。这张“表”在C语言里通常是文件描述符数组在AS语言里我们就用一个整数数组。每次有新客户端接入调用接受连接的操作拿到一个新句柄把这个句柄追加到数组里。主循环每隔一段时间遍历一下这个数组对每一个句柄尝试接收数据。如果有设备断开就把这个句柄从数组里清掉。数组容量就代表最大支持的客户端数量比如10个。轮询间隔很关键。间隔太短控制器CPU会被刷屏影响机器人运动控制间隔太长客户端发来的指令响应会迟钝。我一般取50ms到100ms作为普通状态上报和指令下发足够用。如果你的工艺对响应时间要求很高可以缩到10ms但要在机器人动作任务之外跑别和运动控制抢时间。这种方法和C语言里的select/poll模型其实是一个思路都是定期检查一堆连接谁有数据只是AS语言没有直接封装多路复用接口我们就自己用循环遍历实现简单但可靠。2.3 应用层协议先解决粘包和半包TCP本身是字节流它不保证一次RECV收到的就是一条完整指令也不保证一条指令不会被拆成两段传过来。这就是C语言网络编程中天天说的粘包和半包问题在AS语言里同样存在。解决办法是设计一个清晰的应用层协议。最简单实用的办法就是用换行符做指令分隔符每个指令以\n结尾。比如客户端发送“GETPOS\n”机器人收到后按换行切分取出一整条逻辑指令再处理。回复也统一用文本格式比如“OK:x100.5,y200.3,z50.0\n”或者错误时返回“ERR:UNKNOWN COMMAND\n”。不建议用固定长度报文。固定长度在C语言里用结构体处理很方便但在AS语言里要拼拆字符串、频繁处理字节很别扭。文本行协议简单直观还能直接在示教器上打印出来看内容现场排查问题方便得多。另外要约定一个心跳机制。现场工人拔网线、设备断电、网线接触不良客户端的断线可能不会立刻让服务器感知到。建议每个客户端每隔一段时间发一条心跳比如“PING\n”服务器收到后回“PONG\n”。服务器同时记录每个客户端最后心跳时间超过30秒没收到心跳就主动关闭对应连接给新客户端腾位置。3. AS语言实现完整代码与逐段拆解3.1 程序整体布局通信任务与运动任务分离川崎AS语言支持多任务这是整个方案能够落地的基础。请一定不要把TCP通信程序和机器人运动程序写进同一个任务。原因很简单网络收发有等待、有延迟一旦某个客户端的处理耗时较长会直接影响机器人运动指令的执行表现就是机器人走走停停。我的推荐布局是三个部分通讯任务单独一个程序负责开服务器、维护连接表、收发数据和解析指令。运动任务单独一个程序负责机器人所有的动作比如回原点、抓取、放置。二者之间通过全局变量通信。通讯任务解析出一条指令后把结果写进全局变量运动任务根据这个全局变量决定下一步要做什么。这样拆还有一个额外好处如果通讯任务因为某条异常数据卡住运动任务不会跟着停机器人至少能安全停下或者继续执行当前动作不至于完全失控。3.2 服务器初始化监听端口与参数选择先写服务器打开和初始化部分。程序名叫TCP_SRV作为后台任务启动。PROGRAM TCP_SRV DECLARE INT srvSock DECLARE INT connList[11] DECLARE INT connCount DECLARE INT i connCount 0 FOR i 0 TO 10 connList[i] -1 NEXT 打开TCP服务器端口端口号8000 srvSock OPEN_TCPSRV(8000) IF srvSock 0 THEN TYPE TCP server open failed RETURN END TYPE TCP server listening on 8000关于端口号有两个小建议。第一端口别选太小1024以下的端口很多系统有特殊用途现场设备也容易冲突8000、8080、9000这类都行。第二一定不要在同一个控制器上多个程序抢同一个端口否则后启动的程序会打开失败这类问题说排查也好排查但一开始规划好能省很多事。注意不同版本的川崎AS指令名会略有一点差异有的系统里OPEN_TCPSRV对应的是其他Socket套装指令逻辑不变写代码前对照一下你控制器AS手册里的网络指令表。3.3 维护连接表收新连接、清理断开连接服务器打开之后主循环要做两件事第一看有没有新客户端要连进来第二看已经连进来的客户端有没有断开有没有发数据。WHILE TRUE DO 尝试接受新连接0表示非阻塞方式 sockTmp ACCEPT(srvSock, 0) IF sockTmp 0 THEN IF connCount 10 THEN connCount connCount 1 connList[connCount] sockTmp TYPE Client connected: , sockTmp ELSE TYPE Connection table full, reject: , sockTmp CLOSE sockTmp END ENDACCEPT这里我用了非阻塞方式没有新连接时立刻返回无效值不会卡住主循环。新连接进来后判断连接表是否还有空位有空位就存进去没有空位就拒绝并关闭。连接表满这个逻辑一定要写。有一个工厂项目里最大客户端数量是3但连接表开到了10结果就是现场出现了好几个幽灵连接占用连接槽位后面正规设备反而连不进来。表容量按现场实际需求定不要无脑开大。3.4 数据接收与指令分发接下来是客户端连接的轮询和数据读取。遍历连接表时要注意不能在正序遍历的过程中随意删除表项否则会把还没检查的连接漏掉。我习惯从表尾往前遍历删除时把最后一个连接补到被删位置效率和安全都兼顾。FOR i connCount TO 1 STEP -1 recvData IF RECV(connList[i], recvData, 0) 1 THEN IF LEN(recvData) 0 THEN CALL HANDLE_CMD(connList[i], recvData) END ELSE TYPE Client disconnected: , connList[i] CLOSE connList[i] connList[i] connList[connCount] connCount connCount - 1 END NEXT 打印当前连接数方便调试 TYPE Current connections , connCount DELAY 50 END END注意RECV的返回值处理。收到数据返回成功标志客户端正常或异常断开返回断开标志。这个标志位的含义在不同系统里可能不太一样但有C语言基础的朋友应该秒懂这就是recv()返回0或负数的情况。收到数据后调用指令处理子程序处理完回到循环继续轮询。这里再强调一下缓冲区清空。每次RECV之前把字符串变量重新初始化为空字符串避免上一次的残留数据和新数据混在一起。这个问题在AS语言里特别容易踩因为字符串变量一旦赋值过有时不会自动清空。3.5 指令触发机器人动作与主任务联动通讯任务本身不做运动控制它把收到的指令解析成全局变量让运动任务去执行。例如收到“HOME”指令就把全局变量cmdReady置1cmdCode置为1收到“PICK”就把cmdCode置为2。运动任务里做对应的CASE判断。为了让这篇代码演示起来更直观我直接给一个简单版本指令处理子程序内部做解析并调用对应的运动子程序。实际项目你按3.1说的拆成全局变量驱动更稳。PROGRAM HANDLE_CMD(conn, data) DECLARE INT conn DECLARE STRING data DECLARE STRING cmd DECLARE STRING reply INT pos cmd data 去掉末尾换行符 WHILE LEN(cmd) 0 AND (RIGHT(cmd, 1) CHR(10) OR RIGHT(cmd, 1) CHR(13)) cmd MID(cmd, 1, LEN(cmd) - 1) END SWITCH cmd CASE HOME CALL MOVE_HOME reply OK:HOME CASE GETPOS reply OK: CURPOS_STR() CASE PICK CALL PICK_PART reply OK:PICK DEFAULT reply ERR:UNKNOWN COMMAND END SEND conn, reply END这里提供的CURPOS_STR是一个示意函数实际使用时你用自己的位姿转字符串函数替代。AS语言里处理字符串不像Python那么顺手遇到截取和清理字符这类操作千万别嫌繁琐调试时把收到的原始内容打出来看往往问题一目了然。3.6 完整参考代码把上面的片段整合起来加上运动子程序示意就是一套可以放到控制器上试跑的完整框架。 川崎AS语言多客户端TCP服务器 后台任务启动不影响主运动任务 以实际控制器AS指令手册为准微调 PROGRAM TCP_SRV DECLARE INT srvSock DECLARE INT connList[11] DECLARE INT connCount DECLARE INT i DECLARE INT sockTmp DECLARE STRING recvData connCount 0 FOR i 0 TO 10 connList[i] -1 NEXT srvSock OPEN_TCPSRV(8000) IF srvSock 0 THEN TYPE TCP server open failed RETURN END TYPE TCP server listening on 8000 WHILE TRUE DO sockTmp ACCEPT(srvSock, 0) IF sockTmp 0 THEN IF connCount 10 THEN connCount connCount 1 connList[connCount] sockTmp TYPE Client connected: , sockTmp ELSE TYPE Connection table full, reject: , sockTmp CLOSE sockTmp END END FOR i connCount TO 1 STEP -1 recvData IF RECV(connList[i], recvData, 0) 1 THEN IF LEN(recvData) 0 THEN CALL HANDLE_CMD(connList[i], recvData) END ELSE TYPE Client disconnected: , connList[i] CLOSE connList[i] connList[i] connList[connCount] connCount connCount - 1 END NEXT TYPE Current connections , connCount DELAY 50 END END PROGRAM HANDLE_CMD(conn, data) DECLARE INT conn DECLARE STRING data DECLARE STRING cmd DECLARE STRING reply cmd data WHILE LEN(cmd) 0 AND (RIGHT(cmd, 1) CHR(10) OR RIGHT(cmd, 1) CHR(13)) cmd MID(cmd, 1, LEN(cmd) - 1) END SWITCH cmd CASE HOME CALL MOVE_HOME reply OK:HOME CASE GETPOS reply OK: CURPOS_STR() CASE PICK CALL PICK_PART reply OK:PICK DEFAULT reply ERR:UNKNOWN COMMAND END SEND conn, reply END PROGRAM MOVE_HOME SPEED 50 JMOVE #HOME END PROGRAM PICK_PART SPEED 50 JMOVE #HOME JMOVE #PICK_POS DELAY 100 SIGNAL 10 DELAY 100 JMOVE #HOME END代码框架本身不复杂核心点在于连接表管理和非阻塞轮询。你拿到实际控制器上时可能只需要改几个指令名、调一下端口号整个多客户端能力就出来了。4. 保姆级部署与联调步骤4.1 第一步设置控制器网络参数在示教器上找到控制器的网络设置页面不同型号位置略有差异一般都在维护菜单里。给机器人控制器设置一个固定IP比如192.168.1.10子网掩码255.255.255.0网关按现场实际填或不填。这里有一个经验能设固定IP就一定要设固定IP绝不要依赖DHCP自动获取。产线上的IPC、相机、机器人控制器都需要互访DHCP分配地址一旦变化所有设备都要跟着改配置现场会非常被动。准备好之后用一台电脑直接网线连到控制器或者通过交换机接入同一网段在电脑上ping一下192.168.1.10能通再继续往下做。这一小步能排除大量网络配置层面的低级问题。4.2 第二步创建AS程序并启动后台任务把前面代码录入控制器的方式有两种。一种是在示教器菜单里新建AS程序文件手动逐行输入另一种是通过川崎的PC编程软件上传这个会快很多。有一点非常关键TCP_SRV这个程序不要用主任务直接启动。AS程序启动通常有主任务入口但你的主任务要留给运动控制程序。推荐在需要启动通讯时在任意任务或命令窗口执行RUN TCP_SRV这样TCP服务器就会作为后台任务运行。想停止通信任务时用对应的任务暂停或停止指令不同型号的AS指令名称不太一样搜一下手册里任务控制那一章即可。启动之后在示教器消息窗口能看到“TCP server listening on 8000”的字样说明监听已经起来了。如果看到“TCP server open failed”大概率是端口被占用或者网络服务未初始化好先把控制器重启一下再试。4.3 第三步用PC上位机模拟多客户端联调联调阶段我一般不用专业上位机程序去测先用Python脚本快速验证机器人的TCP服务器行为。这样功能确认没问题再让正式上位机介入大大减少扯皮。import socket import time clients [] for i in range(3): s socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((192.168.1.10, 8000)) s.sendall(bHOME\n) time.sleep(0.2) s.sendall(bGETPOS\n) data s.recv(1024) print(fclient {i}: {data.decode()}) clients.append(s) time.sleep(0.5) for s in clients: s.close()这个脚本做的事情很直接连续建立3个客户端连接每个连接都发一条HOME和GETPOS然后打印机器人返回的结果。如果3个客户端都能拿到OK开头的回复说明多客户端功能基本没问题。再多测一种情况让其中一个客户端不发送任何数据另一个客户端正常收发确认互不影响。再测一种直接把其中一个客户端的网线拔掉等一会重新连接确认服务器能识别断开并把连接表位置空出来。4.4 第四步观察连接数与响应稳定性在示教器消息窗口里我每次循环都会打印“Current connections N”这行输出就是最直观的连接数指标。多客户端接入时连接数会依次增加拔掉某个客户端网线后在心跳超时或断开检测生效后连接数会自动回落。观察响应稳定性时让客户端脚本循环发100次GETPOS统计机器人回复的时间间隔。正常情况下每个周期差异很小。如果发现某次响应特别慢就要考虑是不是轮询间隔太大或者连接表里有断线未清理的僵尸连接在占用循环时间。5. 常见问题排查与实战经验5.1 故障速查表症状可能原因解决办法客户端连接超时控制器IP不通或服务器程序未启动ping控制器IP确认TCP_SRV在运行服务器打开失败端口被占用或网络服务异常换端口或重启控制器第一个客户端能连第二个连不上使用了单连接阻塞模型或连接表已满改为连接表轮询查看当前连接数收到指令不完整或夹带旧数据缓冲区未清空或TCP半包粘包接收前清空字符串变量用换行符切分RECV后程序卡住用了阻塞模式改为非阻塞探测或单独后台任务处理机器人动作卡顿通信任务与运动任务混在一起拆分为后台任务通过全局变量联动客户端断开后连接数不下降断开检测不及时或心跳超时未清理加上心跳机制超时后强制关闭这张表基本覆盖了我在现场遇到过的绝大多数问题。如果你碰到的情况不在表里先不要急着怀疑控制器把示教器上打印的信息完整记录下来再结合抓包工具去分析。5.2 我在现场踩过的几个坑第一个坑是端口被占用的问题。有一回程序改完重新启动老是提示打开服务器失败一开始以为是代码问题查了半天才发现是上一次异常退出后控制器的网络连接还没有完全释放。重启控制器就好了。后来我养成了一个习惯在手动测试阶段尽量用同一个端口但不要反复热重启程序如果连续启动失败就干脆重启控制器。第二个坑是连接表里留下了“幽灵连接”。TCP连接有一个特性客户端断电或拔网线时服务器端不一定能立刻感知到。如果连接表里堆积了几个断开的连接真正的新客户端反而连不进来。解决方式就是前面说的心跳超时机制每个客户端周期性发送PING服务器端超过一定时间没有收到心跳就主动CLOSE。第三个坑和字符串处理有关。AS语言拼接和处理字符串时容易把换行符、回车符一起带进来。我在调试时打印收到的内容看着是一行命令实际上尾部带着\r\n。如果不做清理SWITCH匹配怎么都匹配不上客户端一直收到ERR。后来我在解析前统一把尾部换行回车清掉问题立刻消失。5.3 给C语言背景读者的快速对照如果你是做C语言网络编程出身现在转到川崎AS语言下面的对照表可以帮你快速切换思维。功能模块C语言实现川崎AS实现建立监听套接字socket bind listenOPEN_TCPSRV接受客户端连接acceptACCEPT接收数据recvRECV发送数据sendSEND关闭连接closeCLOSE多路复用select/poll连接表数组 循环轮询字符串清理手动处理指针、长度LEN、MID、RIGHT等内置函数并发任务pthreadRUN启动后台任务数据共享全局变量/线程锁全局变量标志位看到没有本质上都是同一套东西。AS语言在网络编程的数据结构上不如C语言灵活没有结构体、没有指针但处理简单文本协议反而更直接。你只需要适应它那种“所有变量都是全局级别”的思维方式不要试图在AS里写出C语言的复杂抽象。最后再分享一个个人习惯。每次做完这类TCP通信功能的项目我都会写一个最简单的心跳模拟脚本放在现场电脑上让它在调试期间持续连接机器人、定期发送PING。现场工人或工艺人员看到PONG回包就知道通信还活着一旦脚本报警不用等客户发现自己就能第一时间知道网络出问题了。这个习惯帮我提前预判过好几次故障省了不少半夜上门的麻烦。
返回列表