Traffic Models with CA

Nagel-Schreckenberg

Traffic flow can be modeled with a 1D cellular automaton. Cars occupy cells and move forward each step according to simple rules.

Figure 1: Traffic CA space-time diagram (cars as black pixels).

Model rules

At each step:

  1. Accelerate up to \(v_{\max}\).
  2. Brake to avoid collisions.
  3. Random slowdown with probability \(p\).
  4. Move forward.

Initialize the road

occupied, speeds = initialize_road(length=100, density=0.2, vmax=5)

Update one step (template)

def step(occupied, speeds, vmax=5, p_slow=0.3):
    # TODO: accelerate
    # TODO: brake to avoid collisions
    # TODO: random slowdown
    # TODO: move cars
    return new_occupied, new_speeds
def step(occupied, speeds, vmax=5, p_slow=0.3):
    length = len(occupied)
    positions = np.where(occupied)[0]
    speeds_new = speeds.copy()

    speeds_new[positions] = np.minimum(speeds_new[positions] + 1, vmax)

    gaps = np.zeros_like(positions)
    for idx, pos in enumerate(positions):
        next_pos = positions[(idx + 1) % len(positions)]
        gaps[idx] = (next_pos - pos - 1) % length
    speeds_new[positions] = np.minimum(speeds_new[positions], gaps)

    rng = np.random.default_rng()
    slow_mask = rng.random(len(positions)) < p_slow
    speeds_new[positions[slow_mask]] = np.maximum(
        speeds_new[positions[slow_mask]] - 1, 0
    )

    new_occupied = np.zeros_like(occupied)
    new_speeds = np.zeros_like(speeds)
    new_positions = (positions + speeds_new[positions]) % length
    new_occupied[new_positions] = True
    new_speeds[new_positions] = speeds_new[positions]

    return new_occupied, new_speeds

Space-time diagram

Run the model for many steps and plot the grid.

grid = simulate(steps=200, length=100, density=0.2, vmax=5, p_slow=0.3)
plt.imshow(grid, cmap="binary", interpolation="nearest", aspect="auto")
plt.show()

Full reference: amlab/cellular_automata/traffic.py.