HandEyeTCPServer 手眼标定TCP服务器

HandEyeTCPServer 算子介绍视频教程

HandEyeTCPServer 算子属于线程类算子,用于在 RVS 的分线程中启动一个 TCP server 服务器。

该算子主要应用于手眼协作过程(当前用的最多的是拆垛场景)中, RVS 同机械手(或其他工控机程序)的通信。该服务器仅能接收固定的 8 类字符串信息并对应的回复固定字符串。

8 类字符串分别以 8 个命令作为起始值,分别是 ROBOT_TCP 、ROBOT_JOINTS 、CAPTURE 、CAPTURE_MESSAGE 、GET_JOINT 、GET_POSE 、GET_POSES 、GET_POSES_NEXT 。

说明:代码块中命令参数设置:command_delimiter →space、delimiter → RT,均为默认值。请根据实际场景进行调整。

其中,如果客户希望获得多个目标 pose 姿态列表,可以修改 HandEyeTCPServer 的 number_port 属性数量。客户端仍然按照下述 GET_POSES 的方式发送消息,但是此时回复的消息起始位就不再是 1 ,而是 poselist 的数量,后面跟每一个 poselist 中包含的 pose 数量。

GET_POSES \n
  1. 客户端发送以命令字符 ROBOT_TCP 开始的消息,要求在该 ROBOT_TCP 命令字符后紧跟 command_delimiter+'x y z rx ry rz'+delimiter 。接收到该信息后,该算子(即服务端)会将命令字符后续的 x y z rx ry rz 字符内容转换为 6 个 float 数值,进而赋值给一个 pose 。

    如果上述字符串消息可以合法转换为 6 个 float 则会触发执行该算子右下侧的 tcp 数据端口,并通过算子右下侧的 tcp 端口输出所得的 pose,如果字符串消息不合法,则不进行任何后续响应。

    我们实际使用时,需要将该算子右下侧 ROBOT_TCP 端口后续的触发结束信号返还连接到该算子的左侧 ROBOT_TCP 端口,因为只有成功触发该端口才会执行 TCP 服务端的消息回复。

    对于命令字符 ROBOT_TCP”开始的消息访问,最终统一回复 ROBOT_TCP

    客户端发送:

    ROBOT_TCP x y z rx ry rz \n
    

    算子右侧 tcp 端口输出:

    x y z rx ry rz
    

    服务端回复:

    ROBOT_TCP
    
  2. 客户端发送以命令字符 ROBOT_JOINTS 开始的消息,要求在该 ROBOT_JOINTS 命令字符后紧跟 command_delimiter+'J1 J2 J3 J4 J5 J6'+delimiter

    接收到该信息后,服务端会将命令字符后续的 J1 J2 J3 J4 J5 J6 字符内容转换为 6 个 float 数值,进而赋值给一个 JointArray 。

    如果上述字符串消息可以合法转换为 6 个 float 则会触发执行该算子右下侧的 joints 数据端口,并通过算子右下侧的 joints 端口输出所得的 JointArray ,如果字符串消息不合法,则不进行任何后续响应。

    我们实际使用时,需要将该算子右下侧 ROBOT_JOINTS 端口后续的触发结束信号返还连接到该算子的左侧 ROBOT_JOINTS 端口,因为只有成功触发该端口才会执行 TCP 服务端的消息回复。

    对于命令字符 ROBOT_JOINTS开始的消息访问,最终统一回复 ROBOT_JOINTS

    客户端发送:

    ROBOT_JOINTS J1 J2 J3 J4 J5 J6 \n
    

    算子右侧 joints 端口输出:

    J1 J2 J3 J4 J5 J6
    

    服务端回复:

    ROBOT_JOINTS
    
  3. 客户端发送以命令字符 CAPTURE 开始的消息,要求在该 CAPTURE 命令字符后紧跟 command_delimiter+ 任意内容 +delimiter

    接收到该信息后,该算子会自动触发右下侧的 CAPTURE 端口,进而触发其他后续算子进行运算。

    我们实际使用时,需要将该算子右下侧 CAPTURE 端口后续的触发结束信号返还连接到该算子的左侧 CAPTURE 端口,因为只有成功触发该端口才会执行 TCP 服务端的消息回复。

    对于命令字符 CAPTURE 开始的消息访问,服务端对命令字符后续的其他字符内容不做任何处理,并最终统一回复 “CAPTURE” 字符串消息。

    客户端发送:

    CAPTURE \n 或者 CAPTURE any other message\n
    

    服务端回复:

    CAPTURE
    
  4. 如果需要在 CAPTURE 命令字符消息中发送一些其他字符串给 RVS ,则需要使用 CAPTURE_MESSAGE ,并要求在该 CAPTURE_MESSAGE 命令字符后紧跟 command_delimiter+ 其他内容 +delimiter

    使用原理同上述第 3 条 ,不同的是,这些从客户端发来的其他内容的字符串会通过算子右下侧的 capture_str 端口传递到 RVS 。

    假设项目现场有两台相机一左一右。

    通过给RVS 发送以下命令表示拍摄左侧相机:

    CAPTURE_MESSAGE 0 \n
    

    通过给RVS 发送以下命令表示拍摄右侧相机:

    CAPTURE_MESSAGE 1 \n
    
  5. 客户端发送以命令字符 GET_JOINT 开始的消息,要求在该 GET_JOINT 命令字符后紧跟 command_delimiter+ 其他内容 +delimiter 。接收到该信息后,该算子会自动触发右下侧的 GET_JOINT 端口,进而触发其他后续算子进行运算。

    我们实际使用时,需要将该算子右下侧 GET_JOINT 端口后续的触发结束信号返还连接到该算子的左侧 GET_JOINT 端口,因为只有成功触发该端口才会执行 TCP 服务端的消息回复。

    对于命令字符 GET_JOINT 开始的消息访问,服务端对命令字符后续的其他字符内容不做任何处理。当左侧 GET_JOINT 端口被触发执行消息回复时,如果算子左侧的 all_finished 端口已被触发,则会回复 000000000 000000000 000000000 000000000 000000000 000000000 2 字符串信息,如果算子左侧的 detect_error 端口已被触发,则会回复 000000000 000000000 000000000 000000000 000000000 000000000 3 字符串信息,如果算子左侧的 all_finished 端口和 detect_error 端口均未被触发,则算子内部会检查算子左侧的 joint 端口是否被连接,如果该 joint 端口有连接并且数值有效,则会回复 J1 J2 J3 J4 J5 J6 1给客户端。

    注意

    末尾的1,所有回复字符串的末尾均是标志位。

    1 表示获得正常数值。

    2 表示虽然回复结果无效但是成功结束。

    3 表示回复结果无效且失败。

    另外,字符串前面的六位数值都保留 9 位有效数字,所以为了对齐字符串长度,所有无效数值也都用九个 0 表示。单位:弧度制。

    如果 joint 端口没有连接或者数值无效,则会回复 `000000000 000000000 000000000 000000000 000000000 000000000 3` 给客户端。以command_delimiter 参数设置为逗号,delimiter 参数设置为分号为例,左侧 joint 端口输入 JointArrayJ(0 0 0 3.14159 -1.5709 0) ,则当客户端发送 `GET_JOINT,;` 时,则最终服务端会回复 `+0.000000 +0.000000 +0.000000 +3.141590 -1.570900 +0.000000 1` ,算上中间的空格,字符串总长度是 61 位。

    客户端发送:

    GET_JOINT 其他内容 \n
    
  6. 客户端发送以命令字符 GET_POSE 开始的消息,原理同上述第 5 点 GET_JOINT 的说明相同。

    客户端发送:

    GET_POSE command_delimiter 其他内容 delimiter
    
  7. 客户端发送以命令字符 GET_POSES 开始的消息,要求在该 GET_POSES 命令字符后紧跟 command_delimiter+ 其他内容 +delimiter。接收到该信息后,该算子会自动触发右下侧的 GET_POSES 端口,进而触发其他后续算子进行运算。

    我们实际使用时,需要将该算子右下侧 GET_POSES 端口后续的触发结束信号返还连接到该算子的左侧 GET_POSES 端口,因为只有成功触发该端口才会执行 TCP 服务端的消息回复。对于命令字符 GET_POSES 开始的消息访问,服务端对命令字符后续的其他字符内容不做任何处理。

    GET_POSES 其他内容 \n
    

    该命令字符的作用:通过 RVS 软件的服务端将一个乃至多个 PoseList 发送给客户端。具体的 PoseList 的数量可以根据算子属性 number_port 进行设置,该属性默认是 1,对应算子左侧的 poses_1 端口。一旦更新该属性比如更改为 3 ,则算子左侧会新增 poses_2 、poses_3 端口。

    服务端最终回复的信息格式是:

    PoseList 的总个数   每个 PoseList  pose 个数   所有 pose 的实际数值   标志位
    

    最后的标志位包含 0、1、2、3 四种情形。

    • 0:数据正常且发送完毕

    • 1:数据正常但尚未发送完

    • 2:数据无效但正常运行

    • 3:数据无效且运行异常

    当总共有 3 组 PoseList (各自含有2、1、2个 pose )需要发送时,最后回复给客户端的字符串为:

     3 2 1 2 poselist1_pose1_x  poselist1_pose1_y poselist1_pose1_z poselist1_pose1_rotated_x  poselist1_pose1_rotated_y  poselist1_pose1_rotated_z ...... poselist3_pose2_x  poselist3_pose2_y poselist3_pose2_z poselist3_pose2_rotated_x  poselist3_pose2_rotated_y  poselist3_pose2_rotated_z 0
    

    但是由于传送的数据量比较大,所以算子设置了每次回传信息的最大字节长度,对应算子属性 receive_byte_length 。

    • 当上述字符串字节 (不包含末尾的空格和 0 )总长度小于等于 receive_byte_length-2 时,即直接回复上述字符串。

    • 若大于receive_byte_length-2 ,则会将上述字符串拆分,仅截取字符串前面长度为 receive_byte_length-2 的一段,然后在该截取片段的末尾加上空格和 1 回复给客户端。客户端接收到末尾为 1 的字符串后,需要立刻继续向本算子发送 GET_POSES_NEXT 的请求,此时服务端会将上述字符串剩余的部分继续截取一段长度为 receive_byte_length-2 的字符串并补充空格和 1 ,继续回复给客户端,循环该过程直到发送完成为止,最后一次发送的字符串长度不一定是 receive_byte_length ,但是其末尾一定是补充的空格和 0 。

    根据上述规则,当左侧 GET_POSES 端口被触发执行消息回复时,如果算子左侧的 all_finished 端口已被触发,则会回复 0 2 字符串信息,如果算子左侧的 detect_error 端口已被触发,则会回复 0 3 字符串信息,如果算子左侧的 all_finished 端口和 detect_error 端口均未被触发,则算子内部会检查算子左侧从poses_1 端口到 pose_n 端口中是否有连接以及有效数据,如果有至少一个端口有有效数据,则会按照上述过程回复客户端,如果所有端口都没有有效数值,则会回复 0 3给客户端。

  8. 客户端发送以命令字符 GET_POSES_NEXT 开始的消息,要求在该 GET_POSES_NEXT 命令字符后紧跟 command_delimiter+ 其他内容 +delimiter 。这里的其他内容为无效数值。该 GET_POSES_NEXT 命令字符是 GET_POSES 命令字符的补充,直接单独发送该命令字符串没有意义,使用方法详见上述 GET_POSES 的使用介绍。

    GET_POSES_NEXT 其他内容 \n
    

