Skip to content

SCOPE: Motion plan replay plugin#782

Draft
Marcus (sucrammal) wants to merge 23 commits into
mainfrom
motion-plan-replay
Draft

SCOPE: Motion plan replay plugin#782
Marcus (sucrammal) wants to merge 23 commits into
mainfrom
motion-plan-replay

Conversation

@sucrammal

@sucrammal Marcus (sucrammal) commented Jun 16, 2026

Copy link
Copy Markdown
Member
Screen.Recording.2026-06-18.at.5.01.33.PM.mov
  • Upload multiple motion plans and toggle between them
  • Scrubber to parse the motion plan

@changeset-bot

changeset-bot Bot commented Jun 16, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: f3f70cb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions

github-actions Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://viamrobotics.github.io/visualization/pr-preview/pr-782/

Built to branch gh-pages at 2026-06-22 21:15 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

1. Joints (frame_type: rotational) should not be apart of the main tree
2. Geometry: translations vs. translations. One is the offset from origin frame for the attachment point of the next link, the other is the offset from origin frame to geometry center of the link.
Cameras, grippers, and obstacles parented to model frames (e.g. "left-arm") were permanently orphaned because model frames are  never spawned as ECS entities. They now redirect to the arm's end-effector via model.primary_output_frame, falling back to model.links[last].id: Viam's convention that the last link is  always the end-effector.
Updated motion replay entities get their preserved opacity restored, so slider changes stick across scrubs.
Semantic error messages for duplicate or invalid motion plan JSON.
Fill in gaps (large jumps in angle change) with sub-steps, where every joint moves at a velocity proportional to its total delta.
- planToSnapshots unexported
- GeometryDescriptor collapsed: parseGeometry() now returns Geometry | null directly
-  computeJointedLinkPose moved
-  LocalPose → Pose getting rid of unnecessary type.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great POC! looks very nice!

my main things for when we do the full impl are

  1. we should try to be more koota native and create a plan entity and relationships for the plan <--> plan entities
  2. I think we can use zod to parse the plans a little easier
  3. we should strip back some of the (very cool) functionality you added in P0 (i.e. no linear interp between steps, no preserve opacity and visibility (yet), no multiple plans loaded at a time)

Comment thread src/lib/components/FileDrop/FileDrop.svelte Outdated
Comment thread src/lib/components/FileDrop/useFileDrop.svelte.ts Outdated
Comment thread src/app.css
}

/* Push toasts above the motion plan scrubber bar (fixed bottom-4, ~44px tall). */
body.has-scrubber [aria-label='Toasts'] {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

todo: for final impl we probably should look into how to do this with component placements instead of the app css


const clearActivePlan = () => {
for (const entry of entityMap.values()) {
if (world.has(entry.entity)) hierarchy.destroyEntityTree(world, entry.entity)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for the final setup, lets look into making a new PlanEntity, which we can then give relationships to all the world entities that are part of it (see the selection plugin https://github.com/viamrobotics/visualization/blob/main/src/lib/plugins/Selection/traits.ts for inspo)

this way we can make this flow more koota native and the relationship has an auto destroy hook e.x. https://github.com/viamrobotics/visualization/blob/main/src/lib/plugins/Selection/relations.ts#L9 so we can make it so all world entities auto clean up when a plan entity is removed


const result = reconcileSnapshotEntities(world, snap, entityMap)

for (const spawned of result.spawned) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this loop trying to do? is it manually trying to apply relationships added in the reconcile step?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea its manually rewiring up parent-child relationships that reconcileSnapshotEntities computed but didn't apply yet. The caller (result.spawned[i].relationships actually applies these relationships for the ECS.

let foundFrameSystem = false

let index = 0
for (let i = 0; i < 8; i++) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are the motion plan jsons not valid json? If they are valid, we should just be able to do a big JSON.parse(content) and then use zod to confirm it fits our expected format

const UserSchema = z.object({
  name: z.string(),
  age: z.number(),
})

const user = UserSchema.parse(JSON.parse(json))

@sucrammal Marcus (sucrammal) Jun 22, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes with a small caveat: motion plan files are concatenation of 2 valid JSON objects: the request (frame_system plus start and end poses) plus the motion plan (plan + trajectory). So there is still a little bit of non-zod work to dissect the file into these objects.

Comment thread src/lib/plugins/MotionPlanReplayer/MotionPlanReplayerUI.svelte Outdated
Comment thread src/lib/plugins/MotionPlanReplayer/MotionPlanReplayerScrubber.svelte Outdated
provideMotionPlanReplayer(untrack(() => plans))
</script>

{#if plans === undefined}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be cool if drag and drop just updated the plans array and then we could use the ui for both cases

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Designed a more explicit hook here (f3f70cb) which calls the same addPlan hook from app vs. standalone.

Comment thread src/lib/plugins/MotionPlanReplayer/interpolate-trajectory.ts Outdated
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