Skip to content

Communication Architecture

Data transmission between the perception host and the motion host is based on the UDP protocol, with raw data stored in little-endian byte order. On the perception host, the Message Transformer software package handles conversion between ROS messages and UDP messages:

  • Converts UDP messages from the motion host into ROS topic messages and publishes them; also sends motion control commands from the perception host to the motion host via UDP;
  • Receives control commands from the App to enable and disable certain AI functions on the perception host.

Message Transformer Architecture

For the target IP when sending messages to the motion host, refer to Section 4.1 of the Lite3_MotionSDK documentation. The target port is 43893. By default, the motion host only reports data to the perception host. To receive data reported by the motion host, refer to Sections 4 and 5 of the Lite3_MotionSDK documentation. SSH into the motion host and modify the target address for data reporting to the development host’s static IP.

First, the raw data format for UDP transmission is introduced. Based on whether complex data is carried, UDP commands can be divided into two categories: simple commands and complex commands.

The simple command format is [xxxx yyyy zzzz], where each letter represents one byte.

Simple commands use the CommandHead struct to store raw data:

struct CommandHead{
uint32_t code;
uint32_t paramters_size;
uint32_t type;
};
  • xxxx is the value of CommandHead.code, representing the command code.
  • yyyy is the value of CommandHead.paramters_size, representing the command value. When the command code has no valid command value, yyyy = 0.
  • zzzz is the value of CommandHead.type, used to distinguish command types. The type value for simple commands is 0, i.e., zzzz = 0.

Usage example:

// Example
struct CommandHead command_head = {0};
command_head.code = 1; // Command code
command_head.paramters_size = 0; // Command value
command_head.type = 0; // Command type
sendto(sfd, &command_head, sizeof(command_head), destination_address, address_length)

Complex commands carry specific data content, with the format [xxxx yyyy zzzz bbbbbbbb......], where each letter represents one byte.

Complex commands use the Command struct to store raw data:

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 is the value of Command.head.code, representing the command code.
  • yyyy is the value of Command.head.paramters_size, representing the length of the data content bbbbbbbb.......
  • zzzz is the value of Command.head.type, used to distinguish command types. The type value for complex commands is 1, i.e., zzzz = 1.
  • bbbbbbbb...... is the value of Command.data, representing the data content carried by the command.

Usage example:

// Example
int32_t to_be_send_data[64];
struct Command command = {0};
command.head.code = 52; // Command code
command.head.paramters_size = sizeof(to_be_send_data); // Data length
command.head.type = 1; // Command type
memcpy(&command.data, &to_be_send_data, sizeof(to_be_send_data));
sendto(sfd, &command, sizeof(command.head) + command.head.paramters_size, destination_address, address_length);

Taking joint velocity as an example:

struct RobotJointVel{
double joint_vel[12];
};
// Taking joint velocity as an example
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){ // Check if complex command
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){ // Check command code, parse data
RobotJointVel *joint_vel = (RobotJointVel *)temp_values;
}
}
}