ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

KUKA机器人KRL通讯编程:EthernetKRL与Socket实战及避坑指南

KUKA机器人KRL通讯编程:EthernetKRL与Socket实战及避坑指南

简介:面向工业自动化工程师与KUKA机器人程序员,系统讲解KRL语言通讯编程及与上位机网络交互的实现路径。内容涵盖KRL程序结构、任务与子程序组织,以及基于Net模块的TCP/UDP通信方法;针对NetSend、NetReceive等关键函数给出可参考示例,并说明网络配置、端口分配、数据格式与协议设计要点,帮助读者理解机器人与上位机之间的数据流与控制逻辑。资源包共12个文件,包含5个KRL源码文件、4份说明文档,以及数据、工程配置等辅助文件,源码可用于分析通讯流程,文档辅助理解设计思路,压缩包整体仅12KB,轻量易用。目前已有556人学习下载,适合正在入门或需要快速上手KUKA通讯编程的开发者作为参考资料。

1. PEV项目的KUKA机器人KRL通讯编程:先搞清楚你调的是哪条链路

PEV这个前缀具体是什么项目编号你可能查不到,也不需要查,它代表的是一类典型需求:把KUKA机器人控制器里的工艺数据、完成状态、报警码,用KRL写的通讯程序稳定送出去,再把上位机或PLC下发的任务号、参数收回来。这类活儿在现场一般就叫“KRL通讯编程”,看起来只是写几段收发代码,实际坑全藏在协议选型、变量声明和程序执行时机里。我见过太多人把时间耗在“明明连上了但数据不对”这种问题上,其实90%都出在KRL程序结构和通讯配置没对齐。这篇文章就按我调这类项目的顺序来写,适合机器人调试工程师、产线集成商和刚接手KUKA控制器的上位机开发,读完至少能少走两轮弯路。

2. KRL程序结构:SRC和DAT是通讯代码的两条腿

2.1 SRC与DAT文件的基本骨架

KUKA机器人的每个程序都由一对文件组成:SRC存放执行逻辑,DAT存放数据声明。写KRL通讯程序时,我一般先建好这对文件,再往里填通讯逻辑。下面是一个最小可用的SRC文件骨架,注意KRL的注释用分号开头。

&ACCESS RVO &COMMENT CM Communication Demo DEF CM_CommMain( ) ; 局部变量必须先声明后使用 DECL BOOL bExitFlag DECL INT iLoop bExitFlag = FALSE iLoop = 0 WHILE NOT bExitFlag ; 通讯读取和发送逻辑写在这个循环里 iLoop = iLoop + 1 WAIT SEC 0.05 ENDWHILE END

这段代码的逻辑很简单:定义了一个无限循环,循环里以后放通讯读写操作。&ACCESS RVO表示程序拥有读取外部值的权限,WAIT SEC 0.05是KRL里让出CPU的常用方式,避免通讯轮询把解释器跑满。KRL对变量声明很较真,所有变量必须在代码执行前声明完,不能像C语言或Python那样随手在循环里定义新变量。

对应的DAT文件长这样:

DEFDAT CM_CommMain ; 全局变量声明,PERS表示断电保持 PERS BOOL bCommActive = TRUE DECL GLOBAL INT giErrorCount ENDDAT

DAT文件与SRC同名,里面声明的变量对整个程序可见。PERS类型的变量在控制器断电重启后仍保留上次的值,这点对通讯状态恢复很有用。我刚接触KRL时犯过一个错:只建了SRC文件,没有建DAT文件,结果程序根本不能选择运行。KUKA的程序要求SRC和DAT成对出现,这算是KRL编程的第一个规矩。

2.2 变量声明规则:先声明后使用,按作用域分层

KRL的变量声明不像高级语言那么自由,它有明确的作用域约束。局部变量必须写在SRC文件DEF语句之后、END之前;全局变量必须写在DAT文件里;跨程序共享的变量要用GLOBAL修饰;想在多个程序间保持状态,就声明成PERS。

