MediumReal-Time Traffic Conflict Estimation on an Edge Device | by Muhammad Karim, Ph.D. | Medium
Sitemap
Muhammad Karim, Ph.D.

Muhammad Karim, Ph.D.

Computer Vision | Deep Learning | LLM | AI enthusiast | Music Lover

Real-Time Traffic Conflict Estimation on an Edge Device

6 min readSep 1, 2026

--

Measure near-misses, not crashes — tracked trajectories to TTC and PET, in plain numpy.

Press enter or click to view image in full size
Image generated by AI

Every traffic pipeline I have shipped — including the YOLOv8 + TensorRT + ByteTrack one on a Jetson — hits the same wall once the tracker works and the camera is calibrated: now what? Speeds are nice, counts are nice, but the question a transportation engineer actually gets funded to answer is “did this treatment make the site safer?” Crash counts need years to move. Real-time traffic conflict estimation is the practical answer: measure near-misses instead of crashes, continuously, on the edge device itself. This post closes the trilogy — tracked trajectories, calibrated to meters, and now turned into safety events.

Why conflicts instead of crashes

A crash at a single intersection is a rare event; waiting for enough of them to compare a before and after period is a multi-year project. The traffic conflict technique predates deep learning by decades: Perkins and Harris generalized the idea at General Motors in 1967, Hayward formalized time-to-collision in his 1972 Penn State report, and Hydén’s Swedish conflicts technique turned observer ratings into a field method in 1987. What changed since then is not the math — it’s that a small, cheap box can now run the whole observation automatically.

The modern reference for computing these measures is Gettman and Head’s Surrogate Safety Assessment Methodology (FHWA-RD-03–050; journal version in TRR 1840). It defines the surrogate measures and their algorithms for conflict points (crossing flows) and conflict lines (merging and following flows) — the taxonomy any serious implementation should follow.

The two measures that carry most of the weight

Time-to-collision (TTC). Hayward’s definition: the time required for two vehicles to collide if they continue at their present speed and on the same path. For a tracked pair you compute it at every time step and keep the minimum over the interaction. Thresholds are a research choice, not physics: the SSAM algorithms use 1.5 s as their worked example of an upper limit for a valid conflict at intersection approaches — from van der Horst’s dissertation work — while higher critical values (2.6–3.5 s) appear in high-speed car-following.

Post-encroachment time (PET). The time gap at the crossing point between one road user leaving the area and the other arriving. PET needs only where the paths cross and when each user passed it — no velocities, which makes it the more robust choice when detection noise is unavoidable. Its weakness: it assumes you can identify the conflict point, which is exactly where two vehicles occlude each other in the image.

The code

Prerequisites from the previous two posts: per-road-user tracks (id, pixel positions, timestamps) from the tracker, and a homography mapping undistorted pixels to ground meters. Everything below is plain numpy.

Get Muhammad Karim, Ph.D.’s stories in your inbox

Join Medium for free to get updates from this writer.

TTC, in closest-approach form:

import numpy as np

def min_ttc(a, ta, b, tb, extent=1.0):
"""Minimum time-to-collision between two ground-plane tracks.

a, b: (N,2) tracks in meters; ta, tb: strictly increasing seconds.
extent: center-to-contact distance that counts as a collision (m).
"""
bx = np.stack([np.interp(ta, tb, b[:, 0]),
np.interp(ta, tb, b[:, 1])], axis=1)
dt = np.diff(ta)
va = np.diff(a, axis=0) / dt[:, None] # velocity samples
vb = np.diff(bx, axis=0) / dt[:, None]
r = a[:-1] - bx[:-1] # relative position
v = va - vb # relative velocity
vv = np.einsum('ij,ij->i', v, v)
t = np.where(vv > 0, -np.einsum('ij,ij->i', r, v) / vv, np.inf)
ok = (t > 0) & (t < 10) # plausible horizon
dmin = np.linalg.norm(r + v * t[:, None], axis=1)
hit = ok & (dmin <= extent) # on a collision course
return float(np.min(t[hit])) if hit.any() else np.inf

PET, in path-crossing form:

