FIG. 01
RECORD DATA
Category
Software
TARİH
The team's first attempt was the classic one: download yolo11n.pt, hook it up to the gimbal camera's RTSP stream, take the drone up to 20 meters. The result was zero detections on the ground targets. The same model had no trouble finding a person, a car or a bag in a web image on a desktop.
There is no mystery here. General-purpose datasets like COCO are built from eye-level photographs in which the object fills a good chunk of the frame. From a camera looking straight down at 20 meters, that same target shrinks to a few dozen pixels and its silhouette changes completely. The model has simply never seen that distribution.
In this post we walk through the training pipeline we built for SUAS 2026 from end to end: how we collected the data, how we wrote data.yaml, what drove our choice of model size and imgsz, and what we read out of results.csv. We verified every command and parameter name against the official Ultralytics documentation, version 8.4.105 (as of July 24, 2026, Python 3.8-3.13). The YOLO11 family is still actively supported in that release, and the API below is identical for the newer YOLO26 models.
Your data sets the model's ceiling
Every hour spent before you type the training command pays for itself. Only about 11% of our first dataset was real aerial footage; the rest was ground-level imagery pulled off the internet. The sample ratio between the two classes had drifted out to 7:1 as well. The validation score that version produced looked good, but it had no counterpart in the field.
Here is the critical point: if you put web images in the validation set, the model banks points on easy examples and you never measure real flight conditions. Once we rebuilt the validation set from aerial footage only, the numbers got honest — and on that set mAP50 (mean average precision at an IoU, that is intersection-over-union, threshold of 0.50) reached 0.950.
Data source | Strength | Weakness | When to use it |
|---|---|---|---|
Web images | Fast, plentiful, free | Wrong viewpoint and wrong scale | To teach the general appearance of a class, in the training set only |
Our own flight recordings | Exactly the right distribution | Expensive to collect, weather dependent | The entire validation set, plus the bulk of training |
Simulated frames (Gazebo + SITL) | Unlimited supply, labels come for free | Texture and lighting are not realistic | Topping up rare scenarios and background variety |
Rule of thumb: on every new data round, grow the validation set first and the training set second.
Folder layout and data.yaml
Ultralytics finds label files by swapping the images directory in the image path for labels. That means the folder names have to be exactly these:
Each label file holds one object per line: the class index, then a normalized (scaled into the 0-1 range) center-x, center-y, width and height.
And the data.yaml file looks like this:
The indices in names and the class numbers in the label files must match exactly. Breaking that mapping is the most common reason a model trains quietly on the wrong thing.
Model size: n, s, m or l?
The numbers below come from Ultralytics' official YOLO11 table, measured on the COCO validation set at imgsz=640. The speed figures were taken on an NVIDIA T4 with TensorRT10 — the timings on your own companion computer (the board that runs vision in flight) will come out different, so treat that column as a relative comparison only.
Model | mAP50-95 | T4 TensorRT10 (ms) | Parameters (M) | FLOPs (B) | When |
|---|---|---|---|---|---|
YOLO11n | 39.5 | 1.5 | 2.6 | 6.5 | Weak hardware, when high FPS is non-negotiable |
YOLO11s | 47.0 | 2.5 | 9.4 | 21.5 | Speed/accuracy balance, first prototype |
YOLO11m | 51.5 | 4.7 | 20.1 | 68.0 | Jetson-class board plus small objects |
YOLO11l | 53.4 | 6.2 | 25.3 | 86.9 | Not real-time, post-processing |
We settled on YOLO11m running on a Jetson Orin NX 16GB. Going from m to l buys roughly 2 mAP points on COCO but raises inference time visibly; in our flight budget that trade did not pay off. On our own board we get 15-20 FPS on full-frame 640 inference.
Choosing imgsz: the lifeline for small objects
imgsz defaults to 640. In aerial imagery this is the parameter that decides everything. If the target is 25 pixels wide in a 1080p frame, downscaling to 640 drops it to roughly 15 pixels, and even the model's finest feature map struggles to catch it.
We train at imgsz=1280. That has a price: at the same batch size, GPU memory and epoch time both climb noticeably, and most of the time you have to lower the batch. In flight we process the full frame at 640 and, for suspicious regions, split the frame into overlapping tiles with SAHI (sliced inference). SAHI's slice_height, slice_width, overlap_height_ratio and overlap_width_ratio parameters set how much the tiles overlap; do not leave the overlap at zero, or you will lose objects cut in half at a slice boundary.
On the inference side, always state imgsz explicitly. If the training value and the inference value quietly diverge, the score you saw in validation will not match the behavior you see in the field.
The basic training command
From the CLI:
The same job through the Python API:
Parameter | Default | Our value | Reasoning |
|---|---|---|---|
| 100 | 300 | Ultralytics gives 300 as a starting recommendation; if you are not overfitting you can push past 600 |
| 100 | 50 | The default effectively disables early stopping in a 100-epoch run |
| 16 | 8 | Lowered because |
| 640 | 1280 | Small targets |
|
|
| Fixing the data first pays off more than tuning this by hand |
| 1.0 | 0.1 (on trial runs) | To confirm in 10 minutes that the pipeline works |
Reading the results
When training finishes, the outputs are written under runs/detect/train/: weights/best.pt and weights/last.pt, results.csv, results.png, BoxPR_curve.png, BoxF1_curve.png, confusion_matrix.png, and the val_batch*_labels.jpg / val_batch*_pred.jpg comparison frames.
The columns in results.csv are: epoch, time, train/box_loss, train/cls_loss, train/dfl_loss, metrics/precision(B), metrics/recall(B), metrics/mAP50(B), metrics/mAP50-95(B), val/box_loss, val/cls_loss, val/dfl_loss, lr/pg0, lr/pg1, lr/pg2.
We read three signals:
If
val/box_lossfalls and then starts climbing again, overfitting has set in;best.ptis already taken from that turning point.High precision with low recall means the model is being cautious and missing targets — the fix is more data variety or a higher
imgsz.The other way around, high recall with low precision, means it is producing false positives; pick the operating point off the PR curve and raise the
confthreshold for flight. We fly withconf=0.25.
One thing not to confuse: mAP is a metric that is independent of the confidence threshold. The conf value you fly with is a separate engineering decision and it does not change mAP.
Common traps
Leaving web images in the validation set. This is the most expensive mistake there is. It inflates the score and buys you nothing in the field.
train/val leakage. If you split consecutive frames from the same flight across the two sets, the model memorizes what is essentially the same image. Split by flight, not by frame.
namesdrifting out of sync with the label indices. If the class order changes in your labeling tool, the whole dataset silently goes bad. After every export, hand-check 10 random labels.Trusting
batch=-1blindly. AutoBatch targets around 60% of GPU memory; if another process is sharing the card, you can run out of memory mid-training.Training for 100 epochs with the default
patience=100. Early stopping never fires, and you just burn time.Scale mismatch. Training at 640 and running inference at 1280 (or the other way around) changes your detection count in ways you will not expect.
Practical summary
Build the validation set from real flight recordings only, and measure everything against it.
Choose
imgszfrom the pixel size of the target — in aerial detection, 640 is usually not enough.Pick the model size from the real FPS on your companion computer, not from the COCO table.
Run the first pass with
fraction=0.1and few epochs to confirm the pipeline works end to end, then move to full training.Archive
results.csvon every run; if you cannot compare two training runs, you will never know what actually helped.
