AnyLearn
All lessons
Roboticsadvanced

Graph Search: Dijkstra and A*

Discretise C-space into a grid, then search it intelligently. Understand Dijkstra's optimality guarantee, how A* accelerates it with admissible heuristics ($f=g+h$), why consistency matters, and where greedy search goes wrong.

Not signed in β€” your progress and quiz score won't be saved.
Lesson progress1 / 8

From C-space to graph

The C-space C\mathcal{C} is continuous; computers need discrete graphs. The standard move is to overlay a regular grid: slice each DOF axis into cells of width Ο΅\epsilon, giving (1/Ο΅)d(1/\epsilon)^d nodes. Each node nn connects to its lattice neighbours (4-connected or 8-connected in 2-D; 2d2d or 3dβˆ’13^d - 1 in dd-D) with edge weight equal to the Euclidean distance between centres.

Before adding a node or edge to the graph, run the collision checker. Only nodes in Cfree\mathcal{C}_{\text{free}} are kept. The result is a finite, weighted, undirected graph G=(V,E,w)G = (V, E, w). Finding the shortest collision-free path from qsq_s to qgq_g is now exactly the single-source shortest path problem β€” and Dijkstra's algorithm solves it optimally. The catch: ∣V∣|V| is exponential in dd, so grid search only works up to about 4 DOF in practice.

Full lesson text

All 8 steps on one page β€” for reading, reference, and search.

Show

1. From C-space to graph

The C-space C\mathcal{C} is continuous; computers need discrete graphs. The standard move is to overlay a regular grid: slice each DOF axis into cells of width Ο΅\epsilon, giving (1/Ο΅)d(1/\epsilon)^d nodes. Each node nn connects to its lattice neighbours (4-connected or 8-connected in 2-D; 2d2d or 3dβˆ’13^d - 1 in dd-D) with edge weight equal to the Euclidean distance between centres.

Before adding a node or edge to the graph, run the collision checker. Only nodes in Cfree\mathcal{C}_{\text{free}} are kept. The result is a finite, weighted, undirected graph G=(V,E,w)G = (V, E, w). Finding the shortest collision-free path from qsq_s to qgq_g is now exactly the single-source shortest path problem β€” and Dijkstra's algorithm solves it optimally. The catch: ∣V∣|V| is exponential in dd, so grid search only works up to about 4 DOF in practice.

2. Dijkstra's algorithm

Dijkstra maintains a priority queue (min-heap) of (cost, node) pairs and a distance map g[n]g[n] β€” the best known cost to reach nn from the start. It expands the cheapest unvisited node and relaxes its neighbours:

g[m]←min⁑(g[m],β€…β€Šg[n]+w(n,m))g[m] \leftarrow \min\bigl(g[m],\; g[n] + w(n, m)\bigr)

Guarantees: Dijkstra is complete (finds a path if one exists in the finite graph) and optimal (the path has minimum total weight). Optimality relies on non-negative edge weights β€” satisfied for Euclidean grids. Complexity is O((∣V∣+∣E∣)log⁑∣V∣)O((|V| + |E|) \log |V|) with a binary heap.

Problem for robotics: Dijkstra explores in expanding cost shells β€” roughly circular wavefronts around the start. It has no notion of direction toward the goal, so it wastes time exploring nodes that are far from qgq_g. In a 1000x1000 grid, Dijkstra may visit nearly every cell before reaching the goal in a corner. A* fixes this with a heuristic.

3. A* and the evaluation function

A* replaces Dijkstra's queue key g(n)g(n) with:

f(n)=g(n)+h(n)f(n) = g(n) + h(n)

where g(n)g(n) is the exact cost from start to nn, and h(n)h(n) is a heuristic estimate of the remaining cost to the goal. The node with the lowest ff is expanded next.

Admissibility is the key condition: h(n)≀hβˆ—(n)h(n) \leq h^*(n) for all nn, where hβˆ—(n)h^*(n) is the true optimal remaining cost. An admissible heuristic never overestimates.

