2026 · drone video · laptop CPU
Aerial Track and Range
Tracking and ranging from a single drone camera
A camera tells you which direction something is in, but not how far away it is. I built a system that takes drone video, finds and follows every person and vehicle, and works out how far away each one is. Every distance comes with an error bar, and the system says when not to trust it. It all runs on a laptop with no GPU.
2.4%
median distance error on synthetic flights with exact truth
flat ground, given the true height: 14.7%
Real flight from the AU-AIR dataset, not used in development. It was recorded at 5 frames a second; the frames in between are generated so it plays smoothly. Green tags are reliable, amber uncertain.
A camera gives direction, not distance
Step 1 · The ray
Each box tells me the direction of an object from the camera. To turn that into a distance, I draw a line from the camera through the bottom of the box, where the object touches the ground, and find where that line meets the ground.
Step 2 · The usual shortcut
The catch is knowing where the ground is. The usual shortcut assumes the ground is flat and that the drone knows its height above it. On a slope the line carries on to an imaginary flat ground, and the distance comes out far too long. A real drone does not know its height above the ground anyway.
Step 3 · Terrain from motion
So I estimate the shape of the ground from the drone's own motion. The same patch of ground, seen from two positions a fraction of a second apart, can be triangulated, the way two eyes judge depth. The line now meets the real surface, at the car.
Follow one car through the system
Every frame passes through seven steps. Each one adds something to what the system knows about the car, shown in the box under each step. The ranging step also takes two extra inputs: where the drone is, and the shape of the ground beneath it.
- 1
Video frame
from a single drone camera
Adds: 1920 × 1080 pixels
- 2
Detect
YOLO26n, fine-tuned on drone footage
Adds: box: car 0.84
- 3
Track
BoT-SORT with camera-motion compensation
Adds: car, ID 12
- 4
Range
a ray from the camera to the ground
Adds: ID 12: 46 m
- drone pose and height
- terrain from motion
- 5
Uncertainty
propagated, calibrated, flagged
Adds: 46 ± 3 m, reliable
- 6
Kalman filter
position, speed and heading
Adds: 8 m/s, heading 270°
- 7
Output
a table and an annotated video
Adds: one row in tracks.csv
Example values, to show what each step adds.
Detect and track
I fine-tuned a small YOLO26n detector on VisDrone, a dataset of drone footage, and paired it with the BoT-SORT tracker, which corrects for the camera's own movement. Each colour and number in the video is one identity, kept from frame to frame.
I left out appearance-based re-identification on purpose: the tracker uses only motion and overlap, so it never analyses faces.
VisDrone test footage, tracked by my system.
Fine-tuning did most of the work
Off the shelf, the detector misses almost everything seen from above. Drag the line across the video to compare it with the fine-tuned version on the same footage.
off the shelf
0.32
fine-tuned
0.62
Mean identity F1 (IDF1) on validation data: how long each object keeps the same identity. Higher is better. On the test set, vehicles reach 0.760 and people 0.436.
Small people are the weak point
How big something is in the picture is measured in pixels, the dots that make up the image. All four people marked here are from this one frame. The closest is 159 pixels tall; the furthest only 24, a smudge of colour.
The detector finds 91% of people taller than 128 pixels, but only 16% of those between 16 and 32 pixels, and nearly a third of the people in the test footage are that small.

0 to 16 px

16 to 32 px

32 to 64 px

64 to 128 px