算子参数

  • 端口/port:创建TCP server服务器所使用的本机服务端口。

    说明:实际运行时,如果提示 TCP 服务端创建失败,往往是所选取的端口已经被其他服务所占用,更换端口即可。如果在同一个 RVS 软件中,创建多个 TCP 服务端算子,彼此的 port 也要互斥。

  • 连接数量/connections:可支持同时连接的客户端数量。

  • 服务器模式/server_mode:运行模式。

    • Once:表示完成一次 TCP 对话以后自动断开同客户端的连接(比如客户端首先同该算子建立了 TCP 连接,然后第一次发送了 ROBOT_TCP 命令的字符串之后,继续发送 ROBOT_JOINTS 命令的字符串,此时就会报错)。

    • Continous: 表示客户端建立链接后可以无限次数的对话。

  • 分割符/delimiter:消息结束符。包含 RT 换行符、“;”、“#”、“$”。当选择了其中某一种时,另外三种就会被视作普通字符随意使用 。服务端在接收客户端发送的消息时,会捕获第一个消息结束符之前的所有消息。

    注意:由于不同的通信软件、电脑系统软件、机器人操作系统软件对回车换行的定义不一致,一般不建议选择RT作为消息结束符。

  • 命令分割符/command_delimiter:命令结束符。当选择了其中某一种时,另外一种就会被视作普通字符随意使用。服务端在接收客户端发送的消息时,会将第一个命令结束符之前的所有消息视为命令字符,之后的消息视为内容信息。如果命令字符不是上述功能总结部分的 8个命令中的任意一个,则是无效命令,不做任何后续处理与响应。

    • Space:空格。

    • , :逗号。

  • 回复结束符/arc_eof:回传值结束符。当输入该值时,在回传值结尾添加结束符。默认为空。

  • 机器人TCP/tcp:设置通过 ROBOT_TCP 命令消息接收到的 pose 可视化属性。

    • icon_visOn 打开 pose 可视化。

    • icon_visOff 关闭 pose 可视化。

    • icon_size 设置坐标的尺寸大小。取值范围:[0.001,10] 。默认值:0.1 。

  • 拍摄信息/capture_str:设置曝光属性。打开后则可以将 capture_str 输出端口的内容绑定到交互面板上的文本框并输出显示。

    • icon_visOn 打开曝光。

    • icon_visOff 关闭曝光。

  • 输入数量/number_port: 仅供发送 GET_POSES 命令字符串时使用,详见上述功能总结部分的第7条。

  • 接受字节长度/receive_byte_length:仅供发送 GET_POSES 命令字符串时使用,详见上述功能总结部分的第7条。默认值:1024。单位:byte。取值范围:[1,+∞]。

