This repository contains a variety of python scripts and debugging tools for validating Anvil Robotics's OpenArm hardware platform. Please note that this code is very experimental.
As this code is still experimental, there will be breaking changes, and is not meant for production use cases.
For commercial use cases and customers, Anvil Robotics is developing a high performance robot control and human demonstration data collection hardware system & software suite (early-access to select customers in Dec 2025). Feel free to reach out to Anvil Robotics, if you're interested.
More information about Anvil Robotics is available at https://anvil.bot/
- Python 3.11 or higher
- Ubuntu 24.04 LTS
- An Anvil OpenArm Leader or Follower (See Anvil Store), fully assembled and wired (see Anvil's hardware assembly guide).
(While the code will likely run on other platforms, your mileage may vary)
git clone https://github.com/anvil-robotics/openarm.git
cd openarm
# <or>
git clone git@github.com:anvil-robotics/openarm.git
cd openarmInstall Python virtual environment with:
sudo apt install python3-venvCreate and activate a new virtual environment:
python3 -m venv .venv
source .venv/bin/activateInstall the project dependencies:
pip install -e .Each time you open a new terminal, navigate to inside the openarm directory and reactivate the virtual environment with the following:
source .venv/bin/activate
If you are already familiar VSCode and devcontainers, you can simply open the cloned openarm folder and VSCode will detect the .devcontainer/devcontainer.json file.
Each OpenArm uses 2x Canable-style USB-to-CAN devices (one for the left arm and one for the right arm). Each device is pre-flashed with Candlelight firmware. These devices are automatically detected and enumerated by the Linux host when plugged in. But, by default, the device's name that shows up in Linux depends on the order in which devices are plugged in & detected by the host. So, without any additional setup, it becomes extremely easy to accidentally swap commands between the left and right arms.
Run this setup script to assign persistent names to the USB-CAN devices (based on their unique serial numbers) and bring them online automatically:
# Run the following, if you are connecting a leader+follower pair
sudo bash scripts/setup_can.sh leader_l leader_r follower_l follower_r
# Run the following, if you are connecting a single robot
sudo bash scripts/setup_can.sh robot_l robot_rThe script will guide you through plugging in four (or two) USB-CAN devices: two for the leader arms and two for the follower arms. Each argument corresponds resulting device names, and the order they are should be connected.
For more complex or especially unique setups, you can manually create & install your own udev rules. There are lots of online resources describing udev, and i2rt has an example of what this could look like as well.
Follow the instructions validate in the following file: motor-testing.md.
Ensure that every motor on every arm is moving as expected.
Congratulations! Your Anvil Robotics's OpenArm hardware is now fully verified as being operational.
You are welcome to now try running your own code on the robot, or try any of the additional tools and demos shared below.
Monitor motor angles in real-time across all detected CAN buses:
# Monitor all motors (passive mode - motors disabled)
python -m openarm.damiao.monitor
# Specify custom CAN interface
python -m openarm.damiao.monitor --interface leader_lLow-level control of individual motors for testing and debugging:
# Enable a motor
python -m openarm.damiao enable --motor-type DM4310 --iface follower_l 1 17
# Set MIT Mode and Command motor with MIT parameters
python -m openarm.damiao param set --motor-type DM4310 --iface follower_l 1 17 control_mode 1
python -m openarm.damiao control mit --motor-type DM4310 --iface follower_l 1 1 5 0.3 0 0 0 # kp kd q dq tau
# Set PosVel control mode and Command motor with PosVel command
python -m openarm.damiao param set --motor-type DM4310 --iface follower_l 1 17 control_mode 2
python -m openarm.damiao control pos_vel --motor-type DM4310 --iface follower_l 1 17 1.57 2.0 # position(rad) velocity(rad/s)
# Get motor status
python -m openarm.damiao refresh --motor-type DM4310 --iface follower_l 1 17
# Get motor parameters
python -m openarm.damiao param get --motor-type DM4310 --iface follower_l 1 17 over_voltage
# Disable motor safely
python -m openarm.damiao disable --motor-type DM4310 --iface follower_l 1 17Common Arguments:
--motor-type TYPE: Motor type (DM4310,DM4340,DM6006,DM8006,DM8009)--iface INTERFACE: CAN interface (default:can0)slave_id: Motor ID for commandsmaster_id: Master ID for responses
Available Commands:
enable/disable: Motor power controlset-zero: Set current position as zerorefresh: Get current motor statecontrol: MIT, position/velocity, velocity, or position/force controlparam get/set: Read/write motor parameterssave: Save parameters to flash memory
Reads all configuration registers from all motors on all busses
python -m openarm.damiao.register_dumpAll of these demos are still in the early prototype & experimental stage. Please expect bugs or other unexpected behavior.
Navigate to the openarm directory and run the gravity compensation demo using leader arms:
python -m openarm.damiao.gravity --port leader_l:left --port leader_r:rightThis command enables gravity compensation on the leader arms, allowing them to be moved freely by hand and stay in position without falling due to gravity.
For more information: python -m openarm.damiao.gravity --help
Options:
--port INTERFACE:POSITION: Specify CAN interface and arm positionPOSITIONmust beleftorright- Can specify multiple
--portarguments
Navigate to the openarm directory and run the teleoperation demo using leader and follower arms:
python -m openarm.damiao.monitor -t --gravity \
--follow leader_l:left:follower_l:left \
--follow leader_r:right:follower_r:rightThis command runs the leader arms with gravity compensation and makes the follower arms mirror the leader arms' movements in real time.
For more information: python -m openarm.damiao.monitor --help
Options:
-t, --teleop: Enable teleoperation mode--follow MASTER:POS:SLAVE:POS: Define master-slave mappingsPOSisleftorright(determines arm position)- Mirror mode auto-enabled when positions differ (e.g.,
leftmaster →rightslave)
-g, --gravity: Enable gravity compensation for master arms-v, --velocity SPEED: Velocity for slave motors (default: 1.0)
The Enactic team has released several repositories and demos that work on the open arm. You can follow our guide for building and running the Enactic's software (Using Enactic's Stack), or you can refer directly to the Enactic GitHub (https://github.com/enactic/openarm).
Please note that Enactic is unaffiliated with Anvil Robotics.
Caution
The following tools can flash new configuration parameters to motors. Doing any of the following may result in the motors no longer being able to effectively communicate with the linux host.
Configure Damiao motor IDs, set zero positions, and save parameters:
# Configure motor with specific master and slave IDs
python -m openarm.damiao.configure --channel can0 --set-master 0x11 --set-slave 0x01
# Configure motor as a predefined joint (J1-J8)
python -m openarm.damiao.configure --channel can0 --set-motor J1
# Set both custom IDs and zero position, then save
python -m openarm.damiao.configure --channel can0 --set-master 0x12 --set-slave 0x02 --set-zero --save
# Configure multiple motors (requires --allow-multiple)
python -m openarm.damiao.configure --channel can0 --set-motor J3 --allow-multiple --save
# Override motor type IDs while using predefined configuration
python -m openarm.damiao.configure --channel can0 --set-motor J1 --set-master 0x20Arguments:
--channel CHANNEL: CAN channel (required, e.g., can0, can1)--interface INTERFACE: CAN interface type (default: socketcan)--set-master ID: Set master ID (hex: 0x11 or decimal: 17)--set-slave ID: Set slave ID (hex: 0x01 or decimal: 1)--set-motor MOTOR: Configure as predefined motor (J1-J8)--set-zero: Set current position as zero--save: Save parameters to motor flash memory--allow-multiple: Allow configuring multiple motors at once
Notes:
- When using
--set-motor, both master and slave IDs are set from predefined configuration --set-masteror--set-slaveoverride the predefined values from--set-motor- Motors are auto-detected on the specified channel
- The script assumes DM8009 motor type by default
- Motor configurations include side-specific angle ranges (
min_angle_left,max_angle_left,min_angle_right,max_angle_right) to support mirrored left/right arm setups
Set all motors to zero position automatically across all detected CAN buses:
# Set zero position for all motors on all buses
python -m openarm.damiao.set_zero
# Specify custom CAN interface (Linux only)
python -m openarm.damiao.set_zero --interface can1Monitor motor angles in real-time across all detected CAN buses:
# Monitor all motors (passive mode - motors disabled)
python -m openarm.damiao.monitor
# Specify custom CAN interface (Linux only)
python -m openarm.damiao.monitor --interface can1Track motor angle ranges and automatically set zero positions based on observed range:
# Track angle ranges for left arm
damiao-set-zero-limit --channel can0 --side left
# Track angle ranges for right arm
damiao-set-zero-limit --channel can1 --side rightMove each motor through its full range of motion, then press 'S' to set zero positions.
Low-level control of individual motors for testing and debugging:
# Enable a motor
python -m openarm.damiao enable --motor-type DM4310 --iface can0 1 1
# Control motor with MIT parameters
python -m openarm.damiao control mit --motor-type DM4310 --iface can0 1 1 \
50 0.3 0 0 0 # kp kd q dq tau
# Position/velocity control
python -m openarm.damiao control pos_vel --motor-type DM4310 --iface can0 1 1 \
1.57 2.0 # position(rad) velocity(rad/s)
# Get motor status
python -m openarm.damiao refresh --motor-type DM4310 --iface can0 1 1
# Get/set motor parameters
python -m openarm.damiao param get --motor-type DM4310 --iface can0 1 1 over_voltage
python -m openarm.damiao param set --motor-type DM4310 --iface can0 1 1 max_speed 10.0
# Disable motor safely
python -m openarm.damiao disable --motor-type DM4310 --iface can0 1 1Common Arguments:
--motor-type TYPE: Motor type (DM4310,DM4340,DM6006,DM8006,DM8009)--iface INTERFACE: CAN interface (default:can0)slave_id: Motor ID for commandsmaster_id: Master ID for responses
Available Commands:
enable/disable: Motor power controlset-zero: Set current position as zerorefresh: Get current motor statecontrol: MIT, position/velocity, velocity, or position/force controlparam get/set: Read/write motor parameterssave: Save parameters to flash memory
Run physics-based gravity compensation on specific arms:
# Single arm gravity compensation
python -m openarm.damiao.gravity --port can0:left
# Multiple arms simultaneously
python -m openarm.damiao.gravity --port can0:left --port can1:rightOptions:
--port INTERFACE:POSITION: Specify CAN interface and arm positionPOSITIONmust beleftorright- Can specify multiple
--portarguments
Enable teleoperation where master arms control slave arms in real-time:
# Basic teleoperation (first bus masters others)
python -m openarm.damiao.monitor --teleop
# Custom master-slave with automatic mirror detection
python -m openarm.damiao.monitor -t --follow can0:left:can1:right
# Multiple master-slave pairs
python -m openarm.damiao.monitor -t --follow can1:left:can2:right --follow can0:right:can3:left
# With gravity compensation and custom velocity
python -m openarm.damiao.monitor -t --follow can1:left:can2:right --gravity --velocity 5Options:
-t, --teleop: Enable teleoperation mode--follow MASTER:POS:SLAVE:POS: Define master-slave mappingsPOSisleftorright(determines arm position)- Mirror mode auto-enabled when positions differ (e.g.,
leftmaster →rightslave)
-g, --gravity: Enable gravity compensation for master arms-v, --velocity SPEED: Velocity for slave motors (default: 1.0)