From b2c1ba2101bc2e74684daa696402927354fe0d19 Mon Sep 17 00:00:00 2001 From: jjangddu Date: Tue, 11 Aug 2026 10:44:00 +0900 Subject: [PATCH] =?UTF-8?q?fix(alignment):=20detach=20=EC=A7=80=EC=97=B0?= =?UTF-8?q?=20=C2=B7=20=ED=9E=8C=ED=8A=B8=20=EC=99=94=EB=8B=A4=EA=B0=94?= =?UTF-8?q?=EB=8B=A4=20fix=20=C2=B7=20V1=20sanity=20+=20Detachment=20OR=20?= =?UTF-8?q?=EA=B7=9C=EC=B9=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 증상 (사용자 보고 2026-08-11 · demo-final): 센서가 아예 분리된 상태인데 · alignment 화면에서 전벽/후벽이 하나씩 잡혀 "위로/아래로" 힌트가 왔다갔다 · 몇 초 기다린 후에야 "분리됨" 팝업이 뜸. 원인 (두 요인 조합): 1) DetachmentDetection AND 규칙 (mean<30 AND std<30) 이 노이즈 상황에 너무 관대. 공기 중 EMI/RF 노이즈로 std 가 30 이상인 경우 흔해 detach 판정 지연. 2) Method C detection 이 노이즈 peak 을 우연히 벽으로 오검출 · alignment computePlacementGuide 가 그 벽으로 힌트 계산 → 왔다갔다. 3) V1 default 흐름에는 0/6 walls sanity check 없음 (V2 전용). 수정: 1) DetachmentDetection: AND → OR mean·std 둘 중 하나만 30 미만이면 detach. 노이즈 std 높아도 mean 낮으면 즉시 판정. 실제 방광 신호는 mean·std 둘 다 100+ 수준이라 false positive 실질 낮음. 2) PlacementGuideView V1 브랜치 sanity check 이식 (V2 line 924-949 로직 동일). 6채널 모두 wall 미검출 → 부착 확인 안내 · Start 후면 waitingForStart 복귀 + 팝업 강제 (2026-08-05 V2 sanity 와 동일 UX). Method C 노이즈 오검출 자체는 별건 · sanity + 강화된 Detachment 로 UX 관점 방어. --- .../vesiscan/managers/DetachmentDetection.kt | 10 +++++++-- .../ui/views/monitoring/PlacementGuideView.kt | 21 +++++++++++++++++++ 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/medithings/vesiscan/managers/DetachmentDetection.kt b/app/src/main/java/com/medithings/vesiscan/managers/DetachmentDetection.kt index af217dc..37942fa 100644 --- a/app/src/main/java/com/medithings/vesiscan/managers/DetachmentDetection.kt +++ b/app/src/main/java/com/medithings/vesiscan/managers/DetachmentDetection.kt @@ -79,8 +79,14 @@ object DetachmentDetection { val meanAbs = meanAbsPerCh.average() val stdVal = stdPerCh.average() - // "and" rule: 둘 다 threshold 미만이면 미부착 - val isDetached = meanAbs < THR_MEAN && stdVal < THR_STD + // 2026-08-11: AND → OR 로 변경. + // 기존 AND: mean·std 둘 다 30 미만이어야 detach. 공기 중 EMI/RF 노이즈로 + // std 가 30 이상인 경우가 흔해 detach 판정이 지연 · 사용자가 몇 초 + // 기다린 후에야 팝업 뜨는 문제. + // 신규 OR: 둘 중 하나만 30 미만이어도 detach. 노이즈로 std 높아도 mean 낮으면 + // 즉시 판정. 실제 신호 약할 때 false positive 위험 있으나 · 방광 신호는 + // mean·std 둘 다 100+ 수준이라 실제로 오탐 가능성 낮음. + val isDetached = meanAbs < THR_MEAN || stdVal < THR_STD return DetachmentResult( isDetached = isDetached, diff --git a/app/src/main/java/com/medithings/vesiscan/ui/views/monitoring/PlacementGuideView.kt b/app/src/main/java/com/medithings/vesiscan/ui/views/monitoring/PlacementGuideView.kt index 2a89d24..9fac660 100644 --- a/app/src/main/java/com/medithings/vesiscan/ui/views/monitoring/PlacementGuideView.kt +++ b/app/src/main/java/com/medithings/vesiscan/ui/views/monitoring/PlacementGuideView.kt @@ -997,6 +997,27 @@ fun PlacementGuideView(appState: AppState) { } } // end of !waitingForStart V2 algorithm } else { + // 2026-08-11: V1 sanity check (V2 와 동일 로직 이식 · line 924-949). + // 6채널 모두 wall 미검출 → 부착 이슈 · alignment advice 무시 · 즉시 + // 부착 확인 안내. Start 후면 Start 화면 복귀 + 팝업 강제. + // DetachmentDetection AND→OR 완화 (2026-08-11) 로 대부분 잡히지만 + // Method C 검출이 노이즈에 우연히 반응하는 경우 이 sanity 가 backstop. + val v1DetectedCount = detected.count { it } + if (v1DetectedCount == 0) { + directionHint = context.getString(R.string.placement_check_attachment) + directionIcon = "" + placementScore = 0 + if (!waitingForStart) { + isContinuousMode = false + waitingForStart = true + prevDetached = true + showDetachAlert = true + bleManager.debugLogger.warn("ALIGN_V1 sanity: 0/6 walls · Start 후 부착 실패 · waitingForStart 복귀 + 팝업") + } else { + bleManager.debugLogger.info("ALIGN_V1 sanity: 0/6 walls · override to attachment check") + } + return@post + } // Sphere-fit 계산 (SWEEP 모드에서 사용, 다른 모드에서는 무시됨) val fitResult = com.medithings.vesiscan.managers.BladderSphereSeek .fitSagittalCircle(allWalls, detected)