feat: add leader/follower gripper teleoperation - #11
Merged
Merged
Conversation
Let one gripper mirror another: the leader goes slack so its jaws can be pushed by hand and publishes how far open it is, the follower drives its own jaws to match. The wire carries a normalized opening in [0, 1], not an angle, so the two ends need not share a calibration, mount, or zero point. The algorithm is ported from litearm_device.gripper_teleop, but the transport is a pluggable TeleopTransport instead of a hard zenoh dependency, so the SDK stays stdlib-only: UdpTeleopTransport for real links and InProcTeleopTransport for tests and two grippers in one process. The openness<->radian conversion carries close_sign, which the litearm original omitted, so a reverse-mounted follower moves the correct way. A follower that loses its leader holds its position at the follow gains rather than relaxing.
|
🎉 This PR is included in version 0.5.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
yd-sl
added a commit
that referenced
this pull request
Sep 29, 2026
* feat: run gripper teleop over the validated zenoh link The teleop shipped in #11 invented its own network layer -- a plain-UDP transport on `litegrip/teleop/{master_id}` -- and was merged on hardware-free tests alone. The system actually validated on hardware is a zenoh point-to-point link, so the SDK now uses that structure: the topic and port come from the shared litearm namespace, both ends run in peer mode with multicast and gossip off, the leader listens on a TCP port and the follower connects to it, and the frame stays byte-identical to the litearm stack's. zenoh is an optional extra (`pip install litegrip[zenoh]`), resolved lazily, so `import litegrip` still works on a machine that will never teleoperate. The UDP transport stays available through `link="udp"` and is no longer the default. Alongside the transport, port the safety rules the validated design spec has and #11 was missing: non-finite frames are dropped at the wire boundary rather than clamped onto a hard stop; the follower's target is clamped into its own calibrated travel every cycle; a `send_mit_frame` that returns False and a gripper `error_code` other than "enabled" are counted instead of swallowed; follow gains fall back to the calibration's own kp/kd; a non-positive watchdog is rejected at construction; staleness is read from the slot's "never received" state rather than a 0.0 timestamp sentinel; teleop refuses to start on an uncalibrated or zero-travel gripper; and the leader's zenoh listener is resident for the life of the gripper, because rebuilding it per session leaves the port bound and makes matching fail intermittently. * fix: import teleop_topic outside the zenoh guard in the tests `litegrip.teleop` has no zenoh dependency, so the import belongs at module level. Behind the `HAVE_ZENOH` guard it left `teleop_topic` undefined when zenoh is absent, and the module failed to import instead of skipping — which is exactly the machine CI runs on.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds leader/follower teleoperation: one gripper mirrors another. The leader goes slack so its jaws can be pushed by hand and publishes how far open it is; the follower drives its own jaws to match. What travels over the wire is a normalized opening in
[0, 1], not an angle, so the two ends need not share a calibration, mount, or zero point.The algorithm is ported from
litearm_device.gripper_teleop, with the hard zenoh dependency replaced by a pluggableTeleopTransportso the SDK stays standard-library-only.Changes
src/litegrip/teleop.py(new):GripperTeleop(master/slave loops, watchdog, align), frame codec (>4d, 32 bytes, byte-compatible with litearm),openness <-> radconversion, and two transports —UdpTeleopTransport(default) andInProcTeleopTransport.src/litegrip/gripper.py:teleop_start/teleop_stop/teleop_status; a reentrant_io_lockaroundsend_mit_frame/poll/get_stateso the teleop thread and the caller can share one CAN bus;disconnect()stops an active session first.src/litegrip/__init__.py: exports the teleop classes, codec helpers, andFRAME_SIZE.tests/test_teleop.py(new): codec round-trip, conversion for both mounts, in-proc and UDP transports, master/slave loops, watchdog hold, and theLiteGripAPI surface.examples/teleop.py(new): runnable one-end-per-machine runner (--mode master|slave).README.md/readme_zn.md: a teleoperation section.Two deliberate divergences from the litearm original:
openness -> radconversion carriesclose_sign, which the original omitted, so a reverse-mounted follower moves the correct way.UdpTeleopTransportis plain, unauthenticated UDP and is documented as trusted-network-only.Testing
105 tests, all pass. Also verified a single-process master -> slave loopback with two fake-CAN grippers: the reverse-mounted follower tracks
openness=1to its own open limit (direction not inverted), andteleop_status()reportsframes/stale/opennessas expected.0 issues.
Real-hardware validation (two grippers, one per machine) has NOT been run yet — it needs the operator present.
Issues
None.