Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 

Repository files navigation

zonelearn

Learn occupancy zones from radar coordinates. No zones to draw, no walls to configure by hand.

I write software for a living, so the YAML never bothered me. What did bother me was the step before that: deciding where the sensor goes, then drawing zones I had to redraw every time someone moved a chair.

So I stopped configuring it. I let a 24 GHz radar log target coordinates in my living room for four days and looked at the density. With no input at all, the sofa, my chair and my wife's chair came out as separate zones — and the two sofa seats 0.5 m apart resolved on their own.

What it found

204,796 coordinate pairs, four days, zero configuration:

zone   share  centre x  centre y
   1   23.8%     +0.03      1.21     desk chair (mine)
   2   18.0%     -0.81      3.88     sofa, near end
   3   10.5%     -0.66      1.81     desk chair (my wife's)
   4    3.2%     -1.26      4.38     sofa, far corner

Zones 2 and 4 are the same sofa. They stay separate down to a 3% threshold because there is a real density valley between the cushions.

  4.5m      ..  .---.
  4.2m         .-++.
  4.0m      . ..---..      ..
  3.8m    . ...---.##...  ..-..        <- sofa
  3.5m       ......-.       ...
  3.0m          .-..
  2.0m         ....
  1.8m        .-++-..                  <- her chair
  1.2m         ..-+#-..
  1.0m         ...##-....              <- my chair
  0.5m          ..--..
  0.0m           .-.
                  ^ sensor

Nobody drew these.

Walls

Radar goes through drywall. A sensor near a wall reports the neighbours, and no sensitivity setting removes them cleanly — I tried. What does work is three measurements:

python3 zonelearn.py --csv coords.csv --x-min -3000 --x-max 1000
# 204,796 coordinate pairs — 12,214 (6.0%) outside the room, dropped

That is the whole configuration. Zones stay automatic; only the walls are told once.

Usage

# straight from Home Assistant's own database — nothing to export first
python3 zonelearn.py --ha-db ~/.homeassistant/home-assistant_v2.db --days 3

# from a CSV with x,y columns (millimetres by default; use --scale for other units)
python3 zonelearn.py --csv coords.csv

# a PostgreSQL recorder instead of the default SQLite one
python3 zonelearn.py --ha-recorder sensor.living_target_1 --days 3 --dsn "$DSN"

With --ha-db the sensors are found for you — the first <prefix>_x / <prefix>_y target pair in the database wins, and --entity overrides it. (0, 0) means the radar saw nobody, so those readings are dropped before anything else; on a real recorder that is most of the day.

flag what it does
--cell grid size in mm (default 250)
--frac keep cells above this share of the busiest cell (default 0.10)
--x-min --x-max --y-max wall distances from the sensor, in mm
--scale multiply input by this to get millimetres
--days only the last N days
--entity entity prefix for --ha-db, when auto-detection picks wrong
--keep-origin keep (0,0) readings instead of discarding them
--emit also print zone config: esphome, ha, or both

No dependencies for CSV or SQLite — that is the standard library. psycopg2 only for --ha-recorder. Home Assistant OS keeps the database at /config/home-assistant_v2.db; copy it out before reading if you like, though opening it read-only is safe.

Getting the zones back into the radar

Finding the rectangle is only half of it. --emit writes the other half:

python3 zonelearn.py --ha-db /config/home-assistant_v2.db --emit both
# --- Home Assistant: Developer Tools > Actions, YAML mode ---
- action: number.set_value
  target:
    entity_id: number.ld2450_zone_1_x1
  data:
    value: -1750

ESPHome's ld2450 component takes three zones, so if more are found the three busiest are written and the rest are left for you to merge with a higher --frac.

Sensors

Anything that reports 2-D target coordinates. Coordinates are normalised to millimetres before anything else runs, so the sensor is an input, not the design.

Tested with HLK-LD2450 (24 GHz, 3 targets, x/y/speed) via ESPHome. Should work with any source that gives per-target x and y — LD6002B, exports from commercial sensors, or your own.

Limits

  • Two dimensions. A sitting person and a standing person at the same spot are one zone.
  • A flat threshold does not separate everything. At 10% it finds four zones here. Drop it to 3% and a chair 0.7 m from another chair merges into it, while the two sofa seats still stay apart. A density-valley method (watershed) would do better; this does not do that yet.
  • Zones have no names. It finds where people stop, not what the furniture is.
  • Walls are not learned. They cannot be — a wall is where points should never appear, and that is not visible in the data. Hence the three numbers.
  • Four days of one room is one sample. If you run it, I would like to see what comes out.

Licence

MIT.

About

Learn occupancy zones from radar coordinates. No zones to draw, no walls to configure by hand.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages