feat(camera): clubhead pose fit, scores and delivered angles - #250
Draft
HarjotDhanota wants to merge 2 commits into
Draft
feat(camera): clubhead pose fit, scores and delivered angles#250HarjotDhanota wants to merge 2 commits into
HarjotDhanota wants to merge 2 commits into
Conversation
Club mesh with provenance-checked normalization, calibrated camera model with silhouette projection, and sub-pixel teed-ball location that anchors the world frame. First stage of the clubface impact-location work; not yet wired into the shot pipeline.
Sequence fitting with physical bounds and temporal smoothness, boundary-distance and rotation-consistency scores (silhouette IoU measured inversely related to pose correctness on real masks), clubhead/shaft separation, and delivered loft/face/lie with plausibility envelopes. Includes the technical report: clubhead orientation has no accuracy figure against truth, and this change does not claim one.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #245: the first commit here is #245's geometry stage, and this PR stays a draft until #245 merges, after which it rebases to the estimation commit alone.
What does this PR do?
Second stage of markerless clubface impact location, stacked on the geometry PR. Adds the estimation half of
openflight.camera.clubposeand the technical report on where the work stands.fit.py) — fits the mesh to segmented silhouettes across frames with shared physical bounds and temporal smoothness, after independent per-frame fits produced angles jumping over 100° between adjacent frames.scores.py) — boundary-distance and rotation-consistency scores. Silhouette IoU was measured running inversely to pose correctness on real masks, so it is deliberately not the objective.angles.py) — converts a fitted pose to dynamic loft, face angle and lie, with plausibility envelopes that fail closed.head_split.py) and rigid-motion primitives (motion.py).docs/clubface-impact-location-report.md) and its entry document, which covers setup and running against your own captures. The web version carries eleven figures (real frames with the model's own projections overlaid), and the frame-by-frame fusion view shows the current state on two real shots in about thirty seconds.The substantive positive result is the camera+IWR6843 fusion. Neither sensor can measure clubhead velocity alone: the camera sees image-plane motion but has no depth, the IWR6843 measures range rate along its line of sight but cannot see lateral motion. Combining them as
dP/dt = (dr/dt)·ray + r·d(ray)/dtrecovers the full 3D velocity. Checked against the OPS243, which takes no part in the fit: across 6 fitted shots the fused-to-OPS243 speed ratio is mean 0.970, sd 0.029, spread 0.941–1.015, worst shot 5.9% from unity. This is the architecture Trackman uses for impact location, demonstrated on this hardware. The same fusion showed the clubhead's radar range is not constant — it sweeps 347–438 mm across the fitted frames — so range comes from the radar's own range rate, reducing the pose fit from five free parameters to four.Also validated: ball detection on 21 of 22 captures, impact timing agreeing between camera and radar to 0.66 frames, and camera attitude measured rather than assumed.
Not working: clubhead orientation. Face angle, dynamic loft and impact location have no accuracy figure against truth, and this PR does not claim one. The reason is measured, not suspected: the first 5° of face angle change the projected silhouette by zero pixels, one pixel of segmentation error is worth roughly 10° of face angle, and the club is segmentable for only ~10 pre-impact frames of which the extractor keeps 3 to 5, against a four-parameter fit.
Nothing is wired into the shot pipeline. The experiment scripts and recorded results behind each figure stay on the contributing fork, linked from the report. Two related changes are deliberately deferred to their own PRs: surfacing the trajectory metrics the ballistics simulator already computes, and the club-delivery extension to the Trackman comparison tool.
Why was this required?
Whether this hardware can produce impact location had never been answered with evidence. The answer is specific: the sensors and the fusion are sufficient for clubhead velocity, and the binding constraint on orientation is mask precision and observation count, not the fitting method. That reframes the next step from "better estimator" to "more pixels".
Additional captures are the most useful contribution, in this order: a session recorded alongside a Trackman (nothing here has been scored against a reference instrument, so no club metric has an accuracy figure — this is the one measurement that would change that); captures from other rigs spanning driver-to-wedge speeds (the current session is 7-iron and 9-iron with no speed overlap, which starves several discriminating tests of power); improved clubhead segmentation (recovering more of the ~10 frames roughly doubles observations per shot); and a capture at 1280×800 1:1, which doubles plate scale at the same frame rate.
Automated tests
Six suites, 62 tests: the fit's range-grid behaviour and hard range pinning, pose scores ranking a known pose first on synthetic masks (explicitly not an accuracy claim), delivered angles including that the mesh's own frame is not square (the trap that sent an earlier fit into the wrong hemisphere), head/shaft separation on constructed and measured shapes, and rigid-motion round trips.
Manual (human) testing
Every figure in the report was produced from the 21-shot reference session and cross-checked across all shots rather than the shot that suggested it; the report's appendix lists ten figures published and withdrawn when a cross-set check contradicted them. The fusion result was verified against the OPS243, which takes no part in the fit. Rendered the model's own projection over real frames and stepped through them, which caught two errors the numbers hid (a projection not tracking the club, and an impact frame wrong by 3.89 frames on every shot); both fixes are reflected here. The reference capture session behind the figures is available on request for anyone who wants to reproduce them.
Checklist
uv run pytest tests/ -v)uv run pylint src/openflight/ --fail-under=9)uv run ruff check src/openflight/)