注意:在算子运行后,重新更改了 port 、connections 、server_mode 、delimiter 、command_delimiter、arc_eof 6个属性中的任意一个之后,如果需要生效,都必须重启或者重置 RVS 。

控制信号输入输出

输入:

  • start

    • 触发 start 信号端口运行算子,开始创建 TCP 服务端,创建成功后监听服务端口并等待客户端访问。

      说明:本算子仅需要初始化运行一次即可。

  • stop

    • 触发 stop 信号端口后,开始停止 TCP 服务端的监听。

  • reset

    • 该功能保留。

输出:

  • finished

    • 该功能保留。

  • failed

    • 算子运行失败后触发该端口。

  • started

    • 算子成功建立TCP服务端后触发该端口。

  • processing

    • 该功能保留。

  • stopped

    • 算子成功停止TCP服务端的监听后触发该端口。

  • reset

    • 该功能保留。

功能演示

下面分别针对 8 类命令,进行通讯演示。

在选用演示所用的 TCP 客户端时,由于 RVS 自带的 CommonTCPClient 算子在使用时要求服务端返回的信息必须以四类固定字符结尾,而 HandEyeTCPServer 算子在作为 TCP 的服务端时,返回给 TCP Client 的信息是固定的并且没有额外添加上述四类固定结尾字符,所以这里不再选用 CommonTCPClient 算子作为客户端。

