跳转到内容

通信架构

感知主机与运动主机之间的数据传输基于 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, 目标地址, 地址长度);

以关节速度为例,使用案例如下:

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;
}
}
}