ROS2 Interface Development Documentation•Chapter 2: Joint Commands and Whole-Body Control

Chapter 2: Joint Commands and Whole-Body Control

Updated: October 14, 2026

1 Interface Overview

The joint command and whole-body control interface is the lowest-level interface for robot motion control. It directly sends joint-level control commands (position, velocity, torque) via the /hardware/joint_command topic, and reads joint state feedback via the /hardware/joint_state topic. This interface is the foundation for advanced control implementations such as PD controller standing and RL policy deployment.

The "joint command/state interface" and "whole-body joint control interface" described in the protocol specification use the same set of topics and messages. The former focuses on message field definitions, while the latter focuses on parameters and torque formulas. They are described together here.

2 Message Fields

Communication Interface:

Topic NameMessage TypeDirectionDescription
/hardware/joint_commandJointCommand.msgClient → Joint ControllerPublish joint control message
/hardware/joint_stateJointState.msgJoint Controller → ClientRead joint state feedback

JointCommand message contains the following parameters:

ParameterDescription
positionTarget joint position [rad], ordered according to the model joint sequence
velocityTarget joint velocity, ordered according to the model joint sequence
feed_forward_torqueFeed-forward torque [N·m]
torqueNot supported yet
stiffnessStiffness kp [N·m/rad]
dampingDamping kd [N·m·s/rad]
parallel_parser_typeParallel joint parser type: 0 = CLASSIC_PARSER (classic parser), 1 = RL_PARSER (reinforcement learning parser)

JointState.msg field definition (see GitHub Message Directory for the complete definition):

PLAIN
std_msgs/Header header
float64[] position
float64[] velocity
float64[] torque
FieldTypeDescription
positionfloat64[]Current joint position (rad)
velocityfloat64[]Current joint velocity (rad/s)
torquefloat64[]Current joint torque (N·m)

The total joint torque is calculated by the following formula:

PLAIN
tau = kp * (q_cmd - q) + kd * (qd_cmd - qd) + feed_forward_torque

Here, position corresponds to q_cmd (target position) in the formula, stiffness corresponds to kp, and damping corresponds to kd; q and qd are the current joint position and velocity, read from /hardware/joint_state. The length of all arrays must be consistent with the robot's total number of joints, num_total_joints.

3 Control Frequency and QoS

ItemRequirement
Recommended control frequency100~500 Hz
QoS policyBest Effort (to reduce latency)
Array lengthAll arrays must equal the robot's total number of joints
Joint indexStarting from 0
Trajectory interpolation stepsAffects motion smoothness; more steps result in smoother motion

4 Prerequisites

You must first switch to the joint_bridge (joint passthrough) mode before the joint command interface will respond. See Chapter 1 for the switching method.

Motion ModeGamepad Button
Joint pass-through mode (joint_bridge)[BACK, A]

5 Example A: PD Controller Standing

File Name: joint_bridge_example.py

This example reads the target position and control parameters from a YAML configuration file, generates a smooth joint trajectory, and sends control commands at a high frequency of 500 Hz, achieving precise joint position control.

Run Command:

Bash
python3 src/interface_example/scripts/joint_bridge_example.py src/interface_example/config/pm01/pd_joint_test.yaml
python3 src/interface_example/scripts/joint_bridge_example.py src/interface_example/config/t800/pd_joint_test.yaml
python3 src/interface_example/scripts/joint_bridge_example.py src/interface_example/config/t800pro/pd_joint_test.yaml

Configuration File Format (YAML, see GitHub Configuration Directory for example files):

PM01 (24 DOF)

YAML
target_position:

  - [-0.280673, -0.00314991, -0.0736041, 0.413063, -0.141991, -0.00686322]

  - [-0.280673, 0.00314922, 0.0736043, 0.413063, -0.141991, 0.00686394]

  - [0]

  - [0, 0.054, 0, -0.128, 0]

  - [0, -0.054, 0, -0.128, 0]

  - [0]

kp:

  - [200, 200, 380, 450, 400, 200]

  - [200, 200, 380, 450, 400, 200]

  - [200]

  - [250, 250, 250, 250, 250]

  - [250, 250, 250, 250, 250]

  - [100]

kd:

  - [5, 5, 5, 5, 2, 2]

  - [5, 5, 5, 5, 2, 2]

  - [1]

  - [1, 1, 1, 1, 1]

  - [1, 1, 1, 1, 1]

  - [1]



