OCS2 Arm Controller

ROS 2 control controller for arm MPC via OCS2 (ocs2_arm_controller/Ocs2ArmController).

Source of truth

FSM and launch names below come from ocs2_arm_controller/README.md and the package launch/ files.

  • README: FSM HOME / OCS2 / HOLD

  • Shared MoveJ / Home / Hold primitives: libraries/arms_controller_common/

Repository: fiveages-sim/arms_ros2_control (controller/ocs2_arm_controller/)

FSM

README states:

State

Role

HOME (StateHome)

Move the arm to a predefined home position

OCS2 (StateOCS2)

OCS2 MPC optimal control

HOLD (StateHold)

Hold the position recorded when entering the state

The controller starts in HOLD. OCS2 can only return to HOLD.

README integers (package README still lists them as received on /control_input):

Command

Transition

1

HOLD → HOME

2

HOME → HOLD, or OCS2 → HOLD

3

HOLD → OCS2

Operators and ros2_robot_interface send those same integers on /fsm_command (std_msgs/Int32). That is the stack-wide FSM topic — FSM and Topics. Joystick control_input (arms_ros2_control_msgs/Inputs) on arms_target_manager is a different message, scaled into /left_target/twist.

arms_controller_common also implements MoveJ (StateMoveJ) and FSMCommandPublisher, which publishes std_msgs/Int32 on /fsm_command (values in that header: 1 HOME, 2 HOLD, 3 OCS2, 4 MOVEJ). On mixed OCS2/WBC stacks, Basic Joint Controller treats 3 and 4 as MOVEJ, while this controller treats 3 as OCS2 and 4 as MOVEJ — see Basic Joint Controller.

Split-body launch (分体控制)

split_body.launch.py is the 分体控制 path (launch_mode split_body):

  • Arms: ocs2_arm_controller

  • Body and head: basic_joint_controller (setup_body_controllers for body and head, e.g. body_joint_controller)

  • Optional hands / FT broadcasters from the same launch helper

ros2 launch ocs2_arm_controller split_body.launch.py robot:=<robot>

The same package also has full_body.launch.py (launch_mode full_body), which spawns ocs2_wbc_controller when the robot config type is ocs2_wbc_controller/Ocs2WbcController. That is 全身控制 — see OCS2 WBC Controller.

Cartesian topics (arms_target_manager)

EE goals are not listed as a full topic table in this controller’s README. They live in arms_target_manager (PoseBasedReferenceManager): /left_target, /left_target/stamped, /left_target/twist, /left_target/relative, plus right / dual counterparts and /left_current_target.

On this controller, MOVEJ + /left_target/stamped (and right / dual) can run IK MoveL via lina_planning (StateMoveJ.startLinearTrajectory). Without that library the MOVEJ Cartesian side is a no-op. ocs2_wbc_controller MOVEJ has no IK MoveL (joint arrays only); full_body.launch.py sets enable_movej_cartesian_markers:=false. Body / head Cartesian (/body_target…, /head_target…) are WBC-only — 分体 uses basic_joint_controller for those joints.

Full tables: FSM and Topics. Parameterized MoveL / MoveC / MoveJ also exist as actions (ExecuteLinear, MovecUseIK, JointTrajectory) on this controller; Python: ros2_robot_interface. Types: arms_ros2_control_msgs README.

Demo launch (README)

ros2 launch ocs2_arm_controller demo.launch.py type:=AG2F90-C
ros2 launch ocs2_arm_controller demo.launch.py robot:=arx5 type:=r5
ros2 launch ocs2_arm_controller demo.launch.py hardware:=gz type:=AG2F120S
ros2 launch ocs2_arm_controller demo.launch.py hardware:=isaac type:=AG2F90-C

Build (README): colcon build --packages-up-to ocs2_arm_controller --symlink-install.

End-effectors (type / left_type / right_type)

demo.launch.py, split_body.launch.py, and full_body.launch.py declare type and also call create_robot_profile_launch_arguments() from robot_common_launch. That helper adds left_type / right_type (and use_profile_eef / robot_profile). There is no gripper:= argument.

From create_eef_side_launch_arguments(): left_type is a left EEF key (rg75, ag2f90_c, linkerhand_o7, …). Use with right_type for asymmetric setups; do not pass type:= in that case.

# Symmetric EEF (package README)
ros2 launch ocs2_arm_controller demo.launch.py type:=AG2F90-C

# Different L/R (robot_common_launch README)
ros2 launch ocs2_arm_controller demo.launch.py \
  robot_profile:=/path/to/machine_profile.yaml \
  use_profile_eef:=false \
  left_type:=rg75 right_type:=linkerhand_o7

type may also be arm topology left / right / dual (does not expand to left_type / right_type). Full merge rules: robot_common_launch.

Configuration (README)

Parameter tables (Startup vs Runtime): Controller ROS 2 parameters. Machine YAML lives in {robot}_description/config/ros2_control/ros2_controllers.yaml (there is no in-tree config/ocs2_arm_controller.yaml). README package-specific keys: home_pos, rest_pos, robot_name, default_gains, pd_gains, frames, movel_*. Names that are not parameters: zero_pos, robot_pkg, force_gains.

OCS2 files (selected via robot_name → {robot_name}_description; not ROS 2 parameters):

  • Task: {robot_name}_description/config/ocs2/task.info

  • Planning URDF: xacro via robot_common_launch (planning_urdf_variant:=xacro, planning_urdf_path). Static urdf/{robot}_{robot_type}.urdf lookup was removed (CtrlComponent.cpp)

  • Generated library: {robot_name}_description/config/ocs2/generated

Control mode is auto-detected from ros2_control command/state interfaces (position-only vs force/MIX when position, velocity, effort, kp, kd are all present).

VR teleop on Panthera HT / ARX uses that MIT / MIX path. Tianji / Rokae VR compliance is position commands + vendor HI impedance, not this MIX table — VR Teleoperation.