Consistency (monotonicity) is a stronger condition: h(n)≀w(n,m)+h(m)h(n) \leq w(n,m) + h(m) for every edge (n,m)(n,m). Consistency implies admissibility and ensures each node is expanded at most once β€” the standard assumption for A* on graphs.

Theorem: A* with a consistent heuristic is complete and optimal among algorithms that use the same heuristic. It expands no node with f(n)<fβˆ—f(n) < f^* (the optimal cost) and exactly the nodes needed along the optimal path.

4. Heuristic design and admissibility

The heuristic hh must be fast to compute and tight (close to hβˆ—h^*) to be useful. Common choices for grid/C-space planning:

HeuristicFormulaAdmissible?Notes
Euclidean distanceβˆ₯nβˆ’gβˆ₯2\|n - g\|_2YesTight for obstacle-free spaces
Manhattan distanceβˆ₯nβˆ’gβˆ₯1\|n - g\|_1Yes (4-conn.)Exact for 4-connected, no diagonals
Chebyshev distanceβˆ₯nβˆ’gβˆ₯∞\|n - g\|_\inftyYes (8-conn.)Exact for 8-connected
Weighted Euclideanwβ‹…βˆ₯nβˆ’gβˆ₯2w \cdot \|n - g\|_2, w>1w > 1No (inadmissible)Faster but suboptimal β€” used in WA*
Zero heuristich=0h = 0YesReduces A* to Dijkstra

For a robot arm in joint space, Euclidean joint-space distance is admissible only if the maximum Cartesian speed per unit joint motion is bounded. If your joint weights are wrong, your heuristic may be inadmissible β€” and optimality silently breaks. Always verify admissibility after changing robot geometry or units.

5. Greedy best-first: a cautionary contrast

Greedy best-first search (GBFS) sorts the queue by h(n)h(n) only β€” ignoring g(n)g(n) entirely. It drives hard toward the goal heuristically and is often extremely fast. But it provides no optimality guarantee and is incomplete on graphs with cycles or dead ends.

Consider a narrow corridor: GBFS will rush down the corridor, hit a dead end, and get stuck β€” it has no memory of accumulated cost to guide backtracking intelligently. A* would have balanced gg (the cost of walking down the corridor) against hh (the hope at the end) and avoided the trap.

The A* spectrum:

  • h=0h = 0: Dijkstra (optimal, slow).
  • h=hβˆ—h = h^*: perfectly informed A* (optimal, maximally fast).
  • h>hβˆ—h > h^* inadmissible: Weighted A* / GBFS (fast, suboptimal).

For real robots, Weighted A* with w∈[1.1,3]w \in [1.1, 3] is a practical middle ground β€” you sacrifice a bounded factor of ww in path quality for significant speed gains.

6. A* step in Python

A clean A* implementation on a 2-D occupancy grid illustrates every component:

import heapq
import math

def astar(grid, start, goal):
    """A* on a 2D grid. grid[r][c] = True means obstacle.
       Returns list of (r,c) or None."""
    rows, cols = len(grid), len(grid[0])
    def h(a, b):
        return math.hypot(a[0]-b[0], a[1]-b[1])  # Euclidean, admissible

    g = {start: 0.0}
    came_from = {start: None}
    heap = [(h(start, goal), start)]  # (f, node)

    while heap:
        _, n = heapq.heappop(heap)
        if n == goal:
            path = []
            while n is not None:
                path.append(n); n = came_from[n]
            return path[::-1]
        r, c = n
        for dr, dc in [(-1,0),(1,0),(0,-1),(0,1),
                       (-1,-1),(-1,1),(1,-1),(1,1)]:
            m = (r+dr, c+dc)
            if not (0<=m[0]<rows and 0<=m[1]<cols): continue
            if grid[m[0]][m[1]]: continue          # collision
            w = math.hypot(dr, dc)                 # 1.0 or sqrt(2)
            ng = g[n] + w
            if ng < g.get(m, float('inf')):
                g[m] = ng
                came_from[m] = n
                heapq.heappush(heap, (ng + h(m, goal), m))
    return None  # no path