# Total steps

num_steps:

  - [1500, 1500, 1500, 1500, 1500, 1500]

  - [1500,  1500, 1500, 1500, 1500, 1500]

  - [1500]

  - [1500, 1500, 1500, 1500, 1500]

  - [1500, 1500, 1500, 1500, 1500]

  - [1500]

T800 Development Edition** (25 DOF)**

YAML
target_position:

  - [-0.280673, -0.00314991, -0.0736041, 0.413063, -0.141991, -0.00686322]

  - [-0.280673, 0.00314922, 0.0736043, 0.413063, -0.141991, 0.00686394]

  - [0]

  - [0, 0.054, 0, -0.128, 0]

  - [0, -0.054, 0, -0.128, 0]

  - [0, 0]

kp:

  - [200, 200, 380, 450, 400, 200]

  - [200, 200, 380, 450, 400, 200]

  - [200]

  - [250, 250, 250, 250, 250]

  - [250, 250, 250, 250, 250]

  - [100, 100]

kd:

  - [5, 5, 5, 5, 2, 2]

  - [5, 5, 5, 5, 2, 2]

  - [1]

  - [1, 1, 1, 1, 1]

  - [1, 1, 1, 1, 1]

  - [1, 1]



# Total steps

num_steps:

  - [1500, 1500, 1500, 1500, 1500, 1500]

  - [1500,  1500, 1500, 1500, 1500, 1500]

  - [1500]

  - [1500, 1500, 1500, 1500, 1500]

  - [1500, 1500, 1500, 1500, 1500]

  - [1500, 1500]

T800 Pro (43 DOF, with dexterous hands)

YAML
target_position:

  - [0, 0, 0, 0, 0, 0] # left leg

  - [0, 0, 0, 0, 0, 0] # right leg

  - [0] # waist

  - [0, 0, 0, 0, 1.57, 0, 0] # left arm

  - [-1.2, 0.0, 1.437, -1.23, -1.84487, -1.3, -1.8287] # left hand

  - [0, 0, 0, 0, -1.57, 0, 0] # right arm

  - [1.2, 0.0, 1.437, 1.23, 1.84487, 1.3, 1.8287] # right hand

  - [0, 0] # head



kp:

  - [1080, 480, 880, 1000, 800, 100]

  - [1080, 480, 880, 1000, 800, 100]

  - [200]

  - [120, 120, 120, 120, 120, 30, 30]

  - [20, 20, 20, 20, 20, 20, 20]

  - [120, 120, 120, 120, 120, 30, 30]

  - [20, 20, 20, 20, 20, 20, 20]

  - [100, 100]



kd:

  - [25, 25, 25, 25, 2, 2]

  - [25, 25, 25, 25, 2, 2]

  - [1]

  - [1.8, 1.5, 1.5, 1.8, 1.2, 0.8, 0.8]

  - [0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04]

  - [1.8, 1.5, 1.5, 1.8, 1.2, 0.8, 0.8]

  - [0.04, 0.04, 0.04, 0.04, 0.04, 0.04, 0.04]

  - [1, 1]



# Total steps

num_steps:

  - [1500, 1500, 1500, 1500, 1500, 1500]

  - [1500,  1500, 1500, 1500, 1500, 1500]

  - [1500]

  - [1500, 1500, 1500, 1500, 1500, 1500, 1500]

  - [1500, 1500, 1500, 1500, 1500, 1500, 1500]

  - [1500, 1500, 1500, 1500, 1500, 1500, 1500]

  - [1500, 1500, 1500, 1500, 1500, 1500, 1500]

  - [1500, 1500]

The three robot models have different numbers of joint groups (PM01/T800 have 6 groups, T800 Pro has 8 groups, including 7 joints in each of the left and right dexterous hands). Please select the corresponding configuration file according to the robot model you are using.

Linear Interpolation Algorithm: For each joint, generate a smooth trajectory according to the following formula:

PLAIN
step = (target_position - current_position) / (num_steps - 1)

position_at_step_i = current_position + step × i

This ensures a smooth linear transition from the starting position to the target position.

6 Example B: RL Policy Deployment

Launch File: rl_basic_example.launch.py (located in the GitHub Example Directory)

