Skip to main content

piper_control - Library for controlling AgileX Pipers

Overview

This repo provides low-level python modules for connecting and controlling AgileX Piper robots.

  • piper_connect: a python implementation of the CAN setup scripts bundled with piper_sdk. This are not accessible on pip installs, and we found it useful to be able to query and activate CAN ports programmatically.

  • piper_control: our lightweight wrapper of piper_sdk for controlling AgileX Piper robots.

The piper_sdk API is powerful and quickly maturing, but it's a bit complex and under-documented, and we found it helpful to define a simple abstraction for basic I/O.

There are also several sharp bits in piper_sdk which can make the robots seem tempermental, e.g. becoming unresponsive despite repeated calls to MotionCtrl_2, EnableArm, GripperCtrl, etc. We've bundled our solutions into PiperControl so reset and the various move commands perform as one would expect.

Quick start

Install the dependencies and package:

sudo apt install can-utils
pip install piper_control

Set up the CAN connection to the arm(s):

# Set up the connection to the Piper arm.
# These steps require sudo access.
from piper_control import piper_connect

# Print out the CAN ports that are available to connect.
print(piper_connect.find_ports())

# Activate all the ports so that you can connect to any arms connected to your
# machine.
piper_connect.activate()

# Check to see that all the ports are active.
print(piper_connect.active_ports())

Then control the robot:

from piper_control import piper_interface
from piper_control import piper_init

robot = piper_interface.PiperInterface(can_port="can0")

# Resets the robot and enables the motors and motion controller for the arm.
# This call is necessary to be able to both query state and send commands to the
# robot.
piper_init.reset_arm(
    robot,
    arm_controller=piper_interface.ArmController.POSITION_VELOCITY,
    move_mode=piper_interface.MoveMode.JOINT,
)
piper_init.reset_gripper(robot)

# See where the robot is now.
joint_angles = robot.get_joint_positions()
print(joint_angles)

# Move one joint of the arm.
joint_angles = robot.get_joint_positions()
joint_angles[-2] -= 0.1
print(f"Setting joint angles to {joint_angles}")
robot.command_joint_positions(joint_angles)

See the tutorial.ipynb for a longer walkthrough.

Gravity Compensation

The package includes tools for gravity compensation calibration and execution.

Installation

Install with the gravity compensation dependencies:

pip install piper_control[gravity]

Generate Calibration Samples

Collect joint position and effort samples across the robot's workspace:

piper-generate-samples -o samples.npz

If using uv:

uv run piper-generate-samples -o /tmp/grav_comp_samples.npz

Options:

  • --model-path: Path to MuJoCo XML model (default: bundled model)
  • --joint-names: Joint names in the model (default: joint1-6)
  • --num-samples: Number of samples to collect (default: 50)
  • --can-port: CAN interface name (default: can0)

Try out the Gravity Compensation

Try the gravity compensation model using calibrated samples:

piper-gravity-compensation --samples-path samples.npz

Options:

  • --model-path: Path to MuJoCo XML model (default: bundled model)
  • --joint-names: Joint names in the model (default: joint1-6)
  • --can-port: CAN interface name (default: can0)
  • --model-type: Compensation model type (default: cubic).
    • Choices: linear, affine, quadratic, cubic, features, direct
  • --damping: Velocity damping gain for stability (default: 1.0)

Collision Protection

Set the collision protection level for all joints:

piper-set-collision-protection 5

Options:

  • level: Protection level to set (default: 1)
  • --can-port: CAN interface name (default: can0)

Local development setup

Use this workflow when you need to develop on piper_control directly instead of installing the released package from PyPI.

1. Clone the repository

git clone https://github.com/Reimagine-Robotics/piper_control.git
cd piper_control

2. Choose how you want to manage dependencies

Option A: Virtual environment + pip

python -m venv .venv
source .venv/bin/activate
python -m pip install --upgrade pip
pip install -e .

# Pull in the optional gravity-compensation tools if needed.
pip install -e .[gravity]

Install any extra dev tooling you care about (e.g. pip install pre-commit).

If you want to use our fork of piper_sdk, which fixes some jerkiness issues when using MIT mode on the Piper, you can:

pip uninstall piper_sdk
pip install \
  git+https://github.com/Reimagine-Robotics/piper_sdk.git@master#egg=piper_sdk

Swap @master for another branch or tag if you need something different.

Option B: uv-managed environment

uv reads both pyproject.toml and uv.lock, so it can recreate the exact environment the branch was developed with, including the dev helpers defined in the dev dependency group.

# Create/refresh the .venv using the lockfile and install dev tools.
uv sync --all-extras --group dev

uv tree

uv sync automatically installs the gravity-compensation extras and the dev tools (pre-commit, jupyterlab, pylint). Use uv run <command> to execute tools without activating the environment manually.

[!NOTE] uv sync will use our fork of piper_sdk, which fixes some jerkiness issues when using MIT mode on the Piper.

Generating udev rules for CAN adapters

To avoid needing to run sudo to set up the CAN interface, you can create a udev rule that sets the bitrate and desired name for your CAN adapter.

