通信架构
UDP 通信
Section titled “UDP 通信”感知主机与运动主机之间的数据传输基于 UDP 协议,以小端字节序存放原始数据。感知主机上通过**消息转换器(Message Transformer)**软件包实现 ROS 消息与 UDP 消息之间的转换:
- 将运动主机发送的 UDP 消息转换为 ROS 话题消息并发布,并通过 UDP 将感知主机下发的运动控制指令发送给运动主机;
- 接收来自 App 的控制指令,以开启和关闭感知主机上的部分 AI 功能。

开发者向运动主机发送消息时的目标 IP 请参考 Lite3_MotionSDK 说明第 4.1 节,目标端口为 43893。运动主机默认只向感知主机上报数据,开发者若需接收运动主机上报的信息,可参考 Lite3_MotionSDK 说明第 4 节和第 5 节,通过 SSH 远程连接运动主机,将运动主机发送数据的目标地址修改为开发主机的静态 IP。
首先介绍 UDP 传输的原始数据格式。根据是否携带复杂数据,可以将 UDP 指令分为两类:简单指令和复杂指令。
简单指令格式为 [xxxx yyyy zzzz],一个字母表示一个字节。
简单指令采用结构体 CommandHead 存储原始数据内容。
struct CommandHead{ uint32_t code; uint32_t paramters_size; uint32_t type;};xxxx是CommandHead.code的值,表示指令码。yyyy是CommandHead.paramters_size的值,表示指令码的指令值,当指令码没有有效指令值时,yyyy = 0。zzzz是CommandHead.type的值,用于区分指令类型,简单指令的类型值为 0,即zzzz = 0。
使用案例如下:
// 例程struct CommandHead command_head = {0};command_head.code = 1; // 指令码command_head.paramters_size = 0; // 指令值command_head.type = 0; // 指令类型sendto(sfd, &command_head, sizeof(command_head), 目标地址, 地址长度)复杂指令携带具体的数据内容,其格式为 [xxxx yyyy zzzz bbbbbbbb......],一个字母表示一个字节。
复杂指令采用结构体 Command 存储原始数据内容。
struct CommandHead{ uint32_t code; uint32_t paramters_size; uint32_t type;};
const uint32_t kDataSize = 256;
struct Command{ CommandHead head; uint32_t data[kDataSize];};xxxx是Command.head.code的值,表示指令码。yyyy是Command.head.paramters_size的值,表示数据内容bbbbbbbb......的长度。zzzz是Command.head.type的值,用于区分指令类型,复杂指令的类型值为 1,即zzzz = 1;bbbbbbbb......是Command.data的值,表示该指令携带的数据内容。
使用案例如下:
// 例程int32_t to_be_send_data[64];struct Command command = {0};command.head.code = 52; // 指令码command.head.paramters_size = sizeof(to_be_send_data); // 数据长度command.head.type = 1; // 指令类型memcpy(&command.data, &to_be_send_data, sizeof(to_be_send_data));sendto(sfd, &command, sizeof(command.head) + command.head.paramters_size, 目标地址, 地址长度);复杂指令接收
Section titled “复杂指令接收”以关节速度为例,使用案例如下:
struct RobotJointVel{ double joint_vel[12];};
// 以关节速度为例struct CommandMessage{ CommandHead command; uint8_t data_buffer[1024];};
CommandMessage cm;size_t recv_size = 0;recv_size = recvfrom(server_fd, &cm, sizeof(cm), 0, (sockaddr*)&client_addr, &sockaddr_in_size);
if(cm.command.type == 1){ // 判断为复杂指令 if(recv_size == cm.command.paramters_size + sizeof(cm.command)){ uint8_t* temp_values = new uint8_t[cm.command.paramters_size]; memcpy(temp_values, cm.data_buffer, cm.command.paramters_size); if(cm.command.code == 0x0903){ // 判断指令码,解析数据 RobotJointVel *joint_vel = (RobotJointVel *)temp_values; } }}