DEF CM_VarDemo( ) ; 局部变量的声明区 DECL INT iLocalCounter DECL REAL rTempValue DECL BOOL bFlag ; 在这里写业务逻辑 iLocalCounter = 0 rTempValue = 23.5 bFlag = TRUE END

注意这三行DECL的位置,它们必须紧跟在DEF语句后面,不能夹杂在其他可执行语句之间。KRL编译器对声明顺序有要求,老版本甚至要求DECL变量按字母序排列,虽然新版本放宽了,但养成集中声明的习惯能省很多编译报错的麻烦。

在通讯编程里,我一般把连接状态、重连次数、报文计数器这些变量声明为全局或PERS,因为它们需要在不同程序段、甚至不同SRC文件之间共享。比如某个后台程序负责维护通讯连接,主程序只在需要时读取连接状态标志,这种场景下DECL GLOBAL BOOL gbCommConnected就是最合适的选择。局部变量用于临时存放解析结果和处理中间值。

2.3 SIGNAL信号定义:把IO点映射成逻辑变量

KRL通讯不只是以太网和XML文件,硬接线IO点往往才是最基本的“通讯”方式。SIGNAL语句可以把控制器的物理输入输出映射成一个逻辑名称,让程序不用直接操作$IN[17]这种生硬地址。

DEFDAT CM_SignalDemo ; 定义数字量输入输出信号 SIGNAL DI_PLC_START $IN[17] SIGNAL DI_PLC_RESET $IN[18] SIGNAL DO_ROBOT_READY $OUT[5] SIGNAL DO_COMM_ACTIVE $OUT[6] ; 批量映射8位输入 SIGNAL DI_GROUP_1 $IN[1..8] ENDDAT

SIGNAL语句写在DAT文件的声明区。DI_PLC_START $IN[17]的意思是:把物理输入点17命名为DI_PLC_START,程序中可以直接读写这个名字。批量映射用$IN[1..8]语法,一次性把8个点绑定成一组,后续可以按整组读写。这种做法的价值在于:程序不关心信号到底接在哪个端子,只关心逻辑含义,现场改线时只需要改DAT文件,不用动SRC里的业务代码。

在通讯方案里,我通常把SIGNAL用作“握手信号”。比如以太网通讯建立成功后置位DO_COMM_ACTIVE,PLC收到这个信号才允许机器人启动自动运行;PLC通过DI_PLC_START通知机器人开始工作。这样做的好处是把实时性要求最高的启停逻辑放在硬接线层,把数据量大的工艺参数放在以太网层,两层各司其职,系统最稳。

3. 通讯方案选型:EthernetKRL、套接字还是总线IO

3.1 从IO到以太网:KUKA通讯链路的全景对比

KUKA机器人控制器的通讯手段看起来很杂,但归纳起来就三条路线:硬接线IO、现场总线IO、以太网数据交换。硬接线IO是最传统的,一根线一个点,实时性最好但信息量极少;现场总线IO用PROFINET、EtherNet/IP这类协议,把大量IO点压缩到一根网线里,实时性仍然很好,但本质还是在传开关量或整数;以太网数据交换走TCP/IP,用XML文件或自定义协议传字符串、实数、结构体,传什么都能商量。

通讯方式实时性传输内容布线复杂度KRL编程量典型场景
硬接线IO毫秒级开关量高低急停、安全门、启动确认
总线IO毫秒级开关量、整数低低与PLC交换IO点表
EthernetKRL毫秒到百毫秒级字符串、实数、XML节点低中与上位机/MES交换工艺数据
TCP/IP Socket毫秒级自定义字节流低高高速数据采集、自定义报文

我选型时的判断依据很简单:信号数量少且对实时性要求高,走硬接线或总线IO;数据量大、格式多、需要与PC软件交互,走EthernetKRL或Socket。PEV这类项目通常是机器人配合生产线工作,既有启停握手这类实时性高的信号,又有型号、批次、温度等工艺参数要交换,所以实际项目中往往是总线IO和以太网通讯混用,各管一段。

3.2 EthernetKRL与XML:最常见、最稳妥的以太网通讯方案

