Skip to content

feat: add viser as new manipulation vis backend#2475

Open
TomCC7 wants to merge 43 commits into
mainfrom
cc/feat/viser-vis-rework
Open

feat: add viser as new manipulation vis backend#2475
TomCC7 wants to merge 43 commits into
mainfrom
cc/feat/viser-vis-rework

Conversation

@TomCC7

@TomCC7 TomCC7 commented Jun 12, 2026

Copy link
Copy Markdown
Member

Closes #2412 and supersedes #2413

Adds Viser as a manipulation planning visualizer and rewires manipulation visualization config around a single validated visualization field. The new visualizer is more feature-rich than the original drake-bundled meshcat vis and provides a moveit style UI for plan/preview/execute.

image

Main changes:

  • Add in-process Viser manipulation visualization: runtime, scene rendering, GUI panel, adapter, animation, and tests.
  • Replace loose visualization_backend / visualization_options config with a Pydantic discriminated visualization config for none, meshcat, and viser.
  • Clean up spec creation so manipulation startup creates:
    1. WorldSpec
    2. PlanningSpecs
    3. optional VisualizationSpec
  • Keep Meshcat as an embedded Drake-world visualizer and create Viser as a separate visualization backend.
  • Sync planning-scene metadata to external visualizers so Viser can initialize robot visuals without Viser-specific hooks in WorldMonitor.
  • Include viser[urdf] in the manipulation extra and move Viser usage docs into the manipulation docs.

Notes

  • Viser panel execution is opt-in via allow_plan_execute.
  • The earlier set_planning_target / clear_planning_target VisualizationSpec API was reverted; target controls remain internal to the Viser GUI/scene.

How to Test

uv run dimos run xarm7-planner-coordinator \
  -o manipulationmodule.visualization.backend=viser \
  -o manipulationmodule.visualization.allow_plan_execute=true

Contributor License Agreement

  • I have read and approved the CLA.

@codecov

codecov Bot commented Jun 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.23387% with 515 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
dimos/manipulation/visualization/viser/gui.py 68.15% 118 Missing and 46 partials ⚠️
dimos/manipulation/visualization/viser/scene.py 78.15% 49 Missing and 15 partials ⚠️
...mos/manipulation/visualization/viser/visualizer.py 43.42% 39 Missing and 4 partials ⚠️
dimos/manipulation/manipulation_module.py 36.06% 39 Missing ⚠️
dimos/manipulation/visualization/viser/adapter.py 63.36% 27 Missing and 10 partials ⚠️
...anipulation/planning/monitor/test_world_monitor.py 71.54% 35 Missing ⚠️
dimos/manipulation/visualization/test_factory.py 70.09% 32 Missing ⚠️
dimos/manipulation/visualization/viser/runtime.py 42.55% 27 Missing ⚠️
dimos/manipulation/visualization/viser/state.py 88.23% 14 Missing and 12 partials ⚠️
...on/visualization/viser/test_viser_visualization.py 97.76% 12 Missing and 4 partials ⚠️
... and 6 more
@@            Coverage Diff             @@
##             main    #2475      +/-   ##
==========================================
+ Coverage   69.97%   70.36%   +0.38%     
==========================================
  Files         842      855      +13     
  Lines       74420    76749    +2329     
  Branches     6668     6866     +198     
