City Traffic Light Simulation with Cars

On this page I want to show off a city simulation based on a Neopixel display. Traffic lights controll cars, cars stop before traffic lights, an arsonists lights houses, a fire truck will extinguish burning houses and much more.

How it all began...

It all started with some simple traffic lights. Red-yellow-green, everything fine. Then I thought I need a second traffic light, and two more to make a really crossing. I run out of pins and discovered multiplexing. After several versions, I ended up with Neopixels, to be precise 256 pixels on a 16 x 16 grid which brought me finally to small city map.

The Neopixel City

On a 16x16 Neopixel grid there is a lot more space than just one central crossing. A city map in Excel helped me a lot. There are a total of 5 crossings and 4 blocks of houses (grey), the green blocks on the map represent positions of traffic lights:

In the center there a two parallel lines from the North to South and East to West (and vice versa). Around the town there was only space left for one line in each direction, one clockwise - one counterclock wise. Obviously my city it is based on right hand traffic:

Vehicles / Cars

Beside the traffic lights, the city was still dead. I needed cars. Cars that drive around and stop in front of traffic lights and wait until it turns green, then pick up speed again, pay attention to the traffic, do not cause accidents. No problem, we have a computer here that drives! The cars are individual objects, older and newer, which differ in how they find their way. The first cars were just driving around without any target along the street.

Houses

Houses are no static elements and can start burning. Firetrucks will try to extinguish the fire.

 

If a fire can not extinguish in time, the house will burn down and become a ruin (yellow). Over time, ruins become beautifully landscaped parks again. One day a park will be used to build new houses. So I came to following lifecycle for the houses

Desasters and other Events

The underground organisation SPECTRE came into town and orders two arsonists, which brought even more fire into the town. But also other organisations came into the town. A gardener takes care of the ruins and turn them into nice parks again. If new houses are needed, a brick layer uses the space of parks to build a new house.

The complete life cycle looks like this:

Streets - Waypoints

Streets - yes, where we want to go - we will need streets. The smallest element of a street is one pixel (one tile). A street pixel keeps track of it neighbours. Example: if a car is on pixel 12 heading West, the only possibilty is the next pixel 13. Therefore a car on pixel 12 knows to drive forward to pixel 13. At crossings and some special tiles, a car should be able to change its direction or to thange the lane. As cars are not allowed to reverse its direction, up to 3 neighbours can be chosen from. The information about each tile and its neighbours are stored in an struct array:

const byte street [][4] PROGMEM = {
  {0, 0, 0, 1}, // 00
  {0, 0, 0, 2}, // 01
  {0, 0, 0, 3}, // 02

By the way: there is no street "0": if a car gets on 31, the next tile will be tile 1.

Example 1: Basic Routing

Let's follow a car on street  81 heading North to street 17:

Street 81 is defined as

{78, 0, 0, 0}, // 81

The entries with 0 are not valid, so there is only one possibility: Street 78. Let's read the entry for 78:

{49, 0, 0, 0}, // 78

Again there is only one valid destination: 49

{46, 0, 0, 0}, // 49

In 46 we find:

{17, 0, 0, 0}, // 46

Example 2: Waypoints

Let's follow the green car: start at 65 -> 94 -> 97

{0, 0, 94, 0}, // 65
{0, 0, 97, 0}, // 94

On 97 we have a street with two possibilities:

{0, 0, 126, 98}, // 97

The car can decide whether to go south (126) or west (98).

Example 3:

If we define several waypoints in serial, the car can go a complex route. The car can even change the line as long as it is heading in the same direction. But it will notdisturb the traffic on the opposite line.

Path finding - Vehicle Intelligence

Simple cars (class Vehicle) will follow a strict ruleset which way option they will follow.

Newer cars (class Vehicle2000) will keep their direction as long as they are not blocked. If the tile in their current direction is blocked and if there are alternatives, they will randomly chose where to go next (if allowed).

More intelligent cars (class VehicleChasing) are able to find a specific street target. At each waypoint they will calculate the distance for each posibility and will chose the point with the shortest distance to the target. The calculation is based on the line of sight (shortest hypotenuse) and don't take blocking elments into the calculation. A helper function was needed to overcome the problem of the zig-zag layout of my pixels (position2coordinate(byte position)).

 

Firetrucks (class VehicleFiretuck) will increase their speed if they get the order to go to a burning house, and slow down if there is no fire in the town.

Debug messages can be activated during runtime with Serial commands, e.g. if you send a "f" you can switch on/off the debug messages for a fire truck, "a" for the arsonist, "b" for the brick layer, "g" for the gardener.

The Sketch

Let's talk about the sketch. Lot of elements are classes, some additional functions are used

There is only one object of the class City. It takes care of the currently 48 active houses, but also controls the disasters (fires) that can happen randomly to the houses and notifies the fire department if a fire breaks out.

The houses are no objects because it would cost to much memory. Every house has a house number (the pixel to be lit as house) and an "address" - a street next to it so that the fire truck can come to an address (=street) and extinguish the fire:

const byte houses[][2] PROGMEM = {
// pos, street
{34, 29},
{35, 28},

The traffic lights showing a simple red/yellow/green. They are grouped into Crossings. In total there are 5 crossings (1 large and 4 small ones) . The large crossing in the middle has 8 traffic lights (4 directions  x 2 lines). 4 smaller crossings are in the North, East, South and West end. You can set the individual intervalls for the phases.

Vechicles where already explaind. They can have different driving characterics. The fire trucks are pretty strict about their destination, if their path is blocked, they stop and continue if the way is free again. The gardener is heading to a ruin and will need some time to recreate the ruin to a park. The brick layer acts very similar and converts parks to houses. The arsonist heads to any house and start a fire at this house. The other cars keep moving and try a different route. Each vehicle can have it's own color on creation and can have it's own speed. The cars of the gardener, construction team and arsonist are chasing vehicles. This means they can find a target street. These vehicles get their order (where to go) from their Oranization.

The fire department is controlled separately (in a function).

The Hardware

The sketch with less than 1500 lines of code runs on a NANO clone *) with an Arduino UNO bootloader.

I'm using WS2812B Neopixels as 16x16 matrix *).

 

Buttons can be used to add more parks, start fires or breaking houses to ruins during runtime:

All components fit in an IKEA Ribba picture frame *)

Links

(*) Disclosure: Some of the links above are affiliate links, meaning, at no additional cost to you I will earn a (little) comission if you click through and make a purchase. I only recommend products I own myself and I'm convinced they are useful for other makers.

Protokoll

First upload: 2021-03-23 | Version: 2024-03-22