EthernetKRL是KUKA控制器自带的一种以太网通讯服务,配置放在XML文件里,KRL程序通过EKI_Open、EKI_GetString、EKI_SetReal这一组函数读写XML节点。外部设备通过TCP/IP连接控制器上开启的EthernetKRL服务端口,双方通过约定的XML结构交换数据。这个方案之所以最常见,是因为它把数据结构化了,不用自己纠结报文格式。

一个典型的EthernetKRL配置XML长这样:

<ETHERNETKRL> <CONFIGURATION> <EXTERNAL> <TYPE>Server</TYPE> <PORT>54600</PORT> <MAXLENGTH>1024</MAXLENGTH> </EXTERNAL> </CONFIGURATION> <ROBOTTOKRC> <SEND> <Variable> <Name>sRobotStatus</Name> </Variable> <Variable> <Name>rCurrentTemp</Name> </Variable> </SEND> </ROBOTTOKRC> <KRCTOROBOT> <RECEIVE> <Variable> <Name>sPartID</Name> </Variable> <Variable> <Name>rTargetTemp</Name> </Variable> </RECEIVE> </KRCTOROBOT> </ETHERNETKRL>

这个文件定义了数据交换的两个方向:ROBOTTOKRC段是机器人要发给外部设备的数据,KRCTOROBOT段是外部设备要写入机器人的数据。KRL程序通过变量名引用这些节点,比如EKI_SetString把sRobotStatus的值发出去,EKI_GetString从sPartID节点读取新数据。XML文件放到控制器指定目录后,由KRL程序打开并维护连接。

我一般把EthernetKRL当作首选方案,原因有三个:第一,XML本身带标签,调试时打开抓包软件就能看懂交互内容;第二,通讯连接由控制器的服务程序维护,断线重连有基础保障;第三,KRL侧读写接口简单,不需要自己组包拆包。代价是XML标签解析有开销,所以它不适合高频、大数据量的场景,更适合一秒钟交换几次、每次几十个字段这种典型产线需求。

3.3 什么时候换Socket:高频、自定义报文、上位机不好解析XML

EthernetKRL好用,但也有边界。如果外部设备是个简单的单片机或老式工控机,解析XML标签本身就够它喝一壶的;如果数据交换频率超过每秒几十次,XML解析就成了瓶颈;如果通讯对象和机器人之间需要来回握手确认、心跳检测,定制报文更灵活。这时候就该用TCP/IP Socket。

Socket在KRL里写起来比EthernetKRL繁琐,因为所有收发都是字节流,字符串要自己指定长度,收包要自己考虑半包粘包,但它给你完全的自由。比如一个自定义心跳协议,客户端发PING,服务器回PONG,用Socket就是几十行代码的事,用XML反而要定义两个节点再解析。

选型建议是:和设备通讯方是PLC或MES这种标准化系统,优先EthernetKRL;通讯方是自己开发的PC软件、单片机、视觉系统,且频率快或报文特殊,直接写Socket。还有一条经验:如果双方对通讯细节没有决定权,先用EthernetKRL把业务跑通,再在需要时改Socket,不要在项目初期就陷入报文设计。

4. 把通讯跑通:最小可用的KRL通讯程序长这样

4.1 在WorkVisual里激活EthernetKRL配置

EthernetKRL方案落地第一步不是在SRC里写代码,而是先在WorkVisual里把通讯配置加进去。我总结的步骤是:创建或导入机器人项目,在项目树的机器人系统下添加EthernetKRL选项,编辑生成的XML配置文件,设置服务类型和端口,最后将项目激活到控制器。

配置过程中的关键是选对<TYPE>。外部设备主动连机器人,就配Server模式;外部设备是被动接收方,就配Client模式。大多数产线场景是上位机作为客户端主动连接机器人,所以控制器侧配Server,端口默认常见值是54600,具体以你系统生成的模板为准。<MAXLENGTH>限制每条报文的字节长度,我一般设成1024,够传几十个字段了。

