MISSION LOG

MISSION LOG

RECORD OPEN

ATILIM UAV · GROUND CONTROL STATION

MISSION LOG / DETAIL

Archive

lıVE · READ MODE

Software

Aerial Object Detection with YOLO11: An End-to-End Training Guide

Aerial Object Detection with YOLO11: An End-to-End Training Guide

FIG. 01

RECORD DATA

Category

Software

TARİH

TÜM KAYITLAR

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.

0 0.512 0.334 0.041 0.062
1 0.780 0.611 0.115 0

0 0.512 0.334 0.041 0.062
1 0.780 0.611 0.115 0

And the data.yaml file looks like this:

path: /home/team/datasets/suas2026   # root directory
train: images/train
val: images/val
# test: images/test   # optional

nc: 2
names:
  0: mannequin
  1

path: /home/team/datasets/suas2026   # root directory
train: images/train
val: images/val
# test: images/test   # optional

nc: 2
names:
  0: mannequin
  1

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:

yolo detect train model=yolo11m.pt data=suas2026.yaml \
  epochs=300 imgsz=1280 batch=8 patience=50 \
  device=0 workers=8 project=runs name

yolo detect train model=yolo11m.pt data=suas2026.yaml \
  epochs=300 imgsz=1280 batch=8 patience=50 \
  device=0 workers=8 project=runs name

The same job through the Python API:

from ultralytics import YOLO

model = YOLO("yolo11m.pt")          # start from pretrained weights
results = model.train(
    data="suas2026.yaml",
    epochs=300,
    imgsz=1280,
    batch=8,          # -1 enables AutoBatch, which targets ~60% of GPU memory
    patience=50,      # stop if there is no improvement for 50 epochs
    close_mosaic=10,  # turn off mosaic augmentation for the last 10 epochs
    amp=True,         # mixed precision: faster and lighter on memory
    seed=0,
)
from ultralytics import YOLO

model = YOLO("yolo11m.pt")          # start from pretrained weights
results = model.train(
    data="suas2026.yaml",
    epochs=300,
    imgsz=1280,
    batch=8,          # -1 enables AutoBatch, which targets ~60% of GPU memory
    patience=50,      # stop if there is no improvement for 50 epochs
    close_mosaic=10,  # turn off mosaic augmentation for the last 10 epochs
    amp=True,         # mixed precision: faster and lighter on memory
    seed=0,
)

Parameter

Default

Our value

Reasoning

epochs

100

300

Ultralytics gives 300 as a starting recommendation; if you are not overfitting you can push past 600

patience

100

50

The default effectively disables early stopping in a 100-epoch run

batch

16

8

Lowered because imgsz=1280 fills up memory

imgsz

640

1280

Small targets

optimizer

auto

auto

Fixing the data first pays off more than tuning this by hand

fraction

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_loss falls and then starts climbing again, overfitting has set in; best.pt is 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 conf threshold for flight. We fly with conf=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.

  • names drifting 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=-1 blindly. 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

  1. Build the validation set from real flight recordings only, and measure everything against it.

  2. Choose imgsz from the pixel size of the target — in aerial detection, 640 is usually not enough.

  3. Pick the model size from the real FPS on your companion computer, not from the COCO table.

  4. Run the first pass with fraction=0.1 and few epochs to confirm the pipeline works end to end, then move to full training.

  5. Archive results.csv on every run; if you cannot compare two training runs, you will never know what actually helped.

©2026 ATILIM UAV TEAM

©2026 ATILIM UAV TEAM

Create a free website with Framer, the website builder loved by startups, designers and agencies.