copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros gpio_caterpillar

Repository Summary

Description Purpose-built, Rust-native software engine for robotics
Checkout URI https://github.com/copper-project/copper-rs.git
VCS Type git
VCS Version master
Last Updated 2025-04-25
Dev Status UNKNOWN
CI status No Continuous Integration
Released UNRELEASED
Tags rust robotics ros
Contributing Help Wanted (0)
Good First Issues (0)
Pull Requests to Review (0)

Packages

Name Version
gpio_caterpillar 0.0.0

README

logo

#

Copper

copper GitHub last commit dependency status License Discord

Copper is a user-friendly runtime engine for creating fast and reliable robots. Copper is to robots what a game engine is to games.

  • Easy: Copper offers a high-level configuration system and a natural Rust-first API.

  • Fast: Copper uses Rust’s zero-cost abstractions and a data-oriented approach to achieve sub-microsecond latency on commodity hardware, avoiding heap allocation during execution.

  • Reliable: Copper leverages Rust’s ownership, type system, and concurrency model to minimize bugs and ensure thread safety.

  • Product Oriented: Copper aims to avoid late-stage infra integration issues by generating a very predictable runtime.

Copper has been tested on:

  • Linux (x86_64, armv7, aarch64 & riskv64)
  • MacOS (arm64)
  • Windows (x86_64)

Technical Overview

Copper is a data-oriented Robot SDK with these key components:

  • Task Graph: graph Configures the system’s topology, specifies inter-task communication paths, and sets types for nodes and messages. The Task Graph is generated in RON,

  • Runtime Generator: Creates an execution plan based on the Task Graph’s metadata. It preallocates a “Copper List” to maximize sequential memory access during execution.

  • Zero-Copy Data Logging: Records all messages between tasks without copying data, ensuring efficient logging.

  • Fast Structured Logging: Interns and indexes logging strings at compile time, avoiding runtime string construction and ensuring high-speed textual logging.

You don’t have a real robot yet? Try it in our minimalistic sim environment!

Copper in virtual action

Here is a Copper-based robot in action in a Bevy simulation environment!
The realistic sim is created using Bevy (A Rust Game Engine) and Avian3d (Physics Engine in Rust).

On your mac or linux machine (x86-64 or Arm) just run …

$ cargo install cu-rp-balancebot
$ balancebot-sim 

… to try it locally.

The source code for this demo is available in the examples/cu_rp_balancebot directory.

Implemented Features So Far

  1. Task interface and Lifecycle: These are stable traits you can use to implement new algorithms, sensors, and actuators.
  2. Runtime generation: The current implementation works for up to mid-size robot (~a couple dozen of tasks).
  3. Log reader: You can reread the logs generated by Copper from your robot, sim or resim.
  4. Structured log reader: Debug logs are indexed and string interned at compile time for maximum efficiency.
  5. Components: We have a growing number of drivers, algorithms and standard interfaces. If you have implemented a new component, ping us and we will add it to the list!
  6. log replay / resim: You can deterministically replay/resim a log. If all you tasks are deterministic, you will get the exact same result as a real log on the robot or from the sim.
  7. Simulation: We have a simple simulation environment to test your robot. Test your robot before the hardware is built and try out your robot the first time without risking a real crash.

Components

Category Type   Description Crate Name
Sensors Lidar vlp16 Velodyne/Ouster VLP16 cu-vlp16
  Lidar xt32 Hesai/XT32 cu-hesai
  IMU wt901 WitMotion WT901 cu-wt901
  ADC/Position ads7883 ADS 7883 3MPSPS SPI ADC cu-ads7883
  Encoder ads7883 Generic Directional Wheel encoder cu-rp-encoder
Actuators GPIO gpio Raspberry Pi cu-rp-gpio
  Servo lewansoul Lewansoul Servo Bus (LX-16A, etc.) cu-lewansoul
  DC Motor Driver sn754410 Half-H Driver for CD Motors cu-rp-sn754410
Monitors TUI Monitor monitor Console based monitor cu-consolemon
Algorithms PID Controller   PID Controller cu-pid
Middleware Shared Mem IPC Iceoryx2 source
Iceoryx2 sink
cu-iceoryx2-src
cu-iceoryx2-sink

Kickstarting a copper project for the impatient

You can generate a project from one of Copper’s templates. The generator will ask you the name for your new project interactively:

cargo install cargo-generate
git clone https://github.com/copper-project/copper-rs
cd copper-rs/templates
cargo cunew [path_where_you_want_your_project_created]
    🤷   Project Name:

Check out copper-templates for more info.

What does a Copper application look like?

Here is a simple example of a Task Graph in RON:

