VFH+ Reactive Avoidance
The Nav2 planner → controller pipeline is powerful but heavyweight, and it depends on a global map. Sometimes all that is needed is a fast, lightweight reaction to the obstacle directly in front of the robot — when Nav2 is not running, or when the environment is filled with dynamic obstacles. That is where VFH+ (Vector Field Histogram) fits.
Three Stages of Data Reduction
The core insight of VFH is to progressively simplify a complex environment until only one question remains: which direction to steer.
Rendering diagram…
- Occupancy Grid — the obstacle grid around the robot (same concept as the occupancy grid covered earlier).
- Polar Histogram — the grid is converted into a bar chart around the robot's heading. Each angular sector shows how densely packed obstacles are in that direction.
- Steering Valley — the algorithm finds the sector with the lowest bar (least dense = most open) and picks the valley closest to the goal direction.
Visualized as a polar histogram:
Direction: ← ↖ ↑ ↗ →
Density: ███ █ · ·· ████
↑
valley here (open) → steer this way
In code, the data reduction is just picking the emptiest valley in a histogram.
# grid -> per-direction obstacle density (polar histogram) -> emptiest valley
def steer_vfh(grid, goal_dir, threshold):
hist = [density(grid, s) for s in range(0, 360, 30)] # 12 directions (30 deg)
valleys = [s for s, d in enumerate(hist) if d < threshold]
return min(valleys, key=lambda s: abs(s*30 - goal_dir)) # valley nearest the goal
Why the "+" — Robot Width Compensation
The original VFH treated the robot as a point. VFH+ compensates for the robot's physical width. As an obstacle gets closer, the angular sector it blocks is widened accordingly.
💡 Angular enlargement formula — the blocked angle is expanded by
enlargement_angle = arcsin((r_robot + r_safety) / d_obstacle). This is the same concept as inflation bloating obstacles in costmap space, applied here in the angular domain. Closer obstacles (smaller d) block a wider arc, providing a larger safety margin.
⚠️ Sector resolution limits — with only 12 sectors (30° each), a narrow passage can be misread as "blocked." Increasing angular resolution or relying on VFH+'s compensation is key to threading tight corridors.
Where It Fits — Fallback Driving
VFH+ runs without a global planner, making it well-suited as a fallback driver when the full Nav2 stack is not active. A practical combination: a patrol controller provides the high-level waypoints while VFH+ handles instant obstacle avoidance. This gives you a choice between "heavy global planning" and "light reactive avoidance" depending on the situation.
▶ Simulation footage — a run through a warehouse aisle with Nav2 switched off and VFH+ alone doing the driving. A forklift and pallet jacks are parked on alternating sides, and the robot threads between them. The speed readout falling from 1.06 to 0.16 m/s and climbing back is the whole of the avoidance: pick a direction, slow down as things get closer. This run's VFH+ uses 72 sectors (5 degrees each) — six times finer than the 12-sector example above — and widens each blocked angle using a 0.49 m robot radius plus a 0.49 m safety margin. Result: zero collisions, 0.82 m minimum clearance, 33.5 m completed. Nav2 never came up.
In a nutshell
📌 VFH+ reduces data via occupancy grid → angular polar histogram → lowest-density steering valley to directly select a heading. It compensates for robot width through angular enlargement and is well-suited to fallback driving when Nav2 is unavailable.
References
See standard test results with videos and verdict evidence on the imSim Portal.
View public results →