激活配置后必须在WorkVisual里执行“重启运行系统”或“冷启动”,新配置才生效。这一步经常被忽略,现场改完配置发现连接不上,十有八九是没重启。激活前要确认现场没有正在运行的自动程序,否则机器人会停下来,这点在产线上尤其重要。

4.2 用EKI函数读写XML数据段:核心代码与参数说明

配置激活后,KRL程序就可以使用EKI函数族了。下面是一段完整的通讯主程序,包含打开连接、循环读写、关闭连接三个环节。

&ACCESS RVO &COMMENT EthernetKRL Communication DEF CM_EthernetComm( ) DECL BOOL bOpened DECL INT iRet DECL STRING sPartID[32] DECL REAL rTargetTemp DECL REAL rRobotTemp ; 打开XML配置:参数1是配置名,参数2是服务角色 ; 配置名对应控制器User/Common/CSVXML目录下的XML文件名 bOpened = EKI_Open("CM_Config", "Server") IF bOpened == FALSE THEN ; 打开失败直接停机,便于现场发现 HALT ENDIF WHILE TRUE ; 读取上位机下发的内容:从KRCToRobot节点取字符串和实数 iRet = EKI_GetString("CM_Config", "/KRCToRobot/PartID", sPartID[]) iRet = EKI_GetReal("CM_Config", "/KRCToRobot/TargetTemp", rTargetTemp) ; 模拟业务处理:把目标温度写入机器人温度变量 rRobotTemp = rTargetTemp ; 把机器人状态和当前温度发回上位机 iRet = EKI_SetString("CM_Config", "/RobotToKRC/RobotStatus", "RUNNING") iRet = EKI_SetReal("CM_Config", "/RobotToKRC/CurrentTemp", rRobotTemp) ; 轮询间隔:根据现场节奏调整 WAIT SEC 0.1 ENDWHILE EKI_Close("CM_Config") END

这段代码的逻辑是:程序启动时打开EthernetKRL配置,进入无限循环,每次循环先从/KRCToRobot节点读取上位机写入的数据,再把机器人的状态和当前值写到/RobotToKRC节点,循环间隔100毫秒。EKI_GetString和EKI_GetReal的返回值是状态码,如果读取失败返回非零值,可以用它做错误处理。三个关键参数是:配置名必须和XML文件名一致,通道名必须和XML里的节点路径一致,字符串变量声明时要带长度。

这里最常见的问题是把EKI_Open配置名写成了带.xml后缀的文件路径。配置名指的是WorkVisual里添加EthernetKRL时定义的逻辑名称,不是磁盘文件名,两者对不上就打开失败。我踩过这个坑后,习惯在EKI_Open前面打印配置名,确认无误再往下写。

4.3 最小TCP/IP Socket通讯程序

Socket方案的KRL代码也不复杂,核心是创建套接字、连接、收发、关闭四个动作。下面是一个最小示例,机器人作为客户端连接外部服务器。

&ACCESS RVO &COMMENT Socket Communication DEF CM_SocketComm( ) DECL SOCKET sServer DECL INT iRet DECL STRING sSend[128] DECL STRING sRecv[128] ; 创建TCP套接字:协议类型、目标IP、目标端口 sServer = SocketCreate("TCP", "192.168.10.20", 3000) ; 发起连接 iRet = SocketConnect(sServer) IF iRet <> 0 THEN ; 连接失败,打印错误码后退出 HALT ENDIF ; 发送请求报文 sSend[] = "GET_STATUS" iRet = SocketSend(sServer, sSend[]) ; 等待接收响应,注意阻塞时间与超时设置 iRet = SocketReceive(sServer, sRecv[]) IF iRet > 0 THEN ; iRet大于0表示收到字节数,这里可以做报文解析 ; 机器人收到响应后继续业务逻辑 ENDIF ; 通讯结束关闭套接字 SocketClose(sServer) END

Socket函数的命名在不同控制器版本和选项包下有差异,但逻辑都是这套:SocketCreate指定协议和远端地址端口,SocketConnect建立连接,SocketSend和SocketReceive收发字节流,SocketClose释放连接。KRL的DECL SOCKET类型专门用来保存套接字句柄,程序声明时必须用这个类型,不能用INT代替。字符串收发时要注意长度,sSend[]和sRecv[]必须预先分配足够空间,收包前提前定义上限,防止缓冲区溢出。