This example demonstrates how to deploy a reinforcement learning whole-body control policy. Unlike PD controller standing, the joint commands of the RL policy do not come from a YAML configuration file, but are computed in real time by the policy network based on multi-sensor observations.

Communication Interface:

Topic NameMessage TypeDirection
/hardware/joint_commandJointCommand.msgClient → Joint Controller
/hardware/joint_stateJointState.msgJoint Controller → Client
/hardware/gamepad_keysGamepadKeys.msgSensor → Client
/hardware/imu_infoImuInfo.msgSensor → Client

Run Command:

Bash
ros2 launch interface_example rl_basic_example.launch.py

Operation Procedure:

  1. The robot enters PD standing mode (pd_stand), standing in a flat and open area; note to install a gantry for protection
  2. Evacuate personnel within 3 meters around the robot
  3. Enter joint bridge mode (joint_bridge)
  4. Run the launch file from the command line
  5. After the experiment ends, return to PD standing or Passive mode

7 Example C: Dexterous Hand Loose Fist

File Name: t800pro/loose_fist_example.py

This example demonstrates using the T800Pro robot's three-fingered dexterous hands to perform a loose fist action, and to return from the loose fist action back to the extended state. It supports controlling the left hand, right hand, or both hands simultaneously.

Run Command:

Bash
# Extend both hands
python3 src/interface_example/scripts/t800pro/loose_fist_example.py --pose extend
# Make a loose fist with both hands
python3 src/interface_example/scripts/t800pro/loose_fist_example.py --pose loose_fist
# Make a loose fist, hold, then extend both hands
python3 src/interface_example/scripts/t800pro/loose_fist_example.py --pose both

# Make a loose fist, hold, then extend both hands; motion execution time 3.5s, hold time 2.5s, frequency 500Hz
python3 src/interface_example/scripts/t800pro/loose_fist_example.py --pose both --exec-time 3.0 --hold-time 2.5 --rate 500.0

# Extend the left hand only
python3 src/interface_example/scripts/t800pro/loose_fist_example.py --hand left --pose extend
# Make a loose fist with the left hand only
python3 src/interface_example/scripts/t800pro/loose_fist_example.py --hand left --pose loose_fist

# Extend the right hand only
python3 src/interface_example/scripts/t800pro/loose_fist_example.py --hand right --pose extend
# Make a loose fist with the right hand only
python3 src/interface_example/scripts/t800pro/loose_fist_example.py --hand right --pose loose_fist

Operation Procedure:

  1. The robot enters PD standing mode (pd_stand), standing in a flat and open area; note to install a gantry for protection
  2. Evacuate personnel within 3 meters around the robot
  3. Enter joint bridge mode (joint_bridge)
  4. Run the script from the command line; it is recommended to hoist the robot during testing to prevent falls and damage to components

8 Comparison of Two Implementation Paths

Comparison DimensionPD Controller StandingRL Policy Deployment
Control ObjectiveMaintain a fixed target joint positionDeploy a reinforcement learning policy to achieve whole-body motion
Core AlgorithmLinear interpolation + PD position servoReinforcement learning policy network
Command SourceYAML configuration fileComputed in real time by the policy network based on observations
Required InputsOnly joint state feedback is requiredJoint state + gamepad + IMU
Control Frequency500 HzDetermined by the policy inference frame rate (≤500 Hz)
Safety RequirementsTarget position within the safe rangeFlat open area, gantry protection, 3-meter clearance
Applicable ScenariosStatic pose holding, bridge testingDynamic whole-body motion, RL policy verification

Commonalities of the two: both need to enter joint_bridge mode, both publish commands through /hardware/joint_command, and both need to ensure that the hardware interface node has been started.

9 Notes

  • You must enter joint_bridge mode before sending joint commands; do not run the example program before entering
  • The control frequency must be high enough to ensure control stability; 100~500 Hz is recommended
  • The length of all arrays must equal the robot's total number of joints
  • The kp (stiffness) and kd (damping) parameters need to be adjusted according to joint characteristics; excessively high stiffness may cause system oscillation
  • The target position must be within the joint's safe motion range
  • 500 Hz high-frequency control places certain demands on system performance
  • You can safely exit using Ctrl+C; it is recommended to wait for the motion to complete
  • After the RL policy deployment experiment ends, you should return to PD standing or Passive mode

📎 Source files in the open-source repository for this chapter (click file name to jump to GitHub): GamepadKeys.msg · ImuInfo.msg