0% · 0 / 0

Field guide · edge robotics

PiPER arm on the Jetson — install it, then leave it running

A deployment runbook for the AgileX PiPER arm on a Jetson Orin Nano, run as a remotely-operated, unattended robot. Reflects the real bring-up — including the JetPack 6.2 driver gap that isn't in any official guide.

Arm · AgileX PiPER Link · Tailscale Compute · Orin Nano · JP6.2 Unattended after install

How it fits together

OPERATOR Your laptop SSH + video Tailscale encrypted REMOTE SITE JETSON your-jetson control runs local can_piper 1M PiPER arm USB-to-CAN 2 cameras overview + gripper

The key idea: the arm's control loop runs on the Jetson, next to the hardware. The long-haul link only carries your commands and the camera feed — never the real-time joint control — so hundreds of ms of latency can't destabilize the arm.

On hand before you start

Phase 1 · do this first

Remote access with Tailscale

Prove you can reach the board from outside her network before anything else. Everything after this you can finish remotely.

1

Update the system on good Wi-Fi

Do this while you have real bandwidth.

$ sudo apt update && sudo apt upgrade -y
Don't let anything replace the nvidia-l4t-* packages — that's the GPU/BSP stack and a bad upgrade can brick the graphics/CUDA layer. Plain upgrade is fine; avoid a full dist-upgrade.
2

Install Tailscale and bring it up

One command to install, one to join your network.

$ curl -fsSL https://tailscale.com/install.sh | sh $ sudo tailscale up --ssh --hostname=arm-jetson
Tailscale SSH (--ssh) means no SSH keys to manage — auth goes through your Tailscale identity. The Jetson becomes reachable as arm-jetson from any device on your tailnet.
3

Make it permanent, then prove it from outside

Don't skip the off-network test.

That last step is the acceptance test for the whole deployment. If SSH works only on the site's Wi-Fi, you have local access, not remote access. Verify over the phone hotspot before you fly.

Phase 2 · the arm

PiPER over CAN

The showstopper: JetPack 6.2 ships no gs_usb driver, so the adapter is invisible until you build it. Then the arm is a renamed CAN interface, can_piper.

4

Prep the Jetson & build the CAN driver

Online required — the stock kernel is missing the driver the PiPER adapter needs.

$ wget -q https://github.com/lucianovk/jetson-gs_usb-kernel-builder/raw/main/jetson-gs_usb-kernel-builder.sh $ chmod +x jetson-gs_usb-kernel-builder.sh $ sudo ./jetson-gs_usb-kernel-builder.sh
The single biggest JP6.2 surprise. The stock kernel ships no gs_usb driver, so the PiPER adapter appears in lsusb but never creates a CAN interface. This builder compiles the module against your exact kernel, installs it, and sets it to auto-load. ~1.2 GB source download + a few minutes.
5

Bring up the arm's CAN interface

The arm is the gs_usb USB adapter — not the Jetson's built-in controller.

$ sudo ip link set can1 type can bitrate 1000000 $ sudo ip link set can1 up
can0 is the Jetson's built-in Tegra controller (40-pin pins, nothing attached) — ignore it. The PiPER adapter comes up as can1 (or can2), which is exactly why Phase 4 pins it to a fixed name, can_piper.
6

Talk to the arm with piper_control

Use the wrapper — raw piper_sdk's enable is flaky and dropped the arm mid-move.

# python3 from piper_control import piper_interface, piper_init robot = piper_interface.PiperInterface(can_port="can_piper") piper_init.reset_arm(robot, arm_controller=piper_interface.ArmController.POSITION_VELOCITY, move_mode=piper_interface.MoveMode.JOINT) print(robot.get_joint_positions()) # radians
The control pattern that matters: reset_arm() briefly disables the motors (the arm droops for a moment), so call it once at startup then stream command_joint_positions() — never reset between moves, or the joints drop. Clear the workspace and keep a hand near power before the first commanded move.

Phase 3 · eyes on it

The camera feed

Two low-latency feeds over the same Tailscale link — an overview cam on the canvas and a gripper cam on the wrist. Made permanent in Phase 4.

7

Serve both cameras over WebRTC

Two USB cams → two feeds. Pin each to its stable by-id path.