def _time_at(track, ts, x):
"""Timestamp at which a track passed closest to ground point x."""
d = np.linalg.norm(track - x, axis=1)
k = int(np.argmin(d))
if k == 0 or k == len(ts) - 1:
return float(ts[k])
lo, hi = k - 1, k + 1
seg = track[hi] - track[lo]
t = np.clip((x - track[lo]) @ seg / (seg @ seg), 0.0, 1.0)
return float(ts[lo] + t * (ts[hi] - ts[lo]))

def pet(a, ta, b, tb):
"""Post-encroachment time at the crossing of two paths (seconds)."""
D = np.linalg.norm(a[:, None, :] - b[None, :, :], axis=2)
i, j = np.unravel_index(np.argmin(D), D.shape)
x = 0.5 * (a[i] + b[j]) # conflict point (m)
return _time_at(b, tb, x) - _time_at(a, ta, x)

For crossing conflicts, report both; they disagree in informative ways.

What real-time conflict estimation costs on the edge

Here is the good news: everything above is microseconds of numpy. The compute budget of this pipeline was spent before the safety stage — on the detector. That is why the TensorRT engine from the tracking post is the right base: export once, run batch-1 inference on the Jetson, and treat conflict estimation as a callback on track updates. Two practical notes:

  • Gate candidate pairs by spatial proximity (a few meters) before running TTC/PET. At typical intersection densities the full pairwise matrix is already trivial, but gating keeps it trivial at peak.
  • Run the safety stage on every track update, not on saved video. The output worth storing is the event log — timestamp, pair, measure values, conflict point — not the video itself. A day of events is kilobytes; a day of video is a privacy liability.

Where it goes wrong

  • ID switches. A tracker that merges two vehicles into one identity erases conflicts silently. ByteTrack is sturdy, not perfect: audit switches before trusting a low count, and prefer PET (path-based) when tracks are short.
  • Centroid vs. contact point. Vehicles are 4.5 m long. Map the bottom-center of the detection box to the ground plane, not the box center, and set extent from real vehicle dimensions — not 1.0 by habit.
  • Far-field amplification. The homography error that was tolerable for speeds multiplies in TTC, which divides by closing speed. Measure conflicts inside your ground-control region, full stop.
  • Occlusion at the worst moment. Two vehicles about to conflict overlap in the image exactly when you need them most. Predict through short gaps with a constant-velocity model, and flag events whose tracks were patched.
  • Threshold-only reporting. “14 conflicts below 1.5 s” hides more than it shows. Report the distribution of TTC/PET by hour and location — the shape moves before the count does.

Roadside studies produce paperwork too — crash reports, survey sheets, screenshots, and the documents attached to a paper. That’s personal data as well. If a file needs to leave the device for a report or a paper, redact it first: PrivateRedact strips identifiers from PDFs, scans, DOCX, and images 100% offline — local LLM, no upload.

FAQ

Which measure should I compute first, TTC or PET?

PET for crossing movements — it needs no velocities and survives noisy detections. TTC for following and rear-end interactions, where there is no crisp crossing point. Report both when you can.

Does conflict estimation need special camera placement?

The calibration post’s requirements still apply, plus one: the conflict zone itself must be visible. A camera that sees long approach legs but not the crossing point produces speeds, not conflicts.

How much video do I need for stable estimates?

More than you think — serious conflicts are rarer than vehicles. The method matters as much as the volume: compare like with like (same weekday mix, same hours), and treat the event count as a trend line, not a one-shot verdict.

Can I run this CPU-only?

The safety stage, trivially. The detector is the cost — and that is a TensorRT/Jetson question, covered in the tracking post.

This closes the series this post was promised in. The pieces it builds on: detection and tracking on a Jetson and roadside camera calibration; the wider research direction is in LLMs in traffic safety.

I’m Muhammad Karim — transportation researcher; I build privacy-first, local-first tools: PrivateRedact, Self-Hosted RAG, MeetingMint, AudioRecord.

Originally published at mkarim.us, with the PET code, the Jetson deployment notes, and the calibration/tracking posts this builds on.

--

Muhammad Karim, Ph.D.
Muhammad Karim, Ph.D.

Written by Muhammad Karim, Ph.D.

Computer Vision | Deep Learning | LLM | AI enthusiast | Music Lover