Robot control interface
Map measured joint state, position commands, robot description, joint names, and QoS.
The generic ROS 2 bridge connects Sentinel's control pipeline to the controller you already run. A manipulator integration needs three things:
| Interface | Direction | Purpose |
|---|---|---|
| Robot description | Your stack → Sentinel | Joint names, kinematics, and limits |
sensor_msgs/msg/JointState | Your stack → Sentinel | Continuous measured robot state |
| Position command topic | Sentinel → your stack | Live targets for the robot controller |
Start and validate measured state before Sentinel. Every controlled joint must be present, and the stream must continue while the robot is idle or disarmed.
Publish measured state
Publish sensor_msgs/msg/JointState continuously at 100 Hz or faster:
ros2 topic hz /robot/joint_states
ros2 topic echo /robot/joint_states --oncePopulate:
header.stampwith the measurement time.namewith stable joint names.positionin radians.velocityin radians per second when available.effortin newton-metres when available.
Sentinel matches samples by joint name. The order may change between messages, but each message's names and values must remain aligned.
Configure stale-state detection
The bridge faults when measured state stops arriving for longer than stale_timeout_s. The default is 0.5 seconds:
state:
topic: /robot/joint_states
qos: sensor
stale_timeout_s: 0.5Set the timeout from the expected publisher rate and worst validated network gap. Some high-rate remote systems need a larger value; for example, the Anvil OpenArm integration uses 2.5 seconds.
Setting stale_timeout_s: 0 disables stale-state fault detection. Do not disable it to hide an unreliable publisher or DDS connection.
Provide the robot description
Sentinel uses the URDF for joint names, kinematics, and limits. Choose one source:
| Source | Use when |
|---|---|
| URDF or xacro file | The model ships with the integration |
robot_description parameter | An existing robot_state_publisher owns the description |
Transient-local robot_description topic | The description is already distributed over ROS 2 |
Make the description available before the control pipeline starts.
description:
source: parameter
node: /robot_state_publisher
parameter: robot_description
timeout_s: 10.0For multiple independently controlled systems, use a distinct description source for each one. Do not point two arms at the same unnamespaced description topic unless it contains the complete model expected by both systems.
Choose a command type
Use the message type your controller already accepts.
Use std_msgs/msg/Float64MultiArray with a forward position controller. This is the recommended ros2_control path for Sentinel's continuous target stream.
The message contains no joint names. Declare the exact fixed slot order:
command:
outputs:
- topic: /arm_controller/commands
qos: reliable
joint_names:
- shoulder_pan_joint
- shoulder_lift_joint
- elbow_joint
- wrist_1_joint
- wrist_2_joint
- wrist_3_jointA bimanual system can declare one output per arm. A joint may appear in only one output.
Use trajectory_msgs/msg/JointTrajectory when your controller accepts named position targets:
command:
topic: /robot/joint_trajectory
qos: reliable
single_point: falseThe controller can identify joints from joint_names. Treat the incoming data as a live command stream; do not build up a queue of stale motion.
Never infer a Float64MultiArray slot order from the current JointState message. State may be reordered. The command array order is fixed by the controller configuration.
Map joint names and coordinates
Use remapping when the external controller and Sentinel URDF use different names. Use an affine transform when a joint has a different sign or zero:
command:
topic: /external/arm/joint_trajectory
qos: reliable
joint_remap:
shoulder_pan_joint: joint_1
joint_transform:
wrist_3_joint:
scale: -1.0
offset: 0.0
state:
topic: /external/arm/joint_states
qos: sensor
joint_remap:
joint_1: shoulder_pan_joint
joint_transform:
wrist_3_joint:
scale: -1.0
offset: 0.0
stale_timeout_s: 0.5For commands:
external = scale × sentinel + offsetState applies the inverse transform. Keep the public interface in SI units; do not use transforms to introduce degrees.
Match QoS
Set QoS independently for command and state:
| Preset | Typical use |
|---|---|
reliable | Controller command subscriptions |
sensor or best_effort | High-rate state publishers using sensor-data QoS |
default | Publishers and subscribers using ROS 2 defaults |
If a topic appears in ros2 topic list but samples do not arrive, inspect both endpoints:
ros2 topic info --verbose /robot/joint_statesAdd other capabilities
The same bridge can expose more than an arm:
| Capability | Supported command |
|---|---|
| Gripper | JointTrajectory, Float64, Float64MultiArray, or a slot in an arm array |
| Mobile base | geometry_msgs/msg/Twist |
| Neck | JointTrajectory or PoseStamped |
| Dexterous hand | JointTrajectory or Float64MultiArray |
| Elevator | Float64, Twist, or JointTrajectory |
| PTZ | Twist or TwistStamped |
Start with measured arm state and arm commands. Add optional capabilities after the base integration is stable.
Validate before Sentinel
Inspect the URDF
Confirm that every controlled joint, link, and limit matches the physical robot.
Verify continuous measured state
Confirm the rate, names, units, and values. Leave the controller idle and verify that state continues.
Send one small controller-native command
Test the command topic without Sentinel in a cleared workspace.
Stop the command publisher
Confirm that the controller holds or stops according to the robot's validated watchdog behavior.
Verify from the Sentinel environment
Repeat discovery, sample, QoS, and subscriber checks from the container and domain that will run Sentinel.