Skip to content

Hardware Bridge Layer

lite3_transfer is the low-level hardware and ROS2 bridge layer for Lite3, responsible for:

  • Communicating with Lite3 robot firmware via UDP (using Lite3_MotionSDK)
  • Converting robot state to ROS2 topics (/JOINTS_DATA, /IMU_DATA)
  • Converting ROS2 control commands to UDP commands (/JOINTS_CMD)
  • Reading Retroid gamepad data and publishing to /GAMEPAD_DATA
  • Providing /SDK_MODE and /EMERGENCY_STOP services
Terminal window
# ros2 node info /lite3
/lite3_transfer
Subscribers:
/JOINTS_CMD: drdds/msg/JointsDataCmd
Publishers:
/IMU_DATA: drdds/msg/ImuData
/JOINTS_DATA: drdds/msg/JointsData
/EMERGENCY_STOP_SIGNAL: drdds/msg/StdMsgInt32
Service Servers:
/SDK_MODE: drdds/srv/StdSrvInt32
/EMERGENCY_STOP: drdds/srv/StdSrvInt32

lite3_transfer defines its own RobotInterface abstract base class, which is independent of (and has no inheritance relationship with) the same-named class in Lite3_sdk_deploy. It is oriented toward the hardware bridge layer responsibilities, defining interfaces for reading state from UDP and publishing ROS2 topics.

std::string robot_name_; // Robot name
const int dof_num_; // Degrees of freedom count
Eigen::Matrix<float, Eigen::Dynamic, 5> joint_cmd_; // Joint command matrix
std::atomic<bool> start_flag_; // Start flag
virtual void Start() = 0;
virtual void Stop() = 0;
virtual double GetInterfaceTimeStamp() = 0;
virtual VecXf GetJointPosition() = 0;
virtual VecXf GetJointVelocity() = 0;
virtual VecXf GetJointTorque() = 0;
virtual Vec3f GetImuRpy() = 0;
virtual Vec3f GetImuAcc() = 0;
virtual Vec3f GetImuOmega() = 0;
virtual VecXf GetContactForce() = 0;
virtual void PublishJointData() = 0;
virtual void PublishImuData() = 0;

HardwareInterface inherits RobotInterface and rclcpp::Node, and is the core class of lite3_transfer.

auto hardware_node = std::make_shared<HardwareInterface>("lite3");
hardware_node->Start(); // Start UDP receiving, switch to SDK mode
virtual void Start(){
receiver_->StartWork();
sender_->ControlGet(2); // SDK mode
}
virtual void Stop(){
sender_->ControlGet(1); // Robot mode
}

Publishes joint and IMU data at 200Hz by default, adjustable via /SDK_MODE service.

Joint commands are sent to robot firmware via a 1000Hz timer.

Gamepad Implementation Abstract Base Class

Section titled “Gamepad Implementation Abstract Base Class”

GamepadInterface inherits rclcpp::Node and is the abstract base class for all gamepad implementations.

Pure virtual interfaces:

virtual void Start() = 0;
virtual void Stop() = 0;
virtual drdds::msg::GamepadData GetGamepadData() = 0;

The base class provides a default PublishGamepadData() implementation that calls GetGamepadData() at 200Hz and publishes to /GAMEPAD_DATA.

RetroidGamepadPublisher receives gamepad data via UDP (default port 12121).

class RetroidGamepadPublisher : public GamepadInterface {
private:
std::shared_ptr<RetroidGamepad> gamepad_ptr_; // UDP receiver
int port_; // UDP port
};

Data conversion:

drdds::msg::GamepadData GetGamepadData() override {
RetroidKeys rt_keys_ = gamepad_ptr_->GetKeys();
drdds::msg::GamepadData msg;
msg.left_axis_x = rt_keys_.left_axis_x;
msg.left_axis_y = rt_keys_.left_axis_y;
msg.right_axis_x = rt_keys_.right_axis_x;
msg.right_axis_y = rt_keys_.right_axis_y;
msg.buttons = rt_keys_.value; // Button bitmask
return msg;
}

lite3_transfer uses MultiThreadedExecutor to run the hardware interface and gamepad publisher simultaneously:

rclcpp::executors::MultiThreadedExecutor executor;
executor.add_node(hardware_node);
executor.add_node(gamepad_pub);
executor.spin();

Emergency stop is implemented through a three-layer chain:

  1. UDP command layer: lite3_sdk_service receives estop command, calls /EMERGENCY_STOP service
  2. Hardware layer: lite3_transfer sends emergency stop command code 0x21010C0E to robot firmware
  3. State machine layer: Notifies Lite3_sdk_deploy via /EMERGENCY_STOP_SIGNAL to switch to JointDamping