128+ px
Measured against exact truth
To score distances I need to know the right answer. Mid-Air is a simulated dataset of low drone flights where the true distance of every pixel is known. I froze every setting, then ran the 9 test flights once.
On the same samples, terrain from motion had a median error of 2.4%. Flat ground managed 14.7%, even when I gave it the true height, which a real drone would not have. Terrain from motion was closer on 78% of samples and had the lower median on 8 of 9 flights.
It is not always better. On flight 5 it was worse (17.1% against 10.3%), and one estimate in ten is still more than 35% off.
The flat-ground shortcut breaks on hills
The steeper the ground, the worse the flat assumption gets: its error grows from 6% on level ground to 24% on slopes above 45°. Terrain from motion stays at or below 3.7% on slopes up to 45°.
Error bars that say how far to trust it
Every distance comes with an error bar: a range the true distance should fall inside. "46 ± 3 m" means somewhere between 43 and 49 metres. An error bar is only honest if the truth really does land inside it as often as it claims.
I work out each error bar from the sensor noise, then calibrate them on separate flights using split conformal prediction, so they are wide where the system is unsure and narrow where it is not.
What an honest error bar looks like
Promised 90 in 100. Delivered 84.
A 90% error bar promises to contain the truth 90 times in 100. Straight from the sensor noise, mine managed 74. After calibration, 84. That is still 6 short, which tells me the test flights differ more from the calibration flights than the method assumes, so I report it as it is.
A small model, built on features I can explain, also raises a warning flag. It catches 56% of the estimates that are more than 10% off.
Promised90 in 100
Before calibration74 in 100
After calibration84 in 100
Each dot is a distance
This is not real footage. It is Mid-Air, a simulated drone flight made in a game engine: a camera flying low over grass, rocks and hills. Because the world is simulated, the true distance to every pixel is known, so every estimate my system makes can be checked exactly.
Each mark is one distance estimate, pinned to its spot on the ground as the drone flies past.
- Within 10% of the true distance
- More than 10% off
- Amber: the warning flag was raised
How far the number transfers
The 2.4% uses the simulator's exact camera positions. A real drone's navigation is not exact, so I added noise to every pose and re-ran the development flights.
Errors shared by every frame barely matter: a 5 m GPS offset leaves terrain from motion at 2.7%, while flat ground jumps to 67%. Errors that change from frame to frame matter a lot, because triangulation depends on the small change in position between frames. Past about 7 cm of per-frame noise, flat ground wins.
In practice the method needs the short-term accuracy of an inertial or visual-inertial navigation system, not raw GPS.
Three navigation grades
Combining both kinds of error at three assumed levels of navigation quality. These are my assumptions, not product specifications. Ranging the bottom of a detected car's box, rather than a point on the ground, adds its own 3% short bias, so even with exact poses a car is 5.6% off at the median.
| Navigation | Terrain from motion | Flat ground |
|---|---|---|
| Good | 6.6% | 22% |
| Typical | 11.7% | 40% |
| Poor | 40% | 78% |
Median range error. Flat ground is given the true height in every row.
On real footage
AU-AIR records real flights with GPS and altitude, but no camera calibration and no camera tilt. So I estimated both: the focal length from GPS motion against image motion on separate flights (1259 pixels), and the tilt, frame by frame, from how the ground moves in the footage.
There are no true distances in AU-AIR, so accuracy can't be measured here. What can be measured is whether the system gives the same answer twice.
Looking straight down on a held-out AU-AIR flight. The faint ring marks 10 m on the ground.
What held up on real footage, and what didn't
Held up
7.6%
Consistent distances. Parked cars should stay put, and the system agrees: their estimated positions repeat within 7.6% of their distance, across 343 vehicle segments.
Did not transfer
0.21
Detection score on vehicles (AP50; 0.02 on people). The detector learned on VisDrone and transfers poorly to this footage, so many vehicles are never ranged.
11%
Implausible speeds. Vehicle tracks that imply more than 70 km/h at a junction, most likely from shallow viewing angles and identity switches.
No GPU. A laptop CPU, the AMD Ryzen 7 5700U.
Fast enough on a laptop
Same clip, played at the speed the system can process it. On the left, full accuracy. On the right, the version I tuned for video rate.
9.6 fps full accuracy
3.5× faster
33.4 fps video rate
The right side shows every frame of this 25 fps clip, since 33.4 fps is faster than the clip itself.
How I got from 9.6 to 33.4
At full accuracy, with the detector exported to ONNX, the pipeline runs at 9.6 frames per second. To reach video rate I used a smaller input, ran the detector only on every second frame (the tracker predicts in between), and ran detection alongside tracking.
The cost: identity F1 falls from 0.617 to 0.536. Each result still arrives 47 ms after its frame, so 30 fps holds for throughput, not for the delay on a single frame.
Every setting I measured
The one test still missing
Each dataset checks a different part of the system, and none has everything. So no single test yet runs the whole chain, from video to distance, against known object distances.
The next step I would take is one setup with all three: a drone flown past a car carrying a GPS logger, or a simulator with vehicles and people, scored end to end.
Every known limitation, with numbers| Dataset | Real footage | Labelled objects | True distances |
|---|---|---|---|
| VisDronescores tracking | yes | yes | no |
| Mid-Airscores ranging | no | no | yes |
| AU-AIRreal flights | yes | yes | no |
| Next testa drone, a car, a GPS logger | yes | yes | yes |
How it is built
- Python
- PyTorch
- Ultralytics YOLO26
- BoT-SORT
- ONNX Runtime
- Kalman filter
- Conformal prediction
- pytest
- 382 tests, run by CI on every push, on synthetic data
- Every number on this page generated from a results file
- Each test split run once, after every setting was frozen
- Every design choice logged with its reason and evidence
Footage and data: VisDrone2019 (Zhu et al., IEEE TPAMI 2021), Mid-Air (Fonder and Van Droogenbroeck, CVPR Workshops 2019, CC BY-NC-SA 4.0) and AU-AIR (Bozcan and Kayacan, ICRA 2020, CC BY 4.0). Detector and tracker from Ultralytics (AGPL-3.0). People and vehicles are tracked as boxes only, with no face analysis.
Have a role in mind?
If you are looking for a spark of curiosity, energy and growth in your team, and have a role that suits my CV, let me know. Email is the fastest way to reach me.
Guildford, UK · Right to work in the UK, no sponsorship needed