JointState / JointCommand
Messages for multi-joint robots: manipulator arms, grippers, legged robots, and any system with named revolute or prismatic joints. JointState reports current positions/velocities/efforts; JointCommand sends target positions/velocities.
When to Use
Use JointState to publish feedback from joint encoders (position, velocity, effort). Use JointCommand to send target positions or velocities to a servo controller. Supports up to 16 joints per message.
ROS2 Equivalent
sensor_msgs/JointState and trajectory_msgs/JointTrajectoryPoint — similar structure.
JointState — Feedback
Rust
use horus::prelude::*;
let mut state = JointState::default();
state.joint_count = 6; // 6-DOF arm
// Set joint names (null-terminated, max 31 chars)
state.names[0][..8].copy_from_slice(b"shoulder");
state.names[1][..5].copy_from_slice(b"elbow");
state.names[2][..5].copy_from_slice(b"wrist");
// Set current positions (radians)
state.positions[0] = 0.5; // shoulder at 0.5 rad
state.positions[1] = -1.2; // elbow at -1.2 rad
state.positions[2] = 0.0; // wrist at home
let topic: Topic<JointState> = Topic::new("joint.state")?;
topic.send(state);
Fields
| Field | Type | Unit | Description |
|---|---|---|---|
names | [[u8; 32]; 16] | -- | Joint names (null-terminated strings) |
joint_count | u8 | -- | Number of active joints (max 16) |
positions | [f64; 16] | rad / m | Position: radians (revolute) or meters (prismatic) |
velocities | [f64; 16] | rad/s / m/s | Velocity |
efforts | [f64; 16] | Nm / N | Torque (revolute) or force (prismatic) |
timestamp_ns | u64 | ns | Timestamp |
JointCommand — Control
Rust
use horus::prelude::*;
let mut cmd = JointCommand::default();
cmd.joint_count = 3;
// Target positions
cmd.positions[0] = 1.0; // shoulder to 1.0 rad
cmd.positions[1] = -0.5; // elbow to -0.5 rad
cmd.positions[2] = 0.3; // wrist to 0.3 rad
// Velocity limits
cmd.velocities[0] = 0.5; // max 0.5 rad/s
cmd.velocities[1] = 0.5;
cmd.velocities[2] = 1.0;
let topic: Topic<JointCommand> = Topic::new("joint.command")?;
topic.send(cmd);
Fields
| Field | Type | Unit | Description |
|---|---|---|---|
joint_names | [[u8; 32]; 16] | -- | Joint names (null-terminated strings) |
joint_count | u8 | -- | Number of active joints (max 16) |
positions | [f64; 16] | rad / m | Target positions |
velocities | [f64; 16] | rad/s / m/s | Velocity limits or targets |
efforts | [f64; 16] | Nm / N | Torque/force limits or targets |
timestamp_ns | u64 | ns | Timestamp |
Common Patterns
Arm Controller Node
fn tick(&mut self) {
// IMPORTANT: always recv() every tick
if let Some(cmd) = self.cmd_sub.recv() {
for i in 0..cmd.joint_count as usize {
// SAFETY: clamp to joint limits
let pos = cmd.positions[i].clamp(self.min_limits[i], self.max_limits[i]);
self.write_servo(i, pos);
}
}
// Read encoder feedback
let mut state = JointState::default();
state.joint_count = self.num_joints;
for i in 0..self.num_joints as usize {
state.positions[i] = self.read_encoder(i);
}
self.state_pub.send(state);
}
fn shutdown(&mut self) -> Result<()> {
// SAFETY: move all joints to home position
for i in 0..self.num_joints as usize {
self.write_servo(i, 0.0);
}
Ok(())
}
Related Types
- ServoCommand — Single servo control
- TrajectoryPoint — Timed trajectory waypoint
- Servo Controller — Complete servo bus recipe