Unitree Go2 LiDAR maze navigation in NVIDIA Isaac Lab

360° LiDAR · hierarchical PPO · frozen velocity policy · occupancy-grid planner

IsaacSim Isaac Lab Python rsl_rl License

Tournament submission. This repository is a submission to the Nepher Unitree Go2 LiDAR maze tournament, built on Nepher Robotics' BSD-3-Clause task-go2-lidar-maze template. The code and weights here, not the upstream repository, reproduce this agent; the changes are listed under Tournament submission: what changed from upstream below.

task-go2-lidar-maze is a Nepher Robotics extension for NVIDIA Isaac Lab / Isaac Sim. It trains a Unitree Go2 to navigate enclosed mazes from a 360° LiDAR scan: a frozen velocity-tracking policy, an LSTM that turns one waypoint and four path points into a velocity command, and an online planner that builds an occupancy grid from LiDAR hits.

Use this repository when you need Isaac Lab Gym environments for Go2 LiDAR maze navigation, a hierarchical frozen locomotion stack (Nepher-Go2-LidarMaze-LL-*, Nepher-Go2-LidarMaze-HL-*, Nepher-Go2-LidarMaze-Planner-*), or the EnvHub benchmark go2-maze-v0.

Query-shaped answers: docs/.

Robot Unitree Go2
Simulator NVIDIA Isaac Sim 6.1.0 + Isaac Lab 3.0
Algorithm rsl_rl PPO. Low-level velocity tracker, high-level LSTM actor-critic
HL stack Occupancy-grid planner → LSTM 10 Hz → frozen velocity policy 50 Hz → physics 200 Hz (Envhub-Play adds a LiDAR wall guard between the LSTM and the velocity policy)
HL observation 199 values: 180 LiDAR ranges, one waypoint, four path points, body velocity, gravity, last action
Command vx [-1.5, 3.55] m/s, vy [-1, 1] m/s, yaw rate [-3, 3] rad/s
Deployment map Empty at reset. LiDAR occupancy grid. The planner does not read the maze graph
Eval EnvHub go2-maze-v0, scored by eval-nav navigation.go2 v1
License BSD 3-Clause

Tournament submission: what changed from upstream

Paths under tools/, tests/ and images/, and dev scripts, are in the development repository and are not part of this package.

