Flight Plate Monitor & Flight Plate Control
Disc golf launch analysis from a phone lying on the ground · Android (Java), iOS (C++/Swift), Python prototypes
A printed 3 × 3 dot pattern is stuck to the underside of a disc, and a phone on the ground films the throw from below at 120 fps. Within a couple of seconds the Flight Plate Monitor app announces the disc's speed, spin, launch angle, nose angle, hyzer angle and wobble. A second phone runs the Flight Plate Control app, which shows the live preview, guides the user through camera calibration and receives the results over local Wi-Fi.
The project was inspired by commercial launch monitors such as TechDisc (a sensor disc) and RipReader (a phone app). It explores the measurement problem rather than competing with them.
Why it is hard
The disc passes 1–1.7 m above the lens and moves at up to 20 m/s, spinning at around 900 rpm. It is visible for only 4–18 frames. The phone's rolling shutter reads the sensor row by row over about 5 ms, and in that time the disc moves around 10 cm and turns up to 27°. Ignoring this shears the image of the disc and produces false tilt angles.
Pipeline
- Cascaded trigger. The camera records continuously into a 3 s ring buffer. A cheap 160 × 90 frame-difference motion gate runs on every frame, and the neural detector runs only when something moves.
- Retrained disc detector (ML). I fine-tuned a YOLOX-nano detector from pretrained weights on a data set I labelled myself (frames from my own throw videos, labelled with LabelImg and converted to COCO format). It was exported to ONNX and runs on the phone through OpenCV DNN at 320 × 180 input. A lattice check with normalised cross-correlation then rejects people, pets and clothing that the detector lets through.
- Sub-pixel pattern nodes. A robust Gauss–Newton (Lucas–Kanade family) alignment of a blurred 300 dpi template, using an 8-dof homography and Huber-weighted IRLS over a three-level pyramid, locates the nine nodes far more accurately than the 0.5–0.9 px of blob detection.
- Rolling-shutter physics fit. A 17-parameter model, combining constant-acceleration translation with torque-free symmetric-top rotation (spin plus wobble), is fitted with Levenberg–Marquardt to every node in every frame at once. Each node is evaluated at the moment its sensor row was read out.
- Metrics with uncertainty. All metrics come from the fitted parameters, not from frame-to-frame differences. The accelerometer gives the direction of gravity, and the parameter covariance is propagated to a ± value for every metric.
Validation
Three phones (two Samsung A16s and an iPhone 12 Pro) lay side by side and recorded the same throws independently. The pooled between-phone SDs were:
| Speed | Spin | Launch | Nose | Hyzer | Wobble |
|---|---|---|---|---|---|
| 0.31 km/h | 3.0 rpm | 1.7° | 2.8° | 1.4° | 0.5° |
iOS does not report its rolling-shutter readout time. I estimated it from the throws themselves by fitting it as an additional free parameter. On the Android phones the method recovered the readout time the phone reports to within about 0.2 ms. For the iPhone it gave 2.4 ms rather than the 5 ms assumed until then, and the correction cut the iPhone's nose-angle disagreement with the Android phones from 5.5° to 1.6° RMS.
Engineering
The algorithm was prototyped in Python, implemented in Java for Android, and ported line for line to portable C++ for iOS. Calibration uses Zhang's method with a guided set of poses. On the iPhone it can instead use Apple's factory lens data, for which I measured the undocumented mapping to the binned 120 fps video mode (0.12 px residual).
