Ashiq's Portfolio
Toggle sidebar
Designing an Adaptive Signal S...
Article
5 min read

Designing an Adaptive Signal Switching Algorithm

Algorithms Python Multithreading Computer Vision
Designing an Adaptive Signal Switching Algorithm

Detection tells you what's waiting at the junction โ€” part two covered that. This post is about the brain of the system from part one: the signal switching algorithm that converts vehicle counts into a green-light duration, keeps four signals rotating without a stumble, and does all its thinking inside a five-second window nobody notices.

What the algorithm must balance

A traffic signal has exactly two jobs: move as many users through the intersection as possible, with as little conflict as possible. Translating that into an algorithm meant accounting for a surprisingly long list of realities:

  • The number of lanes feeding the signal.
  • The count of each vehicle class โ€” cars, bikes, buses/trucks, rickshaws โ€” because each clears the junction at a different pace.
  • Start-up lag: nobody moves the instant the light turns green, and the lag grows non-linearly down the queue โ€” the tenth vehicle loses more time than the second.
  • The average crossing time per class, measured from start-up speeds and acceleration.
  • The algorithm's own processing time, which dictates when the camera snapshot must be taken.
  • A minimum and maximum green time, so no direction is starved and no direction hogs the junction.

The formula

All of that condenses into one line โ€” the Green Signal Time:

        ฮฃ over classes ( count[class] ร— avg_crossing_time[class] )
GST = โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
                        (number_of_lanes + 1)

Read it aloud and it's common sense: the time this direction needs is the total crossing time of everything waiting, spread across the lanes that can discharge in parallel. Two buses and thirty bikes produce a very different GST than thirty-two cars โ€” which is exactly why the detector classifies instead of just counting.

The computed value is then clamped. In one run from the report: the formula returned 9 seconds, below the 10-second minimum, so the signal got 10 โ€” enough for a human to react and a queue to actually move. Another cycle computed 25 seconds, inside the bounds, and was used as-is. The clamp isn't a technicality; it's the fairness and safety guarantee that makes an adaptive system trustworthy.

One deliberately boring decision: signals rotate in the same cyclic order as a conventional junction โ€” red โ†’ green โ†’ yellow โ†’ red, one direction after another. A "smartest lane first" scheduler would confuse every driver facing it. The intelligence lives entirely in the durations, never in the order. The average crossing times can even be tuned per city or per intersection from transport-authority data, without touching the algorithm.

The five-second trick: detection inside the yellow light

The elegant part of the design is when the work happens. The junction camera snapshots the next direction at the moment the current signal's green timer runs out โ€” which is exactly when the 5-second yellow phase begins:

current signal:  GREEN (counting down) โ”€โ”€โ–ถ YELLOW (5s) โ”€โ”€โ–ถ RED
                                     โ”‚
next signal:            snapshot taken โ”€โ”€โ–ถ YOLO detection โ”€โ”€โ–ถ GST computed
                                     โ””โ”€โ”€โ”€โ”€โ”€โ”€ all within the yellow window โ”€โ”€โ–ถ turns GREEN with its timer already set

A separate detection thread does the vision work while the main thread counts the timer down. By the time yellow ends, the next green duration is already assigned โ€” the switchover is seamless, with zero visible lag. The red times of the following signals update in the same beat: when signal 2 gets 25 seconds of green, signal 3's red becomes yellow + green = 30 seconds, and the countdown displays stay honest.

I've reused this shape of solution many times since: find the dead time in a process and hide your computation inside it. Five seconds of yellow light was a processing budget nobody was using.

Watching it run

The algorithm's console output (the terminal view in this post's cover image) tells the story per tick โ€” each signal's state and its red/yellow/green timers:

GREEN  TS 1 -> r: 0    y: 5   g: 24
RED    TS 2 -> r: 29   y: 5   g: 20
RED    TS 3 -> r: 118  y: 5   g: 20
RED    TS 4 -> r: 133  y: 5   g: 20

First cycle starts on defaults; every subsequent green is set from live detections. Watching the numbers adapt โ€” a quiet direction getting its 10-second minimum while a queued one earns 25 โ€” is the whole thesis of the project playing out one second at a time.

Common pitfalls

  1. Treating all vehicles as equal. A raw count says two directions are the same; crossing-time weighting says one needs double the green. Weight by class or the formula lies.
  2. No lower bound on green time. A computed 4-second green is technically optimal and practically dangerous. Clamp first, optimise second.
  3. Doing detection synchronously. Run YOLO on the main timer thread and every cycle stutters while the network thinks. The detection thread + yellow-window budget removes the lag entirely.
  4. Ignoring start-up lag. Queues don't discharge at cruise speed; the back of the queue loses disproportionate time. Bake the lag into the average crossing times or every green runs short.
  5. Reordering signals by density. Optimal on paper, chaos on the road. Drivers' expectations are part of the system you're designing.

FAQ

Why divide by lanes + 1 rather than just the lane count?
It's a calibrated discharge factor rather than a pure division โ€” queues don't split perfectly evenly across lanes, and the +1 damps the estimate toward realistic throughput. Like the crossing times, it's a tunable that should be calibrated per intersection.
What happens if detection fails or returns nothing?
The clamp is the safety net: with no detections the formula bottoms out and the signal still gets its minimum green, so the junction degrades to a conservative fixed-timer rather than freezing.
Could this scale beyond a 4-way intersection?
Yes โ€” the algorithm is explicitly scalable to any number of signals; the cycle just grows. Coordinating multiple junctions (green waves along a corridor) is the genuinely harder next problem.
Where does reinforcement learning fit?
RL is the fashionable answer for signal control, but a transparent formula with tunable coefficients is auditable โ€” a traffic authority can read it, reason about it, and certify it. I'd want RL proposing timings inside clamped bounds, not replacing them.

Enjoy algorithm design with real-world constraints? So do I โ€” get in touch.