Unified CAN motor control stack with a vendor-agnostic Rust core, stable C ABI, and Python/C++ bindings.
Chinese version: README.zh-CN.md
motorbridge-studio: https://github.com/tianrking/motorbridge-studio Standalone web control UI built on top ofws_gateway.
v0.4.9aligns the RobStriders-00andrs-06torque-limit metadata with vendor-provided values so model specs and downstream validation use the correct maximum torque ranges.
v0.4.8adds RobStride motor status parsing and display support so bring-up and debugging surfaces can show richer state and fault details.v0.4.8scopesws_gatewaysingle-motor commands to the explicit requested target so one-motor actions do not accidentally fan out across discovered motors.v0.4.7hardens Damiaoensure_modeby switching to a shared timeout budget, reusing the hold-position read during mode preparation, and improving mode write verification. This reduces timeout failures during mode changes and parameter persistence on real hardware.v0.4.7raises thedm-serialtransport timeout from1 msto10 msto reduce cross-platform serial write failures under tighter adapter/driver timing.v0.4.6reuses existing Damiao motor handles duringdamiao_state_manypolling so repeated browser telemetry does not fail on duplicate motor registration.v0.4.6lets browser clients authenticatews_gatewayby sendingMOTORBRIDGE_WS_TOKENin the WebSocket URL query as?motorbridge_ws_token=..., while keeping the existing non-loopback token requirement and header-based auth paths.v0.4.5improves Damiaoensure_modereliability by retrying theRID 10mode-register write after failed readback verification attempts and using a conservative shared 20 ms retry gap across platforms, adapters, and transports.v0.4.5adds experimental RobStride standard-frame preview paths:robstride_cia402for CANopen/CiA402 (F_CMD=1) androbstride_mitfor MIT protocol (F_CMD=2). These command surfaces are documented for testing and protocol bring-up, but are not production-ready yet.v0.4.5adds hardware gallery media and adapter documentation for common CAN and Damiao setups.v0.4.4adds the Damiao-onlydm-devicetransport for DaMiaoUSB2CANFD,USB2CANFD_DUAL, andLINKX4Cadapters through the DM_Device SDK. Rust CLI, Python SDK, Python CLI, Python wheels, C ABI, andws_gatewaynow share the same--transport dm-device --dm-device-type ... --dm-channel ...path. This transport is currently wired only to Damiao motor protocol, and the adapter must be configured/connected in USB mode.- In
dm-devicescan mode, omit--dm-channel/dm_channelto scan all channels on the selected adapter: channel0onusb2canfd, channels0|1onusb2canfd-dual, or channels0|1|2|3onlinkx4c.
DM_Device_SDK is a software development kit for CAN/CAN FD adapter devices.
motorbridge currently supports these DM_Device hardware types, while the
dm-device motor transport is wired only to Damiao motor protocol:
| Device Type | SDK Enum | Channels | --dm-device-type |
Channel Selection |
|---|---|---|---|---|
| USB2CANFD | USB2CANFD |
1 | usb2canfd |
--dm-channel 0 |
| USB2CANFD_DUAL | USB2CANFD_DUAL |
2 | usb2canfd-dual |
--dm-channel 0 / 1 |
| LINKX4C | LINKX4C |
4 | linkx4c |
--dm-channel 0 / 1 / 2 / 3 |
v0.4.4vendors the DM_Device SDK runtime underthird_party/dm_device.dm-deviceis enabled only for targets that have a matching SDK runtime file there. Python wheels do not bundle that vendor runtime; when Python SDK, Python CLI, ormotorbridge-gatewayfirst usesdm-device, motorbridge resolves the matching OS/arch runtime and, if it is missing, tells the user which file to download fromthird_party/dm_deviceand where to place it. Users can also setMOTOR_DM_DEVICE_LIB=/path/to/libdm_device.v0.4.4uses a small C++ shim for the SDK boundary and reuses the already opened SDK handle in long-running processes, which keeps repeated WS scans from requiring a USB unplug/replug cycle.- Linux x86_64 has been release-build, wheel-build, installed-wheel, hardware scan, and WebSocket-scan verified for USB2CANFD_DUAL channel 0/1 and LINKX4C channel 0..3. Windows/macOS SDK runtimes are vendored and path-selected, but final runtime validation still requires those hosts.
v0.4.2optimizes Damiaodm-serialhigh-rate multi-motor control by makingrecv(0ms)truly non-blocking when no serial bytes are pending and by reducing the bounded serial read timeout from 2 ms to 1 ms for synchronous feedback/register reads.v0.4.1adds ABI metadata discovery throughmotor_abi_version()andmotor_abi_capabilities_json(), plus Python/C++ helpers for querying the loaded ABI version and capability JSON.- C++ bindings now match Python for RobStride host-id probing, host-id f32 parameter reads, fault-report snapshots, and active-report toggling.
bindings/api_surface.jsonis the canonical API surface checklist used to keep ABI, Python, C++, and docs aligned.v0.4.1fixes Windows Damiaodm-serialwhole-arm scans throughws_gateway. Active Damiao sessions and state/parameter streams are released before scan probes reuse the serial bridge, preventing the "only joint1 online" failure pattern.- WebSocket clients can use
damiao_state_manyto refresh every discovered Damiao joint in one logical request. State payloads now includemotor_id,feedback_id, andmodel, so browser HMIs can merge telemetry by joint. - Damiao state snapshots now request fresh feedback with a bounded timeout
before returning
pos,vel, andtorq, reducing stale cached values after scan or enable. v0.3.9RobStride semantics remain: unifiedrequest_feedback()is a non-blocking no-op instead of sending a blockingping, because RobStride ping replies do not synthesizeMotorState.- Use
robstride_ping()for RobStride connectivity checks. - Use active report for streaming RobStride state, or typed parameter reads such
as
0x7019 mechPosand0x701B mechVelwhen fresh position/velocity values are required. v0.3.8PP/CSP additions remain available:pos-vel-pp,pos-vel-csp,robstride_send_pos_vel_pp(), androbstride_send_pos_vel_csp().
[STD-CAN]: classic CAN path (socketcan/pcan)[CAN-FD]: dedicated FD path (socketcanfd)[DM-SERIAL]: Damiao serial-bridge path (dm-serial)[DM-DEVICE]: Damiao DM_Device SDK path (dm-device; supports single-channelUSB2CANFD, dual-channelUSB2CANFD_DUAL, and four-channelLINKX4C; Damiao motor protocol only)
Current status:
[CAN-FD]has been integrated as an independent transport path.[DM-DEVICE]is integrated for Damiao and verified on Linux x86_64 with USB2CANFD_DUAL channel 0/1 scans plus LINKX4C SDK channel0..3scans. Build/package support follows the SDK runtime files vendored inthird_party/dm_device/v1.1.0; Python wheels resolve the matching runtime and print setup instructions instead of embedding it.- No motor model is officially marked as CAN-FD validated in this repository yet.
Images live under media. Adapter images are referenced
from the README now; motor images can be added later when real device photos are
available.
| Motor family | Vendor path | Main protocol path |
|---|---|---|
| Damiao | damiao |
MIT / POS_VEL / VEL over classic CAN, Damiao serial bridge, or Damiao DM_Device SDK |
| RobStride | robstride |
Private protocol F_CMD=0, 29-bit extended CAN |
| RobStride CiA402 | robstride_cia402 |
CANopen/CiA402 F_CMD=1, mostly 11-bit standard CAN; experimental/incomplete |
| RobStride MIT | robstride_mit |
MIT F_CMD=2, 11-bit standard CAN; experimental/incomplete |
| MyActuator | myactuator |
MyActuator standard CAN protocol |
| HighTorque | hightorque |
Native ht_can v1.5.5 direct CAN path |
| Hexfellow | hexfellow |
CANopen-over-CAN-FD path |
Adapter images live under media/adapters. Replace or add
files there when new hardware photos become available.
| Adapter / module | CLI transport | Typical use | Image |
|---|---|---|---|
| CANable / candleLight | socketcan |
Classic CAN through Linux SocketCAN, usually can0 |
![]() |
| PCAN-USB | socketcan / PCAN backend |
PCAN adapter on Windows/macOS/Linux backends | ![]() |
| Damiao USB2CAN | dm-serial / Damiao bridge path |
Damiao USB-to-CAN bridge module | ![]() |
| Damiao USB2CANFD | dm-device |
Damiao-only DM_Device SDK path, one SDK channel | ![]() |
| Damiao USB2CANFD_DUAL | dm-device |
Damiao-only DM_Device SDK path, SDK channels 0 and 1 |
![]() |
| Damiao LINKX4C | dm-device |
Damiao-only DM_Device SDK path, SDK channels 0..3 |
![]() |
- Damiao:
- models:
3507,4310,4310P,4340,4340P,6006,8006,8009,10010L,10010,H3510,G6215,H6220,JH11,6248P - modes:
scan,enable,disable,MIT,POS_VEL,VEL,FORCE_POS,set-id,set-zero
- models:
- RobStride:
- models:
rs-00,rs-01,rs-02,rs-03,rs-04,rs-05,rs-06 - modes:
scan,ping,enable,disable,MIT,POS_VEL,POS_VEL_PP,POS_VEL_CSP,VEL, parameter read/write,set-id,zero - host/feedback default:
0xFD(with0xFF/0xFEfallback probing) - note: torque/current control is currently parameter-level only (
write-paramoniq_ref/limits), not a first-class unified mode
- models:
- RobStride CiA402:
- models:
rs-00,rs-01,rs-02,rs-03,rs-04,rs-05,rs-06 - modes:
scan,status,enable,disable,quick-stop,clear-error,zero,watchdog,set-protocol,pos-vel,vel,torque,mit(mapped to CSP) - note: this is for RobStride motors switched to CANopen/CiA402; plain
robstrideremains the private extended-CAN path - status: experimental/incomplete; do not treat it as a production-ready RobStride path yet
- models:
- RobStride MIT:
- models:
rs-00,rs-01,rs-02,rs-03,rs-04,rs-05,rs-06 - modes:
scan,status,enable,disable,clear-error,zero,set-mode,set-can-id,set-host-id,set-protocol,save,active-report,mit,pos-vel,vel, parameter read/write - note: this is for RobStride motors switched to F_CMD=2 MIT protocol; it uses classic CAN standard frames, not the private extended-CAN
robstride --mode mitpath - status: experimental/incomplete; do not treat it as a production-ready RobStride path yet
- models:
- MyActuator:
- models:
X8(runtime string; protocol is ID-based) - modes:
scan,enable,disable,stop,set-zero,status,current,vel,pos,version,mode-query
- models:
- HighTorque:
- models:
hightorque(runtime string; nativeht_can v1.5.5) - modes:
scan,read,mit,pos-vel,vel,stop,brake,rezero
- models:
- Hexfellow:
- models:
hexfellow(runtime string; CANopen profile) - modes:
scan,status,enable,disable,pos-vel,mit(viasocketcanfd)
- models:
RobStride has three independent protocol paths in this workspace:
| Vendor | Motor protocol | CAN ID / frame | Data length | Best use | Current status |
|---|---|---|---|---|---|
robstride |
private protocol, F_CMD=0 |
29-bit extended CAN. The extended ID carries comm_type, host ID, and motor ID. |
CAN 2.0, 8 bytes | Factory-style configuration, parameter read/write, ID changes, diagnostics, private MIT-like motion control | Most mature RobStride path in this repository |
robstride_cia402 |
CANopen/CiA402, F_CMD=1 |
Mostly 11-bit standard CAN: NMT 0x000, SDO 0x600+node / 0x580+node, heartbeat 0x700+node. Protocol switching is a documented 29-bit extended frame 0xFFF. |
CAN 2.0, 8 bytes | CANopen master integration, standard state machine, object-dictionary based control | Experimental/incomplete for production: core CLI path exists, but EDS/PDO/SYNC coverage, real-device validation matrix, and dm-device transport support are not completed |
robstride_mit |
MIT protocol, F_CMD=2 |
11-bit standard CAN. Control uses motor_id; typed commands use (type << 8) | motor_id, for example position 0x100+id, velocity 0x200+id, parameter read 0x300+id. |
CAN 2.0, 8 bytes | Lightweight real-time joint control with pos/vel/kp/kd/tau, plus direct position/velocity commands |
Experimental/incomplete for production: core CLI path exists, but high-rate control ergonomics, real-device validation matrix, and dm-device transport support are not completed |
The standard-frame paths (robstride_cia402 and especially robstride_mit) are good candidates for the DM Device SDK (dm-device-sdk/C&C++) because that SDK can send and receive raw CAN 2.0 frames. Today that is only a future integration path in motorbridge: --transport dm-device is not yet wired as a generic backend for these RobStride vendors. Treat the SDK as a CAN adapter backend, not as a RobStride protocol implementation; RobStride frame encoding still belongs in motor_vendors/robstride_cia402 or motor_vendors/robstride_mit.
RobStride ID roles differ by protocol. In this repository, --motor-id always means "the motor to control". For RobStride private and MIT protocols, --feedback-id is better understood as the host/master ID, not another motor ID. CANopen/CiA402 does not use that host-ID reply convention; it uses standard CANopen COB-IDs derived from the node ID.
| Vendor | --motor-id means |
--feedback-id means |
Send IDs | Receive matching |
|---|---|---|---|---|
robstride |
Private-protocol target motor ID | Host/master ID, default 0xFD |
29-bit extended ID `(comm_type << 24) | (extra_data << 8) |
robstride_cia402 |
CANopen node ID, normally 1..127 |
Ignored/unused | NMT 0x000; SDO request 0x600 + node; protocol switch uses extended 0xFFF |
SDO reply 0x580 + node; heartbeat 0x700 + node |
robstride_mit |
MIT target motor CAN ID | Host/master feedback ID, default 0xFD |
Basic commands and packed MIT control use standard ID motor_id; typed commands use 0x100 + motor_id, 0x200 + motor_id, 0x300 + motor_id, 0x400 + motor_id |
Feedback is accepted when standard ID equals feedback_id and data[0] == motor_id; parameter replies use typed reply ID |
- Damiao production baseline now covers:
scan / enable / disable / MIT / POS_VEL / VEL / FORCE_POS / set-id / set-zero. - RobStride private-protocol production baseline now covers:
scan / ping / enable / disable / MIT / POS_VEL / VEL / parameter read-write / set-id / zero. - RobStride default host/feedback path is
0xFD; scan now tries0xFD,0xFF,0xFE,0x00,0xAAby default. - RobStride
feedback_id/host_idis host-side addressing, not the motordevice_id; scan hits report the motor ID asprobe/device_id. - In RobStride
pos-vel,--vel/--kd/--tauare intentionally ignored and reported as warnings (no hard error).
flowchart TB
APP["User Apps (Rust/C/C++/Python/ROS2/WS)"] --> SURFACE["CLI / ABI / SDK / Integrations"]
SURFACE --> CORE["motor_core (CoreController / bus / model / traits)"]
CORE --> RX["Background RX worker (default)"]
CORE --> MANUAL["poll_feedback_once() (manual-compatible)"]
RX --> CACHE["Latest state cache per motor"]
MANUAL --> CACHE
CORE --> DAMIAO["motor_vendors/damiao"]
CORE --> ROBSTRIDE["motor_vendors/robstride"]
CORE --> ROBSTRIDE_CIA402["motor_vendors/robstride_cia402"]
CORE --> ROBSTRIDE_MIT["motor_vendors/robstride_mit"]
CORE --> MYACT["motor_vendors/myactuator"]
CORE --> HIGHTORQUE["motor_vendors/hightorque"]
CORE --> HEXFELLOW["motor_vendors/hexfellow"]
CORE --> TEMPLATE["motor_vendors/template (onboarding scaffold)"]
DAMIAO --> CAN["CAN bus backend"]
ROBSTRIDE --> CAN
ROBSTRIDE_CIA402 --> CAN
ROBSTRIDE_MIT --> CAN
MYACT --> CAN
HIGHTORQUE --> CAN
HEXFELLOW --> CAN
CAN --> LNX["Linux: SocketCAN"]
CAN --> WIN["Windows (experimental): PEAK PCAN"]
CAN --> HW["Physical motors"]
flowchart LR
ROOT["motorbridge workspace"] --> CORE["motor_core"]
ROOT --> VENDORS["motor_vendors/*"]
ROOT --> CLI["motor_cli"]
ROOT --> ABI["motor_abi"]
ROOT --> STUDIO["motorbridge-studio (separate repo)"]
ROOT --> INTS["integrations/*"]
ROOT --> BIND["bindings/*"]
VENDORS --> VD["damiao"]
VENDORS --> VH["hexfellow"]
VENDORS --> VHT["hightorque"]
VENDORS --> VR["robstride"]
VENDORS --> VRC["robstride_cia402"]
VENDORS --> VRM["robstride_mit"]
VENDORS --> VM["myactuator"]
VENDORS --> VT["template"]
INTS --> ROS["ros2_bridge"]
INTS --> WS["ws_gateway"]
BIND --> PY["python"]
BIND --> CPP["cpp"]
flowchart TB
PYAPP["Python App"] --> CTL["Controller(...) / from_socketcanfd(...) / from_dm_serial(...) / from_dm_device(...)"]
CTL --> ADD["add_damiao_motor / add_robstride_motor / add_myactuator_motor / add_hightorque_motor / add_hexfellow_motor"]
ADD --> MOTOR["MotorHandle"]
MOTOR --> CTRL1["send_mit / send_pos_vel / send_vel / send_force_pos"]
MOTOR --> CTRL2["ensure_mode / enable / disable / set_zero / stop / clear_error"]
MOTOR --> FB1["request_feedback()"]
CTL --> FB2["poll_feedback_once() (backward-compatible)"]
FB1 --> STATE["get_state() latest cached feedback"]
FB2 --> STATE
motor_core: vendor-agnostic controller, routing, CAN bus layer (Linux SocketCAN / Windows experimental PCAN)motor_vendors/damiao: Damiao protocol / models / registersmotor_vendors/hexfellow: Hexfellow CANopen-over-CAN-FD implementationmotor_vendors/hightorque: HighTorque native ht_can protocol implementationmotor_vendors/robstride: RobStride extended CAN protocol / models / parametersmotor_vendors/robstride_cia402: RobStride CANopen/CiA402 over classic CANmotor_vendors/robstride_mit: RobStride F_CMD=2 MIT protocol over classic CAN standard framesmotor_vendors/myactuator: MyActuator CAN protocol implementationmotor_cli: unified Rust CLI- full parameters (English):
motor_cli/README.md - full parameters (Chinese):
motor_cli/README.zh-CN.md - Damiao command/register guide:
motor_cli/DAMIAO_API.md,motor_cli/DAMIAO_API.zh-CN.md - RobStride command/parameter guide:
motor_cli/ROBSTRIDE_API.md,motor_cli/ROBSTRIDE_API.zh-CN.md - MyActuator command/mode guide:
motor_cli/MYACTUATOR_API.md,motor_cli/MYACTUATOR_API.zh-CN.md
- full parameters (English):
motor_abi: stable C ABIbindings/python: Python SDK +motorbridge-clibindings/cpp: C++ RAII wrappermotorbridge-studio: standalone web control UI repository (split out oftools/factory_calib_ui_ws)
Build:
cargo buildBring up CAN:
sudo ip link set can0 down 2>/dev/null || true
sudo ip link set can0 type can bitrate 1000000 restart-ms 100
sudo ip link set can0 up
ip -details link show can0Quick CAN restart (Linux):
# default: can0 / 1Mbps / restart-ms=100 / loopback off
IF=can0; BITRATE=1000000; RESTART_MS=100; LOOPBACK=off
sudo ip link set "$IF" down 2>/dev/null || true
if [ "$LOOPBACK" = "on" ]; then
sudo ip link set "$IF" type can bitrate "$BITRATE" restart-ms "$RESTART_MS" loopback on
else
sudo ip link set "$IF" type can bitrate "$BITRATE" restart-ms "$RESTART_MS" loopback off
fi
sudo ip link set "$IF" up
ip -details link show "$IF"Damiao CLI:
cargo run -p motor_cli --release -- \
--vendor damiao --channel can0 --model 4340P --motor-id 0x01 --feedback-id 0x11 \
--mode mit --pos 0 --vel 0 --kp 20 --kd 1 --tau 0 --loop 50 --dt-ms 20[STD-CAN]
Hexfellow CLI:
cargo run -p motor_cli --release -- \
--vendor hexfellow --transport socketcanfd --channel can0 \
--model hexfellow --motor-id 1 --feedback-id 0 \
--mode status[CAN-FD]
RobStride CLI:
cargo run -p motor_cli --release -- \
--vendor robstride --channel can0 --model rs-00 --motor-id 127 \
--mode vel --vel 0.3 --loop 40 --dt-ms 50RobStride private-protocol MIT / POS_VEL quick checks:
cargo run -p motor_cli --release -- \
--vendor robstride --channel can0 --model rs-00 --motor-id 2 --feedback-id 0xFD \
--mode mit --ensure-strict 1 --pos 0.5 --vel 0 --kp 20.0 --kd 0.5 --tau 0 \
--loop 100 --dt-ms 20
cargo run -p motor_cli --release -- \
--vendor robstride --channel can0 --model rs-00 --motor-id 2 --feedback-id 0xFD \
--mode pos-vel --pos 1.5 --vlim 1.0 --loc-kp 5.0 --loop 1 --dt-ms 20RobStride CiA402 CLI:
cargo run -p motor_cli --release -- \
--vendor robstride_cia402 --channel can0 --model rs-00 --motor-id 1 \
--mode status
cargo run -p motor_cli --release -- \
--vendor robstride_cia402 --channel can0 --model rs-00 --motor-id 1 \
--mode pos-vel --pos 1.57 --vlim 1.0 --acc 4.0 --loop 1RobStride F_CMD=2 MIT protocol CLI:
cargo run -p motor_cli --release -- \
--vendor robstride_mit --channel can0 --model rs-00 --motor-id 1 --feedback-id 0xFD \
--mode mit --pos 0 --vel 0 --kp 20 --kd 0.5 --tau 0 --loop 100 --dt-ms 20
cargo run -p motor_cli --release -- \
--vendor robstride_mit --channel can0 --model rs-00 --motor-id 1 --feedback-id 0xFD \
--mode pos-vel --pos 1.57 --vlim 1.0 --loop 1HighTorque CLI (native ht_can v1.5.5):
cargo run -p motor_cli --release -- \
--vendor hightorque --channel can0 --model hightorque --motor-id 1 \
--mode readRobStride CLI parameter read:
cargo run -p motor_cli --release -- \
--vendor robstride --channel can0 --model rs-00 --motor-id 127 \
--mode read-param --param-id 0x7019MyActuator CLI:
cargo run -p motor_cli --release -- \
--vendor myactuator --channel can0 --model X8 --motor-id 1 --feedback-id 0x241 \
--mode status --loop 20 --dt-ms 50Unified scan (all vendors):
cargo run -p motor_cli --release -- \
--vendor all --channel can0 --mode scan --start-id 1 --end-id 255Focused RobStride scan (Rust CLI and Python CLI use the same host-id defaults):
cargo run -p motor_cli --release -- \
scan --vendor robstride --channel can0 --start-id 1 --end-id 127 \
--feedback-ids 0xFD,0xFF,0xFE,0x00,0xAA
motorbridge-cli scan \
--vendor robstride --channel can0 --start-id 1 --end-id 127 \
--feedback-ids 0xFD,0xFF,0xFE,0x00,0xAALinux remains the primary target. Windows support is experimental and currently backed by PEAK PCAN (PCANBasic.dll).
- Install PEAK PCAN driver + PCAN-Basic runtime on Windows.
- Channel mapping:
can0->PCAN_USBBUS1can1->PCAN_USBBUS2
- Optional bitrate suffix:
@<bitrate>(for examplecan0@1000000).
Validation commands on Windows:
# Scan Damiao IDs
cargo run -p motor_cli --release -- --vendor damiao --channel can0@1000000 --model 4340P --motor-id 0x01 --feedback-id 0x11 --mode scan --start-id 1 --end-id 16
# Move motor #1 (4340P) to +pi rad (~180 deg)
cargo run -p motor_cli --release -- --vendor damiao --channel can0@1000000 --model 4340P --motor-id 0x01 --feedback-id 0x11 --mode pos-vel --pos 3.1416 --vlim 2.0 --loop 1 --dt-ms 20
# Move motor #7 (4310) to +pi rad (~180 deg)
cargo run -p motor_cli --release -- --vendor damiao --channel can0@1000000 --model 4310 --motor-id 0x07 --feedback-id 0x17 --mode pos-vel --pos 3.1416 --vlim 2.0 --loop 1 --dt-ms 20This project supports PCAN on macOS via MacCAN's PCBUSB runtime.
On macOS, PCANBasic.dll is not used.
- A PEAK-compatible USB-CAN adapter recognized by macOS.
motorbridgesource built on macOS.- Bundled archive in this repo:
third_party/pcan/macos/macOS_Library_for_PCANUSB_v0.13.tar.gz. - Or download directly from GitHub:
Use the helper script from repo root:
# user-local install (no sudo, recommended)
./scripts/setup_pcbusb_macos.sh --user-local
# system install (uses package install.sh, requires sudo)
./scripts/setup_pcbusb_macos.sh --systemIf you use --user-local, run motor_cli with:
DYLD_LIBRARY_PATH=$HOME/.local/lib ./target/release/motor_cli ...If you want to download manually first:
mkdir -p /tmp/motorbridge-pcan && cd /tmp/motorbridge-pcan
curl -L -o macOS_Library_for_PCANUSB_v0.13.tar.gz \
https://raw.githubusercontent.com/tianrking/motorbridge/main/third_party/pcan/macos/macOS_Library_for_PCANUSB_v0.13.tar.gzThen install:
tar -xzf macOS_Library_for_PCANUSB_v0.13.tar.gz
cd PCBUSB
sudo ./install.shThe installer places:
libPCBUSB.dylibinto/usr/local/libPCBUSB.hinto/usr/local/include
If your user cannot write to /usr/local, use a local runtime path:
mkdir -p ~/.local/lib ~/.local/include
cp PCBUSB/libPCBUSB.0.13.dylib ~/.local/lib/
ln -sf ~/.local/lib/libPCBUSB.0.13.dylib ~/.local/lib/libPCBUSB.dylib
cp PCBUSB/PCBUSB.h ~/.local/include/Then run motor_cli with:
DYLD_LIBRARY_PATH=$HOME/.local/lib ./target/release/motor_cli ...python3 - <<'PY'
from can.interfaces.pcan.basic import PCANBasic
PCANBasic()
print("PCBUSB load OK")
PYIf using user-local install:
DYLD_LIBRARY_PATH=$HOME/.local/lib python3 - <<'PY'
from can.interfaces.pcan.basic import PCANBasic
PCANBasic()
print("PCBUSB load OK")
PYcargo build -p motor_cli --releasecan0maps toPCAN_USBBUS1can1maps toPCAN_USBBUS2- Optional bitrate suffix is supported (example:
can0@1000000)
./target/release/motor_cli \
--vendor damiao --channel can0 --mode scan --start-id 1 --end-id 16If using user-local PCBUSB:
DYLD_LIBRARY_PATH=$HOME/.local/lib ./target/release/motor_cli \
--vendor damiao --channel can0 --mode scan --start-id 1 --end-id 16Replace motor-id and feedback-id with your scan hits.
./target/release/motor_cli \
--vendor damiao --channel can0 --model 4310 \
--motor-id 0x02 --feedback-id 0x12 \
--mode mit --pos 0 --vel 0 --kp 20 --kd 1 --tau 0 \
--loop 50 --dt-ms 20load PCBUSB failed ...:- Install PCBUSB with
install.sh, or exportDYLD_LIBRARY_PATHfor local install.
- Install PCBUSB with
No CAN backend for current platform:- Use a build that includes the macOS PCAN backend.
hits=0on scan:- Check wiring, power, termination resistor, and CAN bitrate.
Linux uses SocketCAN interface names directly (for example can0, can1).
Do not pass bitrate suffix in Linux channel names (for example can0@1000000 is invalid on Linux SocketCAN).
For CANable, use candleLight/gs_usb firmware so the adapter appears as a SocketCAN interface:
lsusb
lsmod | grep -E 'gs_usb|can_raw|can_dev'
scripts/canable_restart.sh can0
ip -details link show can0Then use can0 as CLI channel:
cargo run -p motor_cli --release -- --vendor robstride --channel can0 --mode scan --start-id 1 --end-id 127Use this Linux-only transport when you want an independent CAN-FD path without changing existing classic CAN or dm-serial behavior.
# Bring up CAN-FD interface first
scripts/canfd_restart.sh can0
# Damiao over dedicated socketcanfd transport
cargo run -p motor_cli --release -- --vendor damiao \
--transport socketcanfd --channel can0 \
--model 4310 --motor-id 0x04 --feedback-id 0x14 \
--mode mit --verify-model 0 --ensure-mode 0 \
--pos 0.5 --vel 0 --kp 20 --kd 1 --tau 0 --loop 80 --dt-ms 20[CAN-FD] (transport integrated; motor verification matrix pending)
Use this path only when your Damiao adapter exposes a serial bridge (for example /dev/ttyACM1) and you want to run Damiao through that private transport:
# Damiao scan over serial bridge
cargo run -p motor_cli --release -- --vendor damiao \
--transport dm-serial --serial-port /dev/ttyACM1 --serial-baud 921600 \
--model 4310 --mode scan --start-id 1 --end-id 16
# Damiao MIT over serial bridge
cargo run -p motor_cli --release -- --vendor damiao \
--transport dm-serial --serial-port /dev/ttyACM1 --serial-baud 921600 \
--model 4310 --motor-id 0x04 --feedback-id 0x14 \
--mode mit --verify-model 0 --ensure-mode 0 \
--pos 0.5 --vel 0 --kp 20 --kd 1 --tau 0 --loop 80 --dt-ms 20[DM-SERIAL]
For deterministic troubleshooting of PCAN and CANable candleLight/gs_usb, use:
Interpretation:
vendor=damiao id=<n>means one Damiao motor is online at motor ID<n>.vendor=robstride ... probe=<n> ... device_id=<n>means one RobStride motor responded at motor/device ID<n>.- In RobStride output,
feedback_id/host_idsuch as0xFDor0xFEis not the motor ID. vendor=hightorque ... [hit] id=<n> ...means one HighTorque motor responded via native ht_can v1.5.5.vendor=myactuator id=<n>means one MyActuator motor responded.hits=<k>at the end of each scan block is the count of discovered devices.
- C ABI:
motor_abi_version()motor_abi_capabilities_json()motor_controller_new_socketcan(channel)motor_controller_new_dm_serial(serial_port, baud)(Damiao-only serial bridge; cross-platform, e.g./dev/ttyACM0orCOM3)motor_controller_new_dm_device(dm_device_type, dm_channel)(Damiao-only DM_Device SDK path; e.g.usb2canfd+0,usb2canfd-dual+0|1, orlinkx4c+0|1|2|3)- Damiao:
motor_controller_add_damiao_motor(...) - Hexfellow:
motor_controller_add_hexfellow_motor(...)(CAN-FD path viasocketcanfd) - RobStride:
motor_controller_add_robstride_motor(...) - MyActuator:
motor_controller_add_myactuator_motor(...) - HighTorque:
motor_controller_add_hightorque_motor(...)
- Python:
motorbridge.abi_version()motorbridge.abi_capabilities()Controller(channel="can0")Controller.from_dm_serial("/dev/ttyACM0", 921600)(Damiao-only)Controller.from_dm_device("usb2canfd-dual", "0")/Controller.from_dm_device("linkx4c", "0")(Damiao-only DM_Device SDK path)Controller.add_damiao_motor(...)Controller.add_hexfellow_motor(...)Controller.add_robstride_motor(...)Controller.add_myactuator_motor(...)Controller.add_hightorque_motor(...)
- C++:
motorbridge::abi_version()motorbridge::abi_capabilities_json()Controller("can0")Controller::from_dm_serial("/dev/ttyACM0", 921600)(Damiao-only)Controller::add_damiao_motor(...)Controller::add_hexfellow_motor(...)Controller::add_robstride_motor(...)Controller::add_myactuator_motor(...)Controller::add_hightorque_motor(...)
Unified mode IDs for ABI/Bindings (ensure_mode):
1 = MIT2 = POS_VEL3 = VEL4 = FORCE_POS
Unified control units:
- position:
rad - velocity:
rad/s - torque:
Nm
Vendor-specific protocol naming/mapping and unsupported operations are documented in:
RobStride-specific ABI/binding helpers include:
robstride_pingrobstride_ping_host_idrobstride_set_device_idrobstride_set_active_reportrobstride_get_fault_reportrobstride_get_param_f32_host_idrobstride_get_param_*robstride_write_param_*
Binding parity is tracked in bindings/api_surface.json.
- Cross-language index:
examples/README.md - C ABI demo:
examples/c/c_abi_demo.c - C++ ABI demo:
examples/cpp/cpp_abi_demo.cpp - Python ctypes demo:
examples/python/python_ctypes_demo.py - Python SDK docs:
bindings/python/README.md - C++ binding docs:
bindings/cpp/README.md
| Asset | Install / Usage | Platform | Primary Audience | Included Capability |
|---|---|---|---|---|
motorbridge-abi-<tag>-linux-x86_64.deb |
sudo apt install ./motorbridge-abi-<tag>-linux-x86_64.deb |
Linux x86_64 | C/C++ users (Ubuntu/Debian) | libmotor_abi + headers + CMake config |
motorbridge-abi-<tag>-linux-*.tar.gz |
extract and link manually | Linux x86_64/aarch64 | C/C++ users (non-deb or cross env) | Same ABI payload as .deb |
motorbridge-abi-<tag>-windows-x86_64.zip |
extract and link/import | Windows x86_64 | C/C++ users | motor_abi.dll/.lib + headers + CMake config |
motor-cli-<tag>-<platform>.tar.gz/.zip |
run bin/motor_cli |
Linux/Windows | Field debug / production tooling | Full unified CLI (scan, mode control, id ops, etc.) |
motorbridge-*.whl, motorbridge-*.tar.gz |
pip install ./... |
depends on wheel tag | Offline Python install from Release assets | Python SDK + motorbridge-cli |
| Channel | Publish Trigger | Python Versions | Platform Matrix | Package Type |
|---|---|---|---|---|
| TestPyPI | Actions -> Python Publish -> repository=testpypi |
3.10 / 3.11 / 3.12 / 3.13 / 3.14 | Linux (x86_64, aarch64), Windows (x86_64), macOS (arm64) | wheel + sdist |
| PyPI | tag vX.Y.Z or manual repository=pypi |
3.10 / 3.11 / 3.12 / 3.13 / 3.14 | Linux (x86_64, aarch64), Windows (x86_64), macOS (arm64) | wheel + sdist |
Python wheels do not embed the DaMiao DM_Device runtime. The first
dm-device use resolves the matching runtime. If it is missing, motorbridge
prints the required file name, GitHub URL, and valid install paths.
| Platform / Arch | Published Python Wheel | DM_Device Runtime Available | Runtime File | OS/runtime ABI notes | Hardware Verified |
|---|---|---|---|---|---|
| Linux x86_64 | yes | yes | linux/x86_64/libdm_device.so |
needs libusb-1.0.so.0, libstdc++.so.6 with GLIBCXX_3.4.32, GLIBC_2.14+ |
yes, USB2CANFD_DUAL channel 0/1 and LINKX4C channel 0..3 scan |
| Linux aarch64 | yes | yes | linux/arm64/libdm_device.so |
needs libusb-1.0.so.0, GLIBC_2.17+, GLIBCXX_3.4.22+ |
pending host validation |
| Windows x86_64 | yes | yes | windows/msvc/dm_device.dll |
needs libusb runtime/driver and Microsoft Visual C++ runtime (MSVCP140*.dll, VCRUNTIME140*.dll) |
pending host validation |
| macOS arm64 | yes | yes | macos/arm64/libdm_device.dylib |
links system libc++, libSystem, libobjc; final OS floor pending macOS host validation |
pending host validation |
| macOS x86_64 | no official wheel | source/manual install only | macos/x86_64/libdm_device.dylib |
links system libc++, libSystem, libobjc; final OS floor pending macOS host validation |
pending host validation |
| Other arch/OS | no | no | none vendored | unsupported | unsupported |
Install from PyPI:
pip install motorbridgeFallback source install:
pip install --no-binary motorbridge motorbridge| Distribution Type | Typical Use | What You Can Do |
|---|---|---|
ABI package (.deb/.tar.gz/.zip) |
C/C++ integration | Call stable C ABI, use C++ RAII wrapper, embed into native robotics stack |
| Python package (wheel/sdist) | Python app/tooling | Use Controller/Motor/Mode APIs and motorbridge-cli |
motor_cli binary package |
Ops / factory / debugging | Direct CAN operations without Python runtime |
| Channel | CI Workflow | Output |
|---|---|---|
| APT repository (GitHub Pages) | .github/workflows/apt-repo-publish.yml |
https://<owner>.github.io/<repo>/apt |
Notes:
.debis currently Linux x86_64 oriented; other Linux targets should use ABI.tar.gz.- macOS x86_64 wheels are intentionally not produced in current matrix.
- Device matrix reference:
docs/en/devices.md. - Distribution channel automation guide:
docs/en/distribution_channels.md.





