ROS2 Topics#
WMX R2 keeps its topic surface small on purpose. Commands are services and actions; topics carry only streamed data — feedback going out, and trajectory or velocity streams coming in.
Topic summary#
Topic (default name) |
Dir |
Type |
Node |
|---|---|---|---|
|
pub |
|
|
|
pub |
|
|
|
pub |
|
|
|
pub |
|
|
|
pub / sub |
|
|
|
pub |
|
|
|
sub |
|
|
|
sub |
|
|
|
pub |
|
|
|
pub |
|
|
|
pub |
|
|
Important
Every one of these names is a node parameter, not a hard-coded string. The names above are the values in the shipped configuration files. They resolve as absolute names, so launching a node inside a ROS namespace does not namespace them — override the topic parameters explicitly for multi-robot setups.
Topics also exist only while the owning node is active: an inactive
lifecycle node publishes and subscribes nothing.
Where the data flows#
flowchart LR
MG["MoveIt2<br/>move_group"]
SV["MoveIt Servo"]
JTC["joint_trajectory_controller"]
JPC["joint_position_controller"]
JSB["joint_state_broadcaster"]
WMX["WMX3 engine<br/>(EtherCAT)"]
RSP["robot_state_publisher<br/>RViz, MoveIt"]
SIM["Isaac Sim / Gazebo"]
MG -->|"FollowJointTrajectory (action)"| JTC
SV -->|"/…/joint_trajectory"| JPC
JTC -->|"StartCSplinePos"| WMX
JPC -->|"StartLinearIntplPos"| WMX
JTC -.->|"/moveit2_trajectory/execution_active"| JPC
JTC -.->|"/servo_node/delta_joint_cmds (zero)"| SV
WMX -->|"encoder"| JSB
JSB -->|"/joint_states"| RSP
JSB -->|"/isaacsim/joint_command<br/>/gazebo_position_controller/commands"| SIM
Manipulator topic and action flow#
Manipulator feedback#
/joint_states#
Published by joint_state_broadcaster. This is the topic MoveIt2,
robot_state_publisher, and RViz consume.
Type |
|
Parameter |
|
Rate |
|
QoS |
default reliable, depth 1 |
Content:
name—joint_namefollowed bygripper_joint_nameposition— the WMXactualPosof each axis, unconvertedvelocity— the servo’sactualVelocity, not a derivative of positioneffort— not filled inheader.stamp— the node clock
Note
Joint values pass through with no scaling. One axis user unit must equal one radian at the joint, and that is set on the WMX side in the axis parameter XML. There is no gear-ratio or offset parameter in any of these nodes. See Robot Parameter Configuration and Validation.
The timer period is 1000 / joint_feedback_rate truncated to whole
milliseconds, so prefer rates that divide 1000 — 100, 125, 200, 250, 500. A
value of 0 or less falls back to 100 Hz with a warning.
Gripper state comes from a single I/O bit, so it is two-valued: the
broadcaster reports gripper_close_value or gripper_open_value, never
anything in between.
wros ros2 topic echo /joint_states
wros ros2 topic hz /joint_states # should match joint_feedback_rate
Simulator mirrors#
The same feedback is republished for a digital twin. Both are optional and off when their parameter is empty.
Topic (parameter) |
Type |
Notes |
|---|---|---|
|
|
Same content as |
|
|
Joint positions only, ordered by
|
( |
|
Joint velocities, ordered by |
gazebo_position_joint_axes and gazebo_velocity_joint_axes list axis
numbers taken from joint_axes, in the order the Gazebo controller lists
its own joints:. gripper_joint_name entries come from an I/O bit
rather than an axis, so they cannot be listed there — they are appended
automatically.
/wmx/axes/status#
Published by wmx_core_motion_node. This is the raw per-axis view: the
diagnostic topic, not the MoveIt one.
Type |
|
Rate |
|
std_msgs/Header header
bool[] amp_alarm
bool[] servo_on
bool[] home_done
bool[] home_switch
bool[] negative_ls
bool[] positive_ls
bool[] motion_complete
float64[] pos_cmd
float64[] velocity_cmd
float64[] actual_pos
float64[] actual_velocity
float64[] actual_torque
Every array is parallel and indexed by axis. pos_cmd is what the engine
was told to do; actual_pos is what the encoder reports. The difference
between them is the following error.
wros ros2 topic echo /wmx/axes/status --once
wros ros2 topic echo /wmx/axes/status --field amp_alarm
Controller arbitration topics#
joint_trajectory_controller (planned motion) and
joint_position_controller (streamed servo motion) can command the same
axes, so they interlock over two topics.
/moveit2_trajectory/execution_active#
Type |
|
QoS |
transient_local, depth 1 — latched, so a controller that starts late still sees the current value |
Published by |
|
Subscribed by |
|
true for the duration of a planned goal. While it holds,
joint_position_controller drops every incoming streamed trajectory.
The latch is one-directional: a servo motion already in flight is not preempted when a planned goal starts. Pause MoveIt Servo, or accept that the first goal wins the race, if both can be commanded at once.
/servo_node/delta_joint_cmds#
control_msgs/msg/JointJog, depth 10. On every goal exit path —
success, abort, or cancel — joint_trajectory_controller publishes a
zero-velocity jog for the goal’s joint names, so MoveIt Servo does not
resume with a stale delta. The name is hard-coded, not a parameter.
Streamed trajectory input#
/movensys_manipulator_arm_controller/joint_trajectory#
Type |
|
Parameter |
|
QoS |
default, depth 1 |
The streaming path from MoveIt Servo into
joint_position_controller. Only the last point of each message is
used — this is a streaming target, not a queued trajectory.
Per axis the node computes velocity = |target − posCmd| / dt and
acc = dec = velocity / (accel_ratio · dt), where dt is the point’s
time_from_start. Because each axis gets its own velocity for a shared
dt, StartLinearIntplPos makes every axis arrive at the same instant.
The step is measured against the commanded position, not the encoder, so
following error does not feed back into the next command. Messages whose
largest per-axis step is below min_step are dropped, which keeps servo
jitter from restarting an interpolation every cycle.
Differential drive topics#
differential_drive_controller drives two EtherCAT wheel axes with
CoreMotion StartVel and exposes the Nav2 contract.
flowchart LR
NAV["Nav2 / teleop"] -->|"/cmd_vel_safe (TwistStamped)"| DDC["differential_drive_controller"]
DDC -->|"StartVel"| WMX["WMX3 engine"]
WMX -->|"actualVelocity"| DDC
DDC -->|"/odom_enc"| EKF["robot_localization EKF"]
DDC -->|"/omega_enc, /omega_cmd"| MON["monitoring / recording"]
EKF -->|"odom → base_link TF"| NAV
Differential-drive data flow#
Topic (parameter) |
Dir |
Type |
Content |
|---|---|---|---|
|
sub |
|
|
|
pub |
|
Pose dead-reckoned over the loop |
|
pub |
|
|
|
pub |
|
|
|
pub |
TF |
Only when |
All four publish at rate (default 100 Hz). position and effort
are left empty on both JointState topics.
Warning
Keep publish_tf at its false default whenever a localization EKF
owns the odom → base_link transform, which is the case as soon as an
IMU is configured. Two publishers on the same TF edge produce a base that
jumps.
Note
cmd_vel_timeout (default 0.25 s) forces the wheel target to zero if
no command arrives in that window. Freshness is measured against
arrival time on this node’s clock. The stop decelerates over
dec_time, so it is not an emergency stop.
Both loop timers are wall timers, but the odometry dt comes from the
ROS clock. A paused /clock under use_sim_time freezes the pose and
also disables the stale-command stop.
Checking topics at runtime#
wros ros2 topic list # only active nodes appear
wros ros2 topic info /joint_states -v # publishers, subscribers, QoS
wros ros2 topic hz /joint_states # confirm the feedback rate
wros ros2 topic echo /wmx/axes/status --once
If a topic is missing, check the node’s lifecycle state before anything else:
wros ros2 service call /wmx/lifecycle/get_node_states wmx_r2_message/srv/GetNodeStates "{}"
See also#
ROS2 Services – engine, lifecycle, axis, I/O, and EtherCAT services
ROS2 Actions – the
FollowJointTrajectoryactionwmx_r2_message –
AxesStatusand the service definitionsNav2 Navigation (Mobile Base) – how the differential topics wire into Nav2