Repository navigation
feat(arm): time-scheduled MoveThroughJointPositionsStreamed (rdk v1.6.0) - #48
Merged
Merged
Conversation
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
…ount writes not packets, sim seams) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
rdk >= v1.1.0 adds MoveThroughJointPositionsStreamed to arm.Arm; both arm models stub it with errors.ErrUnsupported until the real implementation lands. rdk v1.6.0's Frame.Transform no longer truncates at an out-of-bounds joint, so oob_test.go drops the assertion that pinned the old bug. ComputeOOBPosition still clamps to the limit; whether it should stay at all is a follow-up. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
… moves Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
…deg start gate Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
Name the ungated mid-stream ceiling at the code site and pin it with a test; make the Stop test deterministic; Clock.WaitUntil measures against its own Now. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
…amed RPC Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
… the client on error Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
…e tool binary Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
…ools/ layout Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
…UDE.md Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Qv1ZbWg7v4RH7HcR6xUgAi
Also adapts the streamed path to main's three-value clampPositions after the rebase. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9kxn9BZtxsAen11ecEmW
…y >= 1ms Point 0 is due at start and the clock is read again after start is captured, so it always counted as late by a few microseconds. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9kxn9BZtxsAen11ecEmW
…rvos to stop WaitForServosToStop straight after the final goal write read Moving before it had risen (~2ms) and returned while the arm was still travelling: hardware runs showed a 2ms settle and a 1.2 deg base-joint error at return. Reuse the paced path's dwell, which consults Moving only from its second poll. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9kxn9BZtxsAen11ecEmW
Nick Hehr (HipsterBrown)
force-pushed
the
streamed-trajectory
branch
from
September 11, 2026 22:00
a90e94f to
e31621c
Compare
Nick Hehr (HipsterBrown)
marked this pull request as ready for review
September 11, 2026 22:00
This was referenced Sep 11, 2026
…e tools PR CheckTrajectoryTime takes only (prev, t); the simulated Stop check lives in startMove; docs/arm.md's streaming section is a third of its length. tools/stream_trajectory moves to the stacked tools PR with the other tools. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018G9kxn9BZtxsAen11ecEmW
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.
Why
rdk ≥ v1.1.0 adds
MoveThroughJointPositionsStreamedtoarm.Arm: a stream of time-stampedTrajectoryPointbatches. Every in-tree rdk arm ignoresTimeand loops points. This module already knows that pacing by position is not the same as following a producer's timing (thestreamedsetpoint path teleop uses), so this implements the RPC time-scheduled, and it was measured on hardware before un-drafting.What changed
go 1.25.10, api v0.1.577, utils v0.10.1). The only compile break was the new interface method.referenceframe/pose_cloud.go,transformable.go,model_urdf.go,KinematicModelToProtobuf,getLocalFrameSystemPartsand the gripperKinematics()contract are byte-identical across the bump, so every CLAUDE.md invariant about PoseCloud/URDF/module-boundary behaviour still holds. One behavioural change:Frame.Transformno longer bounds-checks joints (OOBErrStringis dead upstream).oob_test.godrops the assertion that pinned the old bug;so101.clampPositionsis now the only write-path limit enforcement, andgeometry.ComputeOOBPosition's read-path clamp is a lossy leftover (follow-up).components/arm/streamed.go): each point is written as an unbounded goal (Speed 0 / Acc 0, oneSetGoalspacket, no read in the loop) atstart + p.Timethrough aservo.Clockseam. 5° first-point gate: one position read; if any joint is farther than that, one blocking profiled move first, then the clock starts.Timemust be 0 then strictly increasing. Late points are written, not dropped.Constraintsignored. One ack per non-empty batch. Batches are received with aselecton ctx, notrange:Stop'sopMgr.CancelRunningblocks until the op returns and cancelling our ctx does not close the channel. After the last point the arm dwells on it (as a paced waypoint would) beforeWaitForServosToStop, so the call does not return on the pre-writeMovingzero. One log line per stream: gate, late points, settle, wall.components/simulated/streamed.go): same contract, re-targets the interpolator per point;Stopends the stream between points.servo.CheckTrajectoryTime,servo.Clock;FakeTransport.WriteCountcountsSYNC_WRITEs by start address so tests can count goal writes.docs/arm.md"Streaming trajectories (experimental)"; CLAUDE.md gotchas.Measured on hardware (laptop-main, STS3215 arm, 1 Mbaud)
nod@ 100 Hztest@ 100 HzWall − gate − trajectory ≈ 3 ms in both: the schedule held exactly at 100 Hz; all overhead is the start gate (which depends only on where the arm was parked). Ten planned single moves at 30 and 80 deg/s, two obstacle-detour plans (8–11 waypoints), two mid-stream splices, and a 40-leg / 70 s continuous loop all held schedule with
stitch late 0and 0.7–2.5° mean joint deviation (the servos' droop under load). The dwell fix above came from those runs: before it, the stream returned withsettle 2msand a 1.2° base-joint error.All measurement tooling (the arm-recorder replay tool, plan-through-motion-service → trajex → this RPC with a paced baseline, and online replanning) is in the stacked PR #51; the module change here is the ~200 lines that matter.
Read before running on hardware
Stopdoes not brake. It ends the stream, butcontroller.StopwritesGoal_Velocity = 0, which is the max-speed sentinel, so the servo still completes its last commanded goal. Pre-existing on every move path; this path amplifies it because the last goal can be far away. Fix belongs incontroller.Stop, not here. Not yet tested mid-stream; do that with power in reach before merging.DoF()limits (so101.json / URDF), not the calibrated limits the module clamps to, and tears the stream down client-side on a violation.Speed 0 / Acc 0; every other move path rewrites both registers, so nothing inherits the sentinel.Follow-ups (not this PR)
controller.Stopshould write the current position as the goal rather than velocity 0.geometry.ComputeOOBPosition's clamp (lossy on rdk ≥ v1.6.0).MoveThroughJointPositionspath cut a planned obstacle detour by up to 38° at 30 deg/s in the stacked PR's measurements; separate issue.Testing
go test ./...,-raceon both arm packages andinternal/servo,go vet,gofmt -s,make bin/armall clean.🤖 Generated with Claude Code