Skip to content

Comments

fix(g1): add video stream and camera_info to G1SimConnection#1335

Merged
spomichter merged 8 commits intodevfrom
fix/g1-sim-video-stream
Feb 21, 2026
Merged

fix(g1): add video stream and camera_info to G1SimConnection#1335
spomichter merged 8 commits intodevfrom
fix/g1-sim-video-stream

Conversation

@spomichter
Copy link
Contributor

Problem

G1 sim shows no video in rerun — G1SimConnection only had lidar and odom outputs, missing color_image and camera_info. MuJoCo-rendered frames were never published.

Solution

Mirror the Go2 connection pattern:

  • Add color_image: Out[Image] and camera_info: Out[CameraInfo]
  • Subscribe to MujocoConnection.video_stream() → publish on color_image
  • Publish camera_info in background thread (1Hz, matching Go2)
  • Add camera_optical TF frame for rerun pinhole projection

How to Test

dimos --simulation run unitree-g1-sim
# Verify video stream appears in rerun viewer

G1SimConnection was missing color_image and camera_info outputs,
so MuJoCo-rendered frames never reached the rerun bridge. Mirrors
the Go2 connection pattern: subscribes to video_stream(), publishes
camera_info in a background thread, and adds camera_optical TF frame.
@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 21, 2026

Greptile Summary

Added video streaming and camera info publication to G1SimConnection to enable rerun visualization. The implementation mirrors the Go2 pattern by subscribing to MujocoConnection.video_stream() and publishing camera info in a background thread at 1Hz. Also unified camera topic paths across blueprints by removing the /g1/ prefix.

Key changes:

  • Added color_image and camera_info outputs to G1SimConnection
  • Added camera_optical TF frame for proper rerun pinhole projection
  • Background thread with proper stop event handling for camera info publication
  • Updated topic paths from /g1/color_image/color_image for consistency

Confidence Score: 3/5

  • Safe to merge with one known issue that affects visualization accuracy
  • The implementation correctly adds video streaming and follows established patterns, but the hardcoded camera intrinsics (1280x720) don't match actual MuJoCo output (320x240), which will cause incorrect pinhole projection in rerun. This issue was already flagged in previous review comments.
  • Pay attention to dimos/robot/unitree/g1/sim.py - the camera intrinsics mismatch needs to be resolved for correct rerun visualization

Important Files Changed

Filename Overview
dimos/robot/unitree/g1/sim.py Added video stream and camera_info outputs to mirror Go2 pattern, but uses incorrect hardcoded camera intrinsics (1280x720) instead of actual MuJoCo resolution (320x240)
dimos/robot/unitree/g1/blueprints/primitive/uintree_g1_primitive_no_nav.py Updated camera topic paths from /g1/color_image to /color_image and conditionally includes camera module only when not in simulation mode
dimos/robot/unitree/g1/blueprints/perceptive/unitree_g1_shm.py Updated shared memory transport path from /g1/color_image to /color_image for consistency

Last reviewed commit: 6233235

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

1 file reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

Comment on lines 111 to 114
def _publish_camera_info_loop(self) -> None:
while True:
self.camera_info.publish(_camera_info_static())
time.sleep(1.0)
Copy link
Contributor

Choose a reason for hiding this comment

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

infinite loop without exit condition

the while True loop has no mechanism to stop when stop() is called. the thread join with 1s timeout might not be sufficient if the loop is sleeping. add a stop event:

Suggested change
def _publish_camera_info_loop(self) -> None:
while True:
self.camera_info.publish(_camera_info_static())
time.sleep(1.0)
def _publish_camera_info_loop(self) -> None:
while not self._stop_event.is_set():
self.camera_info.publish(_camera_info_static())
self._stop_event.wait(1.0)

then add _stop_event: threading.Event to the class and initialize it in start()

Comment on lines 41 to 57
def _camera_info_static() -> CameraInfo:
"""Default camera intrinsics for G1 MuJoCo sim camera."""
fx, fy, cx, cy = (819.553492, 820.646595, 625.284099, 336.808987)
width, height = (1280, 720)

return CameraInfo(
frame_id="camera_optical",
height=height,
width=width,
distortion_model="plumb_bob",
D=[0.0, 0.0, 0.0, 0.0, 0.0],
K=[fx, 0.0, cx, 0.0, fy, cy, 0.0, 0.0, 1.0],
R=[1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0],
P=[fx, 0.0, cx, 0.0, 0.0, fy, cy, 0.0, 0.0, 0.0, 1.0, 0.0],
binning_x=0,
binning_y=0,
)
Copy link
Contributor

Choose a reason for hiding this comment

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

hardcoded camera intrinsics don't match actual video dimensions

the function uses width=1280, height=720 and specific focal lengths, but MujocoConnection actually outputs 320x240 video (per dimos/simulation/mujoco/constants.py:18-19). this mismatch will cause incorrect pinhole projection in rerun.

either:

  1. use MujocoConnection.camera_info_static (already computed correctly)
  2. or match the constants: width=320, height=240 and compute fx/fy from FOV=45°

see mujoco_connection.py:91-114 for the correct implementation

The rerun visual_overrides from #1334 used world/camera_info and
world/color_image (matching Go2), but the G1 transports used /g1/
prefixed topics. This mismatch meant the rerun bridge never applied
the camera_info pinhole overlay.

Remove the /g1/ prefix from color_image and camera_info transports
in the primitive blueprint and the SHM blueprint. Now matches Go2
convention and the rerun overrides work correctly.
In sim, G1SimConnection now provides video from MuJoCo. The webcam
camera_module in the primitive was also publishing to color_image,
causing interleaved webcam + MuJoCo frames. Conditionally skip
camera_module when global_config.simulation is True.
…dition

- Replace hardcoded 1280x720 Go2 intrinsics with computed values from
  MuJoCo constants (320x240, FOV=45°) matching MujocoConnection pattern
- Add _stop_event to camera_info loop for clean shutdown
MuJoCo framebuffer can't exceed 640 width without XML config. Reverted
render resolution to 320x240. Use the same 1280x720 camera intrinsics
as Go2 for rerun display scaling (matches Go2 sim behavior exactly).
Set offscreen framebuffer size in model XML to support the higher
resolution. Previously failed because MuJoCo's default offscreen
buffer is only 640 wide.
@spomichter
Copy link
Contributor Author

@greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

3 files reviewed, no comments

Edit Code Review Agent Settings | Greptile

@spomichter spomichter merged commit 1e8119f into dev Feb 21, 2026
8 checks passed
@spomichter spomichter deleted the fix/g1-sim-video-stream branch February 21, 2026 12:52
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.

1 participant