Usage

  1. Plug in your CAN adapter

  2. Run the command:

    sudo .venv/bin/piper-generate-udev-rule -i can0 -b 1000000
    

    Or run it directly from the repo (without installing):

    sudo ./scripts/piper-generate-udev-rule -i can0 -b 1000000
    

    Or name your robot (e.g. myrobot):

    sudo .venv/bin/piper-generate-udev-rule -i can0 -n myrobot -b 1000000
    
  3. Unplug and replug the adapter to test

Test

ip link show can0

Or if you named it something else:

ip link show myrobot

That's it!

Linting

To lint the codebase, run:

uv run pre-commit

Troubleshooting / FAQ

Is my PiperInterface working?

This snippet is a good check for whether things are working:

from piper_control import piper_interface
from piper_control import piper_init

robot = piper_interface.PiperInterface(can_port="can0")

piper_init.reset_arm(robot)
print(robot.get_joint_positions())
print(robot.get_joint_velocities())
print(robot.get_joint_efforts())
print(robot.get_gripper_state())

If you get the following text, then the CAN connection is not working. See this section on how to debug the CAN connection.

<class 'can.exceptions.CanError'> Message NOT sent

If you get output that looks like this (as in the CAN seems like it is working, but you get all 0's for the rest of the output), see this section.

can0  is exist
can0  is UP
can0  bitrate is  1000000
can0 bus opened successfully.
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
(0.0, 0.0)

Get all zeros when calling get_joint_positions()

Run through these steps:

# Assume you already have the PipeInterface object.
robot = piper_interface.PiperInterface(can_port="can0")

# Reset again.
piper_init.reset_arm(robot)

And after that, calling robot.get_joint_positions() should return non-zero values. If it doesn't, then double-check the CAN connection. See this section.

can.exceptions.CanError errors

Quite often, you may see the following error while connecting or resetting your arm:

<class 'can.exceptions.CanError'> Message NOT sent

There are several possible issues here, and several things you can try to fix it:

  1. Unplug the CAN cable, power off the robot, power on the robot, then plug the CAN cable back in.

    If this works, but the error happens often enough, the issue is the USB cable. The CAN adapter that ships with the Piper is finnicky and sensitive to the USB cable used.

    Be sure to call piper_init.reset() on the robot after starting it again.

  2. Still doesn't work?

    You can check whether the cable itself is working by making sure the CAN adapter is visible:

    lsusb | grep CAN
    

    If nothing shows up, the cable is not working.

    As mentioned above, the CAN adapter is sensitive to this piece.

  3. Still not working? Make sure the CAN interface is set up correctly.

    Some things to try:

    1. Run:

      ifconfig
      

      And verify the output has your CAN interface (e.g. can0).

    2. Check the bitrate of the interface:

      ip -details link show can0 | grep bitrate
      

      Verify this is set to something like 1000000.

    3. Check the state of the CAN interface:

      ip -details link show can0 | grep "can state"
      

      If this is ERROR-ACTIVE or ERROR-PASSIVE, something is wrong here.

    If any of these are not working, try resetting the CAN connection. You can re-run piper_connect.activate() or run the steps manually here:

    sudo ip link set can0 down
    sudo ip link set can0 type can bitrate 1000000
    sudo ip link set can0 up
    

    Check the state afterwards:

    ip -details link show can0 | grep "can state"
    

    Try resetting again for good measure:

    sudo ip link set can0 down
    sudo ip link set can0 up
    
  4. Still not working? Likely one of the other components are not working. Make sure the high-low cables connected to your CAN adapter are inserted properly. This is the most common failure mode we've seen. In particular, ensure that the wire ends are wedged at the top of the hole, not the bottom.

    If needed, swap out your main cable (the aviation connector in the back of the robot) and try again. Try swapping out the CAN adapter too if needed.

Metadata

Release files for piper-control 1.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for piper-control 1.5.0
File Size Uploaded
piper_control-1.5.0.tar.gz 33.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for piper-control 1.5.0
File Interpreter ABI Platform
piper_control-1.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 67.4 kB

Release files / piper_control-1.5.0.tar.gz

Download URL piper_control-1.5.0.tar.gz
Size 33.5 kB
Tags Source
SHA-256 checksum
How to use checksums
b21afa6d6704d50bf8977bb7b20fa1e83813e7da41181cdaa6cc34452a6489ca
BLAKE2b-256 checksum
How to use checksums
57f065e2e8dd54c1c5f00f0501abcae4f7c03f387dbe24953c0af69b88ddd749
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Release files / piper_control-1.5.0-py3-none-any.whl

Download URL piper_control-1.5.0-py3-none-any.whl
Size 33.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
bdde9e650cf0a2e54429bdcca8a20a0ed504c5b56b920268586ac0bfe794bb46
BLAKE2b-256 checksum
How to use checksums
0db2c9b0ee156fc5cb7d028a789d74a1c326196b11835f030082574890a4be51
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.14.2

Release history Release notifications | RSS feed

This release

1.5.0 This release

2 release files

1.4.0

2 release files

1.3.7

2 release files

1.3.6

2 release files

1.3.5

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.0

2 release files

1.1.6

2 release files

1.1.5

2 release files

1.1.4

2 release files

1.1.3

2 release files

1.1.2

2 release files

1.1.1

2 release files

1.1.0

2 release files

1.0.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page