Copper
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) and MacOS (arm64). Testers would be welcomed on Windows and other platforms.
Technical Overview
Copper is a data-oriented Robot SDK with these key components:
-
Task Graph: Described in RON, this configures the system's topology, specifying which tasks communicate and setting types for nodes and messages.
-
Runtime Generator: This component decides on an execution plan based on the 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!
Here is robot developed with Copper in action driving its digital twin in a simulation environment with Bevy (Game Engine in Rust) and Avian3d (Physics Engine in Rust)
You have a mac or a linux machine (x86-64 or Arm) just run ...
... to try it locally.
The source code for this demo is available in the examples/cu_rp_balancebot directory.
Implemented Features So Far
- Task interface and Lifecycle: Those are stable traits you can use to implement new algorithms, sensors, and actuators.
- Runtime generation: The current implementation works for up to middle size robots (~a couple dozen of tasks).
- Log reader: You can reread the logs generated by Copper from your robot, sim or resim.
- Structured log reader: debug logs are indexed and string interned at compile time for maximum efficiency.
- 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!
- 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.
- Simulation: We have a simple simulation environment to test your robot without a real robot.
Components
| Category | Type | Description | Crate Name | |
|---|---|---|---|---|
| Sensors | Lidar | Velodyne/Ouster VLP16 | cu-vlp16 | |
| Lidar | Hesai/XT32 | cu-hesai | ||
| IMU | WitMotion WT901 | cu-wt901 | ||
| ADC/Position | ADS 7883 3MPSPS SPI ADC | cu-ads7883 | ||
| Encoder | Generic Directional Wheel encoder | cu-rp-encoder | ||
| Actuators | GPIO | Raspberry Pi | cu-rp-gpio | |
| Servo | Lewansoul Servo Bus (LX-16A, etc.) | cu-lewansoul | ||
| DC Motor Driver | Half-H Driver for CD Motors | cu-rp-sn754410 | ||
| Monitors | TUI 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 impatients
You can generate a project from a template present in the repo. It will ask you the name you want to pick interactively.
Check out copper-templates for more info.
How 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 *;
// 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.
// this is the ron config we just created.
// Here we define our own Copper Task
// It will be a source flipping a boolean
// We implement the CuSrcTask trait for our task as it is a source / driver (with no internal input from Copper itself).
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 it better or different from ROS?
Performance
In the example directory, we have 2 equivalent applications. One written in C++ for ROS and a port in Rust with Copper.
You can try them out by either just logging on 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 (in ~order of priority), if you are interested in contributing on any of those, please let us know!:
- Parallel Copper Lists: Today Copper is single-threaded; this should enable concurrent Copper Lists to be executed at the same time with no contention.
- ROS/DDS interfacing: Build a pair of sink and source to connect to existing ROS systems, helping users migrate their infra bit by bit.
- Extensible scheduling: Enables a way to give hints to copper to schedule the workload.
- Modular Configuration: As robots built with Copper gain complexity, users will need to build "variations" of their robots without duplicating their entire RON file.
- Distributed Copper: Currently, we can only create one process. We need proper RPC filtering copper lists per subsystem.
