feat: feed the leader velocity forward in gripper teleop - #16
Merged
Merged
Conversation
The follower felt laggy next to the arm teleoperation, and the reason is structural: the arm's MIT command is kp*(q_cmd - q) + kd*(dq_cmd - dq) + tau_ff with a real dq_cmd, while the gripper wire frame carries only an opening. With no velocity term the follower leans on position error alone, so it trails a moving leader by roughly speed / kp -- felt as drag, worst during a fast hand sweep. The wire format cannot change (it is byte-identical to the litearm stack and interoperates with it), so the follower recovers the leader's velocity locally: it differences successive positions and sends the result as its own dq target. `kd * (dq_cmd - dq)` is a genuine torque term, so the estimate is bounded deliberately: the first frame has nothing to difference against, a degenerate interval and a gap longer than MAX_FRAME_GAP_S are both refused, and the result is clamped to `dq_max` (default 10.0 rad/s; `dq_max=0` disables the feedforward). Confirmed on hardware with a hand sweep: the drag is gone and the loop stays quiet -- a resting `dq` noise floor of about ±0.04 rad/s, roughly 0.08 Nm.
|
🎉 This PR is included in version 0.7.0 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
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
The follower felt laggy next to the arm teleoperation, and the reason turned out to be structural rather than a tuning problem.
The arm's MIT command is
kp*(q_cmd - q) + kd*(dq_cmd - dq) + tau_ffwith a realdq_cmd, and the arm link runs at a higher rate. The gripper wire frame carries only an opening — no velocity. With no velocity term the follower leans on position error alone, so it trails a moving leader by roughlyspeed / kp. That is felt as drag, and it is worst during a fast hand sweep. Raisingkpand the loop rate reduced it but could not remove it.The wire format cannot change: it is byte-identical to the litearm stack and interoperates with it. So the follower recovers the leader's velocity locally by differencing successive positions and sends the result as its own
dqtarget.kd * (dq_cmd - dq)is a genuine torque term, so the estimate is bounded on purpose: the first frame has nothing to difference against, a degenerate interval and a gap longer thanMAX_FRAME_GAP_S(0.05 s — longer than that and the "velocity" is a dropout average, not a velocity) are both refused, and the result is clamped todq_max(default10.0rad/s).dq_max=0disables the feedforward for anyone who wants the old behaviour.Changes
src/litegrip/teleop.py—DEFAULT_DQ_MAX = 10.0andMAX_FRAME_GAP_S = 0.05;GripperTeleop(dq_max=...);_estimate_dq(); the slave loop differences a fresh frame and passes the result as thedqof its MIT frame (a hold cycle sendsdq = 0);_send()gains adqparameter;status()reportsdq_cmd.src/litegrip/gripper.py—teleop_start(..., dq_max=DEFAULT_DQ_MAX)passes it through.src/litegrip/__init__.py— exportDEFAULT_DQ_MAX.examples/teleop.py—--dq-max(0 disables) anddq=in the status line.tests/test_teleop.py—VelocityFeedforwardTest: nothing to difference on the first sample, a finite difference, clamping atdq_max, refusal of a dropout-sized gap and of a degenerate interval,dq_max=0disabling it, and an end-to-end check that the slave puts the leader velocity on the wire asdqwith the right sign.README.md/readme_zn.md— the feedforward note anddq_cmdin the status list.Testing
Also run with the
zenohmodule blocked (the CI machine has no zenoh):Ran 149 tests ... OK (skipped=12).Hardware validation, 2026-09-29, leader on can0 / follower on can1, hand-driven jaws:
dqsits at a noise floor of about ±0.04 rad/s, roughly 0.08 Nm of extra torque — small enough that the loop is not audibly or visibly jittery.rejectedand nofaultover the runs.Issues
None — the repository has no issue tracker entries, matching the previous PRs.