Socket方案里最需要设计的是收发时序。如果程序在SocketReceive上死等,而上位机恰好不回包,机器人主程序会一直卡在那里,影响整个自动流程。我一般把SocketReceive放在后台提交程序里,或者用非阻塞方式轮询,主程序只在确认收到数据后才继续运动逻辑。

4.4 多字段数据交换:分隔符拼接比结构体更实用

EthernetKRL传多字段数据很方便,但Socket方案要传几十个字段时,一个个发太凌乱。我一般约定一个简单的文本协议:所有字段按固定顺序拼接,中间用分号分隔。KRL程序负责拆包解析。

DEF CM_ParsePacket(sPacket:IN, sFieldOut:OUT, iFieldIndex:IN) ; 按分号拆分字符串,iFieldIndex从1开始 DECL INT iPos DECL INT iStart DECL INT iLen DECL BOOL bDone iStart = 1 bDone = FALSE iFieldIndex = iFieldIndex - 1 WHILE NOT bDone ; 从当前位置找下一个分号 iPos = StrFind(iStart, sPacket[], ";", 0) IF iPos == 0 THEN ; 没有分号,说明是最后一个字段 iLen = Strlen(sPacket[]) - iStart + 1 iFieldIndex = 0 bDone = TRUE ELSE iLen = iPos - iStart ENDIF ; 截取当前字段 sFieldOut[] = StrPart(sPacket[], iStart, iLen) ; 移动起点到下一个字符 iStart = iPos + 1 ; 计数递减,处理目标字段 iFieldIndex = iFieldIndex - 1 IF iFieldIndex <= 0 THEN bDone = TRUE ENDIF ENDWHILE END

这段代码实现的是最简单的分隔符解析:在收到的字符串里反复找分号,每找到一个分号就截取一段,直到拿到指定索引的字段。StrFind四个参数分别是起始位置、源字符串、查找模式、方向;StrPart三个参数是源字符串、起始位置、长度。这套函数组合是KRL文本处理的基础,解析任何自定义文本协议都会用到。

用分隔符而不是二进制结构体,是因为KRL的字符串操作直观、调试时能直接看出内容,而且编码问题少。缺点在于字段值本身不能包含分号,否则会拆错。我的处理办法是业务上约定不允许传输含分隔符字符,或者先把值做转义。如果你传输的数据包含特殊字符,就得多写一段转义逻辑,这属于文本协议的通病,但相比结构体的字节对齐和大小端问题,文本协议在现场仍然更好排错。

5. KRL通讯编程避坑:5个现场踩过的典型问题

5.1 现象:EthernetKRL服务起不来,外部设备连不上端口

原因:XML配置文件没放到控制器指定目录,或配置文件名和EKI_Open里写的不一致。现场最典型的场景是开发机修改了XML,部署时漏了上传,控制器还在用旧配置,或者服务配置从未激活过。

解决:先在WorkVisual里确认EthernetKRL已添加到项目树,检查配置角色和端口号。再到控制器文件系统里找到User/Common/CSVXML目录,确认XML文件存在且文件名和程序里一致。改动配置后务必在WorkVisual里激活并重启运行系统,不要只拷贝文件不激活。

5.2 现象:连接正常,但程序读到的数据永远是旧值或初始值

原因:KRL程序里EKI函数的通道名与XML节点不匹配,或者XML节点虽然有定义,但外部设备写入的变量名与配置文件不一致。EthernetKRL工作在一个XPath寻址模式下,节点路径写错一个字母,读取就静默失败,函数返回错误码但程序没处理。

解决:把每个EKI_Get调用后的返回值打印到变量或输出缓冲区,看是不是非零。对照XML文件检查通道名路径,特别注意大小写和斜杠方向。在程序开头增加一次校验,打开配置后立刻读一个已知默认值的节点,读不到就报警停机,避免带病运行。

