Getting started
Use as a library
Building your own Copper application instead of starting from the template? Add the crates directly and call them yourself. This example computes the tool pose of an igus ReBeL and solves back to joint angles.
Add Repose
Add the repose umbrella crate, which re-exports every component, along with Copper itself:
[dependencies]
repose = { git = "https://github.com/ReposeRobotics/repose", tag = "v0.7.1" }
cu29 = { version = "1.3.1", features = ["units"] }
cu-spatial-payloads = "1.3.0"Nothing is on crates.io before 1.0, so Repose is consumed as a git dependency pinned to a release tag. If you only need part of the stack, depend on individual crates such as repose-kinematics from the same repository and tag. Copper's own crates (cu29, cu-spatial-payloads) keep their names.
Versions and compatibility
The whole workspace shares one version and is tagged vX.Y.Z; every crate in a tag works together. Before 1.0, a minor bump (0.7 → 0.8) may break the Rust API, payload encoding or RON config. Patch releases never do.
| Repose | Copper (cu29) · Rust (MSRV) |
0.7 (as repose-*) | 1.3 · 1.95 |
0.1 – 0.6 (as cu-*) | 1.3 · 1.95 |
Coming from 0.6 or earlier? The crates were renamed in 0.7: cu-repose is now repose and each cu-<name> component is repose-<name>. Rust paths and RON task types follow, e.g. repose_motion_planning::…; type names keep their Cu prefix. See the versioning policy and the changelog.
Features
std | On by default. Needed for URDF/SRDF import and desktop use. |
default-features = false | Builds the runtime crates as no_std for embedded targets. |
mujoco-sim | Adds the MuJoCo simulator bridge, used by the plan-and-execute example. |
Forward and inverse kinematics
Put this in src/main.rs:
use cu_spatial_payloads::Transform3D;
use cu29::prelude::*;
use cu29::units::si::{angle::radian, f64::Angle};
use repose::kinematics::{CuIkParams, CuIkStatus, CuJointPosition, igus_rebel_chain};
fn main() -> CuResult<()> {
// 6-DOF igus ReBeL; the joint count is part of the type.
let chain = igus_rebel_chain();
// Forward kinematics: joint positions -> tool pose, written in place.
let q: [CuJointPosition; 6] =
[0.0, 0.4, 0.8, 0.0, 0.6, 0.0].map(|r| Angle::new::<radian>(r).into());
let mut tool = Transform3D::<f64>::identity();
chain.forward_kinematics(&q, &mut tool)?;
// Inverse kinematics: back from that pose, seeded nearby.
let seed: [CuJointPosition; 6] =
[0.1, 0.3, 0.7, 0.1, 0.5, 0.1].map(|r| Angle::new::<radian>(r).into());
let mut solution = seed;
let status = chain.inverse_kinematics_dls(&tool, &seed, &CuIkParams::default(), &mut solution)?;
match status {
CuIkStatus::Converged { iterations, .. } => println!("IK converged in {iterations} iterations"),
other => println!("IK did not converge: {other:?}"),
}
Ok(())
}cargo run
# IK converged in 5 iterationsWhat just happened
- Typed units. Joint values are
CuJointPositions built from SIAngles, so radians and metres can't be mixed up. - In-place outputs.
forward_kinematicsandinverse_kinematics_dlswrite into buffers you own and allocate nothing, so they are safe in a real-time loop. - Explicit status. IK reports
Convergedwith its error, or the best configuration it reached, rather than failing silently.
As a Copper task
In an application you don't call the library by hand. You add its task to the graph in copperconfig.ron and pick the robot by preset name:
(
id: "fk",
type: "repose_kinematics::CuForwardKinematicsTask<6>",
config: {"chain": "igus_rebel"},
),Tasks never pick a robot implicitly: chain is either a preset name or an inline list of exactly N joint specifications.
Next steps
The MuJoCo ReBeL example wires the whole pipeline — goal, RRT* planner, timing, follower and controller — against a simulated arm, configured entirely in RON.