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:

Cargo.toml
[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.

ReposeCopper (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

stdOn by default. Needed for URDF/SRDF import and desktop use.
default-features = falseBuilds the runtime crates as no_std for embedded targets.
mujoco-simAdds the MuJoCo simulator bridge, used by the plan-and-execute example.

Forward and inverse kinematics

Put this in src/main.rs:

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(())
}
shell
cargo run
# IK converged in 5 iterations

What just happened

  • Typed units. Joint values are CuJointPositions built from SI Angles, so radians and metres can't be mixed up.
  • In-place outputs. forward_kinematics and inverse_kinematics_dls write into buffers you own and allocate nothing, so they are safe in a real-time loop.
  • Explicit status. IK reports Converged with 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:

copperconfig.ron
(
    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.