==========================================
+ Hits        52077    54001    +1924     
- Misses      20636    20944     +308     
- Partials     1707     1804      +97     
Flag Coverage Δ
OS-ubuntu-24.04-arm 64.44% <78.99%> (+0.52%) ⬆️
OS-ubuntu-latest 65.24% <78.99%> (+0.50%) ⬆️
Py-3.10 65.23% <78.99%> (+0.50%) ⬆️
Py-3.11 65.23% <78.99%> (+0.50%) ⬆️
Py-3.12 65.23% <78.99%> (+0.50%) ⬆️
Py-3.13 65.23% <78.99%> (+0.50%) ⬆️
Py-3.14 65.25% <78.99%> (+0.49%) ⬆️
Py-3.14t 65.24% <78.99%> (+0.50%) ⬆️
SelfHosted-Large 30.15% <26.49%> (-0.14%) ⬇️
SelfHosted-Linux 37.88% <27.45%> (-0.35%) ⬇️
SelfHosted-macOS 36.64% <27.45%> (-0.35%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dimos/manipulation/blueprints.py 100.00% <100.00%> (ø)
...mos/manipulation/planning/monitor/world_monitor.py 54.28% <100.00%> (+2.24%) ⬆️
dimos/manipulation/planning/spec/models.py 100.00% <100.00%> (ø)
dimos/manipulation/planning/spec/protocols.py 100.00% <100.00%> (ø)
dimos/manipulation/test_manipulation_module.py 98.62% <ø> (ø)
dimos/manipulation/test_manipulation_unit.py 100.00% <100.00%> (ø)
dimos/manipulation/visualization/config.py 100.00% <100.00%> (ø)
dimos/robot/manipulators/a750/blueprints.py 100.00% <ø> (ø)
dimos/robot/manipulators/openarm/blueprints.py 100.00% <ø> (ø)
dimos/robot/manipulators/piper/blueprints.py 100.00% <ø> (ø)
... and 17 more

... and 6 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@TomCC7 TomCC7 force-pushed the cc/feat/viser-vis-rework branch from 283a6da to 9087b4d Compare June 12, 2026 20:35
@TomCC7 TomCC7 changed the title WIP: feat/viser-manip-vis feat: add viser as new manipulation vis backend Jun 12, 2026
@TomCC7 TomCC7 mentioned this pull request Jun 12, 2026
1 task
@TomCC7 TomCC7 marked this pull request as ready for review June 12, 2026 20:51
paul-nechifor and others added 12 commits June 16, 2026 23:49
These cosmetic-theme failures were buried in logger.debug (hidden by
default). Raise them to warning and narrow the broad excepts to the
expected theme-API-unavailable exceptions.
evaluate_joint_target set both "pose" and "ee_pose" to the same value
(also calling get_ee_pose twice). Keep the single "ee_pose" key used by
the error returns and read it directly in the GUI.
self.scene is a ViserManipulationScene; has_reference_grid,
ensure_target_controls and set_target_visual_state always exist on it,
so the hasattr guards were noise. The existing 'scene is None' checks
suffice. Tests' minimal scene stand-ins gain the now-required methods.
…cept

RobotModelConfig defines name, end_effector_link, base_link,
home_joints and joint_limits_lower/upper, so the try/except
AttributeError probes in the adapter and panel GUI never fired -- they
were noise that defeated mypy. Access the fields directly. Tests gain a
make_robot_config() builder so their config stand-ins carry the fields
the panel reads.
RobotModelConfig has no init_joints field, so config.init_joints always
raised and the branch was dead. Init joints come from the module's
_init_joints registry; the preset test now supplies them there.
Replace the bare except Exception: pass / = None handlers around Viser
handle attribute writes with warning logs so failures are visible
instead of swallowed.
The _urdf / _meshes accesses on viser handles are already confined to
single helpers; note the dependency on viser internals so it is not
spread elsewhere.
Add a make_panel factory fixture that starts a ViserPanelGui and closes
it (stopping its worker threads) during teardown, so cleanup runs even
when a test fails mid-body. Convert the GUI tests off hand-rolled
try/finally blocks.
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Jun 16, 2026
@github-actions github-actions Bot added ready-to-merge Required CI checks have passed on this PR and removed ready-to-merge Required CI checks have passed on this PR labels Jun 16, 2026
@github-actions github-actions Bot added ready-to-merge Required CI checks have passed on this PR and removed ready-to-merge Required CI checks have passed on this PR labels Jun 16, 2026
@TomCC7

TomCC7 commented Jun 17, 2026

Copy link
Copy Markdown
Member Author

@mustafab0 made some further cleanup and make pink the default ik module. can check again

@mustafab0 mustafab0 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

👍

@paul-nechifor could you also please take a glance at this once?

Its clear from my end.

# Conflicts:
#	dimos/manipulation/blueprints.py
#	docs/capabilities/manipulation/adding_a_custom_arm.md
@github-actions github-actions Bot removed the ready-to-merge Required CI checks have passed on this PR label Jun 18, 2026
self._closed = True
self.state.runtime = PanelRuntime.STOPPING
self._worker.stop()
self._operation_worker.stop(timeout=None)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Indefinite block on shutdown when an operation is in progress

close() calls self._operation_worker.stop(timeout=None), which calls thread.join(timeout=None) — an infinite wait. Since the OperationWorker has no operation-level timeout configured (OperationWorker(self._set_error), no timeout_seconds), the worker thread runs each operation to completion before it can be stopped.

Concretely, if the user clicks Plan and then shuts down the module before the planner finishes, ManipulationModule.stop()world_monitor.stop_all_monitors()visualization.close()gui.close() will block here for up to planning_timeout (default 10 s). Likewise, a running preview animation (default 3 s via adapter.preview_path) will hold the shutdown path open for the animation's full duration.

@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Jun 18, 2026
# Viewer Backends

Dimos supports Rerun as its visualization backend (`rerun` default, or `none` to disable).
Dimos stream visualization uses `GlobalConfig.viewer` and `vis_module(...)` to render typed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nitpick: This sentence has become quite hard to parse.

When writing documentation, you should view it from the perspective of someone unfamiliar with the system. It would be more accessible to say:

Dimos uses Rerun for visualizations. It can be disabled by using dimos --viewer none ....

And then explain what vis_module is for.

(There's no need to mention --viewer rerun, the default. That made sense when we also supported foxglove.)

Viser support is included in the `manipulation` extra:

```bash
uv sync --extra manipulation

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
uv sync --extra manipulation
uv sync --extra manipulation --inexact

Comment on lines +572 to +578
).transports(
{
("joint_state", JointState): LCMTransport("/coordinator/joint_state", JointState),
}
)
# The planner's `coordinator_joint_state` input auto-connects to the
# ControlCoordinator's output on the default `/coordinator_joint_state`
# topic, so no `.transports(...)` override is needed.
# The `.transports(...)` override subscribes the planner to the coordinator's
# published joint-state topic.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
).transports(
{
("joint_state", JointState): LCMTransport("/coordinator/joint_state", JointState),
}
)
# The planner's `coordinator_joint_state` input auto-connects to the
# ControlCoordinator's output on the default `/coordinator_joint_state`
# topic, so no `.transports(...)` override is needed.
# The `.transports(...)` override subscribes the planner to the coordinator's
# published joint-state topic.
)

Note that I have merged #2515

It renames joint_state to coordinator_joint_state and you rely on defaults, so no need to use .transports(...) here.

world = MagicMock()
return WorldMonitor(
world=world,
visualization=cast("VisualizationSpec | None", viz),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

No need for casting. We don't typecheck in tests. But even so, the problem is the bad type in the function definition: viz: object | None.

kinematics=MagicMock(),
)

with (

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This very long with statement is identical to the one above. Use a pytest fixture to remove duplication.

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

Labels

ready-to-merge Required CI checks have passed on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Viser Manipulation Control Panel

3 participants