说明:这里的演示案例是基于ubuntu系统给出的,使用了"echo + nc "的指令方式实现TCP客户端的功能;如果是在Windows版本,建议使用通讯助手等工具。

步骤1:算子准备

右击创建新 Group ,添加 Trigger、Emit、Concatenate、HandEyeTCPServer 算子至 Group 算子图。

步骤2:设置算子参数

  1. 设置 Trigger 算子参数:

    • 类型 → InitTrigger

  2. 设置 HandEyeTCPServer 算子参数:

    • 分隔符 → ;

    • 命令分隔符 → ,

    • 回复结束符 → end

  3. 设置 Concatenate 算子参数:

    • 类型→ pose

    • 输入数量 → 3

  4. 设置 Emit 算子参数:

    • 算子名称 → Message

    • 类型 → String

步骤3:连接算子

打开RVS软件界面,在算子图拖入下图所述算子并连接。由于选用了“echo + nc”的指令方式,所以这里每发送-接收一次消息后都要断开连接,所以选择了Once模式。 Communication_HandEyeTcpServer_nodes

步骤3:运行与结果

  1. 打开 RVS 的运行按钮,HandEyeTCPServer 算子会被 Trigger 算子自动触发,并变为蓝色,日志栏会同时打印算子运行说明如下图所示,表示TCP的服务端已经建立完毕。

    Communication_HandEyeTcpServer_result1

  2. 重新打开一个终端,输入命令 echo “ROBOT_TCP,0.1 0.2 0.1 3.14159 1.5709 1;” | nc localhost 2013 并单击回车键,运行结束后,我们会在这个终端窗口看到回复字符串如下图所示。

    Communication_HandEyeTcpServer_result2

    • 同时 RVS 的运行日志如下。

      Communication_HandEyeTcpServer_result3

  3. 在终端中,继续输入命令echo “ROBOT_JOINTS,0 0 0 3.14159 1.5709 0;” | nc localhost 2013并单击回车键,运行结束后,我们会在这个终端窗口看到回复字符串如下图所示。

    Communication_HandEyeTcpServer_result4

    • 同时RVS的运行日志如下。

      Communication_HandEyeTcpServer_result5

  4. 在终端中,继续输入命令echo “CAPTURE,;” | nc localhost 2013并单击回车键,运行结束后,我们会在这个终端窗口看到回复字符串如下图所示。

    Communication_HandEyeTcpServer_result6

    • 同时RVS的运行日志如下。

      Communication_HandEyeTcpServer_result7

  5. 在终端中,继续输入命令echo “CAPTURE_MESSAGE,0;” | nc localhost 2013并单击回车键,运行结束后,我们会在这个终端窗口看到回复字符串如下图所示。

    Communication_HandEyeTcpServer_result8

    • 同时RVS的运行日志如下。

      Communication_HandEyeTcpServer_result9

  6. 在终端中,继续输入命令echo “GET_JOINT,;” | nc localhost 2013并单击回车键,运行结束后,我们会在这个终端窗口看到回复字符串如下图所示。

    Communication_HandEyeTcpServer_result10

    • 同时RVS的运行日志如下

      Communication_HandEyeTcpServer_result11

  7. 在终端中,继续输入命令echo “GET_POSE,;” | nc localhost 2013并单击回车键,运行结束后,我们会在这个终端窗口看到回复字符串如下图所示,同时RVS的运行日志如下。

    Communication_HandEyeTcpServer_result_10

    • 同时RVS的运行日志如下

      Communication_HandEyeTcpServer_result13

  8. 在终端中,继续输入命令echo “GET_POSES,;” | nc localhost 2013并单击回车键,运行结束后,我们会在这个终端窗口看到回复字符串如下图所示

    image-20230619170731529

    • 同时RVS的运行日志如下

      Communication_HandEyeTcpServer_result15