5.3 现象:Socket连接能建立,但交互几秒钟后连接被重置或挂死

原因:最常见的是防线外的中间设备干预,比如交换机端口配置了空闲超时,或者防火墙拦截了非活跃连接。另一个原因是上位机侧没有按约定的心跳机制回包,KRL程序在等响应,上位机在等指令,双方互相死等。

解决:给Socket通讯增加心跳机制,机器人每隔固定时间发送心跳报文,上位机收到后立即回应,超过一定次数没收到回应就主动断开重连。用抓包工具确认断开方向上是谁发起了FIN或RST,判断是主动断还是被动断。KRL侧把Socket调用放到后台提交解释器里,主程序通过共享变量感知连接状态,不要把主逻辑阻塞在收发上。

5.4 现象:上位机收到的中文字符串全是乱码

原因:KRL的STRING类型本质是单字节字符序列,而现代上位机软件多数按UTF-8处理文本。机器人侧把中文按本地编码发出,上位机按UTF-8解码,两边码表不一致就乱码。这在传递工件名称、操作员姓名时尤其常见。

解决:最省事的办法是业务上约定通讯内容只用ASCII字符,中文信息用编号或拼音代替。如果必须传中文,上位机侧按控制器发送的编码方式解码,不要用默认的UTF-8硬解。也可以走Base64编码,但KRL侧手写Base64比较麻烦,我一般宁可改业务约定,也不在机器人侧折腾字符集转换。

5.5 现象:通讯程序一运行,机器人运动就卡顿或直接进入STOP状态

原因:把阻塞型通讯操作放在了运动主程序的关键路径上。KRL主程序里如果有SocketReceive死等,或EKI读取频率过高占用解释器资源,运动指令的执行就会被延迟。某些通讯异常还会触发控制器系统性的停止反应。

解决:把通讯循环放到独立的后台提交程序(Submit)里,主程序通过共享变量和它交互。后台程序负责收发和解析,主程序只在状态标志位变化时才读取结果。EKI或Socket调用周围要加错误处理,通讯失败时置位错误标志并停止后续尝试,不要连续快速重连。轮询间隔不要低于20毫秒,给运动插补让出足够的计算时间。

6. 验证通讯与进阶调试:用抓包和模拟端把链路看穿

6.1 先用抓包确认链路状态,再谈业务逻辑

通讯程序写完别急着联调业务,先确认链路本身是通的。我会在PC上用抓包工具抓控制器和外部设备交互的TCP包,重点看三件事:连接是否建立成功、报文内容是否符合约定、断开时是谁先发起的。抓包只需要在通讯端口上过滤,不用看全部流量。如果抓包显示数据包正常往返,但业务值不对,问题就出在KRL程序逻辑或XML节点映射上;如果数据包压根没到,问题在网络配置或服务没启动。用这个分层排查法,能省掉大量翻代码的时间。

6.2 模拟外部设备:不接PLC也能全流程自测

在产线停机时段不好折腾PLC和上位机,我习惯用PC写一个极简的模拟端。如果是EthernetKRL方案,模拟端就是一个TCP客户端,连接控制器端口后按XML格式组织报文发送和接收;如果是Socket方案,模拟端就是一个socket服务器,收到请求后按约定返回预设响应。模拟端的价值在于可以任意构造异常场景,比如断线重连、延迟回包、发半个包,把KRL侧的健壮性验证到位。自测通过后再和真实PLC或MES联调,能减少一半以上的现场扯皮。

6.3 收尾习惯:把通讯状态做成机器人看得见的变量

我最后总是会在HMI或示教器上留一个通讯状态监视页,显示连接状态、最近一次收发时间、错误计数这三个值。连接断开和正常收发的区别一眼就能看出来,不用每次连调试线查日志。这个习惯帮我省了无数次现场排查时间。现在每到一个新现场,我第一件事就是先确认通讯协议在哪一层跑、回包长什么样、断了之后谁先重连。把这些弄明白,写KRL通讯编程就只是体力活了。希望帮到你。

本文还有配套的精品资源,点击获取

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进