Knowing your own position isn't enough on its own for autonomous movement. You also need to build a map of the surroundings (SLAM), and plan and follow a path to a goal on that map (navigation). newbot uses a hybrid setup that builds a 2D map on top of odometry from a 3D LiDAR, and reaches goals with Nav2.
Image: Robot Operating System logo, Wikimedia Commons (CC BY-SA 4.0)
SLAM: A Hybrid of 3D Odometry and 2D Mapping
The original plan called for mapping with a 2D LiDAR only, but to actually make use of the 3D LiDAR (Livox Mid-360), this was changed to a hybrid setup: 3D LiDAR-Inertial Odometry (FAST-LIO2) as the odometry source, with a global map built in 2D by slam_toolbox. The tidied-up 3D point cloud FAST-LIO outputs is sliced horizontally and converted, through an intermediate node, into the 2D LiDAR scan format (LaserScan) that feeds slam_toolbox. Since 2D SLAM can't handle a raw 3D point cloud directly, this conversion is the crucial link between the two.
The TF tree is map --(slam_toolbox)--> odom --(FAST-LIO)--> base_link --(URDF)--> livox_frame.
Nav2: Autonomous Movement to a Goal
Nav2 is the navigation stack that plans and follows a path to a specified goal on the map while avoiding obstacles. The controller generates a speed command, which passes through velocity smoothing and collision monitoring before reaching the motors (see "How Safe Velocity Control Works" for the details of that path).
While bringing up Nav2, a serious wiring mistake was found hiding in the remap settings of the official launch file — one that effectively disabled the safety mechanism. The navigation controller's raw output (unsmoothed, not collision-checked) was configured to be published under the same topic name as the output after collision monitoring, creating a path where a downstream node could unintentionally receive an unchecked command. This was caught by reading the source directly, and it was a fortunate catch — found before it ever ran on real hardware.
The Road to the First Real Autonomous Run
Beyond the Nav2 configuration, a few environment-specific problems had to be resolved before autonomous driving could be confirmed on real hardware. Field testing turned up momentary communication drops caused by Wi-Fi power-saving settings, which destabilized ROS2's inter-node communication layer (DDS), along with a bug where a goal wasn't properly cleared after navigation completed and lingered around. Resolving these one at a time led to confirmed autonomous driving on real hardware — reaching a specified goal.
A Desk Review That Stabilized Nav2's Startup
A pre-deployment review fixed several bugs hiding in Nav2's own startup process.
- An empty Behavior Tree XML path: the
default_nav_to_pose_bt_xmlparameter was set to an empty string, which could cause startup to fail while trying to load a BT file from an empty path. The parameter itself was removed, leaving it to Nav2's bundled default BT. - A missing node under lifecycle management: in the Nav2 version in use,
navigation_launch.pyalso starts the smoother_server and waypoint_follower under lifecycle management, and without matching configuration, autostart could stall. Standard minimal configuration for both nodes was added to the parameter file to resolve it. - Removing undetermined elements: whether the route_server and docking_server that Nav2's standard launch uses internally would actually start was configuration-dependent and unpredictable, so it was replaced with a custom launch file that explicitly brings up only the 8 nodes actually needed plus the lifecycle_manager, deterministically ruling out that class of startup failure.
2026-08-19: The First Successful Autonomous Run
After all of that accumulated work, "autonomous driving with no deadlock" was achieved for the first time on August 19, 2026. It completed 8 minutes 30 seconds and 14.88 m with zero occurrences of localization runaway, Nav2 deadlock, a lingering goal, or an emergency stop, finishing frontier exploration (a method that successively targets the boundary of unexplored territory) and transitioning into the full-coverage phase.
What actually got it there wasn't a new feature — it was fixing three geometric and orientation errors. Specifically: the LiDAR's mounted orientation was off by 90 degrees; the chassis rear-overhang's measured dimension didn't match the configured value; and the formula for the sweep radius needed for in-place turning only used the front-end dimension, forgetting to account for the (farther) rear end. All three were basic errors in the physical dimensions of the drivetrain — an input error, not an algorithm flaw — and the result strongly suggests most of the earlier deadlocks had been caused by bad inputs, not algorithmic defects. As supporting evidence, an experiment that temporarily disabled the obstacle guard to remove that constraint entirely was also tried, and the success rate didn't improve — it got worse. That result points to something deeper than simple over-caution behind the deadlocks — a more structural design issue.
Why Two of Those Dimension Errors Went Unnoticed for So Long
The rear-overhang and sweep-radius errors weren't simply a case of forgetting to measure — they trace back to the "truth" for these dimensions being scattered across multiple places. The chassis dimensions were written independently in three separate spots: the URDF, Nav2's parameter file, and the obstacle guard's own configuration file. The guard's config even carried a comment saying it was "kept matched to the footprint value" — and because the two provisional values happened to agree with each other, no contradiction ever turned up under review, no matter how closely the numbers were compared. The sweep-radius formula was worse in a subtler way: the formula itself was spelled out right there in a comment, but it only used the front-end dimension, and the (farther) rear-end dimension was simply never plugged in. Having the formula written down is not the same as having it verified.
The response this led to is a shift toward treating the URDF as the single source of truth for dimensions, with derived values — footprint, overhang, sweep radius — computed from it rather than written independently. Tests now cover the derivation itself, checking the sweep radius and the inscribed/circumscribed radii against the footprint as input, aiming to structurally prevent this same class of bug from recurring.
The Physical Constraint of Test Environment Size
It's also become clear that the sheer size of the test environment has a strong effect on evaluating autonomous driving. The test space in use is about 1.9 m², the robot's circumscribed radius is 0.327 m, and the diameter of the circle needed to turn in place works out to 0.654 m. A simple calculation puts the area where in-place turning is even possible at under 30% of the total. Because differential drive can't move sideways, turning in place is effectively the only way to change orientation — meaning that in over 70% of the test environment, the moment the robot tries to reorient, it risks contacting a wall or obstacle. That's a constraint on a completely different axis from driving distance or how clever the exploration algorithm is. On the other hand, the driving distance actually needed (a theoretical value derived from the coverage width) comes out lower than the distance actually driven — so "driven enough distance but still couldn't cover the space" is understood here as a shortage of orientation freedom, not driving volume. That framing puts re-evaluating in a larger environment ahead of algorithm improvements as the higher-priority move.
The Coverage-Exploration Scoring Function and Its Limits
The scoring function that picks the next target from the boundary of unexplored territory (a frontier) uses a simple formula built from the frontier's connected-component size \text{size} and the distance d from the robot.
Size is raised to the sublinear power of 0.6 to prevent the scoring from simply favoring size proportionally, which would keep prioritizing one giant frontier while perpetually deprioritizing smaller-but-nearby unexplored regions. The +1.0 in the denominator is a safeguard against the score blowing up when the robot's current position is extremely close to the frontier (d \to 0). Real-world use has also surfaced a few structural limits in this formula.
- Distance is straight-line, not path distance: because it uses the robot's straight-line distance rather than the actual driving path length, a frontier that's behind a wall and actually requires a long detour can end up overvalued as "close"
- Reachability isn't considered: if the chosen goal turns out to be unreachable, that's only discovered after Nav2's 60-second timeout, and it only lands on the exclusion list after a retry — a single unreachable goal can burn several minutes
- No hysteresis: since the score changes as the map updates mid-drive, the target can flip back and forth (oscillate) over a short span
- Doesn't account for the obstacle guard's clamp state: the exploration node has no visibility into which directions the obstacle guard is clamping, so it can keep picking a direction that's physically blocked (this connects to the design issue covered in "How Safe Velocity Control Works" — the guard and Nav2 having no awareness of each other)
Directions under consideration for improvement: replacing straight-line distance with the planner's actual path cost, cheaply checking reachability before sending a goal, and shortening the timeout while branching the response by failure reason (blocked by a clamp vs. no path exists at all).
Ranking What's Next by Effect Divided by Cost
After this round of measurement, the next investments are now ranked by "effect ÷ cost" — the items higher on the list are judged to have lower implementation cost for greater effect.
- Securing a larger test environment — zero implementation cost for the largest possible effect. Until this happens, no other improvement's effect can even be measured correctly
- Curing the LiDAR power issue at the root (the dropout root cause covered in "How Self-Localization and Sensor Fusion Work") — several dropouts per day contaminates every other test result
- Improving encoder resolution — raises odometry quality and opens the door to actively using wheel odometry through the EKF as a fallback when LiDAR drops
- Deriving geometry from the URDF, guarded by tests — structurally prevents the same class of bug covered in the section above
- Adding near-field sensors — fills the LiDAR's blind spot (obstacles under about 18 cm tall, nearby)
- Re-localization after a LiDAR restart — upgrades auto-recovery from "odometry resumes" to "self-position genuinely recovers"
- Integrating the obstacle guard's information into Nav2 — the structural fix that actually lowers the deadlock rate itself (see "How Safe Velocity Control Works" for details)
The ordering itself backs up the conclusion this round of measurement pointed to. The top two items aren't algorithm improvements at all — they're about securing physical headroom: test environment size and a stable power supply. The one item that touches the algorithm (#7) sits at the bottom of the list; investing in a smarter exploration algorithm or a more sophisticated planner is judged, for now, to be a lower priority.