Key details: diagonal moves cost 2\sqrt{2}, keeping Euclidean distance admissible. The heap may contain stale entries β€” the ng < g.get(m, inf) guard handles this without a separate closed set.

7. Grid resolution tradeoffs

Choosing Ο΅\epsilon (cell size) is an engineering decision with hard tradeoffs:

Fine grid (Ο΅\epsilon small): more cells explored, higher memory, slower per query, but paths hug obstacles more tightly and kinematic feasibility is easier to post-process.

Coarse grid (Ο΅\epsilon large): fast, low memory, but may miss narrow passages entirely. A gap narrower than Ο΅\epsilon is invisible to the planner β€” this is resolution incompleteness. The planner declares no path exists when one does.

For 2-D mobile robots, Ο΅=5–10 cm\epsilon = 5\text{–}10\,\text{cm} is standard. For 3-D manipulation, grids above 4 DOF at any useful resolution exceed memory limits. At this point:

  1. Hierarchical planning β€” coarse grid for global structure, local sampling for details.
  2. Anytime algorithms (ARA*, AD*) β€” return a suboptimal path immediately, improve over time.
  3. Switch to sampling-based β€” PRM or RRT sidestep the grid entirely.

Grid search is not obsolete: for 2-D navigation it remains the most reliable approach, powering the global costmap in ROS navigation stacks worldwide.

8. When A* is not enough

A* is the gold standard for grid search, but several robotics scenarios break its assumptions:

  1. Dynamic obstacles. The graph changes during planning. D* Lite (Koenig & Likhachev, 2002) replans incrementally, reusing prior search effort β€” the algorithm behind the Mars rovers.
  2. Kinodynamic constraints. Edge costs depend on velocity state, not just position. A* on a position grid ignores dynamics; lattice planners add state (q,qΛ™)(q, \dot{q}) to the node, exploding the graph size.
  3. High DOF. Above 4–5 DOF, ∣V∣|V| is astronomical. Sampling-based methods (next lesson) take over.
  4. Non-holonomic robots. A car cannot move sideways; valid edges in the graph must respect turning radius. Dubins/Reeds-Shepp paths replace straight-line edges.

The lesson: A* is complete, optimal, and elegant β€” and also the wrong tool for about half of practical motion planning problems in robotics. Know its limits before reaching for it.

Check your understanding

The lesson ends with a 5-question quiz. Take it in the player above to see your score.

  1. A* is guaranteed to find the optimal path when:
    • The heuristic $h(n)$ is consistent (monotone) and edge weights are non-negative.
    • The heuristic $h(n)$ overestimates the true cost to the goal.
    • The search space is a tree with no cycles.
    • The start and goal are connected by a straight line in C-space.
  2. Greedy best-first search is faster than A* in practice because it:
    • Uses a better heuristic that is always admissible.
    • Only considers $h(n)$ and ignores accumulated cost $g(n)$, but sacrifices completeness and optimality.
    • Expands nodes in reverse from goal to start, halving the search space.
    • Applies Dijkstra locally and A* globally.
  3. Which heuristic makes A* equivalent to Dijkstra's algorithm?
    • $h(n) = h^*(n)$, the true remaining cost.
    • $h(n) = 0$ for all $n$.
    • $h(n) = g(n)$, the cost from the start.
    • $h(n) = 2 \cdot \|n - g\|_2$.
  4. A motion planner uses Weighted A* with weight $w=2.5$. Which statement is correct?
    • The returned path is optimal because A* is always optimal.
    • The returned path has cost at most $2.5 \times$ the optimal cost, but the search terminates faster.
    • The planner is incomplete; it may fail to find a path even if one exists.
    • The heuristic is admissible because $w > 1$ inflates $h$ safely.
  5. A grid planner at resolution $\epsilon = 10\,\text{cm}$ fails to find a path through a $7\,\text{cm}$ doorway. This failure is called:
    • Probabilistic incompleteness, inherent to all grid methods.
    • Resolution incompleteness: the doorway is narrower than $\epsilon$ and invisible to the planner.
    • Inadmissibility: the heuristic overestimates cost near narrow passages.
    • A false negative in the collision checker.

Related lessons