AI CCTV 목적을 원론부터 재점검한 결과. 셋 다 새 추론 모델 없이 지금 있는 자세추정·구역·트랙 이력만으로 구현했다. ■ 추락 (fall_from_height) 잡고 있던 것은 같은 높이의 '넘어짐'이었고, 사망 1위 유형인 '떨어짐'은 못 잡았다. GhostIncidentDetector 가 트랙이 이미 위험 자세일 때만 무장하므로 (_DANGEROUS), 발판에 서 있다가 떨어지는 사람은 소실 순간 STANDING 이라 아무것도 안 걸렸다. 판정의 핵심은 bbox 중심이 아니라 하단(y2)이다. 쭈그려 앉으면 중심은 내려가도 발은 그 자리이고, 추락은 발 밑선이 통째로 내려간다 — 중심으로 재면 쭈그려 앉아 작업하는 사람이 전부 추락이 된다. 원근 접근은 y2 도 내려가므로 높이 증가 가드로 따로 막는다. Track.frames 가 90프레임을 들고 있어 별도 버퍼가 없다. 발보는 두 갈래로 나뉘되 한 사고는 한 건이다: 프레임 안에서 넘어짐이 확정되면 유형을 승격하고, 화각 밖으로 사라지면 ghost 무장을 넓혀 그쪽으로 낸다. 심각도 CRITICAL — 척추·두부 손상 주의가 통보에 실리려면 넘어짐(HIGH)과 등급이 갈라져야 한다. 고소 구역은 rules_json 의 elevated 키로 붙어 스키마 변경이 없고, 구역이 없어도 하강 신호만으로 동작한다. ■ 동시 다발 쓰러짐 (multi_down) 같은 구역에서 짧은 시간에 둘이 쓰러진 것은 넘어짐 두 건이 아니라 질식· 가스·폭발이다. 이 기능의 본체는 감지가 아니라 통보 문구다 — 밀폐공간 사망자의 상당수가 구조하러 들어간 사람이므로, 링크를 열기 전에 메일 본문에서 "공기호흡기 없이 진입하지 마십시오"가 읽혀야 한다. Channels::guidance 로 유형별 안내를 붙였고 문구를 테스트로 고정했다(문구가 사라지면 기능이 사라진 것과 같다). 서버에 둔 이유: 가스는 카메라 화각을 존중하지 않아 노드를 넘는 판단이 필요하고, 현장 즉시 대응은 첫 쓰러짐의 경광등이 이미 확보한다. 오탐 방어의 핵심은 근접성 — 넓은 공장에서 2분 안의 무관한 넘어짐 두 건은 실제로 일어나므로 같은 구역이거나 좌표가 가까워야 묶는다. 한 사람이 두 번 넘어진 것은 한 명으로 센다. ■ 구역 프리셋 6종 + max_workers:0 수정 밀폐공간 감시에 필요한 것(제한구역+2인1조+체류상한)은 이미 전부 있었는데 화면이 "최소 인원을 넣으세요"라고만 물어 아무도 그렇게 쓰지 못했다. 안전관리자는 min_workers 가 아니라 밀폐공간을 생각한다. 그 과정에서 잠복 결함 하나: max_workers:0 이 falsy 로 걸러져 "아무도 있으면 안 되는 구역"(크레인 짐 아래·굴착기 선회 반경 = 깔림 사고의 전형)이 통째로 꺼져 있었다. 운영자가 저장해 놓고도 아무 일이 안 일어났다. 감지기·입력란 (min=1)·저장 직렬화(>0)·목록 표시 네 곳에서 막혀 있었다. ■ 검증 pytest 322 passed / phpunit 263 passed. 신규 테스트 30건. 이벤트 유형 2종 추가는 test_event_type_parity 가 강제하는 5곳(엣지 enum · EventValidator · schema CHECK · 프런트 라벨 5파일 · 필터 옵션) 전부 동기화했고 기존 설치용 마이그레이션을 추가했다. ■ Pi 번들 1.1.0 과 달리 이번에는 edge/ 가 바뀌었으므로 Pi 번들 재빌드가 필수다. 낡은 1.0.0 번들은 dist/ 에서 제거했다 — 두면 추락 감지가 빠진 노드가 그대로 납품된다. arm64 + 네트워크 빌드 머신에서 재빌드해야 Pi 납품이 가능하다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EBjuR7DW6PNNMows4KnbYU
199 lines
13 KiB
Markdown
199 lines
13 KiB
Markdown
# CHANGELOG — move-detect (작업장 안전 AI CCTV)
|
|
|
|
버전 표기는 저장소 루트 `VERSION` 파일이 단일 출처입니다.
|
|
납품 산출물 파일명에도 같은 버전이 들어갑니다:
|
|
`move-detect-cafe24-<VERSION>-<YYYYMMDD>.zip` / `move-detect-pi-<VERSION>-<YYYYMMDD>-<arch>.tar.gz`
|
|
|
|
---
|
|
|
|
## 1.2.0 — 2026-09-04 · 추락 · 동시 다발 쓰러짐 · 구역 프리셋
|
|
|
|
AI CCTV 의 목적을 원론부터 다시 점검한 결과입니다. 셋 다 **새 추론 모델 없이** 지금 있는
|
|
자세추정·구역·트랙 이력만으로 구현했습니다.
|
|
|
|
### 추가 — 추락 (`fall_from_height`)
|
|
|
|
이 제품이 잡던 것은 같은 높이에서의 **넘어짐**이었습니다. 산업재해 사망 1위 유형인
|
|
**떨어짐**은 잡지 못했습니다 — `GhostIncidentDetector` 가 트랙이 이미 위험 자세일 때만
|
|
무장하므로, 발판 위에 서 있다가 떨어지는 사람은 소실 순간 정상 자세라 아무것도 걸리지
|
|
않았습니다.
|
|
|
|
- 판정은 bbox 중심이 아니라 **발 밑선(y2)** 으로 합니다. 쭈그려 앉으면 중심은 내려가도 발은
|
|
그 자리이고, 추락은 발 밑선이 통째로 내려갑니다. 중심으로 재면 쭈그려 앉아 작업하는
|
|
사람이 전부 추락이 됩니다. 원근 접근은 높이 증가 가드로 막습니다.
|
|
- 프레임 안에서 확정되면 넘어짐 유형을 **승격**합니다 — 한 번의 사고는 한 건입니다.
|
|
- 화각 밖으로 사라지면 ghost 경로로 발보합니다. 떨어져 나간 사람에게는 이쪽이 유일한 경로입니다.
|
|
- 심각도 **위급**. 척추·두부 손상 가능성이 있어 함부로 옮기면 안 되고, 그 주의가 통보에
|
|
실리려면 넘어짐(높음)과 등급이 갈라져야 합니다.
|
|
- 구역 규칙 `elevated: true` 로 발판·개구부를 표시하면 정확해집니다. 구역이 없어도 동작합니다.
|
|
|
|
### 추가 — 동시 다발 쓰러짐 (`multi_down`)
|
|
|
|
같은 구역에서 짧은 시간에 두 명 이상이 쓰러진 것은 넘어짐 두 건이 아니라 **질식·가스 누출·
|
|
폭발**입니다. 그리고 이때 사람이 가장 많이 죽는 방식은 첫 번째 사람이 왜 쓰러졌는지 모르고
|
|
**구조하러 들어간 사람이 같이 죽는 것**입니다.
|
|
|
|
그래서 이 기능의 본체는 감지가 아니라 **통보 문구**입니다. 메일 본문에 링크를 열기 전에
|
|
"공기호흡기 없이 진입하지 마십시오"가 바로 읽힙니다.
|
|
|
|
- 서버에서 판단합니다 — 가스는 카메라 화각을 존중하지 않고, 현장 즉시 대응은 첫 쓰러짐의
|
|
경광등이 이미 확보합니다.
|
|
- 오탐 방어의 핵심은 **근접성**입니다. 넓은 공장에서 2분 안의 무관한 넘어짐 두 건은 실제로
|
|
일어나므로, 같은 구역이거나 좌표가 가까워야 묶습니다. 한 사람이 두 번 넘어진 것은 한 명입니다.
|
|
|
|
### 추가 — 용도별 구역 프리셋
|
|
|
|
밀폐공간 감시에 필요한 것(제한구역 + 2인1조 + 체류 상한)은 이미 전부 있었는데, 화면이
|
|
"최소 인원을 넣으세요"라고만 물어서 아무도 그렇게 쓰지 못했습니다. 안전관리자는
|
|
`min_workers` 가 아니라 **밀폐공간**을 생각합니다.
|
|
|
|
밀폐공간 · 고소작업 개구부 · 프레스 협착 · **출입 금지(크레인 하부·선회 반경)** ·
|
|
지게차 통로 · 화기작업 6종. 고른 뒤 값을 바꾸는 것은 그대로 허용합니다.
|
|
|
|
### 수정 — "아무도 있으면 안 되는 구역"이 조용히 꺼져 있었습니다
|
|
|
|
`max_workers: 0` 이 falsy 로 걸러져 규칙이 통째로 동작하지 않았습니다. 크레인으로 든 짐
|
|
아래, 굴착기 선회 반경처럼 **아무도 있어서는 안 되는 범위**가 정확히 그 규칙이고 깔림
|
|
사고의 전형입니다. 운영자가 저장해 놓고도 아무 일이 일어나지 않았습니다.
|
|
|
|
네 곳에서 막혀 있었습니다 — 감지기(`and` 검사), 입력란(`min="1"`), 저장 직렬화(`> 0`),
|
|
목록 표시. 전부 고쳤습니다.
|
|
|
|
---
|
|
|
|
## 1.1.0 — 2026-09-04 · 감시 자신의 고장을 실제로 신고하기
|
|
|
|
1.0.0 은 "감지에 대응하는 시스템"이었습니다. 그런데 **감시 장비 자신이 멈춘 것**은 여전히
|
|
아무도 신고하지 않았습니다 — 타입은 있는데 발행하는 코드가 없었습니다. 1.1.0 이 그것을 채웁니다.
|
|
|
|
소개서가 내세우던 "감지 17종"은 그때까지 실제로 15종이었습니다. 이제 17종이 사실입니다.
|
|
|
|
### 재감사에서 잡은 것 (3차, 14건 전량 해소)
|
|
|
|
위 기능을 넣으면서 만든 결함들입니다. 상세는 `docs/delivery-audit.md` 3차 재감사 절.
|
|
|
|
- **통보가 통째로 멈출 수 있었습니다.** 장치 건전성 단계가 예외 처리 밖에 있어, 그 단계에서
|
|
난 오류가 통보와 재시도까지 죽였습니다. 새벽에 넘어진 사람이 통보되지 않고 콘솔은 멀쩡해
|
|
보이는 상태가 매분 반복됩니다. 단계를 격리했습니다.
|
|
- **간헐 고장을 못 잡거나, 반대로 쏟아냈습니다.** 카메라가 깜빡이며 살아났다 죽으면
|
|
(리본 접촉 불량의 전형) 저하 구간이 계속 끊겨 두 시간을 고장 나 있어도 아무것도 올라오지
|
|
않고, 동시에 같은 고장이 수십 건으로 갈라졌습니다. 회복을 '지속된 정상'으로만 인정하고,
|
|
재고지 주기당 1건으로 묶었습니다.
|
|
- **`__device__` 라는 내부 값이 메일·웹훅으로 회사 밖에 나갔습니다.** 이제 어디서나
|
|
"장치 전체"로 표시됩니다.
|
|
- 사건 상세에서 **존재하지 않는 카메라에 위험구역을 저장**할 수 있었습니다(저장되면 아무것도
|
|
지키지 않는 구역이 '활성'으로 남습니다). 막았습니다.
|
|
- 카메라별 위험 순위·카메라 대수·오탐율에 유령 카메라가 섞이던 것을 제외했습니다.
|
|
|
|
### 수정 — 아무도 발행하지 않던 이벤트 2종
|
|
|
|
`monitoring_degraded`(감시 성능 저하)와 `device_fault`(장치 이상)는 타입 정의·서버 검증·
|
|
콘솔 필터·통보 라벨까지 전 구간에 있었으나 **생성하는 코드가 어디에도 없었습니다.** 실제로
|
|
값을 넣는 것은 데모 시더뿐이었고, 소개서가 내세우던 "17종"은 실제로 15종이었습니다.
|
|
|
|
엣지가 낼 수 없는 유형입니다 — 전원이나 회선이 끊긴 노드는 자기가 죽었다고 신고할 수
|
|
없습니다. 그래서 서버가 하트비트의 **부재**로 판정합니다.
|
|
|
|
- `App\Alert\DeviceHealthWatch` 추가. 1분 크론(`dispatch_alerts.php`)의 첫 단계로 실행되어,
|
|
같은 실행 안에서 발행된 장치 이벤트가 곧바로 통보 대상이 됩니다.
|
|
- 장애당 1건. event_uuid 앵커가 장애 구간의 시작(`device_fault` 는 마지막 하트비트 시각)이라
|
|
크론이 60번 돌아도 이벤트는 하나입니다. 복구 후 재발은 별건으로 잡힙니다.
|
|
- 성능 저하는 **지속**을 요구합니다(기본 5분). 한 번 튄 프레임률로 알람이 울리면 운영자는
|
|
이 유형을 무시하게 되고, 그러면 정작 감시가 멈춘 날에도 아무도 보지 않습니다.
|
|
- 한 번도 하트비트를 보낸 적 없는 장치는 **장애가 아닙니다**(미설치 노드). 새 현장 첫날이
|
|
빨간 벽이 되지 않도록.
|
|
- 설정: `device_stale_seconds`(180) · `degraded_sustain_seconds`(300) ·
|
|
`degraded_fps_ratio`(0.6) · `target_fps`(12). 노드를 낮은 프레임으로 운용하는 현장은
|
|
`target_fps` 를 실제 값으로 맞춰야 합니다.
|
|
|
|
장치 단위 이벤트는 카메라가 없으므로 `camera_id` 에 `__device__` 센티넬을 씁니다. 콘솔은
|
|
"장치 전체"로 표시하고, 구역 편집기의 카메라 목록에서는 제외합니다(존재하지 않는 카메라에
|
|
구역을 그리려는 일이 없도록).
|
|
|
|
---
|
|
|
|
## 1.0.0 — 2026-08-18 · 첫 정식 납품 버전
|
|
|
|
0.9.0 까지가 "감지하는 시스템"이었다면, 1.0.0 은 **"감지에 대응하는 시스템"** 입니다.
|
|
그 차이를 만드는 것이 아래 첫 항목입니다.
|
|
|
|
### 추가 — 통보 (이 버전의 이유)
|
|
|
|
이전까지 감지는 브라우저 탭이 열려 있고 사람이 보고 있어야만 전달됐습니다. 새벽 3시에
|
|
넘어진 작업자는 DB 에 행 하나를 남기고 아침을 맞았습니다.
|
|
|
|
- **현장 경광등·부저** (엣지 GPIO). 서버도 인터넷도 거치지 않고 감지한 노드가 직접 울립니다.
|
|
정전·회선 장애로 업링크가 끊긴 순간은 오히려 위험이 높아지는 때이므로, 어떤 현장이든
|
|
이것부터 설정합니다.
|
|
- **메일 · 웹훅** 채널과 1분 주기 디스패처. 알림에는 현장 이미지를 싣지 않고 링크만 보냅니다
|
|
(스냅샷은 개인정보이고, 메일링 리스트로 나가는 순간 인증 프록시의 통제를 벗어납니다).
|
|
- **미확인 경보 에스컬레이션**. 위급/높음이 정해진 시간 동안 확인되지 않으면 그 사실 자체가
|
|
사건이 되어 상위 담당자에게 통보됩니다. "아무도 보지 않았다"가 처음으로 기록에 남습니다.
|
|
|
|
### 추가 — 조용한 실패 드러내기
|
|
|
|
- **카메라 화각 변경 · 가림 · 조도 저하 감지.** 위험구역 판정은 카메라가 어제와 같은 곳을
|
|
본다는 전제 위에 서 있는데, 청소하다 삼각대를 스치면 제한구역이 엉뚱한 바닥을 가리키면서
|
|
시스템은 계속 초록불이었습니다.
|
|
- **저장 공간 부족 경고.** 디스크가 차면 이미지 저장만 조용히 실패하고 화면은 멀쩡해 보입니다.
|
|
- **설정 지연 표시가 실제로 동작**하게 됐습니다. 지표는 있었지만 그것을 켜는 사건이 없었습니다.
|
|
|
|
### 추가 — 관리 화면 (그전에는 DB 직접 수정이 유일한 방법)
|
|
|
|
- 감지 임계값 편집 (허용 범위·단위·설명을 화면이 강제)
|
|
- 통보 경로 설정
|
|
- 정비 중 알람 억제 — 사유 필수, 최대 8시간, 자동 만료.
|
|
**감지와 기록은 그대로 계속되고 통보만 보류**되며, 보류했다는 사실도 기록에 남습니다.
|
|
- 위험성평가 연계 · 교대 인수인계 · 설비 성능 추이 · 설치 점검(`/setup`)
|
|
|
|
### 추가 — 증거와 규정
|
|
|
|
- **감사로그 위·변조 탐지** (해시체인). 수정·삭제·우회삽입이 전부 드러납니다.
|
|
- **사고조사 패키지 1클릭** — 전후 타임라인, 통보 이력, 위험요인, 조치 이력, 서명란.
|
|
인쇄해서 그대로 제출할 수 있습니다.
|
|
- 현장별 현황 (본사 계정)
|
|
|
|
### 변경 — 화면 (UI/UX 점검 21건)
|
|
|
|
- **감지 스트림이 다시 시간순이 됐습니다.** 확인 여부가 순서를 바꾸던 탓에 사고 재구성이
|
|
불가능했고, 확인하면 그 행이 사라지며 아래가 밀려 올라와 다른 이벤트를 잘못 확인할 수
|
|
있었습니다. 확인은 이제 제자리에서 처리됩니다.
|
|
- 이벤트 상세에서 바로 확인/오탐 판정 · 구역 설정에 카메라 선택기 · 날짜로 조회 ·
|
|
경보음 음소거(새 위급 오면 자동 해제) · 일괄 확인(위급 제외) · 키보드 조작
|
|
- 표본이 부족한 수치는 비율 대신 원시 숫자만 보여줍니다. 6건→8건을 "▲33% 악화"로 칠하면
|
|
안전회의에서 반박당하고, 그 순간 그 화면 전체가 신뢰를 잃습니다.
|
|
|
|
### 도구 (`move_detect_app/tools/`, 웹에서 접근 불가)
|
|
|
|
| 도구 | 용도 |
|
|
|------|------|
|
|
| `dispatch_alerts.php` | 알림 발송 (크론 1분) · `--dry-run` 지원 |
|
|
| `maintain.php` | 보존기간 정리 (크론 일 1회) |
|
|
| `backup.php` | DB 덤프 + 사진 아카이브 + 복구 안내 |
|
|
| `verify_audit_chain.php` | 감사로그 무결성 검증 |
|
|
| `seed_demo.php` | 시연·시험용 데모 데이터 (운영 DB 실행 금지) |
|
|
|
|
### 업그레이드 (0.9.x → 1.0.0)
|
|
|
|
`migrations/2026-08-09-safety-platform.sql` 을 적용합니다. **파일 안의 9번 블록은 DB 종류에
|
|
따라 문법이 다릅니다** — MySQL 8.0.16+ / MariaDB / 제약 없음 세 경우의 안내가 파일에
|
|
적혀 있으니 `SELECT VERSION();` 으로 확인 후 맞는 것을 실행하세요.
|
|
|
|
적용 후 `config.php` 에 `public_base_url`, `timezone`, `escalate_after_seconds` 를 추가하고
|
|
(템플릿 참조), 자동실행(크론) 2개를 등록해야 알림이 동작합니다 — README-UPLOAD.md 8번.
|
|
|
|
### 알려진 제약
|
|
|
|
- **보호구 미착용 · 중장비 근접 · 화재 감지**: 감지기 배선과 이벤트 계약은 준비되어 있으나
|
|
**추론 모델이 확보되지 않아 기본 비활성**입니다. 라이선스 조건을 만족하는 가중치와 Hailo
|
|
컴파일이 선행되어야 합니다. 켜져 있지 않다는 사실을 화면에서도 숨기지 않습니다.
|
|
- 실장비 검증 잔여 항목은 `docs/delivery-audit.md` 의 "OFF-HW 잔여" 절을 참조하세요.
|
|
|
|
---
|
|
|
|
## 0.9.0 — 2026-08-08
|
|
|
|
납품 감사(43건) 대응. CSP nonce 도입, 보안 헤더 PHP 이관, /logout CSRF, 계정 잠금 응답에서
|
|
사용자명 열거 제거. 엣지 큐 flush 상한, 타이머 env 전수 검증, 프레임 스킵 시 미블러 얼굴
|
|
유출 차단, systemd 워치독. PDO 통합 테스트 복구, 라우트 테이블 대조, 산출물↔소스 패리티
|
|
검사, CI 신설. 납품물에 version/git_commit/built_at 스탬프.
|