(
    tasks: [
        (
            id: "src",                   // this is a friendly name
            type: "FlippingSource",      // This is a Rust struct name for this task see main below
        ),
        (
            id: "gpio",                  // another task, another name
            type: "cu_rp_gpio::RPGpio",  // This is the Rust struct name from another crate
            config: {                    // You can attach config elements to your task
                "pin": 4,
            },
        ),
    ],
     cnx: [
        // Here we simply connect the tasks telling to the framework what type of messages we want to use. 
        (src: "src",  dst: "gpio",   msg: "cu_rp_gpio::RPGpioMsg"),
    ],    

Then, on your main.rs:


// Import the prelude to get all the macros and traits you need.
use cu29::prelude::*;

// Your application will be a struct that will hold the runtime, loggers etc.
// This proc macro is where all the runtime generation happens. If you are curious about what code is generated by this macro
// you can activate the feature macro_debug and it will display it at compile time.
#[copper_runtime(config = "copperconfig.ron")]  // this is the ron config we just created.
struct MyApplication {}

// Here we define our own Copper Task
// It will be a source flipping a boolean
pub struct FlippingSource {
    state: bool,
}

// We implement the CuSrcTask trait for our task as it is a source / driver (with no internal input from Copper itself).
impl<'cl> CuSrcTask<'cl> for FlippingSource {
    type Output = output_msg!('cl, RPGpioPayload);

    // You need to provide at least "new" out of the lifecycle methods.
    // But you have other hooks in to the Lifecycle you can leverage to maximize your opportunity 
    // to not use resources outside of the critical execution path: for example start, stop, 
    // pre_process, post_process etc...
    fn new(config: Option<&copper::config::ComponentConfig>) -> CuResult<Self>
    where
        Self: Sized,
    {
        // the config is passed from the RON config file as a Map.
        Ok(Self { state: true })
    }
    
    // Process is called by the runtime at each cycle. It will give:
    // 1. the reference to a monotonic clock
    // 2. a mutable reference to the output message (so no need to allocate of copy anything)
    // 3. a CuResult to handle errors
    fn process(&mut self, clock: &RobotClock, output: Self::Output) -> CuResult<()> {
        self.state = !self.state;   // Flip our internal state and send the message in our output.
        output.set_payload(RPGpioPayload {
            on: self.state,
            creation: Some(clock.now()).into(),
            actuation: Some(clock.now()).into(),
        });
        Ok(())
    }
}


fn main() {

    // Copper uses a special log format called "unified logger" that is optimized for writing. It stores the messages between tasks 
    // but also the structured logs and telemetry.
    // A log reader can be generated at the same time as the application to convert this format for post processing.
  
    let logger_path = "/tmp/mylogfile.copper";
    
    // This basic setup is a shortcut to get you running. If needed you can check out the content of it and customize it. 
    let copper_ctx =
        basic_copper_setup(&PathBuf::from(logger_path), true).expect("Failed to setup logger.");
        
    // This is the struct logging implementation tailored for Copper.
    // It will store the string away from the application in an index format at compile time.
    // and will store the parameter as an actual field.
    // You can even name those: debug!("This string will not be constructed at runtime at all: my_parameter: {} <- but this will be logged as 1 byte.", my_parameter = 42);  
    debug!("Logger created at {}.", logger_path); 
    
    // A high precision monotonic clock is provided. It can be mocked for testing. 
    // Cloning the clock is cheap and gives you the exact same clock.
    let clock = copper_ctx.clock;  
    
    debug!("Creating application... ");
    let mut application =
        MyApplication::new(clock.clone(), copper_ctx.unified_logger.clone())
            .expect("Failed to create runtime.");
    debug!("Running... starting clock: {}.", clock.now());  // The clock will be displayed with units etc. 
    application.run().expect("Failed to run application.");
    debug!("End of program: {}.", clock.now());
}


But this is a very minimal example for a task; please see lifecycle for a more complete explanation of a task lifecycle.

Deployment of the application

Check out the deployment page for more information.

How is Copper better or different from the ROS (Robot Operating System)?

As explained above, Copper is a “user-friendly runtime engine” written in Rust which manages task execution, data flow, logging, and more.

In contrast, the ROS is an open-source set of software libraries and tools primarily written in C++ and Python.

Let’s talk about some of the benefits and differences between the two.

Performance

In the example directory, we have 2 equivalent applications. One written in C++ for ROS and a port in Rust with Copper.

examples/cu_caterpillar
examples/ros_caterpillar

You can try them out by either just logging onto a desktop, or with GPIOs on a RPi. You should see a couple order of magnitude difference in performance.

Copper has been designed for performance first. Not unlike a game engine, we use a data-oriented approach to minimize latency and maximize throughput.

Safety

As Copper is written in Rust, it is memory safe and thread safe by design. It is also designed to be easy to use and avoid common pitfalls.

As we progress on this project we plan on implementing more and more early warnings to help you avoid “the death by a thousand cuts” that can happen in a complex system.

Release Notes

You can find the release notes here.

Roadmap

[!NOTE] We are looking for contributors to help us build the best robotics framework possible. If you are interested, please join us on Discord or open an issue.

Here are some of the features we plan to implement next, if you are interested in contributing on any of those, please let us know!:

  • Buffer Pools: Implement a large buffer (Host or Device/GPU) pools for 0 copy large inter-task transfers.
  • Log Compression & Selection: Implement a pluggable compression system for logs and its resim counterpart. For example to encode video from images. Selection is about NOT logging something if it is not needed.
  • Modes: Implement a way to have multiple DAGS in the RON configuration file and have a centralized way to switch from one to another. This is useful for running the stack in predeterministic modes: for example Data acquisition, Full autonomy, Charging, etc.
  • Microcontroller and RTOS support: Modify all necessary Copper code packages to remove dependencies on the standard library (std) to support “no_std” (#![no_std]) to support running the code on bare-metal on microcontrollers. This will allow a seamless environment for high level calls on a standard kernel (ML inference etc…) and low level tasks on MCUs (control, HW interfacing…).
  • Parallel Copper Lists: allow Copper lists to be executed in a staggered and parallel way to improve throughput.
  • ROS2/DDS interfacing: Build a pair of sink and source to connect to existing ROS2 systems, helping users migrate their stack bit by bit.
  • Modular Configuration: As robots built with Copper gain complexity, users will need to build “variations” of their robots without duplicating their entire RON file.
  • Extensible scheduling: Enable a way to give hints to Copper to better schedule workloads at compile time.
  • Swarm support: Implement Zenoh to allow a swarm of robots powered by Copper to cooperate.
  • MCAP support: Allow the interfacing of Foxglove and ROS ecosystems at the logging level.

CONTRIBUTING

Contributing to Copper-rs

First off, thank you for considering contributing to Copper-rs! We welcome contributions from everyone. This document provides guidelines for contributing to the project.

Getting Started

Prerequisites

  • Rust: Ensure you have a recent stable Rust toolchain installed. You can install it using rustup.
    curl --proto '=https' --tlsv1.2 -sSf [https://sh.rustup.rs](https://sh.rustup.rs) | sh
    
  • cargo-nextest: For running tests efficiently.
    cargo install cargo-nextest
    
  • Platform-Specific Dependencies: Depending on your operating system and the features you intend to work with, you might need additional dependencies. Refer to the Continuous Integration Setup section below for details extracted from our CI workflow. For Ubuntu 22.04, please checkout our Dockerfile.

Building the Project

To build the project, navigate to the root directory and run:

cargo build --workspace

For a release build, use:

cargo build --release --workspace

Running Tests

We use cargo-nextest for running tests. To run all unit tests:

cargo nextest run --workspace --all-targets

To run tests including specific features (matching the CI ‘debug’ mode non-CUDA features):

cargo nextest run --workspace --all-targets --features macro_debug,mock,perf-ui,image,kornia,python,gst,faer,nalgebra,glam,debug_pane,bincode

Contribution Workflow

  1. Fork the Repository: Create your own fork of the copper-rs repository on GitHub.

  2. Clone Your Fork: Clone your forked repository to your local machine.

    git clone https://github.com/YOUR_USERNAME/copper-rs.git
    cd copper-rs
    
  1. Create a Branch: Create a new branch for your changes. Choose a descriptive branch name. ex: yang/chore/beta_clippy , gbin/feat/soa , gbin/fix/balancebot_sim .
    git checkout -b user/feat/description
    
  1. Make Changes: Implement your feature or fix the bug. Write clear and concise code.

  2. Run Checks: Before committing, ensure your code adheres to the project’s standards:
    • Formatting: cargo fmt --all -- --check
    • Clippy Lints: cargo clippy --workspace --all-targets -- --deny warnings
    • Tests: cargo nextest run --workspace --all-targets (run with relevant features if applicable)
    • Typos: Ensure you run a spell checker. We use typos --config .config/_typos.toml .
  3. Commit and push Changes: Commit your changes with clear and descriptive commit messages. (Consider using Conventional Commits)
    git add .
    git commit -m "feat: new feature"
    git push user/feat/description
    
  1. Create a Pull Request: Open a pull request (PR) from your branch to the master branch of the main copper-rs repository. Provide a clear description of your changes in the PR.

Dependency Management

We keep our dependencies minimal and up-to-date.

We use

``` to identify and remove unused dependencies. Before submitting a PR, please run:

1. Install 
```cargo-machete
cargo install cargo-machete

  1. Run cargo-machete to find unused dependencies:
cargo machete --with-metadata

  1. Handling False Positives
``` is not perfect. If it incorrectly flags a necessary dependency (e.g., required by a feature flag or build script), add it to the ignored list in the relevant Cargo.toml file:


```toml
[package.metadata.cargo-machete]
# Explain *why* the dependency is needed despite not being directly used in code.
# e.g. "Required for X feature" or "Used in build.rs"
ignored = ["some-crate", "another-crate"]

Reporting Issues

If you encounter a bug or have a feature suggestion, please open an issue on the GitHub repository. Provide as much detail as possible, including:

  1. A clear description of the issue or feature.
  2. Steps to reproduce the bug (if applicable).
  3. Your operating system and Rust version.
  4. Relevant logs or error messages.

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros

copper-rs repository

rust robotics ros