Skip to content

Expose joint-coupled iSWAP rotation: STARBackend.iswap_rotate_to_angles() - #1049

Merged
BioCam merged 4 commits into
PyLabRobot:mainfrom
BioCam:expose-iswap_rotate_to_angles
May 20, 2026
Merged

Expose joint-coupled iSWAP rotation: STARBackend.iswap_rotate_to_angles()#1049
BioCam merged 4 commits into
PyLabRobot:mainfrom
BioCam:expose-iswap_rotate_to_angles

Conversation

@BioCam

@BioCam BioCam commented May 19, 2026

Copy link
Copy Markdown
Collaborator

Adds iswap_rotate_to_angles(rotation_angle=, wrist_angle=, ...), a public
deg-based wrapper over the private _iswap_rotate_increments primitive.
The wrapped primitive issues a single R0 PA command coupling the iSWAP
rotation drive (Joint 1) and wrist drive (Joint 2).

Both args are optional; at least one must be supplied.

  • Both supplied: joints arrive together under one motion plan so the
    gripper sweeps a straight joint-space path. Sequential single-axis calls
    would instead produce a two-segment path through the intermediate
    (W_target, T_now) pose.
  • One supplied: the other drive's current position is read from firmware
    and pinned. Same behaviour as the existing single-axis rotators.
  • Neither: raises ValueError naming both args.

Position conversions go through the calibrated path: rotation floats use
piecewise-linear interpolation between the LEFT / FRONT / RIGHT EEPROM
stops (via the existing _iswap_rotation_drive_angle_to_increments); wrist
floats use the linear iswap_wrist_drive_deg_per_increment factor anchored
on motor zero; enum inputs use the EEPROM increment directly. Float inputs
additionally snap to a stop's exact stored increment when within one
increment of precision, so values read via
iswap_rotation_drive_request_angle or iswap_wrist_drive_request_angle
round-trip bit-exact (including PARKED_RIGHT where the extrapolated formula
is FP-vulnerable).

Speed and acceleration are in deg/sec and deg/sec²; defaults (77.4048 /
526.3524 / 101.5938 / 736.5548) round-trip bit-exact to the previous
firmware integer defaults (25_000 / 170 / 20_000 / 145), so motion
characteristics are unchanged.

Two refactor follow-ups in this PR:

  1. The enum-or-float-with-snap logic is extracted into pure static helpers
    (_iswap_rotation_drive_resolve_to_increments,
    _iswap_wrist_drive_resolve_to_increments) next to the existing
    _*_angle_to_increments siblings. I/O-free, easy to test.

  2. The two single-axis rotators iswap_rotation_drive_rotate_to_angle
    (Expose iSWAP rotation drive request/set angle #1022) and iswap_wrist_drive_rotate_to_angle (Expose STARBackend.iswap_wrist_drive_{request,rotate_to}_angle() #1037) are renamed to
    underscored private versions. The new method covers their case via
    iswap_rotate_to_angles(rotation_angle=X) / (wrist_angle=X); the
    renamed methods remain available for direct one-joint troubleshooting.
    No callers in the repo; symbols are <3 weeks old in upstream.

Part 16 of the "Tame the iSWAP" epic:
https://discuss.pylabrobot.org/t/intro-to-epic-tame-the-iswap/517

🤖 Generated with Claude Code

BioCam and others added 3 commits May 19, 2026 23:15
_iswap_rotation_drive_resolve_to_increments and
_iswap_wrist_drive_resolve_to_increments collapse the enum-or-float
dispatch with snap-to-stop into two static helpers next to their
_*_angle_to_increments siblings. iswap_rotate_to_angles and both
single-axis rotators route their angle resolution through them, so the
snap guarantee now also applies to the singles. As a side effect this
restores the missing leading subject of the rotation_angle out-of-range
error message in iswap_rotate_to_angles. No public-API changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Rename iswap_rotation_drive_rotate_to_angle and
iswap_wrist_drive_rotate_to_angle to underscored versions. The public
entry point is iswap_rotate_to_angles, which covers the single-axis
case via rotation_angle=None / wrist_angle=None and additionally drives
both joints together. Keeping the renamed methods leaves a direct
one-joint path available for troubleshooting without two parallel
public APIs. No callers in the repo; symbols are <3 weeks old in
upstream (introduced in PyLabRobot#1022 and PyLabRobot#1037).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@rickwierenga

Copy link
Copy Markdown
Member

Tested on real hardware (STAR, this branch checked out). Setup: lh.setup(skip_autoload=True, skip_core96_head=True), then move_all_channels_in_z_safety() + move_iswap_y(...) to give the arm clearance. Compared the new unified iswap_rotate_to_angles(rotation_angle, wrist_angle) against back-to-back _iswap_rotation_drive_rotate_to_angle + _iswap_wrist_drive_rotate_to_angle across 9 (rotation Δ, wrist Δ) pairs from the parked baseline (rotation = +91.342°, wrist = −135.003°). For each case I reset to baseline between A and B and read joint state + FK pose via iswap_request_joint_state() / iswap_request_pose() afterward.

rotation Δ wrist Δ A elapsed B elapsed speedup joint diff pose XYZ diff yaw diff
−2° −2° 0.57s 0.27s 2.11× 0.0000° 0.0000 mm 0.0000°
−7° +3° 0.67s 0.37s 1.81× 0.0000° 0.0000 mm 0.0000°
−12° −10° 0.82s 0.43s 1.91× 0.0000° 0.0000 mm 0.0000°
−8° 0.57s 0.38s 1.50× 0.0000° 0.0000 mm 0.0000°
−5° 0.52s 0.33s 1.58× 0.0000° 0.0000 mm 0.0000°
+0.5° +5° 0.58s 0.33s 1.76× 0.0000° 0.0000 mm 0.0000°
−5° +10° 0.73s 0.38s 1.92× 0.0000° 0.0000 mm 0.0000°
−15° −15° 0.92s 0.48s 1.92× 0.0000° 0.0000 mm 0.0000°
−30° +20° 1.17s 0.67s 1.75× 0.0000° 0.0000 mm 0.0000°

Totals: A = 6.55s, B = 3.62s → 1.81× overall.

Also confirmed:

  • Single-axis path (rotation_angle=X, wrist_angle=None) reads firmware for the held drive and pins it correctly — rotation snapped to target, wrist held at its prior value within readback precision.
  • ValueError raised cleanly when one of my random deltas pushed wrist past the hardware range (−165° vs −152.39° min); no command sent.

Conclusion: End-state parity is exact across all 9 angle pairs (0.0000° joint, 0.0000 mm FK pose, 0.0000° yaw) — the unified call lands in the same place as the sequential pair. The ~1.81× speedup is per-command overhead (single R0 PA vs two R0 PAs); the savings are most visible on short moves where motion time is dominated by command round-trip. LGTM 👍

@BioCam
BioCam marked this pull request as ready for review May 20, 2026 09:06
@BioCam
BioCam merged commit 53875e7 into PyLabRobot:main May 20, 2026
21 checks passed
@BioCam

BioCam commented May 20, 2026

Copy link
Copy Markdown
Collaborator Author

@rickwierenga, thank you very much for the thorough review and testing!

@BioCam
BioCam deleted the expose-iswap_rotate_to_angles branch May 20, 2026 10:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants