Skip to content

RSDK-14379: world state store service -> motion service - #6285

Draft
Devin T. Currie (DTCurrie) wants to merge 6 commits into
viamrobotics:mainfrom
DTCurrie:feed-world-state-store-service-to-motion-service
Draft

RSDK-14379: world state store service -> motion service#6285
Devin T. Currie (DTCurrie) wants to merge 6 commits into
viamrobotics:mainfrom
DTCurrie:feed-world-state-store-service-to-motion-service

Conversation

@DTCurrie

@DTCurrie Devin T. Currie (DTCurrie) commented Jul 28, 2026

Copy link
Copy Markdown
Member

What

Wire a configured world_state_store service into the builtin motion service so that a Move
automatically plans around the store's dynamic obstacles, without the caller re-passing a WorldState
on every call. Also make the fake world state store an exemplary, self-contained detection simulator
that a real (module) store can be modeled after.

This is additive and opt-in: when the new motion attribute is unset, behavior is unchanged.

Why

Viam keeps no persistent world representation between motion planning calls, so today an application has
to be the source of truth for the world and re-pass a WorldState on every Move. The world state
store docs already describe the service as an input source for the motion service, but nothing consumed
it. This change implements that consume path for the Move (cartesian) flow.

Static obstacles (configured component geometries) were already auto-included by the planner via the
frame system. The gap this closes is dynamic obstacles, for example objects detected at runtime by a
depth camera or lidar.

What changed

Motion service (services/motion/builtin/builtin.go):

  • New config attribute world_state_store_service_name (a single service name). Validate returns it as
    a required dependency; Reconfigure resolves it onto the service.
  • On each Move, the store is read fresh and its transforms are converted into planner obstacles and
    supplemental transforms, then merged with any request-supplied WorldState (merge, not override).
  • Basic validation that never drops anything or fails the call: obstacles whose parent frame is not in
    the frame system are warned and skipped, transforms whose name collides with an existing frame are
    warned and skipped, and duplicate names or UUIDs across the request and the store are disambiguated so
    both are kept.
  • A startup Info log states whether a store was wired in, and a per-Move Debug log lists the obstacles
    the store contributed.

World state store fake, made an ideal example (services/worldstatestore/fake/):

  • depth_camera mode: three colored blocks (red, green, blue) built through the motion-tools draw API
    so color metadata and geometry render correctly in the visualizer and participate in collision.
  • lidar mode: a single, fixed point cloud representing a ring of surface hits around the arm, with
    per-scan range jitter for realism (it does not spin). The cloud serializes to an octree, so it is a
    real collision obstacle. The ring is positioned so the arm starts clear of every surface.
  • A non-lossy, multi-subscriber broadcaster replaces the previous single, lossy channel. Late
    subscribers receive the full current world as ADDED, then every change.

Tests:

  • End to end store to motion consume, the merge and validation rules, the point cloud round trip
    (returns arrive as collision-capable octrees), and the fake simulators.

No go.viam.com/api changes are required. The motion service consumes the store through the existing
ListUUIDs and GetTransform RPCs.

How to test

A manual harness lives in _local_wss_test/. See _local_wss_test/README.md for the full walkthrough.
It configures a simulated arm (so moves play out as visible motion rather than snapping), a static
table obstacle under the arm, a fake world_state_store, and a builtin motion service pointed at it.

Setup:

  1. Check out this branch.
  2. On app.viam.com, create a machine and paste the components and services from
    _local_wss_test/config.json into the machine's raw JSON config.
  3. Download the machine config the way you normally would (the app adds the cloud block for you), and
    save it outside the repo, for example ~/machine.json.
  4. Run this branch's viam-server against that download:
    go run ./web/cmd/server -config ~/machine.json.
    You should see world state store "worldstate" wired into this motion service on startup.
  5. Open the motion-tools visualizer (viamrobotics/visualization) and connect to the machine, using the
    machine address and API key from the downloaded config.