# /opt/mediamtx/mediamtx.yml — one path per camera, stable by-id nodes paths: overview: # C920, aimed at the canvas runOnInit: > ffmpeg -f v4l2 -input_format mjpeg -video_size 1280x720 -framerate 30 -i /dev/v4l/by-id/usb-046d_HD_Pro_Webcam_C920_XXXXXXXX-video-index0 -c:v libx264 -preset ultrafast -tune zerolatency -pix_fmt yuv420p -b:v 2M -g 30 -f rtsp rtsp://localhost:8554/overview runOnInitRestart: yes gripper: # wrist cam — MJPG, not its H264 (see note) runOnInit: > ffmpeg -f v4l2 -input_format mjpeg -video_size 1280x720 -framerate 30 -i /dev/v4l/by-id/usb-H264_USB_Camera_H264_USB_Camera_XXXXXXXX-video-index0 -c:v libx264 -preset ultrafast -tune zerolatency -pix_fmt yuv420p -b:v 2M -g 30 -f rtsp rtsp://localhost:8554/gripper runOnInitRestart: yes
Two gotchas. Pin each camera to its /dev/v4l/by-id/… path (yours will differ — read them from ls /dev/v4l/by-id/) — raw /dev/videoN numbers swap when two cams are present. And the wrist cam enumerates as an "H264 USB Camera" but only offers MJPG as a capture format, so encode it like the C920 — -input_format h264 fails. Two 720p encodes run ~60% on the 6-core; drop the gripper to 640x480 to bank CPU for the control app.
Latency reality: a long-haul (e.g. intercontinental) link is hundreds of ms round trip. Use the video to watch and supervise; drive the arm with commands that run on the Jetson — don't close a real-time loop across the ocean. Reached by the arm-jetson Tailscale name, nothing is exposed publicly.

Phase 4 · survive a reboot

Make everything come back by itself

A power blip or reboot must recover with nobody touching it. Each piece is a service. (Verified: both come back after a cold reboot, untouched.)

8

Auto-bring-up CAN as can_piper

Finds the adapter by driver, renames it, sets 1 Mbps — resilient to enumeration order.

# /etc/systemd/system/piper-can.service [Unit] Description=Bring up PiPER USB-CAN as can_piper @ 1 Mbps After=systemd-udev-settle.service [Service] Type=oneshot ExecStart=/usr/local/sbin/piper-can-up.sh RemainAfterExit=yes [Install] WantedBy=multi-user.target
$ sudo systemctl enable --now piper-can.service $ ip -br link show can_piper # -> UP
9

Camera (and later, arm) as services

MediaMTX restarts on failure. The arm service waits for its control program.

# /etc/systemd/system/mediamtx.service [Unit] Description=MediaMTX camera WebRTC stream After=network-online.target Wants=network-online.target [Service] Type=simple WorkingDirectory=/opt/mediamtx ExecStart=/opt/mediamtx/mediamtx /opt/mediamtx/mediamtx.yml Restart=always RestartSec=3 [Install] WantedBy=multi-user.target
Deliberately not auto-enabling the arm. An arm that powers itself into torque on boot is a hazard. It stays limp until your control program connects and runs its one reset_arm() — then holds and takes commands.
10

Keep it awake and let it power back on

No sleeping, clean comeback after a power cut.

A small UPS on site rides out brief outages and prevents unclean shutdowns that could corrupt the SD card.

Phase 5 · before you leave

The leave-it acceptance test

One real test proves the whole thing: reboot it cold and confirm it recovers with nobody helping.

11

Cold-reboot and walk away

From your laptop, ideally already off her network.

If it survives a cold reboot on the hotspot test, you're done — that's exactly the failure it'll face after a power blink at the site while you're away.

For an on-site helper — if it stops responding

Physical checks only. Two minutes. No terminal needed.

  1. Lights on? Check the Jetson has a power light and the arm is powered.
  2. Power-cycle the Jetson. Unplug its power, wait 30 seconds, plug it back in. Everything restarts on its own in a couple of minutes.
  3. Check the cables. Ethernet into the router, the USB-to-CAN and camera plugged into the Jetson, the arm's own power on.
  4. Still stuck? Message the operator — they can get in remotely once it's back on the network.
You're deployment-ready once all boxes are ticked Phase 1 and Phase 5 are the ones that decide whether this works remotely.