Skip to content

Commit 46796ea

Browse files
github-actions[bot]jss265claude
committed
piper_up_down: add --hold-wrist; record the first motion run
The PiPER rests with its wrist off zero (J5 ~22 deg, J6 ~61 deg), so the all-joint zero guard refused to start. --hold-wrist moves only J2/J3 and holds J1 and J4-J6 at their starting angles. Ran 3 cycles on the arm. Co-authored-by: Joshua Spiesman <99856667+jss265@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent bb81bbc commit 46796ea

2 files changed

Lines changed: 28 additions & 15 deletions

File tree

‎CLAUDE.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,12 @@ enables the CAN bus, so the motion rule above applies to it too. `piper_sdk` and
143143
are installed in a user venv at `~/piper-venv` (Debian's system Python has no pip, so this
144144
is the only place they live). [`scripts/piper_up_down.py`](scripts/piper_up_down.py) is the
145145
first motion script. Copy it to the Pi and run it with `~/piper-venv/bin/python`. Without
146-
`--go` it only reads joint state.
146+
`--go` it only reads joint state. The arm's rest pose has the wrist off zero (about J5 22°,
147+
J6 61°), so the all-joint zero check refuses to start from rest. Use `--hold-wrist` there,
148+
which moves only J2/J3 and holds the other joints where they are. First motion: 2026-10-09,
149+
PR #16, `--go --hold-wrist`, 3 cycles of ±25° at 20% speed, each pose reached in 0.8 s.
150+
To record a run, use `rpicam-vid --codec mjpeg`. The Pi 5 has no H.264 encoder, this build
151+
has no libav, and the Pi has no ffmpeg, so the default and `--codec libav` both write nothing.
147152

148153
**Using the Pi as a proxy.** Some vendor sites (and YouTube's player) block GitHub Actions IP
149154
ranges; the Pi's residential IP is not blocked. The Pi is on constrained Wi-Fi and may be

‎scripts/piper_up_down.py‎

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,19 @@
88
99
"Up" is the shoulder (J2) lifting by --lift-deg while the elbow (J3) opens by the same
1010
amount, so the forearm keeps roughly its angle and the whole arm rises. Every other joint
11-
holds zero. can0 must already be up at 1 Mbit/s; this script never touches the interface.
11+
holds zero, or with --hold-wrist holds the angle it started at (the PiPER rests with its
12+
wrist off zero, so this raises the arm without swinging the wrist). can0 must already be up
13+
at 1 Mbit/s; this script never touches the interface.
1214
1315
Safety:
1416
- Without --go nothing is sent to the arm.
1517
- Refuses to start unless joint feedback is live and every joint is within --zero-tol-deg
16-
of zero.
18+
of zero (only J2 and J3 with --hold-wrist).
1719
- Waits for each pose to be reached before the next; a pose that is not reached in
1820
--timeout-s aborts the cycle.
19-
- Always commands zero before exiting, and disables the motors only once the arm is back
20-
at zero. Disabling elsewhere would drop the arm, so then it is left enabled and holding.
21+
- Always commands the start pose before exiting, and disables the motors only once the arm
22+
is back there. Disabling elsewhere would drop the arm, so then it is left enabled and
23+
holding.
2124
"""
2225
import argparse
2326
import os
@@ -74,6 +77,8 @@ def main():
7477
ap.add_argument("--tol-deg", type=float, default=1.5, help="pose reached tolerance")
7578
ap.add_argument("--zero-tol-deg", type=float, default=5.0, help="start/end zero tolerance")
7679
ap.add_argument("--timeout-s", type=float, default=15.0, help="per-pose timeout")
80+
ap.add_argument("--hold-wrist", action="store_true",
81+
help="hold J1 and J4-J6 at their starting angles instead of zero")
7782
args = ap.parse_args()
7883

7984
if not 0 < args.lift_deg <= 45:
@@ -97,15 +102,18 @@ def main():
97102
print("joints (deg):", fmt(q0))
98103
print("enabled:", piper.GetArmEnableStatus())
99104
print(piper.GetArmStatus())
100-
if any(abs(v) > args.zero_tol_deg for v in q0):
105+
checked = q0[1:3] if args.hold_wrist else q0
106+
if any(abs(v) > args.zero_tol_deg for v in checked):
101107
sys.exit(f"arm is not within {args.zero_tol_deg} deg of zero; not moving")
108+
109+
home = [q0[0], 0.0, 0.0, *q0[3:]] if args.hold_wrist else [0.0] * 6
110+
up = [home[0], args.lift_deg, -args.lift_deg, *home[3:]]
111+
print("home (deg):", fmt(home))
112+
print("up (deg):", fmt(up))
102113
if not args.go:
103114
print("read-only run (no --go): nothing sent to the arm")
104115
return
105116

106-
zero = [0.0] * 6
107-
up = [0.0, args.lift_deg, -args.lift_deg, 0.0, 0.0, 0.0]
108-
109117
t0 = time.time()
110118
while not piper.EnablePiper():
111119
if time.time() - t0 > 5:
@@ -121,21 +129,21 @@ def main():
121129
break
122130
time.sleep(args.pause_s)
123131
print(f"cycle {i}/{args.cycles}: down", flush=True)
124-
if not move_to(piper, zero, args.speed, args.tol_deg, args.timeout_s):
132+
if not move_to(piper, home, args.speed, args.tol_deg, args.timeout_s):
125133
break
126134
time.sleep(args.pause_s)
127135
completed += 1
128136
finally:
129-
print("returning to zero", flush=True)
130-
move_to(piper, zero, args.speed, args.tol_deg, args.timeout_s)
137+
print("returning home", flush=True)
138+
move_to(piper, home, args.speed, args.tol_deg, args.timeout_s)
131139
q = joints_deg(piper)
132-
if all(abs(v) <= args.zero_tol_deg for v in q):
140+
if all(abs(a - b) <= args.zero_tol_deg for a, b in zip(q, home)):
133141
t0 = time.time()
134142
while piper.DisablePiper() and time.time() - t0 < 5:
135143
time.sleep(0.01)
136-
print("disabled at zero:", fmt(q), flush=True)
144+
print("disabled at home:", fmt(q), flush=True)
137145
else:
138-
print("WARNING: not back at zero, leaving motors enabled so the arm does not drop:",
146+
print("WARNING: not back home, leaving motors enabled so the arm does not drop:",
139147
fmt(q), flush=True)
140148
print(f"completed {completed}/{args.cycles} cycles", flush=True)
141149

0 commit comments

Comments
 (0)