Katkat

Multi-Agent Collision Simulator (Unicycle Kinematics)

Applied IntuitionSoftware Engineer · Technical Screen

Last updated

Implement a discrete-time multi-agent simulator using unicycle kinematics and forward Euler integration to track agent trajectories and detect inter-agent collisions.

Problem Statement

In autonomous driving simulation, tracking dynamic obstacles and verifying that trajectories remain collision-free are fundamental tasks. You are tasked with implementing a discrete-time 2D multi-agent simulation engine using the standard unicycle kinematic model.

Each agent $i$ is modeled as a circular disc with radius radius_i and state (x, y, theta):

  • x, y: 2D Cartesian position in meters.
  • theta: Heading angle in radians measured counter-clockwise from the positive x-axis.

At each time step k (from k = 0 to k = steps - 1), agent i receives control input (v, omega):

  • v: Forward linear velocity in m/s.
  • omega: Angular velocity in rad/s.

Kinematics & Discrete Update

Using Forward Euler integration with step size dt, an agent's state at step k + 1 is updated from step k via:

x[k+1]     = x[k] + v[k] * cos(theta[k]) * dt
y[k+1]     = y[k] + v[k] * sin(theta[k]) * dt
theta[k+1] = theta[k] + omega[k] * dt

Collision Detection

A collision occurs between agent i and agent j (i != j) at any discrete time step if the Euclidean distance between their centers is less than or equal to the sum of their radii:

sqrt((x_i - x_j)^2 + (y_i - y_j)^2) <= radius_i + radius_j

Check for collisions at the initial state (step = 0), and after every update step step = 1, ..., steps.

Implement the function find_first_collision that simulates the system and returns the earliest time step and the pairs of agents that collide. If multiple distinct pairs collide at that same earliest time step, return all such pairs sorted in ascending order by their IDs.

Function Signature

def find_first_collision(
    agents: list[dict],
    controls: list[list[tuple[float, float]]],
    dt: float,
    steps: int
) -> tuple[int, list[tuple[int, int]]] | None:
    """
    Simulates multi-agent unicycle trajectories and returns the first collision.
    
    agents: list of dicts with keys 'id' (int), 'x' (float), 'y' (float), 
            'theta' (float), 'radius' (float).
    controls: controls[i][k] contains (v, omega) for agents[i] at step k.
    dt: simulation time delta in seconds.
    steps: number of discrete time steps to simulate (controls has length == steps).
    
    Returns:
      (first_step, [(id_a, id_b), ...]) where id_a < id_b, sorted lexicographically.
      Returns None if no collision occurs across steps 0 through steps.
    """
    pass

Examples

Example 1

agents = [
    {"id": 0, "x": 0.0, "y": 0.0, "theta": 0.0, "radius": 1.0},
    {"id": 1, "x": 5.0, "y": 0.0, "theta": 3.141592653589793, "radius": 1.0}
]
# Agent 0 moves right at 2 m/s; Agent 1 moves left at 2 m/s. dt = 1.0, steps = 2
controls = [
    [(2.0, 0.0), (2.0, 0.0)],
    [(2.0, 0.0), (2.0, 0.0)]
]
dt = 1.0
steps = 2

# At step 0: dist = 5.0 > 2.0 (no collision)
# At step 1: Agent 0 is at (2, 0), Agent 1 is at (3, 0). dist = 1.0 <= 2.0 (collision)
# Output: (1, [(0, 1)])

Example 2

agents = [
    {"id": 10, "x": 0.0, "y": 0.0, "theta": 0.0, "radius": 0.5},
    {"id": 20, "x": 10.0, "y": 10.0, "theta": 1.57, "radius": 0.5}
]
controls = [
    [(1.0, 0.0)],
    [(1.0, 0.0)]
]
dt = 0.5
steps = 1
# Output: None

Constraints

  • 2 0.0
  • Radii are strictly positive (radius > 0).
  • Coordinates and velocities are finite floats.

Hints

Hint 1

Check for collisions at step 0 before applying any control updates, as agents may already overlap initially.

Hint 2

Avoid floating-point square root operations in your inner collision check by comparing squared distances: dx^2 + dy^2 <= (r1 + r2)^2.

Hint 3

Make sure each agent's state update at step k+1 strictly depends on its state at step k and control input at step k.

Follow-up questions

  • Forward Euler can cause numerical drift over long horizons. How would you modify the update step to use a 2nd or 4th-order Runge-Kutta (RK4) integrator?
  • Discrete-time collision checks can suffer from 'tunneling' (two fast agents passing through each other between steps). How would you implement continuous collision detection (CCD) for circular agents moving linearly during dt?
  • If the number of agents grows to 10,000, checking every pair takes O(N^2) per step. What spatial data structures would you use to improve query performance?