ROS2 Interface Development Documentation•Chapter 1: Motion State Machine

Chapter 1: Motion State Machine

Updated: October 14, 2026

1 Motion State Switching Is Done via Two ROS2 Topics:

Topic NameMessage TypeDirectionDescription
/motion/motion_stateMotionState.msgMotion Management Service → ClientRead current motion state
/motion/set_motion_stateMotionStateRequest.msgClient → Motion Management ServiceSend motion state switching request

The MotionState message contains the current motion state name and the available_transition_motions list, which lists the target states that can be directly switched to currently. Developers send a MotionStateRequest message to request a state transition.

2 State Definition and List

The motion state machine is a prerequisite for all robot control interfaces. Most motion control interfaces only respond when the robot is in the correct motion state, so developers must first understand the definition and switching methods of the state machine.

The following table lists all available motion states and their meanings, and indicates the support status for each robot model:

State NameDescriptionPM01T800
idleIdle; whole-body joints disabled✅✅
passiveResistance mode✅✅
pd_standPD controller standing✅✅
rl_ampMechanical gait✅—
rl_basicNatural walking gait✅✅
rl_terrainTerrain gait✅—
lower_body_balanceLower-body balance; upper-limb low-damping gait (responds to upper-limb joint control interface)✅✅
joint_bridgeJoint passthrough mode (responds to whole-body joint control interface)✅✅
pd_sitgroundStand up from a seated or lying position✅✅
rl_recover_proneStand up from a prone position on the ground✅—
rl_floor_sittingSit down to the ground from standing✅—
walk_serverWalking control server—✅
rl_mimic_supine_to_stanceSupine → Standing—✅
rl_mimic_prone_to_stanceProne → Standing—✅
rl_mimic_stance_to_supineStanding → Supine—✅
rl_mimic_sitdown_to_stanceSitting → Standing—✅
rl_mimic_stance_to_sitdownStanding → Sitting—✅

3 Switching Interface

Communication interface: Motion state switching is accomplished through two ROS2 topics.

  • State feedback topic: /motion/motion_state, message type: interface_protocol/msg/MotionState
  • Switch request topic: /motion/set_motion_state, message type: interface_protocol/msg/MotionStateRequest

The detailed message field definitions are given in the following sections.

MotionState.msg field definition (from GitHub):

PLAIN
string current_motion_task
string[] available_transition_motions
FieldTypeDescription
current_motion_taskstringCurrent motion state name
available_transition_motionsstring[]List of target states that can be directly switched to currently

MotionStateRequest.msg field definition:

PLAIN
string target_motion_name
FieldTypeDescription
target_motion_namestringTarget motion state name

4 State Transition Rules

  • Before switching, ensure the target state is in the available_transition_motions list; otherwise the switch request will be ignored
  • Simply send one MotionStateRequest message and the robot will begin switching
  • State switching has a 3-second timeout limit; if it times out, the switch fails
  • Some states cannot be switched between directly and require passing through an intermediate state (for example, switching from idle to rl_terrain may need to go through pd_stand first). For specific state transition limitations, refer to the product manual. The available transition targets are not fixed; instead, they are returned dynamically by the robot at runtime via the available_transition_motions field—developers should first read this field in code, confirm the target state is in the list, and then send the switch request
  • Mismatched QoS configuration will cause message reception to fail; be sure to confirm that the QoS of the subscription and publication are consistent

5 Example: Complete Switching Flow

File name: switch_to_terrain_walking_gait_example.py

This example demonstrates the complete flow of switching from the default standing state to a target motion state (such as the lower-body balance mode), including state listening, whitelist checking, switch requests, and result verification. The example uses lower_body_balance, which is supported on both PM01 and T800 series.

Run command:

The code example is located in the engineai_ros2_workspace/src/interface_example/scripts directory; switch to that directory before running the command above.

Bash
# Using lower-body balance mode as an example (supported by both PM01/T800)
python3 switch_to_terrain_walking_gait_example.py --target-motion lower_body_balance

# Or use the shorthand
python3 switch_to_terrain_walking_gait_example.py -m lower_body_balance

Program execution flow:

  1. Initialize the node and subscribe to /motion/motion_state
  2. Wait to receive the current motion state information
  3. Check whether the target state is in the available_transition_motions list
  4. Publish the state switch request to /motion/set_motion_state
  5. Monitor the switching progress; wait for the switch to complete or time out
  6. Automatically exit after completion

Minimal example (for logic reference only) (simplified from the GitHub script switch_to_terrain_walking_gait_example.py, the same directory also contains switch_to_target_motion_example.py, and the two differ only in the target gait but share the same logic. For the complete timeout/exit/error-handling logic, see the full GitHub version); the core logic is: subscribe to state → check whitelist → publish switch request:

Python
import rclpy
from rclpy.node import Node
from rclpy.qos import QoSProfile, ReliabilityPolicy, DurabilityPolicy
from interface_protocol.msg import MotionState, MotionStateRequest

class MotionStateSwitcher(Node):
    def __init__(self, target_motion: str):
        super().__init__("motion_state_switcher")
        # Publisher: QoS must use RELIABLE
        self._pub = self.create_publisher(
            MotionStateRequest, "/motion/set_motion_state",
            QoSProfile(depth=1, reliability=ReliabilityPolicy.RELIABLE,
                       durability=DurabilityPolicy.VOLATILE))
        # Subscriber: QoS uses BEST_EFFORT
        self._sub = self.create_subscription(
            MotionState, "/motion/motion_state",
            self._on_state,
            QoSProfile(depth=1, reliability=ReliabilityPolicy.BEST_EFFORT,
                       durability=DurabilityPolicy.VOLATILE))
        self._target = target_motion
        self._available = []
        self.create_timer(0.1, self._tick)

    def _on_state(self, msg: MotionState):
        self._available = msg.available_transition_motions

    def _tick(self):
        if self._target in self._available:
            req = MotionStateRequest()
            req.target_motion_name = self._target
            self._pub.publish(req)
            self.get_logger().info(f"Switching to {self._target}")

rclpy.init()
node = MotionStateSwitcher("lower_body_balance")
rclpy.spin(node)

Note: The complete script also includes timeout handling, switch-success detection, and graceful exit logic; it is recommended to refer to the full GitHub version for actual use.

6 Notes

  • Ensure the motion controller node has been started; otherwise the state message cannot be received
  • State switching takes some time; please wait patiently and do not repeatedly send requests
  • If it times out, check whether the target state name is correct (pay attention to case sensitivity)
  • Use Ctrl+C to interrupt the program at any time
  • Only after a successful switch will the corresponding motion control interface take effect (for example, the joint command interface is only available in joint_bridge mode)

📎 Source files in the open-source repository for this chapter (click file name to jump to GitHub): MotionState.msg · MotionStateRequest.msg · switch_to_target_motion_example.py · switch_to_terrain_walking_gait_example.py