ros2_robot_interface¶
Python client that talks to this stack over ROS 2 topics (and a few actions / services).
Repository: fiveages-sim/ros2_robot_interface
Public exports: ROS2RobotInterface, ROS2RobotInterfaceConfig, ControlType, FSM constants (FSM_HOME, FSM_HOLD, FSM_OCS2, FSM_MOVEJ, FSM_COMPLIANCE), and the ROS2*Error exceptions. Exhaustive method docs: API_REFERENCE.md on GitHub.
Installation¶
Install through fa-py-libraries (Python 3.12 env). That repo’s ./init.sh all installs ros2_robot_interface among the other submodules.
git clone https://github.com/fiveages-sim/fa-py-libraries.git
cd fa-py-libraries
./init.sh all
Datagen / motion-queue path: from a lerobot_ros2 checkout, ./init.sh all-motion (or ./init.sh all) installs the same package as submodules/ros2_robot_interface.
Manual fallback
Standalone clone of ros2_robot_interface and pip install -e . (inside a project venv; --no-deps on uv so rclpy is not fetched from PyPI) is only for package development outside those umbrellas. The stack entry is the deploy workspace / fa-py-libraries, not pip install ros2-robot-interface on system Python.
Quick start¶
The following example is the package README “Basic Example”, pinned at commit 200ea42. How to refresh the pin (and recompute :start-line: / :end-line:): Documentation Build.
from ros2_robot_interface import ROS2RobotInterface, ROS2RobotInterfaceConfig
from geometry_msgs.msg import Pose
# Create configuration
config = ROS2RobotInterfaceConfig(
joint_states_topic="/joint_states",
end_effector_pose_topic="/left_current_pose",
end_effector_target_topic="/left_target",
joint_names=["joint1", "joint2", "joint3", "joint4", "joint5", "joint6"]
)
# Create and connect interface
interface = ROS2RobotInterface(config)
interface.connect()
# Get joint state
joint_state = interface.get_joint_state()
if joint_state:
print(f"Joint positions: {joint_state['positions']}")
# Get end-effector pose (returns None if not connected)
pose = interface.left_arm_handler.get_pose()
if pose:
print(f"End-effector position: ({pose.position.x}, {pose.position.y}, {pose.position.z})")
else:
print("Interface not connected or pose not available")
# Send target pose
target_pose = Pose()
target_pose.position.x = 0.5
target_pose.position.y = 0.0
target_pose.position.z = 0.3
target_pose.orientation.w = 1.0
interface.left_arm_handler.send_target(target_pose)
# Control gripper
interface.left_gripper_handler.send_joint_positions(0.5) # 行程/开度目标值为 0.5(非“50%百分比”语义)
# Disconnect
interface.disconnect()
connect() auto-detects dual-arm pose topics, gripper / hand controllers, and split vs whole-body joint topics when they are already in the ROS graph. is_connected is a property. Cartesian and joint sends go through handlers (left_arm_handler, right_arm_handler, left_gripper_handler, …), not a move_j / move_l wrapper.
With auto_switch_fsm_before_control=True (the config default), pose publishes switch the controller to OCS2 and joint publishes switch it to MOVEJ via /fsm_command.
Topic ↔ API map¶
Operator-facing topics on a running OCS2 / basic_joint_controller stack, and the Python calls that publish or read them. Rows below are those API_REFERENCE.md names. Split (分体) vs whole-body (全身) prefixes: 分体控制 vs 全身控制. Int32 FSM values: FSM and Topics.
State¶
Topic |
Message |
Python |
Notes |
|---|---|---|---|
|
|
|
Arms, head, waist, grippers — whatever the robot publishes |
|
|
|
Returns the inner |
|
|
|
Controller echo of the active Cartesian target; used by |
|
|
|
WBC body pose; returns inner |
|
|
|
WBC body command echo |
|
|
|
WBC constraint snapshot; confirms |
Whole-body (WBC) availability
/body_target*, /head_target*, /mode_command, and WbcCurrentState need ocs2_wbc_controller and that robot’s 全身 launch/config. Default mock demos (demo.launch.py) and 分体 (split_body.launch.py) do not imply these features are present. The controller is a private submodule; capability bits are machine-dependent (WbcCapability). Public Taku mock uses split_body.launch.py robot:=taku (arm MPC + body/head basic + grippers), not WBC. full_body.launch.py robot:=taku needs the private WBC submodule; shipping config/ocs2/fixed_base_tcp.info is not enough. When that config and the private module are present, Taku full-body default headMode is HEAD_GAZE. Details: FSM and Topics.
FSM and WBC mode¶
Topic |
Message |
Python |
Notes |
|---|---|---|---|
|
|
|
|
|
|
|
Latched; same integer meanings |
|
|
|
WBC strings such as |
Cartesian targets (OCS2)¶
Unstamped /left_target replaces the current EE goal with one pose (typical for VR / high-rate teleop). Stamped /left_target/stamped runs TF into the controller frame and interpolates a MoveL sequence (typical for vision pick). Far unstamped jumps can jerk. send_relative is a one-shot increment, not an absolute pose in that frame. send_velocity is a latched velocity stream: the controller integrates each Twist; 0.2 s without a new message stops; keep publishing at ≥5 Hz to move continuously.
On ocs2_arm_controller, the same /left_target/stamped topic in MOVEJ can run IK MoveL when lina_planning is present (keep FSM at MOVEJ; turn off pose auto-switch to OCS2). WBC MOVEJ has no IK MoveL path.
Topic |
Message |
Python |
Notes |
|---|---|---|---|
|
|
|
Pose already in controller |
|
|
|
TF to |
|
|
|
One-shot increment (m, rad) + MoveL. Empty |
|
|
|
Velocity |
|
|
|
Path length 2 ( |
|
|
|
Dual-arm Cartesian waypoints. Deprecated in the Python API; new code uses |
|
|
|
WBC; immediate absolute in |
|
|
|
WBC body MoveL |
|
|
|
WBC one-shot body increment + MoveL |
|
|
— |
WBC Head 6D on |
MoveJ joint targets¶
std_msgs/Float64MultiArray. connect() prefers WBC topics when both exist. Implicit FSM → MOVEJ.
Topic |
Python |
When |
|---|---|---|
|
|
全身 / |
|
same handler |
分体 / |
|
same, single-arm |
Single-arm OCS2 (no |
|
|
Unified dual-arm + body on WBC |
|
|
全身 waist / body |
|
|
分体 waist ( |
|
|
全身 head |
|
|
分体 / always-on head |
|
|
e.g. |
|
|
分体 waist trajectory. WBC: same |
|
|
分体 head trajectory |
Gripper and hand¶
connect() picks hand_controller vs gripper_controller (and left/right names). Discrete open/close is the RViz-style topic; Float64 position is the Python stroke command; target_percent is 0–1.
On adaptive_gripper_controller, send_joint_positions is direct position mode (no force feedback). send_target_command / send_position_percent use the switch / percent channels (force feedback while closing). On basic_joint_controller hands, the same percent/switch topics blend Home open/close configs (target_command_enabled).
Topic |
Message |
Python |
Notes |
|---|---|---|---|
|
|
|
Adaptive gripper switch |
|
|
|
Stroke in hardware units, not a 0–1 percent. Clamp with |
|
|
|
Percent channel; raises if the publisher was not detected |
|
|
same |
Dexterous hand open/close when |
|
|
same |
Home-config blend on |
|
|
|
Per-joint hand MoveJ |
Waist (basic_joint_controller / WBC body)¶
Implicit FSM → MOVEJ. Split prefix /body_joint_controller/…; WBC prefix /ocs2_wbc_controller/…. Topic methods are fire-and-forget; API_REFERENCE prefers execute_waist_lifting_pose_*_action when you need a result.
Topic |
Message |
Python |
Notes |
|---|---|---|---|
|
|
|
Single-axis height delta (m) |
|
|
|
Local relative motion |
|
|
|
Absolute |
|
|
|
Velocity factor |
|
|
|
Turning velocity factor |
Action ↔ API map¶
These wait for an action result (or timeout). They are not the topic rows above. Default arm names on ROS2RobotInterfaceConfig point at ocs2_arm_controller; override the config if the controller is namespaced. Waist action name is auto-detected (/ocs2_wbc_controller/waist_lifting_pose or /body_joint_controller/waist_lifting_pose). Type definitions: arms_ros2_control_msgs README. Topic vs Action vs Service: FSM and Topics.
Action |
Typical path |
Python |
Notes |
|---|---|---|---|
|
|
|
Parameterized MoveL ( |
|
|
|
MoveC ( |
|
|
|
Parameterized MoveJ ( |
|
|
|
Goal |
wait_for_*_action_server helpers exist for each. Unstamped / stamped topics switch to OCS2; these Cartesian / joint actions default to MOVEJ. Signatures and max_* fallbacks: API_REFERENCE.md.
Service ↔ API map¶
Python wraps ExecutePath among the msgs README srv types. Service is a single request/response (no progress).
Service |
Name |
Python |
Notes |
|---|---|---|---|
|
|
|
Left/right |
The msgs README also defines srv ExecuteLinear, ExecuteCircle, MovecUseIK, JointTrajectory, CartesianPath, and KinematicsService. Those have no ros2_robot_interface method — call them with ROS 2 clients if the running controller advertises them.
call_compliance_zero_wrench() is a separate FT zeroing service in API_REFERENCE, not one of the cartesian msgs types above.
Lift 2S vendor /body_control
ARX Lift 2S has a separate vendor chassis/lift command path (/body_control). That stack is not ros2_robot_interface and is not the OCS2 body topics in the table (/body_joint_controller/target_joint_position or /ocs2_wbc_controller/target_joint_position/body). Hardware bring-up: ARX Lift 2S.