Skip to content

Repository files navigation

can-motor-control

Rust control library for Damiao-family CAN motors, with first-class Python bindings.

Status

v0.1.0 (in development) — single-arm control over Linux SocketCAN or native macOS gs_usb. Linux supports CAN-FD; macOS gs_usb supports classical CAN. See docs/can-fd.md and docs/gs-usb-macos.md.

Why

The two reference implementations — cmjang/DM_Control_Python and enactic/openarm_can — are either a single-file script that fuses sends with receives, or a C++ tree with a binding crate that rebuilds on every release. This library is built as a Rust core with a thin PyO3 binding so it installs as a single wheel and runs in realtime control loops without GIL contention.

Installation

# Python (once 0.1.0 is published):
pip install can-motor-control

# Rust from GitHub while crates.io publishing is deferred:
[dependencies]
can-motor-control = { git = "https://github.com/TomCC7/can-motor-control", tag = "v0.0.1" }
damiao-codec = { git = "https://github.com/TomCC7/can-motor-control", package = "can-motor-damiao-codec", tag = "v0.0.1" }

Rust users can depend on the public GitHub repository directly while crates.io publication is deferred. Pin a tag or commit for reproducible builds. The companion package names are prefixed (can-motor-codec, can-motor-damiao-codec) for a future crates.io release, while their Rust import crate names remain motor_codec and damiao_codec.

Build from source:

# Rust workspace
cargo build --workspace --release

# Python wheel
cd crates/can-motor-control-py
maturin build --release
pip install target/wheels/can_motor_control-*.whl

Layout

crates/
  motor-codec/          # no_std, package can-motor-codec, import motor_codec
  damiao-codec/         # no_std, package can-motor-damiao-codec, import damiao_codec
  can-motor-control/       # std, native transport + Robot/Group/Motor + builder
  can-motor-control-py/    # PyO3 bindings, built by maturin
configs/            # example TOML robot configs
examples/           # Python hardware bring-up ladder (00_-06_) + Rust example
tests/python/       # Python smoke + integration tests
docs/               # architecture + multi-vendor + CAN-FD notes
openspec/           # source of truth: proposals, designs, specs, tasks

Hardware bring-up examples

Real hardware bring-up should be done by walking the examples/00_* -> examples/06_* ladder in order. Each step adds exactly one kind of risk; do not skip steps when bringing up an arm for the first time. Mock and MockCanBus-driven tests prove the API surface and CI wiring, not that real hardware will respond correctly.

Python example Rust example Sends Hardware required
00_can_interface_check.py 00_can_interface_check nothing none (checks /sys/class/net)
01_single_motor_enable_disable.py 01_single_motor_enable_disable enable + disable one powered motor
02_read_single_motor.py -- enable + live refresh-poll + disable one powered motor
03_single_motor_mit_hold.py 03_single_motor_mit_hold bounded MIT hold one powered motor
04_single_motor_control_modes.py -- one bounded mode/run one powered motor
05_gripper_opening_control.py 05_gripper_opening_control normalized gripper opening (open, set_opening, close) one gripper motor
05_gripper_control.py 05_gripper_control raw gripper PosForce sequence, MIT, or PosVel one gripper motor
06_read_robot_motor.py -- live OpenArm/OpenYam zero-torque reader configured OpenArm or OpenYam single arm

Each example prints its assumptions before sending anything and disables on exit. Durations are hard-capped; live readers accept --mock for API-shape checks, but mock execution does not validate hardware.

For Linux setup, see docs/socketcan-setup.md. For macOS adapter selection and diagnostics, see docs/gs-usb-macos.md. Run Rust examples with cargo run -p can-motor-control --example <Rust example> -- ....

Documentation

Hosted documentation is published from main to https://tomcc7.github.io/can-motor-control/ by the GitHub Pages workflow.

A local docs site (Python API reference, guides, and hosted Rust rustdoc) is built with MkDocs plus cargo doc. The Python API prose is generated from the package's docstrings, so the native extension is built first. The environment is managed by uv and pinned by uv.lock for reproducibility (the root pyproject.toml is a dev-only environment, not a published package).

# Serve with live reload at http://127.0.0.1:8000
make docs

# Or render a static site into ./site
make docs-build

make docs-build runs scripts/build-docs.sh, which provisions the uv environment, builds the MkDocs site, runs cargo doc --no-deps --workspace --locked, and copies rustdoc into site/rustdoc/ for deployment. No separate virtualenv or pip install step is needed — uv provisions everything from uv.lock.

Releasing

Maintainers should follow the release runbook in docs/release.md before publishing to PyPI or crates.io. It covers registry setup, release-candidate dry-runs, crates.io publish order, PyPI Trusted Publishing, and post-publish install checks.

Source of truth

The architecture, every requirement, and the implementation plan live under openspec/. New features should propose changes there first.

Platforms

Target Hardware transport Scope
Linux SocketCanBus Classical CAN and CAN-FD
macOS GsUsbBus gs_usb channel 0, classical CAN

Each build exports only its native hardware transport. Both export MockCanBus. The macOS wheel uses IOKit through Rust nusb; it requires no Homebrew libusb, PyUSB, or Python gs_usb package.

License

Dual-licensed under Apache-2.0 OR MIT.

About

No description, website, or topics provided.

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages