Skip to content

Jeff/feat/g1 raycast ivan#2348

Open
leshy wants to merge 34 commits into
mainfrom
jeff/feat/g1_raycast_ivan
Open

Jeff/feat/g1 raycast ivan#2348
leshy wants to merge 34 commits into
mainfrom
jeff/feat/g1_raycast_ivan

Conversation

@leshy

@leshy leshy commented Jun 3, 2026

Copy link
Copy Markdown
Member

No description provided.

jeff-hykin and others added 30 commits May 27, 2026 00:00
Renames uintree_g1_primitive_no_nav.py to unitree_g1_primitive_no_nav.py,
adds unitree_g1_onboard.py at the corrected path, updates the variable
name and all importers, and regenerates the all_blueprints.py registry
keys (drops the typo'd uintree-g1-primitive-no-nav entry, adds the
correctly-spelled unitree-g1-onboard and unitree-g1-primitive-no-nav).
Prefix with _ so it is not registered as a standalone runnable
blueprint; it stays a shared composition for nav-simple and
nav-onboard. unitree-g1-nav-simple is the only new blueprint.
@codecov

codecov Bot commented Jun 3, 2026

Copy link
Copy Markdown

@greptile-apps

greptile-apps Bot commented Jun 3, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR introduces a new unitree_g1_nav_simple navigation blueprint for the G1 robot using raycasting-based costmaps and A* replanning, alongside several supporting refactors: deadzone compensation for the G1 DDS SDK, configurable TF frame IDs for FastLio2, Odometry.to_pose_stamped() conversion, and a shared _unitree_g1_onboard sub-blueprint. It also standardizes the costmap Rerun visualization by centralizing the LUT into costmap_to_rerun.

  • New nav stack (unitree_g1_nav_simple): wires _unitree_g1_onboardRayTracingVoxelMapCostMapperReplanningAStarPlannerMovementManager, with the robot's build system switched from cargo build --release to nix build path:. for the Rust voxel raytracer.
  • Deadzone compensation added to G1HighLevelDdsSdk.move(), boosting sub-threshold velocity commands to the minimum effective magnitude before forwarding to the loco client.
  • Blueprint refactor: uintree_g1_primitive_no_nav.py renamed to unitree_g1_primitive_no_nav.py (typo fix), visualization extracted to unitree_g1_vis, and onboard sensors/DDS factored into _unitree_g1_onboard for reuse across blueprints.

Confidence Score: 5/5

Safe to merge; changes are well-scoped refactors and additions with no data-loss or crash-inducing paths introduced.

The core logic changes (deadzone compensation, frame_id propagation, Odometry conversion, A* frame fix) are all contained and correct. The new nav stack composes existing tested modules. No issues were found that would cause incorrect navigation behavior or data corruption on the changed paths.

Hardware deployment of unitree_g1_nav_simple warrants manual verification of the increased speed (0.85 m/s) and the deadzone boost interaction, particularly for lateral velocity commands on the G1.

Important Files Changed

Filename Overview
dimos/robot/unitree/g1/blueprints/navigation/unitree_g1_nav_simple.py New G1 nav blueprint; module-level assert on G1 config is correct since G1 always has non-None clearance values, but vis duplication with _unitree_g1_onboard is handled by autoconnect deduplication.
dimos/robot/unitree/g1/effectors/high_level/dds_sdk.py Adds deadzone compensation to move(); applies the same linear deadzone threshold to both vx and vy, which may not match the G1's actual lateral movement deadzone characteristics.
dimos/mapping/costmapper.py Adds costmap-to-Rerun helper with pre-built color LUT and initial_safe_radius_meters config; the LUT is correctly constructed covering all 102 index slots.
dimos/navigation/replanning_a_star/module.py Adds Odometry input and robot dimension config overrides; both odom and odometry subscriptions are always wired (pre-existing concern flagged in earlier review).
dimos/navigation/replanning_a_star/min_cost_astar.py Path frame_id now derived from the costmap's frame_id rather than hard-coded 'world'; clean improvement.
dimos/robot/unitree/g1/blueprints/primitive/unitree_g1_onboard.py New shared sub-blueprint grouping FastLio2, G1HighLevelDdsSdk, and vis; remaps global_map to global_map_fastlio to avoid naming conflicts.
dimos/msgs/nav_msgs/Odometry.py Adds to_pose_stamped() conversion method using existing position/orientation properties; implementation is correct.
dimos/hardware/sensors/lidar/fastlio2/module.py TF publish now uses config.frame_id/child_frame_id; defaults match prior FRAME_ODOM/FRAME_BODY constants, so existing callers are unaffected.