The stack is unchanged in shape: LiDAR → planner → LSTM (10 Hz) → frozen velocity policy (50 Hz) → joints, except that in Envhub-Play a rule-based LiDAR command filter, the wall guard below, sits between the LSTM and the velocity policy and can replace the LSTM's command. The evaluation hooks are untouched: hl_maze/eval_compat.py, benchmark.py (L*), the Envhub-Play termination thresholds and timing, maze_goal, the 0.5 m reach radius and the 0.3 m final-waypoint tolerance.

  • Velocity policy. Retrained on rough ground: Nepher-Go2-LidarMaze-LL-Rough-v0 (generated noise, waves, slopes and low boxes with a level curriculum; commands that ramp at 10 Hz like the maze action term; roll/pitch-rate penalties; the benchmark's sustained leg-contact rule as a termination; the critic sees a height scan, the exported actor keeps the 48-value input). -Robust-v0 fine-tunes it with a terrain-relative swing-foot clearance term. Export with scripts/export_ll_policy.py --task Nepher-Go2-LidarMaze-LL-Rough-Play-v0. The shipped best_policy/ll_policy/ll_policy.pt is an equal weight average (tools/ll_soup.py) of four of our own fine-tunes of Nepher-Go2-LidarMaze-LL-Rough-LatticeScratchPlain-v0: the same recipe trained from scratch on generated ground, most of it lattice noise that reproduces the height recipe of the public go2-maze-terrain-v0 bundle with the amplitude scaled by curriculum level (tasks/ll_velocity/mdp/terrains.py). The four were trained for reliability on recorded velocity commands of our own maze runs, for a steadier body (two rounds) and for tighter tracking. Those recordings came from generated bundles, except that the first replay round, an ancestor of all four, also drew on one recording made on the 60 public go2-maze-terrain-v0 mazes (60 of the 120 episodes in its replay pool). Only the commanded velocities are used, never positions or walls, and the ground is generated.
  • Command envelope (velocity_map.py, constants.py): vx [-1.5, 3.55] m/s, vy [-1, 1] m/s, yaw rate [-3, 3] rad/s, planar command capped at 3.55 m/s; rate limits 5 m/s² and 12 rad/s² (a command that the Envhub-Play wall guard below replaces skips them; the next steps ramp from it as usual; in Envhub-Play the forward command may fall by at most 3.5 m/s², see gentler braking below). apply_envelope tapers |vy| from 1 m/s at 1.5 m/s forward to 0.15 m/s at 3.55 m/s and holds |wz| to 6.5 m/s² over planar speed. With STRAIGHT_WZ_LO/HI at 0.8 / 1.2 rad/s as shipped (constants.py; zero turns it off) the envelope runs before the planar cap on near-straight commands, so a sideways request the envelope removes anyway no longer shrinks forward speed; hard turns keep the map the LSTM was trained on, and near-straight commands at speed differ from it.
  • Online planner v2 (nav_core/v2, default in WaypointCommand): uses only the live LiDAR scans, the robot pose, and the start and goal positions. No maze layout, graph or scenario data is read; a lattice and cell pitch are only ever inferred online from those inputs (see wall completion below). Each scan updates a 0.25 m occupancy grid (returns near the predicted ground only clear space, which keeps most terrain out of the wall map); an exact distance field to the goal is traced every step; the LSTM gets a waypoint up to 6 m along the smoothed route (exactly the goal once it is on the seen route) and four points at 1.5/3/4.5/6 m along it. The waypoint timer measures progress: waypoint_timeout fires after 12 s without 0.5 m of distance-to-goal progress. That distance is read on the planner's cost field. In Envhub-Play the route commitment below lowers the field along the committed path, so there 0.5 of progress can take up to about 1.7 m of walking: the timer is stricter, never more lenient, and its thresholds are unchanged. The stock planner stays selectable (planner="stock").
  • Confirmed walls stay mapped. A wall return at least 0.45 m above the predicted ground (in Envhub-Play also above the level ground under the robot, flat_check below) marks its cell as a confirmed wall that never decays (persist_height); lower returns, which may be terrain, still fade unless reinforced. Without it, dead ends seen only briefly faded within 2-3 s and the route swung back into them.
  • Beams without a return (evaluation only, miss_ceiling). A LiDAR beam that returns nothing normally clears the cells along it. Body tilt lifts part of the ring, and a beam just above a wall top then clears cells through the wall. With this setting a beam without a return clears space only below the highest return seen so far in the episode, a lower bound on the wall tops read from the scans themselves. It reads no maze data. It was found by comparing map errors on the public bundle, whose walls top out at 1.00 m, with our generated bundles, whose walls reach 1.15 m.
  • Wall completion (evaluation only, wall_complete). The mazes of this benchmark sit on a square lattice, and a wall fills a whole lattice edge, so a wall seen along part of an edge is a wall along all of it. nav_core/v2/lattice.py infers that lattice online from the planner's own state: spawn and goal are taken as cell centres, a candidate pitch (2.5, 3.0 or 3.5 m) must fit their offset and explain the confirmed wall cells of the LiDAR map, and without one clear candidate the feature stays off and the planner runs without it (the 6 m crop margin of Envhub-Play, class default 2 m, still applies). An edge with confirmed wall evidence then prices its still unobserved cells, so routes stop running through gaps in partly seen walls. The price is finite, so nothing is blocked, the map itself is never written, and a lattice that stops explaining the map is dropped for the episode. No scenario file, tile origin or maze layout is read. This is an assumption about the maze family, like the prior below: its settings were tuned on generated mazes, and it was then checked on held-out generated mazes and on the 60 public go2-maze-terrain-v0 mazes.
  • Lattice-aligned prior (evaluation only, lattice_prior). Once wall completion has locked a lattice, a second small network (nav_core/v2/lprior.py, lattice_prior_weights.pt, about 462k parameters) replaces the plain prior below for that environment. It sees the planner's own map redrawn on the inferred cell grid, centred on the goal: per edge whether it is closed and how much of its gap has been seen free, per cell how much has been seen, whether it was visited, the start and goal cells, the bounding box of known cells and the pitch. It predicts which cells and edges lie on the route to the goal. Because a perfect maze leaves the explored region through exactly one border edge, those predictions set the cost of unknown cells and a fee for crossing each border edge. The robot's own cell is not an input; the region it has proven open and the cells it has visited are. Costs are finite, so nothing is blocked, and without a locked lattice the plain prior runs unchanged. The weights were fitted offline on mazes from the same official_like generator described below, from our own random streams; the 60 public mazes and their seeds were never in its training data, its settings were tuned on generated mazes before it was first evaluated on the public mazes. Later paired runs on the public bundle and two generated bundles raised lp_beta from 10 to 16; lp_beta 24, lp_p_ref 0.15 and 0.3 and lp_edge_fee_m 90 were tried in those runs and not adopted. It therefore also relies on our re-implementation of the benchmark's generator. Envhub-Play evaluates the network twice per step, on the canvas and on its x-y transpose, and averages the two outputs (lp_views 2): the generator's maze statistics are symmetric under that transpose, and the weights are unchanged. The border-edge fee also prices observed cells in the wall band of an edge that is not yet proven open (lp_fee_seen), so a route cannot pass a partly seen gap without paying it. Both settings were chosen in the 2D harness and in paired runs on generated bundles only; the public bundle (seeds 2, 3 and 4) was run only for reporting, and no setting changed after its results.
  • Learned exploration prior (evaluation only, prior_enabled). A small fully-convolutional network (nav_core/v2/prior.py, about 344k parameters) reads only the planner's own state: its LiDAR-built wall-evidence and observed maps pooled to 1 m, the robot's current position, the position where it was reset, and the goal position. For every 1 m cell it predicts how likely that cell is to lie on the remaining route. Never-observed cells that probably belong to a dead-end region cost more in the distance field, and a route that leaves observed space through a frontier the network rates unlikely pays a one-time entry fee (risk_fee_m). Observed cells, walls and every other planner rule are unchanged. At run time it reads no maze layout, graph, lattice, cell pitch or scenario data.
    • How the shipped weights were made. Nepher-Go2-LidarMaze-Planner-Envhub-Play-v0 loads nav_core/v2/prior_weights_ol.pt, fitted offline by supervised learning in the 2D kinematic harness: the equal-weight average of two fine-tunes of one network, one trained with a cross-entropy loss and one that adds a branch-ranking loss. Its training mazes come from the official_like generator in worldgen/maze.py: our re-implementation of the maze generator of the public go2-maze-terrain-v0 bundle, with outer-ring endpoints and a selection rule, both fitted to the statistics of the 60 public mazes. Those 60 mazes themselves, and their seeds, were never in this network's training data: every training maze was drawn from our own random streams, and labels from generated mazes exist only in that offline training. For these weights, the public mazes were used to fit the generator's statistics and as a test set.
    • How the settings were chosen. prior_p_ref, prior_beta, prior_mult_max, risk_fee_m / risk_fee_odds_cap and near_wall_cost_2 (set in tasks/hl_maze/maze_env_cfg.py) were tuned on generated mazes and on paired runs in local copies of the benchmark, which include the public go2-maze-terrain-v0 bundle. Runs on the public bundle, alongside generated bundles, also chose the velocity-policy and LSTM checkpoints, the velocity-policy average, the blend of the two prior fine-tunes, the sideways taper, lp_beta, the straight-line map (STRAIGHT_WZ_LO/HI) and the four wall features below (route commitment, level-ground check, recovery hold and wall guard), so public scores are not an independent test of the final selection. crop_margin_m 6 was chosen on generated mazes; wall completion itself was first noticed on the 60 public mazes in the 2D harness and then confirmed on generated mazes.
    • The class defaults keep the older nav_core/v2/prior_weights.pt (fitted on Wilson, Kruskal, backtracker and Prim mazes) and leave the fee off. The prior is switched on in the Envhub-Play task only; HL training runs without it. If its weights cannot be loaded the planner runs without the prior and the fee, so scripts/package_agent.sh refuses to package such a tree. Tools: tools/prior/ (data generation, training, harness comparison).
  • Gentler braking (evaluation only, max_fwd_decel 3.5 m/s²). Over 344 robot-hours of our maze runs, a falling forward command took about 15 % of the time but about half of the leg-ground contacts that ended episodes; 201 of those 352 were on the front-left calf. In a larger set of runs, two thirds of 502 braking-time failures began within 0.2 s of the start of braking, most with the brake at the 5 m/s² limit and almost all while the LSTM was commanding a turn. In Envhub-Play the forward command may now fall by at most 3.5 m/s², which also limits speeding up in reverse; increases still follow the 5 m/s² limit, as do sideways commands, and yaw stays at 12 rad/s². On maze commands replayed at the benchmark's contact-check cadence, this cut leg contacts by about a third. In paired maze runs, failures fell by about 40 % on the public bundle with completion time unchanged, and by about 25 % on a generated bundle of larger mazes, where completion time rose by about 0.3 s. A 4 m/s² limit cut fewer contacts on replay (about a fifth) but more failures on the larger mazes (about a third) with no measurable time cost there, and 3 m/s² was slower still. This package keeps 3.5 m/s², the strongest setting on replay; on the public bundle the two limits cut failures about equally. The wall guard below runs after this limit and can still brake fully. The action term also has a braking-onset limit (brake_jerk), a separate forward acceleration limit (max_fwd_accel) and a scale for forward commands below top speed (fwd_boost). They stay off in Envhub-Play: in paired maze runs brake_jerk cost about 0.2 s, max_fwd_accel showed no clear gain, and fwd_boost saved at most about 0.15 s but added failures. The failure statistics came from our runs on generated bundles and on the public bundle. The settings were screened on replayed commands recorded on generated bundles, then compared in paired runs over the public bundle and a generated bundle, so public scores are not an independent test of this choice.
  • Route commitment (evaluation only, commit_keep). Two routes of almost equal cost used to swap at every map update. The robot then braked and turned toward the new route and swept a hip into the lattice post between the two; most wall hits we traced happened this way. Cells within one map cell of the path published at the previous step (its first 6 m) now cost c - 0.7 min(c, 1) per metre (commit_keep 0.3, commit_half_cells 1); observed cells that cost more than free space, such as the wall margin, keep their cost. Free observed cells drop from 1.0 to 0.3 and never-observed cells lose at most 0.7 per metre, so a route that the map or the priors rate clearly better still wins. Flip-flops of the 3 m look-ahead point (a jump of more than 0.6 rad that reverses within 1 s) fell from about 2.4 to 0.6 per 30 s. The waypoint timer reads progress on this field (see the planner bullet above).
  • Level-ground wall check (evaluation only, flat_check). A return's height is also measured from the level ground under the robot, not only from the ground predicted by the learned slope, and the lower of the two counts: for the 0.20 m wall threshold and for the strong-evidence and confirmed-wall heights. A beam that points down relative to either needs a return 0.5 m high to count as strong evidence. When the slope estimate was off, far ground returns lifted into the wall band drew false wall arcs that could close a corridor. No tested bundle has more than 0.15 m of ground relief; where the ground falls away from the robot by more than that, this check can drop real wall returns.
  • Cut-off recovery hold (evaluation only, recovery_hold_s 2 s). Once 2 s pass without progress, the cut-off recovery counters are no longer reset when a route reopens for a single step, so further cut-off steps continue the clearing ladder (1.5 m, 3 m, whole map) instead of restarting it. While held, the count of cut-off steps after which wall completion switches off for the episode (3) is cumulative rather than consecutive.
  • Wall guard (evaluation only, wall_guard_on, tasks/hl_maze/mdp/wall_guard.py). Between the LSTM and the velocity policy, every 10 Hz step rolls the body forward 0.8 s from its measured velocity, under the new command for 0.2 s and then a stop, and checks the swept footprint against the returns of the current LiDAR scan in the body frame. The command passes unchanged while such a stop leaves the footprint no deeper in the returns than it is now. Otherwise it is replaced by the closest command, by weighted change, from a small grid of slower, reversing, harder- or softer-turning and sideways (±0.4 m/s) commands that passes the same test, or by the least-violating one if none does. A robot pinned for 0.8 s under a strong command, for example with its head against a wall, backs off at -0.6 m/s for 0.6 s; the back-off itself is not checked. The guard reads the LiDAR returns, the robot's pose and velocity, the command and the number of steps since reset. In our runs it was active on about 0.07 % of control steps.
    • How these four were found and chosen. They came from failure traces of our own runs on generated bundles and on the 60 public go2-maze-terrain-v0 mazes. A few public mazes motivated parts of them: a corridor closed by false wall arcs (the level-ground check and the recovery hold) and robots wedged head-first against a wall (the guard's back-off). The public bundle's wall layout was read offline, in analysis only, to check the map, the route and the contact point near failures. The guard's footprint and hold time came from an offline study of baseline traces from the public bundle and two generated bundles. The commitment strengths 0.5, 0.3 and 0.15 were first tried as a cost factor. The form of the discount (a fixed margin per metre), whether to ship the commitment at all, and the decision to ship all four were made on paired runs pooled over the public bundle and two generated bundles. The public bundle was in every one of those comparisons, in the acceptance gate and in the confirmation seeds, so public scores are not an independent test of these features. No setting is tied to a particular maze, and the shipped code reads no maze data.
  • Maze LSTM. Trained with the online planner in the loop on generated multi-tile terrain worlds, Nepher-Go2-LidarMaze-HL-World-v0 (worldgen/, training only), full start-to-goal episodes with random starts and goals. Same network and observation layout as upstream (199 actor values; the critic's 200th value is the planner's distance-to-goal). Training-only extras in Nepher-Go2-LidarMaze-HL-World-TrueProg-v0: a reward for progress along the true route distance of the generated training maze (so turning into a dead end costs reward at once), and a share of episodes that start and end at the two ends of the maze's longest route (diameter_frac). Neither is read at evaluation. The last three training stages of the shipped checkpoint ran on worlds with 45-60 % of tiles on lattice15/lattice15t, ground that imitates go2-maze-terrain-v0.

Who this is for

  • Researchers training quadruped navigation on the Unitree Go2 in Isaac Lab from a 360° LiDAR scan, not from a privileged maze map.
  • Teams composing a hierarchical frozen policy: an LSTM that outputs a velocity command while a frozen locomotion policy writes the joints.
  • Benchmarks on the EnvHub bundle go2-maze-v0: 60 enclosed mazes, one robot per tile, fixed spawn and goal.
  • The Unitree G1 hurdle stack is task-humanoid-run-jump.

How it runs

Online planner       occupancy grid → one waypoint + path points     deployment
        │
        ▼
LSTM PPO             199-D obs → vx, vy, ωz                        10 Hz
        │
        ▼
Frozen velocity      tracks that command → joint targets           50 Hz
        │
        ▼
PhysX                Unitree Go2                                  200 Hz

In Envhub-Play a LiDAR wall guard between the LSTM and the frozen velocity policy can replace the command on rare steps (see the tournament notes above).

Train bottom-up: velocity policy → export TorchScript → maze LSTM. The upstream HL-v0 task and Nepher-Go2-LidarMaze-HL-Play-v0 replace the online planner with a privileged A* waypoint on the maze graph.

Three pieces:

  • Low-level policy. Velocity tracking on rough and lattice ground; the flat LL-v0 task keeps vx [-2, 3] m/s and yaw rate [-2, 2] rad/s. Exported to TorchScript and then frozen.
  • High-level policy. A velocity command out. The inputs are one waypoint, four points on its path (1.5, 3, 4.5 and 6 m ahead with planner v2; 1 m to 4 m in the privileged and stock modes), and a 360° LiDAR scan. An LSTM actor-critic, because a waypoint up to five cells away can sit behind a corner the current scan cannot see.
  • Planner (v2, default). At deployment the map starts empty. Each LiDAR beam lowers the wall evidence of the 0.25 m cells it crosses (misses clear their ray too, up to 0.9 m above the predicted ground; on uneven ground a rising beam can still pass over a wall and clear cells of it); a return adds wall evidence only if it lies at least 0.20 m above the predicted ground (base height minus stance height, tilted by the terrain gradient learned from the robot's own track), so most beams that dip into the floor or hit a terrain rise leave no lasting wall (weakly supported walls decay; a far ground return that reads as tall stays until free evidence clears it). An exact geodesic distance field to the goal is solved on the GPU (Warp, 8-direction Gauss-Seidel sweeps): cells with a wall-cell centre closer than 0.5 m are blocked (a 2.0 m corridor keeps at least three free lanes), the 0.5-1.0 m band costs more, unobserved cells cost 1.25 per metre (the learned priors above change this in the Envhub-Play task). Every 10 Hz step the route is traced from the robot, string-pulled over observed cells that keep 0.75 m from wall cells, and cut to the observed prefix plus 1.5 m. The waypoint slides 6 m along it (it never has to be reached); the four path points sit 1.5/3/4.5/6 m along it; once the goal is on that route within 6 m the waypoint is exactly the goal. A robot cut off from the goal forgets nearby wall evidence after 0.5 s. The waypoint timer restarts on every 0.5 m of geodesic progress (12 s budget, 30 s inside one 2 m disc). Code: nav_core/v2; the original planner stays available with commands.waypoint.planner="stock". Envhub-Play adds the level-ground wall check, route commitment and the recovery hold described above. Offline closed-loop benchmark: scripts/planner_v2_harness.py (no Isaac Sim).

The upstream HL-v0 task and Nepher-Go2-LidarMaze-HL-Play-v0 take waypoints from a privileged A* on the maze graph. The online planner never reads that graph and never reads the path: line. On every reset, each environment draws a random start cell and a different random goal cell, and draws again when A* finds no path between them. The robot is placed on the start centre plus ±0.4 m, with a yaw in [-π, π). The goal is that cell's centre.

The maze USD is spawned once, at /World/Maze. Every environment shares it (env_spacing = 0, robot-robot collisions filtered). The LiDAR sits on top of the head, in the base frame, and ray-casts the maze, the ground plane at /World/ground/terrain, and that robot's own visual meshes. Robots do not see each other. A beam stops on a wall, the floor, or the body.

Gym environments

Id What it is
Nepher-Go2-LidarMaze-LL-v0 Train the velocity policy
Nepher-Go2-LidarMaze-LL-Play-v0 Play it, at the final command ranges
Nepher-Go2-LidarMaze-HL-v0 Train the maze policy on privileged waypoints
Nepher-Go2-LidarMaze-HL-Play-v0 Play that task with one environment. Loads the latest high-level checkpoint
Nepher-Go2-LidarMaze-Planner-Play-v0 Same policy with the online planner. New reachable start and goal per robot each episode
Nepher-Go2-LidarMaze-Planner-Envhub-Play-v0 Same planner on the go2-maze-v0 benchmark. One robot per maze tile, fixed spawn and goal. Loads best_policy/best_policy.pt

./isaaclab.sh train and play discover these through the isaaclab.tasks entry point. The tasks are registered by this package, not by isaaclab_tasks; the package imports isaaclab_tasks.utils only to wrap the checkpoint resolver (tasks/checkpoint.py), and the scripts use its load_cfg_from_registry. scripts/environments/list_envs.py only prints ids that contain Isaac, so these six do not appear in that table.

Install

Clone task-go2-lidar-maze. Isaac Sim has to run with its bundled Python, so every command goes through ./isaaclab.sh from the Isaac Lab root.

Inside an Isaac Lab tree this package is source/task-go2-lidar-maze/source/go2_lidar_maze. A standalone clone of this repository uses source/go2_lidar_maze instead.

./isaaclab.sh -p -m pip install -e source/task-go2-lidar-maze/source/go2_lidar_maze

TensorBoard logs are under this project, not the Isaac Lab logs directory. From the Isaac Lab root:

./isaaclab.sh -p -m tensorboard.main --logdir source/task-go2-lidar-maze/logs --port 6006

Train

Isaac Lab 3 does not have a --headless flag. --viz none runs without a window. Leave it off for the high-level task and the window can draw every robot: a line for each LiDAR beam, a sphere where that beam hits the maze, the ground, or the robot, a green sphere at that episode's spawn, and a red sphere at its waypoint.

Each of those drawings has its own switch, and each defaults to off. Append any of env.vis_lidar_beams=true, env.vis_lidar_hits=true, and env.vis_start_goal=true to the play command. Nepher-Go2-LidarMaze-Planner-Envhub-Play-v0 turns the start and goal drawing on by itself when a window is open. That drawing is a green sphere on each tile's maze start, a red sphere on that tile's maze goal, and a blue sphere on the current target waypoint. The points sampled along the path are not drawn. --viz none still skips it.

./isaaclab.sh train --task Nepher-Go2-LidarMaze-LL-v0 --viz none
./isaaclab.sh -p source/task-go2-lidar-maze/scripts/export_ll_policy.py --checkpoint PATH/model.pt
./isaaclab.sh train --task Nepher-Go2-LidarMaze-HL-v0 --viz none
./isaaclab.sh play --task Nepher-Go2-LidarMaze-HL-Play-v0 --viz none
./isaaclab.sh play --task Nepher-Go2-LidarMaze-Planner-Play-v0 --viz none
./isaaclab.sh play --task Nepher-Go2-LidarMaze-Planner-Envhub-Play-v0 --video --num_envs 60

Query pages

Each file answers one search. Prefer these over the whole README when the question is specific.

Query Page
Isaac Lab Unitree Go2 LiDAR maze navigation docs/isaac-lab-unitree-go2-lidar-maze.md
Frozen Go2 velocity tracking docs/isaac-lab-go2-velocity-tracking.md
Hierarchical frozen policy stack docs/hierarchical-frozen-go2-policy.md
LiDAR occupancy grid, no prior maze map docs/go2-lidar-occupancy-grid.md
Gym IDs Nepher-Go2-LidarMaze-* docs/nepher-go2-lidar-maze-gym-ids.md
EnvHub go2-maze-v0 docs/envhub-go2-maze-v0.md
Go2 LiDAR maze vs G1 hurdle jump docs/go2-lidar-maze-vs-g1-hurdle.md

Index: docs/README.md.

FAQ

What robot and simulator does this target? Unitree Go2 in NVIDIA Isaac Sim 6.1.0 with Isaac Lab 3.0 (Python 3.12, rsl_rl PPO). The physics step is 0.005 s (200 Hz). The frozen velocity policy runs at 50 Hz. The LSTM runs at 10 Hz.

What does the high-level policy output? A velocity command in vx [-1.5, 3.55] m/s, vy [-1, 1] m/s, yaw rate [-3, 3] rad/s. Zero means standing still. A frozen locomotion policy tracks that command and writes the joint targets. In Envhub-Play a LiDAR wall guard can replace the command on rare steps.

Does deployment need the maze map? The occupancy grid starts empty. LiDAR hits fill it, and a distance field on that grid chooses the waypoint. The upstream HL-v0 task and Nepher-Go2-LidarMaze-HL-Play-v0 use a privileged A* path on the maze graph. The online planner never reads that graph.

Which Gym ID should I start with? Velocity tracking: Nepher-Go2-LidarMaze-LL-v0, then scripts/export_ll_policy.py. Maze policy: Nepher-Go2-LidarMaze-HL-v0. Online planner: Nepher-Go2-LidarMaze-Planner-Play-v0. Fixed benchmark: Nepher-Go2-LidarMaze-Planner-Envhub-Play-v0 on go2-maze-v0.

How is go2-maze-v0 scored? eval-nav scores Nepher-Go2-LidarMaze-Planner-Envhub-Play-v0 with navigation.go2 v1. The bundle is 60 enclosed mazes, one robot on each tile, spawn and goal taken from that maze.

Where do the policies load from? export_ll_policy.py writes best_policy/ll_policy/ll_policy.pt. The high-level task raises FileNotFoundError until that file exists. Nepher-Go2-LidarMaze-HL-Play-v0 and Nepher-Go2-LidarMaze-Planner-Play-v0 load the latest model_*.pt under logs/go2_lidar_maze_hl unless you pass --checkpoint. Nepher-Go2-LidarMaze-Planner-Envhub-Play-v0 loads best_policy/best_policy.pt.

License

Copyright (c) 2026, Nepher Robotics. All rights reserved. Distributed under the BSD 3-Clause License.

Keywords: Unitree Go2, quadruped, LiDAR, maze navigation, occupancy grid, reinforcement learning, PPO, LSTM, hierarchical policy, frozen policy, NVIDIA Isaac Lab, Isaac Sim, rsl_rl, EnvHub, go2-maze-v0, Nepher.

Downloads last month

-

Downloads are not tracked for this model. How to track
Video Preview
loading