Kinematics · repose-kinematics
Joint values & limits
A joint value in Repose always carries its unit. A revolute joint has an angle, a prismatic joint has a length, and the type system keeps them apart so a value can never be read in the wrong unit.
Positions: CuJointPosition
pub enum CuJointPosition {
Revolute(Angle),
Prismatic(Length),
Fixed,
}Build one with .into() from an Angle or Length. Read it back with angle() or displacement(), which return an error when the variant does not match, or with si_value() for the raw radians or metres (0.0 for Fixed).
let q: CuJointPosition = Angle::new::<radian>(0.5).into();
let a = q.angle()?; // Ok(0.5 rad)
let d = q.displacement(); // Err: expected prismatic coordinate
let mid = q.interpolate(other, 0.5)?; // same-kind linear interpolationA configuration of the whole arm is [CuJointPosition; N]. Every chain method checks that the variant in slot i matches joint i's type before doing any maths.
Velocity, acceleration, jerk
The derivatives follow the same pattern:
CuJointVelocity | Revolute(AngularVelocity) (rad/s) or Prismatic(Velocity) (m/s). |
CuJointAcceleration | rad/s² or m/s². |
CuJointJerk | rad/s³ or m/s³. |
Each has from_coordinate(position, value), which builds the matching variant for a joint from a raw SI number, and si_value(). Trajectory limits and the controller use these types, so per-joint limits on a mixed arm stay correct.
In RON
Enums are snake_case, keyed by variant:
"goal": [{"revolute": 0.0}, {"revolute": 0.4}, {"prismatic": 0.12}],
"max_velocities": [{"revolute": 0.8}, {"revolute": 0.8}, {"prismatic": 0.2}],Templates are the one deliberate exception: a robot program's Step values are plain degrees and metres for readability, and the runner converts them.
Limits: CuJointLimits
Revolute { lower, upper, max_velocity } | Bounded rotation in radians. |
Prismatic { lower, upper, max_velocity } | Bounded travel in metres. |
Continuous { max_velocity } | Unbounded rotation. lower()/upper() return ±π as the sampling interval for planners. |
Fixed | No motion. |
Helpers: CuJointLimits::revolute(..)/prismatic(..) constructors, contains(q), clamp(q) and validate(q). Lower must not exceed upper and velocities must be positive and finite; chain.validate() checks all of it.
"limits": {"revolute": {"lower": -3.12, "upper": 3.12, "max_velocity": 2.16}}
"limits": {"continuous": {"max_velocity": 3.0}}Comparing radians with metres: CuJointMetric
Planners need one number for "how far apart are two configurations", but a mixed arm has radians and metres. CuJointMetric defines how much of each counts as one unit of distance:
pub struct CuJointMetric { pub angular_scale: Angle, pub linear_scale: Length }
// default: 1 rad and 1 m both count as 1.0metric.normalized(q) divides a value by its scale. Planner parameters like step_size, connection_radius or resolution are measured in these normalised units, and path cost is the L2 length in them. For an all-revolute arm with the default metric, normalised units are simply radians.
If your arm has a long prismatic axis, raise linear_scale (for example to 0.1 m) so that 10 cm of travel weighs as much as 1 rad of rotation.
Collision sampling: CuJointResolution
Collision checks between two configurations are discrete samples. CuJointResolution sets the largest step between samples per joint kind:
"collision_check_step": {"angular": 0.08, "linear": 0.01} // default: 0.08 rad, 1 cmSmaller is safer and slower. Thin obstacles or long links (where a small joint step moves the tool a lot) need a finer resolution. resolution.metric() turns it into a CuJointMetric.