feat(analysis): 결과화면 볼 궤적 오버레이 (M3 조기착수)#41
Open
devpark435 wants to merge 25 commits into
Open
Conversation
CustomPaint가 자동으로 클리핑한다는 오해 소지 있는 표현 수정 — 현재 안전한 건 컨테이너 밖 좌표가 실제로도 화면 밖 영역이라 우연히 문제없는 것뿐, 명시적 클리핑 보장이 아님을 명확히 함.
[analysis_state_machine]
- 실기기 증상1: 손/어프로치 구간까지 궤적선이 그려짐
→ release phase(릴리즈 직후 4프레임 전환 창)는 공이 아직 레인 평면 밖(손 안)일 수 있어
homography 투영이 왜곡됨. release phase의 궤적 누적을 제거하고 flight phase부터만
누적. flight에서도 yM>=0(파울라인 이후) 가드를 belt-and-suspenders로 추가.
- 실기기 증상2: 브레이크포인트 구간에서 궤적이 끊기고 speed 측정도 anchorMismatch로 실패
→ YOLO가 훅 브레이크포인트(보통 6~12m)에서 일시적으로 검출을 놓치면 5프레임 연속
미검출만으로 곧장 impact로 오판정, impactFrame이 너무 이르게 확정되어 다운스트림
핀-루미넌스 신호와 어긋남. null-count impact 폴백에 레인 깊이 게이트(18.29m의 약
75%인 14.0m 이상)를 추가 — 브레이크포인트에서는 오탐하지 않고, 회복 못 하면
impactFrame이 null로 남아 정직하게 실패한다.
[test]
- release phase 궤적 누적 안 됨 / flight phase yM<0 필터링 / 브레이크포인트 구간
미검출 오탐 안 함 / 핀 근처 미검출은 정상 impact 전이, 4개 회귀 테스트 추가.
[pin_impact_detector_service] - 실기기 로그에서 release+minTravelFrames 지점(검색 시작지점 자체)에서 곧바로 오탐(핀 충돌 프레임: 56)되던 증상. 원인은 루프 첫 비교만 release 프레임과 20+ 프레임 격차로 비교하고 이후는 직전 프레임(1프레임 격차)과 비교하는 불일치 — 첫 비교가 카메라 미동/조명 드리프트 누적만으로 임계값을 넘겨 오탐. - 씨드를 release가 아닌 searchStart 프레임으로 바꿔 release~searchStart 구간의 자연드리프트를 비교 대상에서 제외, 이후 전 구간을 일관된 1프레임 격차로 비교하도록 수정. - 실기기 재현 회귀 테스트 1건 추가(기존 4건 영향 없음, 수기 트레이스로 확인 후 실행 검증). [analysis_pipeline] - 손구간(backswing/approach) 렌더링 의혹(미확인 사용자 리포트)을 다음 실기기 테스트에서 확인하기 위한 궤적 진단로그 추가. 동작 변경 없음.
실기기 재현: 검색구간 전체(56~149)를 훑어도 핀임팩트가 아예 미감지됨(오탐 아님). 핀존비율(상단 20%)이 실제 촬영각도랑 안 맞는지, 임계값(15%/30)이 너무 빡빡한지 구분하기 위한 진단. 동작 변경 없음, 로그만 추가.
궤적 오버레이 데이터(레인 y범위)는 정상인데 실기기 화면에는 여전히 손 구간에 선이 그려진다는 리포트 — TrajectoryOverlay가 BoxFit.cover 매핑에 쓰는 VideoPlayerController.value.size가 분석용 ffmpeg 추출 프레임(480x853 portrait, 호모그래피 좌표계 기준)과 가로세로 비율이 일치하는지 확인하기 위한 진단. 동작 변경 없음, 로그만 추가.
[원인] - 오버레이가 정적이라 영상 시작(백스윙 중)부터 전체 궤적이 그려져 "손 구간에 선이 그려진다"로 보임 — 궤적 데이터 자체는 정상이었음 (실기기 3회 연속 레인 y범위 정상 확인, 비디오 크기 가설도 반증) [변경] - TrajectoryFramePoint 도입: 궤적 점에 분석 프레임 번호 보존 - TrajectoryOverlay: 재생 위치에 동기화해 공이 지나간 지점까지만 선을 그리는 점진 렌더링 — 레인 위에 페인트되는 느낌, 루프 재시작 시 자연 리셋 - VideoPlayerValue.position이 ~500ms 간격으로만 갱신되므로 Ticker로 vsync마다 위치 보간 추정(0.25x에서 선 끊김 방지) - 비디오 크기 진단 debugPrint 제거(가설 반증 완료)
[원인] - 핀존이 프레임 상단 20% x 전폭 하드코딩 — 레인 전체가 잡히는 실기기 구도(480x853)에서 핀은 존의 극히 일부라, 핀이 전부 넘어져도 존 전체 대비 변화율 2~3%에 그쳐 임계값 15%를 구조적으로 못 넘음 (실측: 탐색구간 최대 9.4%, 실제 충돌 프레임에서도 미달) [변경] - computePinZone: 핀덱 라인(y=18.29m)과 1.29m 앞 지점을 laneToFrame으로 투영해 핀 실제 위치 주변의 타이트한 존 산출 (원근 스케일 기반으로 위 1.5유닛/아래 0.5유닛 확장, 퇴화 시 null → legacy 존 폴백) - 호모그래피 존 경로는 임계값 10% (신호 대 잡음 개선 반영), legacy 경로는 15% 및 기존 동작 완전 유지 - 감지/미감지 debugPrint에 존 경로·좌표 진단 정보 추가
[궤적 렌더] - 균일 두께 폴리라인 → 레인 평면에 투영된 리본(공 폭 ±0.11m)으로 교체. 가까우면 넓고 멀면 좁은 원근이 생겨 화면 위 선이 아니라 레인 위에 그려진 느낌을 만든다 [궤적 정제 (trajectory_refiner)] - 선두 트림: 릴리즈 직후 손 근처 공(레인 평면 밖)의 왜곡 투영 포인트를 y 속도 일관성([0.08, 0.8] m/frame) 기준으로 제거 — 다운스윙 구간부터 선이 생기던 문제의 원인 - 단조 필터(y 역행 오검출 드롭) + xM 3점 이동평균(좌우 지터 완화) [핀 충돌 탐색] - 탐색 시작을 릴리즈+20프레임 대신 추적된 공이 16m 도달한 프레임으로 게이트 — 릴리즈 직후 볼러 팔로스루가 핀존에 걸려 나던 오탐(실측: 프레임 57에서 18.7% 오탐, 실제 충돌 ~115) 원천 차단. 이 오탐이 FSM 임팩트와 불일치를 만들어 속도 측정까지 막고 있었다
[궤적 렌더] - 정제된 궤적을 3차 최소제곱 x(y) 곡선으로 피팅 후 0.25m 균일 리샘플 — 검출 지터로 각진 폴리라인 대신 매끈한 곡선(직진+훅). 프로 샷트레이서 방식 - 리본을 꼬리→머리 불투명도 램프(코멧) + 공 위치 글로우 헤드로 렌더 [핀존] - 존 수직 크기를 레인 바닥 원근(낮은 카메라 각에서 극도로 압축, 실측 17px)이 아니라 압축 없는 레인 폭 기준 스케일로 유도 — 서 있는 핀(수직 물체)을 실제로 담는 존이 됨 (~110px). 직전 실측에서 최대 변화율 지점이 실제 충돌(111)로 이동했으므로 존 크기만 남은 상태였다
[궤적 정제] - 트림 전 xM/yM 3점 중앙값 프리필터 추가 — 반사광 등 고립 스파이크가 선두 트림의 속도 유효성 검사를 계속 실패시켜 궤적 앞 절반을 잘라먹던 문제(실측: 선이 9.24m부터 시작) 해소. 손구간 왜곡 (연속 드리프트)은 중앙값을 통과하므로 트림이 여전히 잘라낸다 [핀 충돌] - 호모그래피 존 경로를 임계값 첫-돌파(10%)에서 탐색창 내 최대 변화율 프레임 채택(바닥값 6%)으로 변경 — 탐색이 이미 공의 16m 근접 이후로 게이트돼 창이 충돌 근처로 좁고, 실측 2연속 런에서 최대 변화율 지점이 실제 충돌 옆(111, 107)인데 크기(9.4%)만 문턱 아래라 미감지되던 문제 해소. legacy 경로는 기존 동작 유지
정제 단계가 초반 구간의 지속적 검출 노이즈를 잘라내면(실측: 8m대 시작) 선 앞 절반이 비어 보인다. 릴리즈 직후 2~8m는 스키드 구간이라 물리적으로 직선에 가까우므로, 피팅된 곡선 시작점의 기울기를 원시 궤적 시작 y(최소 2.5m)까지 그대로 늘려 릴리즈 직후부터 선이 보이게 한다. 3차식 외삽은 꼬리가 요동쳐 선형만 사용. xM은 레인 폭, frame은 0 이상으로 클램프.
[원인] - 기존 구현은 release+8~24 프레임 구간의 원시 검출 좌표로 프레임간 이동거리 중앙값을 산출 — 이 구간(레인 2~8m)은 검출 y 지터가 ±0.5m/frame에 달해 거리 절댓값이 부풀려지는(±노이즈가 전부 +로 합산) 계통적 과대추정으로 50km/h 상한을 넘겨 outOfRange로 실패. 게다가 이 경로에 로그가 없어 무음 실패였다(실기기: 궤적·핀 감지 성공인데 구속 측정불가, 로그 없음) [변경] - 정제 궤적(중앙값+트림+단조+스무딩 완료)에 y = a + b·frame 최소제곱 회귀 → b×fps가 속도. 지터가 양방향으로 상쇄되어 강건 - 신뢰도 = 회귀 R² × impact 신뢰도 페널티 × release confidence - 모든 실패 경로에 수치 포함 debugPrint — 무음 실패 제거 - 원시 detections/homography 의존 제거 (파이프라인 시그니처 정리)
실사용에서 far 코너를 핀덱(핀 발밑)이 아닌 브레이크포인트/오일라인 부근에 두는 캘리브레이션 오류 발생 — 코너를 어디에 놓아야 하는지 시각 가이드가 전혀 없었다. 이 스큐가 구속 저평가(15.8km/h)와 궤적 시작/끝 오프셋(손~BP 구간)의 근원. - 코너 4점으로 호모그래피를 실시간 산출해 에로우(4.57m)· 레인지파인더(12.19m) 기준선을 프레임에 투영 — 드래그 중 실제 레인 마킹과 겹치도록 맞추면 캘리브레이션 검증 완료 - 파울라인/핀덱 라인은 라벨만 표시(코너 외곽선과 중복 회피) - 배너 문구에 기준 명시: 가까운 두 점=파울라인 양끝, 먼 두 점= 핀이 서 있는 레인 끝 바닥(핀 몸통 아님) - 퇴화 사각형(드래그 중 교차 등)은 그리드만 생략, 크래시 없음
호모그래피는 레인 평면 전제인데 공 중심(바닥에서 반지름 ~11cm 위)을 투영해, 평면 밖 점이 실제보다 앞으로 찍히는 계통 오차가 있었다. 낮은 카메라 각에서 오버슛이 거리에 비례해 커진다 — 실측 물증: 궤적 y가 핀덱(18.29m)을 넘는 18.65m까지 관측, 회귀 구속(13.7km/h)과 릴리즈→임팩트 앵커 교차검증(~26km/h)의 2배 불일치. - BallDetection.contactPoint(bbox 바닥 중점 = 공-레인 접점, 평면 위의 점이라 바이어스 0) 추가, 공 위치→레인 매핑 전 지점 교체 - refined 궤적 (frame:y) 진단 덤프 추가 — y(t) 형태로 잔존 왜곡 판독용 - 실기기 재현 회귀 테스트가 접점 투영만으로 outOfRange 실패에서 속도 산출 성공으로 전환됨 (계통 오차 실재의 테스트 증거)
320 입력에서 far-lane 공이 3~5px로 뭉개져 생기는 localization 지터 (정제 드롭률 58%, 회귀 R² 0.67 실측)를 640 입력 + s급 모델로 개선 시도. 기존 yolov8n(@320, 1클래스 전용학습)은 롤백용으로 애셋 유지 — 상수 5개 되돌리면 복귀. - COCO 80클래스 출력(1,84,8400) 파싱: sports ball 클래스 행만 사용, 1클래스(5차원) 레이아웃도 같은 경로에서 지원 - init 시 출력 shape 진단 로그 — 레이아웃 가정 실기기 검증용 - 주의: 범용 COCO 모델이라 전용학습 nano 대비 recall이 후퇴할 수 있음 — 다음 실기기 로그(raw 검출 수·드롭률·R²)로 비교 판정
신형 litert 변환기 산출물은 torch 채널-우선(NCHW [1,3,640,640]) 입력을 요구하는데 입력 빌더가 구형 export(NHWC) 고정이라 CONV_2D가 준비 단계에서 실패, 전 프레임 검출 0건 (실기기 재현: input_channel % filter_input_channel != 0). 로컬 인터프리터로 텐서 스펙 확인 후 채널-우선 경로 추가 — 롤백용 NHWC 경로도 상수 분기로 유지. init 진단 로그에 입력 shape/레이아웃 추가.
yolo11s 교체 후 검출 0건 원인 규명 과정: - 로컬 파이썬 추론으로 모델 자체는 정상 확인(bus.jpg에서 person 0.93, 좌표 정규화·sigmoid 적용 확인) → 문제는 앱 쪽 전처리 - 기존 스쿼시 리사이즈는 480x853 포트레이트를 640x640으로 뭉개 공을 타원으로 왜곡 — ultralytics 학습 전처리(letterbox)와 불일치 변경: - letterbox(비율 유지 + 회색 114 패딩) 전처리 + 검출 좌표 역매핑 (computeLetterbox/unletterbox 순수 함수, 단위테스트 포함) - 전 프레임 최고 신뢰도 진단 로그 — 검출 0건이 추론 붕괴(점수 ~0)인지 임계값 근접 미달(전처리/클래스 문제)인지 다음 로그에서 즉시 판별
새 검출 모델로 데이터 품질이 확보된(raw=refined 37, 드롭 0%) 후 남은 이음새 3개: [속도] - impact low 시 하드 거부(anchorMismatch)를 신뢰도 페널티(x0.7)로 완화 — 현재 속도는 정제 궤적 회귀라 임팩트 프레임을 계산에 안 쓰는데, 옛 앵커 산식 시절 게이트가 남아 완벽한 회귀 데이터를 통째로 거부 하고 있었다 [핀 충돌] - argmax 바닥값 6% → 4%: 3연속 런에서 위치는 항상 실제 충돌 프레임 (107/118/118)으로 정확, 크기만 5.9~10.2% 변동 — 5.9% 실측이 0.1%p 차로 잘리는 것 방지 [궤적] - 끝 연장(extendCurveEnd): 원거리(17m+)에서 공이 화면상 수 픽셀이라 검출이 끊겨 선이 핀 직전에 멈춤 — 마지막 추적점이 14m 이상이면 핀덱(18.29m)까지 끝 기울기로 연장. 시작 연장과 대칭
파이프라인이 산출하는 구속 신뢰도(회귀 R² × 임팩트 신호 × 릴리즈 신호)를 화면에 노출 — 측정값을 얼마나 믿어도 되는지 사용자가 즉시 판단할 수 있게 한다. 0.75 이상 "신뢰도 높음" / 0.5 이상 "보통" / 미만 "참고용". _StatRow의 기존 badge 슬롯 활용(미사용 warning 해소).
[auth] - Info.plist에 GIDClientID 추가 — 로컬 빌드는 CI와 달리 dart_defines.json 주입이 없어 GOOGLE_IOS_CLIENT_ID가 빈 값이 되고, google_sign_in 7.x가 Info.plist 폴백(GIDClientID)도 못 찾아 "No active configuration" 에러로 Google 로그인 자체가 불가했다. iOS client id는 이미 같은 파일의 URL scheme에 노출된 값(시크릿 아님) [core] - 앱 업데이트 확인을 debug 빌드에서 스킵 — 스토어 미등록 번들 id 조회 에러 로그만 만들고 안내 의미도 없음
[핀 폭발 감지 재설계] - 실기기 프레임 실물 판독: 기존 방식(직전 프레임 diff argmax, 이후 첫-돌파+지속성)이 공의 존 통과(117~119)를 실제 핀 폭발(~128)로 오인 — 공 접근은 씨드 대비 diff에 프레임당 +2%p 완만한 램프를, 폭발은 뚜렷한 급증(+4.9%p) 후 34% 영구 고착을 만든다(실영상 계측) - 새 판정: (1) 창 끝 5프레임 중앙값 >= 15% (영구 변화 게이트 — 공 통과/스파이크는 원상복구되어 탈락) (2) 시점 = 씨드 대비 diff의 최대 단일 프레임 증가(Δd argmax). 실영상에서 128 정확 적중 검증 - legacy(존 미지정) 경로는 기존 동작 유지 [앵커 기반 구속] - 중·원거리 호모그래피 스케일 왜곡(광각 렌즈 + 원거리 원근 민감도) 으로 회귀 구속이 과대(측정 속도가 레인 따라 32→40으로 가속하는 물리적 모순 실측). 릴리즈 직후 y(에로우로 검증되는 근거리)와 핀덱 물리 상수 18.29m, 시간 = 릴리즈→핀 폭발로 산출하는 앵커를 primary로 승격 — 회귀는 교차검증(15% 초과 불일치 시 신뢰도 차감) - FSM-핀 일치 허용창 3/12프레임으로 완화 (FSM은 왜곡 영향으로 이르게 잡히는 실측 반영)
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.
Summary
AnalysisStateMachine이 이미 release~flight 단계에서 누적하던 궤적 데이터(그동안 버려지고 있었음)를 파이프라인에서homography.laneToFrame()으로 프레임좌표 변환해 노출computeCoverRect(BoxFit.cover 대응) +TrajectoryOverlay위젯 — 기존computeContainRect(BoxFit.contain, 레인확인화면용)와 별개, 서로 안 건드림진행 경위
원래 M3(궤적)는 M1/M2 검증 후 마지막 단계였으나 우선순위 변경으로 조기 착수. 브레인스토밍 스킬로 스펙 §11 작성 → writing-plans로 3태스크 플랜 → subagent-driven-development로 태스크별 구현+컨트롤러 직접 재검증 → 최종 전체 브랜치 리뷰(opus, Critical/Important 0건).
Test plan
flutter test test/features/analysis/ test/golden_reference/— 64/64 통과flutter analyze— 0 errors (기존 baseline 10개만)