mjviser

repository·main·Indexed 42 days ago

https://github.com/mujocolab/mjviser

A web-based MuJoCo viewer powered by the Viser framework. It provides a browser-based interface for visualizing MuJoCo simulations with built-in controls for simulation, joint manipulation, and force visualization. It includes a CLI for quick model loading and a Python API featuring the Viewer and ViserMujocoScene classes for custom simulation lifecycles, batched rendering for multiple environments, and interactive GUI controls for overlays like contact points, tendons, and convex hulls.

Tokens
4K
Snippets
14
Records
22
Agent score
55%

What's inside mjviser

  1. Quick start with mjviser CLI

    main

    You can run the mjviser web-based viewer directly from the command line. It supports fuzzy path matching to find XML models in your current directory.

    Installation options

    • Using uvx (no installation required):
      uvx mjviser path/to/model.xml
    • Using pip:
      pip install mjviser
      mjviser path/to/model.xml

    Common CLI usage

    • Fuzzy matching: If you provide a partial name, it searches for matching .xml files (e.g., mjviser humanoid finds **/humanoid*.xml).
    • Custom port: Use --port to change the binding port (default is 8080).
    • Loading robot_descriptions models: If robot_descriptions is installed, you can load models by name using uvx with the --with flag.
    NOTE

    If your system Python is 3.14+ and you encounter build issues, explicitly specify a compatible Python version with uvx -p 3.13.

    # Run with a specific model
    uvx mjviser path/to/model.xml
    
    # Run with a specific port
    mjviser model.xml --port 7070
    
    # Load a model from robot_descriptions by name
    uvx --with robot_descriptions mjviser go1
  2. Visualize convex hulls for MuJoCo bodies

    main
    You can visualize the convex hulls of bodies that contain mesh or SDF geoms. This is useful for debugging collision shapes. The hulls are precomputed as merged meshes for each body and can be toggled via the _show_convex_hull setting. Hulls are rendered as batched meshes and do not cast or receive shadows by default.
  3. Update overlay decorations (contacts, tendons, frames, inertia)

    main

    To render MuJoCo's decorative overlays (like contact points, tendons, frames, and inertia) in Viser, the _update_decor_from_mjvscene method is used. It leverages mujoco.mjv_updateScene to populate an mjvScene and then maps those decorative geoms to batched Viser meshes.

    Key behaviors:

    • Tendons: Rendered using cylinder-style meshes.
    • Arrows: Rendered as a combination of a shaft (cylinder) and a head (cone). The shaft is 80% of the total length, and the head is 20%.
    • Joints: The alpha channel for joint geoms is overridden by the MuJoCo model's joint visualization settings (mj_model.vis.rgba.joint[3]).
    • Batching: Decorations are grouped by type and tendon status to minimize the number of Viser handles used.
  4. How the Viewer manages simulation pacing

    main

    The Viewer uses a real-time pacing mechanism to ensure the simulation runs at a target speed relative to real-world time.

    1. Budgeting: It calculates a _budget based on the elapsed time and the desired speed multiplier.
    2. Stepping: It executes as many step_fn calls as necessary to consume the budget.
    3. Capping: If the simulation cannot keep up with the target speed (e.g., physics is too heavy), the status display will show [CAPPED]. This indicates the simulation is hitting the frame time deadline.
    4. Rendering: Rendering is decoupled from physics stepping and is targeted at 60 Hz (_FRAME_TIME). It only triggers if the state is 'dirty' (changed) or if the scene explicitly needs an update.
  5. Use the mjviser Python API

    main

    To integrate the viewer into a Python script, import the Viewer class from mjviser. Pass a MuJoCo model and data object to the constructor and call .run() to start the web server and simulation loop.

    The viewer provides simulation controls, joint/actuator sliders, contact/force visualization, camera tracking, and keyframes in your browser.

    import mujoco
    from mjviser import Viewer
    
    model = mujoco.MjModel.from_xml_path("robot.xml")
    data = mujoco.MjData(model)
    Viewer(model, data).run()
  6. Extend mjviser with callbacks

    main

    The Viewer class accepts three optional callback functions to customize the simulation lifecycle:

    • step_fn(model, data): Executed at every simulation step. Defaults to mujoco.mj_step.
    • render_fn(scene): Executed at every render frame. Defaults to scene.update_from_mjdata(data).
    • reset_fn(model, data): Executed when the simulation is reset.

    For maximum control, you can bypass the Viewer class and use ViserMujocoScene directly with a standard viser.ViserServer instance. This allows you to add custom GUI elements, scene overlays, and other Viser features.

    import viser
    import mujoco
    from mjviser import ViserMujocoScene
    
    server = viser.ViserServer()
    model = mujoco.MjModel.from_xml_path("robot.xml")
    data = mujoco.MjData(model)
    
    # Use ViserMujocoScene for manual control
    scene = ViserMujocoScene(server, model, num_envs=1)
    scene.create_visualization_gui()
    
    # Add custom Viser GUI elements
    with server.gui.add_folder("My Controls"):
      slider = server.gui.add_slider("Force", min=0, max=100, initial_value=0)
    
    while True:
      mujoco.mj_step(model, data)
      scene.update_from_mjdata(data)
  7. Manage decorative visualization handles

    main

    The ViserMujocoScene manages a cache of handles for decorative elements to ensure efficient updates.

    • Hide decorations: Use _hide_all_decor() to set visible=False on all existing decoration handles without removing them from the scene.
    • Clear decorations: Use _clear_decor_handles() to completely remove all decoration handles and clear the internal cache.
  8. Refresh visualization state

    main

    If you have manually modified the MuJoCo model or need to force a re-render from the last known state without providing new data, use refresh_visualization().

    You can also use request_update() to trigger a re-render if you have installed a custom refresh handler via set_refresh_handler(handler).

  9. Manage convex hull handles

    main

    The ViserMujocoScene provides methods to control the lifecycle of convex hull visualizations:

    • Clear hulls: Use _clear_hull_handles() to remove both fixed (world-attached) and dynamic (body-attached) convex hull handles and clear their caches.
  10. Configure Viewer callbacks

    main

    When initializing Viewer, you can override the default simulation and rendering behaviors using the following arguments:

    • step_fn(model, data): Called each simulation step. Defaults to mujoco.mj_step.
    • render_fn(scene): Called each render frame to push state to the ViserMujocoScene. Defaults to scene.update_from_mjdata(data). Use this for custom rendering or multi-environment setups.
    • reset_fn(model, data): Called when the 'Reset' button is pressed in the GUI. Defaults to mj_resetData followed by mj_forward. Use this to reset custom simulation state (e.g., warp data).
    • num_envs: Number of parallel environments (passed to ViserMujocoScene).
    • server: An optional viser.ViserServer. If None, a new server is created.
    def custom_step(model, data):
        # Your custom physics logic
        mujoco.mj_step(model, data)
    
    def custom_render(scene):
        # Custom rendering logic
        scene.update_from_mjdata(data)
    
    viewer = Viewer(
        model=model,
        data=data,
        step_fn=custom_step,
        render_fn=custom_render,
        num_envs=1
    )
  11. Add GUI controls for scene and visualization

    main

    The ViserMujocoScene provides methods to inject interactive controls into the Viser GUI:

    • create_scene_gui(...): Adds camera controls (distance, azimuth, elevation) and environment selection (slider, 'Hide others' checkbox) to a 'Environment' folder.
    • create_overlay_gui(): Adds a 'Visualization' tab containing toggles for contact points, contact forces, tendons, inertia, actuators, and convex hulls, along with color and scale sliders.
    • create_groups_gui(): Adds a 'Groups' tab to show/hide specific geometry (G0-G5) and site (S0-S5) groups.
    • create_visualization_gui(...): A convenience method that creates a tabbed layout containing all the above controls (Scene, Visualization, and Groups tabs).
    # Add a full tabbed GUI to the server
    tabs = scene.create_visualization_gui(
        camera_distance=-1.0, 
        camera_azimuth=120.0, 
        camera_elevation=20.0
    )
    
    # You can then add your own custom tabs to the returned group
    with tabs.add_tab("My Custom Tab", icon=viser.Icon.SETTINGS):
        scene.server.gui.add_label("Hello World")