Sequence Diagram

sequenceDiagram
    participant Lidar as FastLio2 (LiDAR SLAM)
    participant DDS as G1HighLevelDdsSdk
    participant RT as RayTracingVoxelMap
    participant CM as CostMapper
    participant RAP as ReplanningAStarPlanner
    participant MM as MovementManager

    Lidar->>RT: global_map_fastlio (PointCloud2)
    Lidar->>RAP: odometry (Odometry to_pose_stamped)
    RT->>CM: voxel_map (PointCloud2)
    CM->>CM: _apply_initial_safe_radius()
    CM->>RAP: global_costmap (OccupancyGrid)
    RAP->>RAP: "A* replan with costmap frame_id"
    RAP->>MM: path (Path)
    MM->>DDS: cmd_vel (Twist)
    DDS->>DDS: _boost_above_deadzone(vx, vy, vyaw)
    DDS->>DDS: loco_client.move(vx, vy, vyaw)
Loading

Reviews (2): Last reviewed commit: "Auto-fixes for jeff/feat/g1_raycast_ivan..." | Re-trigger Greptile

Comment on lines +115 to +133
def _apply_initial_safe_radius(self, grid: OccupancyGrid) -> None:
radius_meters = self.config.initial_safe_radius_meters
if radius_meters <= 0 or grid.grid.size == 0:
return

resolution = grid.resolution
origin_x = grid.origin.position.x
origin_y = grid.origin.position.y

rows, columns = np.ogrid[: grid.grid.shape[0], : grid.grid.shape[1]]
cell_world_x = columns * resolution + origin_x
cell_world_y = rows * resolution + origin_y
distance_squared_meters = cell_world_x**2 + cell_world_y**2

# Half-cell tolerance: a cell counts as inside if any part of it overlaps
# the disc. Avoids floating-point boundary flakiness from radius/resolution.
effective_radius_meters = radius_meters + resolution * 0.5
safe_mask = distance_squared_meters <= effective_radius_meters**2
grid.grid[safe_mask] = 0

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 Safe radius centered on world origin, not on the robot

_apply_initial_safe_radius computes each cell's distance from the world origin (0, 0) rather than from the robot's current position. The occupancy grid's origin is set to (min_x, min_y) of the global-frame point cloud, so cell_world_x = columns * resolution + origin_x gives the cell's position in the world frame. Squaring and summing those values gives distance-squared from (0, 0), not from wherever the robot actually is. Once the robot has moved any meaningful distance from its start point, the safe disc sits at the map origin rather than under the robot. CostMapper would need an odometry input to know the robot's current position and compute the correct offset.

Comment on lines 80 to +87
self.register_disposable(Disposable(self.odom.subscribe(self._planner.handle_odom)))
self.register_disposable(
Disposable(
self.odometry.subscribe(
lambda msg: self._planner.handle_odom(msg.to_pose_stamped())
)
)
)

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.

P2 Dual odometry subscriptions can produce double position updates

Both odom (In[PoseStamped]) and odometry (In[Odometry]) are unconditionally subscribed, and both call self._planner.handle_odom. Any blueprint that wires up both streams will drive handle_odom twice per cycle. Guarding each subscription on the transport being non-None (as is done for stop_movement) or making them mutually exclusive would prevent this.

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.

3 participants