Manual check (Move Frame plugin):

  • On the arm, confirm the Motion service dropdown is builtin, drag the gizmo, and Execute. Moves into
    a block or below the table are rejected with a constraint violation; moves to clear space plan normally.

Scripted play-through (good for recording a demo):

  • With the server and visualizer up, in another terminal run the demo client, which authenticates with
    the machine API key and plays a single path, then exits:

    go run ./_local_wss_test/main.go -mode depth_camera \
      -address <machine-address> -api-key-id <cloud.api_key.id> -api-key <cloud.api_key.key>
    
  • -mode depth_camera sweeps the three blocks and refuses to enter them. -mode lidar threads the
    point-cloud ring and refuses its surfaces. Set the store's input_sensor_type to match the mode you
    run. The script prints how many transforms the store serves (3 for depth_camera, 1 for lidar) so you
    can confirm the match before recording.

Note: the motion service must be named builtin so the visualizer's default target is the store-aware
one, otherwise RDK's auto-created default builtin shadows it. This is called out in the README.

Coordination

This changes the motion planner's input path, so it should be reviewed with the motion planning team
before merge. It is behind an opt-in attribute and default behavior is unchanged when the attribute is
unset.

  • delete local test fixtures
Screen.Recording.2026-08-03.at.1.52.47.PM.mov
Screen.Recording.2026-08-03.at.1.54.19.PM.mov

@viambot viambot added the safe to test This pull request is marked safe to test from a trusted zone label Jul 28, 2026
@DTCurrie Devin T. Currie (DTCurrie) changed the title rough draft world state store service -> motion service Jul 28, 2026
…rld state params with dependent world state from store
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Jul 29, 2026
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Jul 29, 2026
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Aug 3, 2026
@viambot viambot added safe to test This pull request is marked safe to test from a trusted zone and removed safe to test This pull request is marked safe to test from a trusted zone labels Aug 3, 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.

delete this before merge

Comment thread _local_wss_test/main.go

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.

delete this before merge

Comment thread _local_wss_test/README.md

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.

delete this before merge

@dgottlieb Dan Gottlieb (dgottlieb) changed the title world state store service -> motion service RSDK-14379: world state store service -> motion service Aug 4, 2026
func (ms *builtIn) plan(ctx context.Context, req motion.MoveReq, logger logging.Logger) (motionplan.Plan, error) {
// Merge any request-supplied world state with the configured store's so a Move avoids both sets of
// obstacles; conflicts are logged, not fatal.
storeObstacles, storeTransforms := ms.storeWorldStateParts(ctx)

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.

This looks exactly as I hoped!


// normalizeTransformGeometry fills in an identity geometry center when the store leaves it unset, which
// the geometry converter requires. It clones so the store's proto is never mutated.
func normalizeTransformGeometry(tf *commonpb.Transform) *commonpb.Transform {

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.

All these other helpers in builtin.go -- we can keep in the motion service package, but please move them to a separate (world_state.go?) file.

I think I actually want these in the referenceframe/worldstate.go. But I also think there's some refactoring to be had. And it's not right for me to ask you to do that here. I don't know exactly what I want without doing some tinkering myself. An end state where we just have some file that I can delete as I move functionality over is all I need to see here.

return err
}

t.Run("obstacle in a known frame plans", func(t *testing.T) {

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.

These tests are fine. But the test I really want to see is that adding a huge obstacle via the world state store can make planning fail. That will give me the negative acknowledgment I need that the world state store is being consumed.


// DepthCameraWorld simulates a depth camera detecting three colored blocks on a table and reporting them
// into the store, refreshed each capture cycle. No real camera or vision service is involved.
type DepthCameraWorld struct {

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.

Is this and the lidar world stuff just helpers for simulation? Presumably for (sim_test?) testing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

safe to test This pull request is marked safe to test from a trusted zone

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants