01문서 개요
1.1 이 교재의 목적
학원은 "사람이 모이는 공간"이다. 그런데 원장이 매일 묻는 질문 대부분은 눈으로 확인하거나 사람에게 물어야 답이 나온다.
- 지금 3강의실에 수업 중인가, 비어 있는가?
- 이 강의실 공기가 답답하다. 환기가 필요한가?
- 에어컨과 조명이 빈 방에서 계속 켜져 있지 않은가?
- 시간표상 수업이 있는데 실제로 사람이 있었나?
이 교재의 목적은 두 가지다.
- 센서가 무엇을 측정하고 무엇을 측정하지 못하는지 정확히 이해한다.
- ESP32, HLK-LD2410C, SCD41로 강의실 1개짜리 프로토타입을 직접 만들고, 데이터가 맞는지 검증하는 방법을 익힌다.
센서를 "마법 상자"로 보면 실패한다. 센서는 물리량을 숫자로 바꾸는 장치일 뿐이다. 그 숫자를 "학생 12명이 집중하고 있다"로 해석하는 것은 센서가 아니라 사람의 가정이다. 이 교재는 그 가정을 분리해서 다루는 훈련을 반복한다.
1.2 대상 독자
| 항목 | 내용 |
|---|---|
| 주 독자 | 학원 공간 센서화 사업을 준비하는 창업자 |
| 이미 아는 것 | 프로그래밍 기초(파이썬, 아두이노 수준) |
| 처음 접하는 것 | 센서 원리, 하드웨어 배선, 신호 처리, 오탐·미탐 개념 |
| 이 교재가 주는 것 | 개념 지도, 선택 기준, 실습 경로, 검증 방법, 개인정보 원칙 |
| 이 교재가 주지 않는 것 | 전기·전파 인증 법무 자문, 상용 제품 양산 설계 |
1.3 전제 조건
| 구분 | 전제 |
|---|---|
| 지식 | 변수, 함수, JSON, HTTP 요청이 무엇인지 안다. 옴의 법칙은 몰라도 된다. |
| 도구 | Windows 또는 macOS PC, USB 케이블, Wi-Fi 환경 |
| 부품 | ESP32 DevKit V1, HLK-LD2410C, Sensirion SCD41, 브레드보드, 점퍼선(M-F), USB 케이블, 5V USB 전원 어댑터 |
| 소프트웨어 | Arduino IDE 2.x, Python 3.11 이상 |
| 시간 | 1-4주 학습과 1개 강의실 프로토타입 기준(자세한 것은 17장) |
| 마음가짐 | 측정값을 의심하고, 틀렸을 때 기록하는 습관 |
1.4 학원 센서화의 범위와 비범위
범위(MVP 에서 다루는 것)
| 영역 | 내용 |
|---|---|
| 공간 존재 | 강의실에 사람이 있는가(존재/움직임/정지) |
| 공기 환경 | CO₂, 온도, 습도 |
| 이벤트 | 입실·퇴실 추정, 환기 필요, 센서 고장 |
| 시각화 | 공간별 현재 상태와 시계열 그래프 |
| 검증 | 사람이 직접 센 값과 비교한 오탐·미탐 측정 |
비범위(MVP 에서 제외하는 것)
| 제외 항목 | 이유 |
|---|---|
| 영상 상시 수집 | 학생(미성년자 다수)의 얼굴·행동이 남는다. 개인정보 위험이 가장 크다. |
| 음성 상시 수집 | 대화 내용이 녹음될 수 있다. |
| 개인 식별·추적 | 레이더만으로 신원을 알 수 없고, 알려고 하면 감시가 된다. |
| 집중도·감정 판단 | 센서가 측정하지 못하는 것을 추론으로 포장하게 된다. |
| 정확한 인원 계수 | MVP 부품(LD2410C, SCD41)으로는 불가능하다. 필요하면 다른 센서나 출결 기록을 쓴다. |
| 출결의 법적 근거 | 센서 값은 출결 기록이 아니다. 출결은 별도 검증된 기록이다(2장). |
원칙: 개인정보 최소 수집이 기본값이다. "수집할 수 있으니 수집한다"가 아니라 "꼭 필요한 최소만 수집한다"로 설계한다.
1.5 교재 구성과 읽는 순서
flowchart LR
A[1-2장 개념] --> B[3-4장 센서 지도와 선택]
B --> C[5-6장 레이더 심화]
C --> D[7장 공간 설계]
D --> E[8-14장 구현]
E --> F[15-20장 검증·운영]
기초 개념(1-4장) → 레이더 심화(5-6장) → 공간 설계(7장) → 구현(8-14장) → 검증과 운영(15-20장) 순서다. 급하면 2장, 4장, 7장만 먼저 읽어도 방향은 잡힌다.
1.6 정확성 약속
이 교재는 다음 규칙을 지킨다.
- 제조사 공식 문서를 우선 인용하고 URL과 확인일을 남긴다.
- 확인하지 못한 값은
추정또는미확인 — 구매 전 확인으로 쓴다. - 실험 결과 수치를 지어내지 않는다. 예시는 "예시"로 표시한다.
- 센서 감지값(관측), 인원수 추정(추론), 출결 기록(검증된 사실)을 섞지 않는다.
ESP32 GPIO에 5V 신호를 직접 입력하지 마라. ESP32 는 3.3V 로직이다. 이 경고는 배선이 나오는 모든 장에서 반복한다.
02학원 공간 데이터화의 기본 개념
2.1 세 층의 구분: 관측, 추론, 사실
센서 프로젝트에서 가장 흔한 실수는 서로 다른 근거를 한 덩어리로 취급하는 것이다. 먼저 세 층을 분리한다.
| 층 | 이름 | 정의 | 예시 | 신뢰의 근거 |
|---|---|---|---|---|
| 1 | 직접 관측값 (Observation) | 센서가 물리량을 측정해 낸 원시 값 | co2_ppm = 850, presence = true, 거리 210 cm | 센서 사양과 보정 상태 |
| 2 | 파생 이벤트 (추론, Inference) | 규칙이나 모델이 관측값으로 계산한 해석 | "강의실 사용 중", "환기 필요", "약 12명 추정" | 규칙의 타당성, 검증 실험 |
| 3 | 검증된 사실 (Verified fact) | 사람이나 시스템이 확인해 기록한 것 | 출결 체크, 담당 교사가 센 인원, 수업 일지 | 확인 절차와 책임자 |
flowchart TB
P[물리 세계: 사람, 공기, 전파] --> S[센서]
S --> O["층1 관측값<br/>presence, co2_ppm"]
O --> R[규칙·모델]
R --> E["층2 파생 이벤트<br/>ROOM_OCCUPIED 등"]
V["층3 검증된 사실<br/>출결 기록, 교사 계수"] -. 비교·검증 .-> E
핵심 규칙은 한 줄이다.
층2 는 층3 로 검증하기 전까지 "추정"이다. 층1 이 정확해도 층2 가 틀릴 수 있다.
같은 상황, 세 층으로 쓰기
| 상황 | 층1 관측 | 층2 추론 | 층3 사실 |
|---|---|---|---|
| 오후 4시 3강의실 | presence true, 정지 표적 true, CO₂ 1,350 ppm | 사용 중, 환기 필요 | 출석부에 학생 9명 |
| 방금 환기 후 | CO₂ 하락 중 | 환기 효과 있음(추정) | 교사가 창문을 열었다고 기록 |
| 센서 오류 | 데이터 5분간 없음 | SENSOR_FAULT | 기술자가 전원 케이블 이탈 확인 |
수치는 설명용 예시이며 실측 결과가 아니다.
2.2 직접 관측값의 성질
관측값에도 한계가 있다. 센서 값은 "참값"이 아니라 "센서가 본 값"이다.
| 성질 | 설명 | 학원 예시 |
|---|---|---|
| 오차 | 참값과 센서 값의 차이 | CO₂ 센서 보정이 어긋나면 일정하게 높거나 낮게 나옴 |
| 노이즈 | 무작위 흔들림 | 온도가 0.1도 단위로 흔들림 |
| 지연 | 변화가 값에 반영되기까지의 시간 | CO₂는 공기가 센서에 닿고 나서야 변함 |
| 오탐 (false positive) | 없는데 있다고 말함 | 선풍기, 커튼 움직임을 사람으로 오인 |
| 미탐 (false negative) | 있는데 없다고 말함 | 정지한 사람을 놓침 |
| 결측 | 값이 아예 없음 | Wi-Fi 끊김, 센서 초기화 실패 |
관측값을 저장할 때는 "측정 실패"를 숫자 0 이 아니라 null 과 품질 플래그(quality)로 구분한다. 0 은 "측정했는데 0"이고, null 은 "측정 못 함"이다(9장).
2.3 CO₂ 만으로 인원수를 계산할 수 없다
흔한 오해가 있다. "CO₂ 농도는 사람 수에 비례하니 인원을 알 수 있다."
사람은 숨을 쉴 때 CO₂ 를 내뿜는다. 그러므로 CO₂ 는 사람 존재의 간접 신호다. 그러나 인원수를 계산하려면 다음을 알아야 한다.
| 필요한 정보 | 왜 변수인가 |
|---|---|
| 방 부피 | 같은 사람 수라도 큰 방은 천천히 오른다 |
| 환기량 | 문·창문·공조 상태에 따라 CO₂ 가 빠져나감 |
| 외기 CO₂ 농도 | 기준선이 장소·시간마다 다름 |
| 개인별 배출량 | 나이, 활동량, 체격에 따라 다름 |
| 센서 위치 | 출입구 근처인지 구석인지에 따라 값이 다름 |
| 시간 | 농도는 천천히 변해서 방금 나간 사람이 반영되지 않음 |
이 변수들을 모르는 상태에서 인원 = (현재 CO₂ - 외기) / 상수 같은 식은 근거가 없다. CO₂ 는 다음 용도로만 쓴다.
- 환기 필요 판단(
VENTILATION_NEEDED) - 사용 여부의 보조 신호(퓨전, 8장)
- 추세를 보는 지표
CO₂ 만으로 정확한 인원수를 계산할 수 없다. 인원 추정에 쓰려면 방마다 사람이 직접 센 값과 비교하는 교정 실험이 필요하고, 그 결과도 "이 방, 이 조건에서의 대략적 경향"일 뿐이다(15장).
2.4 레이더만으로 신원·집중도를 알 수 없다
mmWave 레이더(5장)는 "거기에 무언가 움직이거나 반사하는 물체가 있다"를 알려 준다. 그것이 누구인지는 알려 주지 않는다.
| 질문 | 레이더(LD2410C 등)로 가능한가 |
|---|---|
| 사람이 있는가 | 가능(존재 감지, 한계는 6-7장) |
| 움직이는가, 정지해 있는가 | 가능(제품 기능 범위 내) |
| 대략 어느 거리인가 | 가능(해상도 제한 있음) |
| 몇 명인가 | LD2410C 는 불가. 다중 표적 제품도 제한적 |
| 누구인가 | 불가 |
| 집중하고 있는가 | 불가 |
| 졸고 있는가 | 불가 |
| 공부하는가, 휴대폰을 보는가 | 불가 |
"움직임이 적다 = 집중하고 있다"는 센서가 측정한 사실이 아니라 근거 없는 해석이다. 이 교재는 집중도 판단을 목표로 삼지 않는다. 개인 행동 감시로 이어질 수 있고, 학부모·학생과의 신뢰도 해친다.
레이더만으로 신원이나 집중도를 판단할 수 없다.
2.5 이벤트란 무엇인가
이벤트(Event)는 "상태가 바뀌었거나 조건이 충족되었다는 기록"이다. 관측값은 계속 쌓이지만, 이벤트는 의미 있는 순간만 남긴다.
| 용어 | 정의 | 예시 |
|---|---|---|
| 관측(Observation) | 센서가 보낸 원시 측정값 | 30초마다 오는 JSON |
| 상태(State) | 규칙이 계산한 공간의 현재 상황 | "3강의실: 사용 중" |
| 이벤트(Event) | 상태 변화나 조건 충족의 기록 | ROOM_OCCUPIED 시작 시각 16:02 |
이 프로젝트의 기본 이벤트 4종(상세는 8장, 14장, 별도 파일 08_event_rules.md)은 다음과 같다.
| 이벤트 | 조건(요약) | 해제 |
|---|---|---|
ROOM_OCCUPIED | 최근 5분 창에서 presence true 비율 60% 이상 | EMPTY 성립 시 |
ROOM_EMPTY | 최근 10분 presence true 없음 | OCCUPIED 성립 시 |
VENTILATION_NEEDED | co2_ppm 1200 초과 상태 10분 지속 | co2_ppm 1000 미만 10분 지속 |
SENSOR_FAULT | 장치 데이터 5분 이상 미수신 또는 quality false 지속 | 정상 수신 재개 |
시간 창과 임계값은 초기값이며 실측 후 조정한다. 이 값들이 정답이라는 뜻이 아니다.
이벤트가 필요한 이유
- 원장은 30초마다 쌓인 숫자를 읽지 않는다. "환기 필요"라는 한 줄을 읽는다.
- 이벤트는 시작과 끝이 있어 지속 시간을 계산할 수 있다("3강의실 어제 총 사용 시간").
- 같은 기록에 근거(
evidence)를 붙이면 "왜 그렇게 판단했는지" 되짚을 수 있다.
2.6 디지털 트윈 개념
디지털 트윈(digital twin)은 실제 공간·설비의 상태를 소프트웨어에 비춰 둔 "디지털 쌍둥이"다. 거창한 3D 모델을 뜻하지 않는다.
| 수준 | 내용 | 이 교재의 위치 |
|---|---|---|
| 0 | 실제 세계만 있음 (원장의 눈과 기억) | 출발점 |
| 1 | 공간 목록과 현재 상태 표 | MVP 목표 |
| 2 | 시계열 이력과 이벤트 타임라인 | MVP 목표 |
| 3 | 시간표·출결과 연동, 예측·최적화 | 확장 단계 (18장) |
| 4 | 3D 시각화·시뮬레이션 | 범위 밖 |
MVP 의 디지털 트윈은 다음 세 가지다.
- 학원(
academies) → 공간(spaces) → 장치(devices) 계층 구조 - 공간별 현재 상태와 최근 관측 이력
- 상태 변화를 기록한 이벤트(
events) 목록
flowchart LR
A[학원] --> S1[3강의실]
A --> S2[입구]
A --> S3[사무실]
S1 --> D1["장치 academy_room_A_01"]
D1 --> O1[관측 시계열]
S1 --> ST1[현재 상태]
ST1 --> EV1[이벤트 기록]
트윈이 현실과 어긋나는 경우(센서가 떨어졌는데 서버는 "비어 있음"으로 표시)를 항상 가정한다. 그래서 SENSOR_FAULT 이벤트가 있다.
2.7 데이터화의 위험: 숫자가 사실처럼 보인다
대시보드에 "현재 인원 12명"이라고 크게 표시하면 읽는 사람은 사실로 받아들인다. 그것이 추정이라면 UI 에도 "추정"이라고 쓴다. 층1, 층2, 층3 를 UI 에서도 분리하는 것이 설계 원칙이다.
| 나쁜 표시 | 좋은 표시 |
|---|---|
| 현재 인원 12명 | 사용 중 (센서 감지). 출석부 인원은 시간표 연동 시 별도 표시 |
| 집중도 높음 | (표시하지 않음) |
| 환기 불필요 | CO₂ 850 ppm (관측), 환기 필요 이벤트 없음 |
03센서 기술 지도
이 장은 학원에서 쓸 수 있는 센서 기술을 계열별로 훑는다. 각 센서마다 측정 원리, 출력값, 해상도·샘플링, 정확도와 오차 원인, 설치 조건, 개인정보 위험을 정리한다.
일반 수치 표기 원칙: 이 장의 수치는 계열의 일반적 경향을 설명하는 것이다. 제품마다 다르므로 구매 전 해당 제품의 공식 데이터시트를 확인해야 한다. 구체 제품 값은 6장과 별도 문서에서 출처와 함께 다룬다.
3.0 데이터 유형 먼저 이해하기
센서가 내놓는 데이터의 형태에 따라 처리 방법과 개인정보 위험이 달라진다.
| 유형 | 설명 | 예시 | 처리 난이도 | 개인정보 민감도 |
|---|---|---|---|---|
| Boolean | 참/거짓 하나 | PIR 감지됨, 문 열림 | 매우 낮음 | 낮음 |
| scalar | 숫자 하나 | CO₂ ppm, 온도, 거리 | 낮음 | 낮음 |
| vector | 숫자 묶음 | 표적의 x,y 좌표와 속도, 거리 구간별 에너지 | 중간 | 중간 |
| time series | 시간에 따라 쌓인 값 | 30초 간격 CO₂ 기록 | 중간 | 맥락에 따라 다름 |
| point cloud | 3차원 점들의 집합 | 레이더·LiDAR 점군 | 높음 | 중간-높음 |
| image / audio | 영상, 음성 | 카메라, 마이크 | 매우 높음 | 높음 |
flowchart LR
B[Boolean] --> S[scalar] --> V[vector] --> T[time series] --> PC[point cloud] --> IA[image / audio]
B -. 정보량·처리 부담·개인정보 위험이 커지는 방향 .-> IA
일반적으로 오른쪽으로 갈수록 풍부한 정보를 주지만 처리가 어렵고 개인정보 위험이 커진다. 목적에 필요한 가장 왼쪽 유형을 고르는 것이 기본 전략이다.
3.1 전파 계열
3.1.1 mmWave 레이더
| 항목 | 내용 |
|---|---|
| 측정 원리 | 전파를 쏘고 반사파를 받아 거리·속도·각도를 추정(5장) |
| 대표 대역 | 24GHz, 60GHz 등 |
| 출력값 | 제품에 따라 존재 Boolean, 거리 scalar, 표적 vector, 점군(point cloud), 원시 신호까지 다양 |
| 해상도·샘플링 | 제품·모드마다 다름. 가공 출력 모듈은 초당 수 회에서 수십 회 수준이 일반적(추정, 제품별 확인) |
| 정확도·오차 원인 | 다중 경로 반사, 선풍기·커튼 같은 움직임, 금속 반사체, 벽·가구 가림, 설치 각도 |
| 설치 조건 | 전원 필요. 감지 방향에 금속·큰 반사체가 없을 것. 플라스틱 커버 너머 동작 가능한 경우가 많으나 제품별 확인 |
| 개인정보 위험 | 영상 없음. 신원 식별 불가. 다만 위치·움직임 이력은 행동 패턴 정보가 될 수 있어 목적 제한 필요 |
장점은 정지한 사람을 미세 움직임(호흡 등)으로 감지할 수 있는 제품이 있다는 점이다. 단점은 제품마다 출력이 매우 다르다는 점이다(5.7절).
3.1.2 UWB (Ultra-Wideband)
| 항목 | 내용 |
|---|---|
| 측정 원리 | 매우 짧은 펄스의 도착 시간으로 태그와 앵커 사이 거리 측정(ToF 방식), 여러 앵커로 위치 산출 |
| 출력값 | 태그 위치(vector), 거리(scalar) |
| 해상도 | 수십 cm 급 정확도를 내세우는 제품이 많음(추정, 환경에 따라 달라짐) |
| 오차 원인 | 벽·사람 몸에 의한 신호 가림(NLOS), 앵커 배치 |
| 설치 조건 | 공간에 앵커 여러 개 설치. 사람이 태그를 지녀야 함 |
| 개인정보 위험 | 태그 소지자의 위치를 지속 추적하므로 위험이 큼. 동의와 목적 제한이 필수 |
개인 식별이 가능하다는 것은 장점이자 위험이다. 학생에게 태그를 지급해 위치를 추적하는 것은 MVP 범위가 아니다.
3.1.3 RFID
| 항목 | 내용 |
|---|---|
| 측정 원리 | 리더가 보낸 전파에 태그(카드·스티커)가 응답하여 고유 ID 전달 |
| 종류 | 저주파(LF), 고주파(HF, NFC 포함), 초고주파(UHF) |
| 출력값 | 태그 ID (문자열), 경우에 따라 신호 세기(RSSI) |
| 인식 거리 | HF 는 보통 수 cm, UHF 는 더 멀 수 있음(제품별 확인) |
| 오차 원인 | 금속·액체 근처 인식률 저하, 태그 방향 |
| 설치 조건 | 리더 전원과 통신, 태그 배포 |
| 개인정보 위험 | 태그 ID 가 사람과 연결되면 개인정보. 접촉·근접 시점만 기록하면 위험이 상대적으로 낮음 |
RFID 는 "누가 태그했는가"를 알려 주지만 "지금 방에 있는가"는 알려 주지 않는다. 출입 시점 기록용이다.
3.2 광학 계열
3.2.1 PIR (수동 적외선)
| 항목 | 내용 |
|---|---|
| 측정 원리 | 사람 체온이 만드는 적외선의 변화를 감지. 움직임 감지 |
| 출력값 | Boolean (움직임 있음/없음), 일부는 아날로그 |
| 해상도·샘플링 | 거리 정보 없음. 반응은 빠르나 한번 감지 후 일정 시간 출력을 유지하는 제품이 많음 |
| 오차 원인 | 정지한 사람은 놓침(미탐), 햇빛·히터·에어컨 바람에 오탐, 유리 너머 불투과 |
| 설치 조건 | 시야각 내 장애물 없을 것. 천장 또는 벽 |
| 개인정보 위험 | 매우 낮음 |
| 비고 | 가격이 낮고 단순하다. 조용히 앉아 공부하는 강의실에는 단독으로 부적합 |
3.2.2 ToF (Time of Flight) 거리 센서
| 항목 | 내용 |
|---|---|
| 측정 원리 | 적외선 레이저 펄스가 물체에 반사돼 돌아오는 시간으로 거리 측정 |
| 출력값 | 거리 scalar. 다중 구역(multi-zone) 제품은 격자형 거리 값(vector) |
| 해상도 | 단일 포인트부터 수 x 수 격자까지 제품별로 다름(제품별 확인) |
| 오차 원인 | 검은색·반짝이는 표면, 강한 햇빛, 유리 |
| 설치 조건 | 출입구 위 천장에서 아래로 향해 지나가는 사람의 높이를 측정하는 방식으로 쓰임 |
| 개인정보 위험 | 낮음. 해상도가 높아져도 얼굴을 알아볼 수준이 아니나, 높이·체형 정보는 얻을 수 있음 |
| 비고 | 출입구 통과 계수에 쓸 수 있으나, 두 명이 나란히 지나가면 오차가 난다 |
3.2.3 LiDAR
| 항목 | 내용 |
|---|---|
| 측정 원리 | 레이저를 회전·스캔하며 ToF 로 거리를 재서 2D 또는 3D 점군 생성 |
| 출력값 | 점군(point cloud) |
| 해상도·샘플링 | 각도 해상도와 회전 속도에 따라 다름. 제품별 확인 |
| 오차 원인 | 투명·반사 표면, 햇빛 |
| 설치 조건 | 시야 확보, 전원·통신. 상대적으로 고가(미확인 — 구매 전 확인) |
| 개인정보 위험 | 얼굴은 안 보이나 사람의 위치·동선·형태를 정밀 추적할 수 있음. 목적 제한 필요 |
| 비고 | 학원 MVP 에는 과한 사양. 확장 논의용 |
3.2.4 카메라
| 항목 | 내용 |
|---|---|
| 측정 원리 | 이미지 센서가 가시광(또는 적외선)을 영상으로 기록 |
| 출력값 | image / video |
| 인원 계수 능력 | 이미지 처리(컴퓨터 비전)로 상대적으로 높은 정확도를 낼 수 있음. 단 가림·조명·각도 영향 |
| 오차 원인 | 조명, 가림, 학습 데이터와 환경 차이 |
| 설치 조건 | 시야, 조명, 전원, 대역폭, 연산 자원 |
| 개인정보 위험 | 가장 높음. 얼굴·행동·복장이 기록됨. 미성년자 포함 |
| 비고 | 이 교재에서는 상시 수집 제외. 법적·윤리적 검토가 선행되어야 한다 |
"엣지에서 영상은 버리고 인원수만 보낸다" 방식도 있지만, 장치가 영상을 처리하는 순간 개인정보 처리가 된다. 해당 방식도 MVP 범위 밖이다.
3.3 환경 계열
3.3.1 NDIR CO₂ 센서
| 항목 | 내용 |
|---|---|
| 측정 원리 | 적외선이 공기를 지날 때 CO₂ 가 특정 파장을 흡수하는 정도로 농도 측정(NDIR: 비분산 적외선) |
| 변형 | 광음향(photoacoustic) 방식 등 다른 원리의 제품도 있음 (SCD41 은 제조사가 광음향 NDIR 방식이라 설명 — 구매 전 데이터시트로 확인) |
| 출력값 | CO₂ ppm (scalar) |
| 샘플링 | 보통 수 초 간격. 제품별 확인 |
| 오차 원인 | 보정(캘리브레이션) 편차, 온습도, 센서 위치, 기압 |
| 설치 조건 | 공기 흐름이 있는 곳. 사람 입김이 직접 닿는 위치, 창문·문·에어컨 송풍구 바로 앞은 피함 |
| 개인정보 위험 | 낮음 |
| 한계 | 반응이 느림. CO₂ 만으로 인원수 계산 불가(2.3절) |
"CO₂ 센서" 중 값싼 MOX 방식은 실제 CO₂ 가 아니라 가스 반응으로 CO₂ 를 "추정"하는 경우가 있다(eCO₂). 학습용이라도 실제 NDIR 계열인지 확인한다.
3.3.2 온습도 센서
| 항목 | 내용 |
|---|---|
| 측정 원리 | 정전용량형 습도 소자, 반도체 온도 소자 |
| 출력값 | 온도 scalar, 상대습도 scalar |
| 오차 원인 | 센서 자체 발열, 직사광선, 보드의 열 |
| 설치 조건 | 발열 부품(ESP32, 전원부)에서 떨어뜨리기 |
| 개인정보 위험 | 낮음 |
| 비고 | SCD41 에 온습도 측정이 포함되어 있으나 CO₂ 보정용 보조 값 성격이 있다. 정밀 환경 측정이 필요하면 별도 센서를 검토 |
3.3.3 VOC / 가스 센서
| 항목 | 내용 |
|---|---|
| 측정 원리 | 금속산화물(MOX) 소자의 저항이 가스에 따라 변함 |
| 출력값 | 지수(index) 또는 상대 값. 제품에 따라 ppb 환산 값 |
| 오차 원인 | 종류 구분이 어려움, 습도·시간에 따른 드리프트 |
| 설치 조건 | 공기 흐름 |
| 개인정보 위험 | 낮음 |
| 비고 | "냄새가 변했다"는 추세 지표에 적합. 정확한 농도 측정 장비가 아니다 |
3.3.4 미세먼지 센서
| 항목 | 내용 |
|---|---|
| 측정 원리 | 레이저 산란: 공기 중 입자가 레이저를 산란시키는 양을 측정 |
| 출력값 | PM1.0, PM2.5, PM10 질량 농도 (scalar) |
| 오차 원인 | 습도, 입자 종류, 팬 수명 |
| 설치 조건 | 팬으로 공기를 흡입하므로 소음과 먼지 쌓임에 유의 |
| 개인정보 위험 | 낮음 |
3.4 음향·진동 계열
| 항목 | 내용 |
|---|---|
| 측정 원리 | 마이크는 음압 변화, 진동 센서는 가속도 변화를 전기 신호로 변환 |
| 출력값 | 소음 수준(dB) scalar 또는 오디오 파형(audio) |
| 오차 원인 | 마이크 보정, 위치, 배경 소음 |
| 설치 조건 | 위치와 방향에 민감 |
| 개인정보 위험 | 파형을 저장하면 대화 내용이 남는다 — 높음. 장치 내에서 dB 값만 계산하고 파형을 버리는 설계도 가능하나, 장치가 음성을 처리한다는 사실 자체가 우려가 된다 |
| 비고 | 이 교재에서는 음성 상시 수집 제외. 소음 수준(dB)만 필요한 경우도 전용 소음 센서 모듈 검토 후 별도 동의 절차 |
진동 센서(가속도계)는 문이 열리고 닫힘, 책상 충격 등을 감지할 수 있으나, 사람 존재 판단에는 간접적이다.
3.5 전기·접촉 계열
| 센서 | 측정 원리 | 출력 | 학원 용도 | 개인정보 위험 |
|---|---|---|---|---|
| 도어 마그네틱(리드) 센서 | 자석이 가까이 있으면 접점이 닫힘 | Boolean | 문 열림/닫힘 | 낮음 |
| 압력·좌석 센서 | 눌림에 따라 저항 변화 | Boolean 또는 scalar | 좌석 점유 | 낮음-중간(좌석이 특정인과 연결되면 개인정보) |
| 전류 센서 | 도체 주변 자기장 또는 션트 저항 | 전류 scalar | 조명·에어컨 가동 여부 | 낮음 |
| 스위치/버튼 | 접점 | Boolean | 수동 입력 | 낮음 |
접촉·전기 센서는 단순하고 신뢰도가 높다. "문이 열렸다"는 거의 사실에 가까운 관측이다. 단, 문 열림 = 사람 출입은 아니다(추론).
3.6 출입 인증 계열 (QR / NFC)
| 항목 | 내용 |
|---|---|
| 측정 원리 | QR: 카메라/스캐너가 코드 판독. NFC: 근거리 무선으로 ID 읽기 |
| 출력값 | 인증 토큰, 학생 ID (문자열) |
| 오차 원인 | 스캔 환경(밝기), 카드 훼손 |
| 설치 조건 | 입구 단말, 서버와 통신 |
| 개인정보 위험 | ID 가 사람과 연결되므로 개인정보에 해당. 접근 권한, 보관 기간, 동의 절차 필요 |
| 비고 | 출결 기록(층3, 검증된 사실)의 후보 소스. 센서 이벤트를 검증할 때 비교 기준이 됨 |
QR/NFC 는 센서라기보다 "본인이 입장했음을 확인하는 절차"에 가깝다. 센서 관측(층1)과 출결(층3)을 이으면 오탐 검증이 가능해지지만, 연동은 MVP 이후 의사결정 사항이다(17장).
3.7 비교표
비교 기준은 "일반적 경향"이다. 제품·설치·환경에 따라 달라지므로 실험으로 검증해야 한다. 가격은 구체 금액을 쓰지 않고 상대적 구간으로 표시하며, 실제 가격은 구매 전 확인한다.
| 센서 | 정지 인원 감지 | 인원 계수 | 개인 식별 | 다중 인원·가림 대응 | 가격대(상대) | 개발 난이도 |
|---|---|---|---|---|---|---|
| PIR | 약함(정지 시 놓침) | 불가 | 불가 | 약함 | 낮음 | 매우 낮음 |
| mmWave 존재 감지 모듈 (LD2410C 류) | 가능(제품 기능 범위) | 불가 | 불가 | 개별 구분 불가, 가림에 중간 | 낮음-중간 | 낮음 |
| mmWave 다중 표적 모듈 (LD2450 류) | 제한적 | 제한적(표적 수 한계) | 불가 | 제한적 | 낮음-중간 | 중간 |
| mmWave 개발 보드 (IWR6843 류) | 가능(자체 알고리즘 구현 시) | 알고리즘에 따라 가능성 | 불가 | 중간(구현 의존) | 높음 | 높음 |
| ToF 다중 구역 | 정지 시 가능 | 출입구 통과 계수에 제한적 | 불가 | 약함-중간 | 낮음-중간 | 중간 |
| LiDAR | 가능 | 가능성 있음 | 불가 | 중간 | 높음 | 높음 |
| 카메라 | 가능 | 가능성 높음 | 가능(위험) | 상대적으로 좋음 | 낮음-중간 | 높음 |
| NDIR CO₂ | 간접 신호 | 불가 | 불가 | 해당 없음 | 중간 | 낮음 |
| UWB | 태그 소지자만 | 태그 수만큼 | 가능(태그 연결 시) | 앵커 배치에 의존 | 높음 | 높음 |
| RFID/NFC | 해당 없음 | 태그 수만큼(출입 시점) | 가능(태그 연결 시) | 해당 없음 | 낮음-중간 | 낮음-중간 |
| 마이크 | 소음으로 간접 | 불가 | 가능(음성 저장 시) | 약함 | 낮음 | 중간 |
| 도어 센서 | 해당 없음 | 불가 | 불가 | 해당 없음 | 매우 낮음 | 매우 낮음 |
비교표 읽는 법
- "정지 인원 감지"는 조용히 앉은 학생을 놓치지 않는가이다. 강의실에서 가장 중요한 항목이다.
- "인원 계수"와 "개인 식별"은 다르다. 인원 계수만 필요하면 식별 능력이 있는 센서를 쓰지 않는 편이 낫다.
- 이 표의 어느 칸도 실측 결과가 아니다. 우리 강의실에서 성능은 15장의 실험으로 확인한다.
04센서 선택의 공학적 기준
4.1 선택 절차: 질문에서 시작한다
센서부터 고르면 실패한다. 먼저 "어떤 결정을 내리려는가"를 쓴다.
flowchart TB
Q[1 결정할 질문] --> M[2 필요한 정보 유형]
M --> C[3 후보 센서]
C --> F[4 기준별 평가]
F --> P[5 소규모 실험으로 검증]
P --> D[6 채택 / 폐기]
| 단계 | 예시 |
|---|---|
| 질문 | 빈 강의실의 에어컨을 자동으로 끄고 싶다 |
| 필요한 정보 | 사람이 있는가(Boolean 수준이면 충분). 몇 명인지는 불필요 |
| 후보 | PIR, mmWave 존재 감지, 카메라 |
| 평가 | PIR 은 정지 학생 미탐. 카메라는 프라이버시 위험. mmWave 존재 감지가 균형 |
| 검증 | 한 강의실에서 2주 측정 후 오탐·미탐 확인 |
필요한 정보가 "존재 여부"인데 "인원 수"를 측정하는 센서를 사면 비용·복잡도·개인정보 위험만 늘어난다.
4.2 여덟 가지 기준
| 기준 | 질문 | 확인 위치 |
|---|---|---|
| 측정 범위 | 우리 방 크기를 덮는가 | 데이터시트의 최대 거리, 각도 |
| 정확도 | 결정에 필요한 오차 이내인가 | 데이터시트, 실험 |
| 샘플링 | 변화를 놓치지 않을 속도인가 | 출력 주기, 응답 시간 |
| 설치 | 천장/벽, 전원 위치, 가림, 간섭 | 현장 답사 |
| 전력 | USB 5V 로 구동되는가, 상시 전원 가능한가 | 데이터시트의 전압·전류 |
| 통신 | 무엇으로 연결되는가(UART, I²C, Wi-Fi) | 인터페이스 사양 |
| 가격 | 방 개수만큼 곱했을 때 감당 가능한가 | 시장 조사(조사일 명시) |
| 프라이버시 | 영상·음성·식별 정보를 만드는가 | 3장의 개인정보 위험 항목 |
4.2.1 측정 범위
- 최대 감지 거리만 보지 말고 방의 실제 형상에 맞춰 본다. 책상, 칠판, 벽 때문에 유효 범위는 줄어든다.
- 각도(화각)는 설치 위치에 따라 사각지대를 만든다(7장).
- "최대 5 m"는 이상적 조건의 값일 수 있다. 정지 표적은 움직이는 표적보다 감지 거리가 짧은 제품이 많으므로 데이터시트에서 구분해 읽는다.
4.2.2 정확도
- 정확도는 "오차의 크기"이고, 이 프로젝트에서 더 중요한 것은 오탐·미탐 비율이다.
- 오탐과 미탐은 서로 교환 관계다. 감도를 높이면 미탐이 줄고 오탐이 늘어난다. 어느 쪽이 더 곤란한지 먼저 정한다.
| 시나리오 | 더 곤란한 오류 | 이유 |
|---|---|---|
| 빈 방 에어컨 자동 종료 | 미탐 (사람이 있는데 꺼짐) | 학생이 불편하다 |
| 사용 시간 통계 | 균형 | 장기 평균이 중요 |
| 보안·야간 침입 알림 | 미탐 | 놓치면 안 됨 |
| 원장 알림 | 오탐 | 잦은 오알림은 신뢰를 잃음 |
4.2.3 샘플링
| 대상 | 변화 속도 | 필요한 주기(일반적 판단) |
|---|---|---|
| 입퇴실 | 수 초-수 분 | 수 초 단위 |
| 공간 사용 여부 | 분 단위 | 30초 정도면 충분 |
| CO₂ | 수 분 단위 | 수십 초 |
| 온습도 | 수 분-수십 분 | 수십 초 이상 |
이 프로젝트는 전송 주기를 30초로 고정하고 presence 상태 변화 시 즉시 1회 전송한다(9장, 10장). 센서 내부 측정 주기와 서버 전송 주기는 다르다는 점에 주의한다.
4.2.4 설치
| 점검 항목 | 내용 |
|---|---|
| 위치 | 천장 중앙 / 모서리 벽 상단 / 책상 위 |
| 전원 | 가까운 콘센트, USB 전원 어댑터 |
| 간섭 | 에어컨 바람, 선풍기, 커튼, 금속 캐비닛 |
| 외관 | 눈에 띄는지, 학생이 만질 수 있는지 |
| 유지보수 | 천장 위 설치는 교체가 어려움 |
4.2.5 전력
- 상시 전원이 있는 강의실 센서는 USB 5V 어댑터로 충분한 경우가 많다.
- 배터리 센서(도어 센서 등)는 전력 소모와 교체 주기를 따로 계산한다.
- 센서마다 허용 전압이 다르다. 예: SCD41 은 3.3V 이며 5V 를 걸지 않는다. LD2410C 는 공식 소개 페이지 기준 5-12V 공급이다(6장).
4.2.6 통신
| 방식 | 특징 | 이 프로젝트 사용처 |
|---|---|---|
| UART | 2선 직렬 통신. 속도 설정 필요 | ESP32 ↔ LD2410C |
| I²C | 2선, 주소로 장치 구분 | ESP32 ↔ SCD41 (주소 0x62) |
| Wi-Fi | ESP32 내장 | 장치 → 서버 HTTP POST |
| GPIO | 단순 high/low | LD2410C 의 OUT 핀(옵션) |
4.2.7 가격
- 가격은 부품 단가 외에 방 수 x 단가 + 케이스·전원·설치 인건비·유지보수를 본다.
- 이 교재는 가격을 알리익스프레스·국내 오픈마켓 기준 범위로 적고 조사일을 표기한다. 미조사 시
미확인이다(18장).
4.2.8 프라이버시
- 센서는 아래 순서로 위험이 커진다: 환경 센서 < 접점 센서 < PIR < 레이더 < LiDAR < 카메라·마이크.
- "개인정보를 수집하지 않는다"고 말하려면 무엇이 저장되는지를 정확히 설명할 수 있어야 한다. 위치 좌표 이력도 행동 정보가 될 수 있다.
- 기본값은 최소 수집이다(16장).
4.3 학원 시나리오별 판단 예시
아래는 "무엇을 쓰면 좋은가"의 판단 근거를 보여 주는 예시이며, 실측 결과가 아니다.
시나리오 A: 빈 강의실 감지 (에어컨/조명 절약)
| 항목 | 판단 |
|---|---|
| 필요 정보 | 존재 여부 (Boolean) |
| 부적합 | PIR 단독 (정지 학생을 놓침), 카메라 (프라이버시) |
| 후보 | mmWave 존재 감지(LD2410C), CO₂ 보조 |
| 주의 | 미탐 시 수업 중 에어컨이 꺼질 수 있으므로 자동 제어는 지연 시간을 길게 잡고 수동 해제를 둔다 |
| 검증 | 수업 중 정지 상태의 학생을 놓치지 않는지 확인(15장) |
시나리오 B: 환기 알림
| 항목 | 판단 |
|---|---|
| 필요 정보 | 공기 질 추세 |
| 후보 | NDIR CO₂ (SCD41) |
| 주의 | 환기 알림은 인원 계수 목적이 아니다. 임계값은 초기값이며 현장에서 조정 |
| 검증 | 창문을 열었을 때 값이 내려가는지 확인 |
시나리오 C: 출입구 통과 계수
| 항목 | 판단 |
|---|---|
| 필요 정보 | 들어온 사람 수와 나간 사람 수 |
| 후보 | ToF 다중 구역, 출입구용 양방향 계수 센서, 출입 인증(QR/NFC) |
| 부적합 | LD2410C (방향·개수 판별 불가) |
| 주의 | 둘이 동시에 지나가면 한 명으로 셈 |
시나리오 D: 정확한 출석 확인
| 항목 | 판단 |
|---|---|
| 필요 정보 | 누가 출석했는가 |
| 정답 | 출결 시스템 (QR/NFC/교사 확인). 센서는 출결을 대신하지 않는다 |
| 센서 역할 | 출결 기록과 비교하여 센서 값을 검증 |
시나리오 E: 화장실·탈의 공간
| 항목 | 판단 |
|---|---|
| 필요 정보 | 사용 중 여부, 청소 시점 |
| 후보 | 도어 센서(잠금 상태), 환기 센서 |
| 금지 | 카메라, 마이크. 개인 위치 추적 |
| 주의 | 레이더를 쓰더라도 존재 여부 외 데이터를 저장하지 않고, 학부모·학생에게 안내 |
시나리오 F: 학생 집중도 평가
| 항목 | 판단 |
|---|---|
| 요구 | "졸고 있는 학생 알림" |
| 답변 | 센서로는 불가능하며 시도하지 않는다 |
| 이유 | 측정하지 못하는 것을 추론으로 포장하면 오판과 감시 문제가 생긴다 |
4.4 선택 체크리스트
- 결정할 질문을 한 문장으로 썼다.
- 필요한 정보가 Boolean 인지, 개수인지, 위치인지 구분했다.
- 후보 센서마다 정지 인원 감지 능력을 확인했다.
- 개인정보 위험을 3장 기준으로 평가했다.
- 전압과 로직 레벨(3.3V/5V)을 확인했다.
- 소규모 실험으로 오탐·미탐을 측정할 계획이 있다.
- 센서 없이 해결 가능한 문제인지 검토했다(수동 기록으로 충분한가).
05mmWave 레이더 기초와 심화
mmWave(밀리미터파) 레이더는 이 교재의 핵심 센서다. 수식보다 직관을 먼저 잡고, 필요한 만큼만 수식을 쓴다.
5.1 전파 반사: 어둠 속의 손전등과 메아리
산에서 "야호" 하고 외치면 메아리가 돌아온다. 소리가 돌아오는 시간이 길수록 산이 멀다. 레이더는 소리 대신 전파를 쓴다.
sequenceDiagram
participant R as 레이더
participant O as 사람/물체
R->>O: 전파 송신 (TX)
O-->>R: 반사파 수신 (RX)
Note over R: 시간 지연 → 거리<br/>주파수 변화 → 속도<br/>안테나 간 차이 → 각도
전파는 빛과 같은 속도(초속 약 30만 km)로 간다. 그래서 반사파가 돌아오는 시간은 너무 짧아 직접 재기 어렵다. FMCW 는 이 문제를 영리하게 푼다(5.2절).
사람 몸은 전파를 반사한다. 수분이 많아 전파를 잘 반사하는 편이다. 금속은 더 강하게 반사한다. 얇은 플라스틱·종이·일부 벽재는 전파를 상당히 통과시키지만, 콘크리트·금속은 크게 막는다(제품·주파수별 차이).
5.2 FMCW 로 거리 추정: 사이렌 소리의 비유
FMCW(Frequency Modulated Continuous Wave, 주파수 변조 연속파)는 다음과 같이 이해한다.
비유: 구급차의 사이렌이 "삐~~~" 하고 높은 음으로 계속 올라간다고 하자. 소리를 내보낸 뒤 메아리가 돌아올 때, 그 사이 사이렌 음은 이미 더 높아져 있다. 보낸 음과 돌아온 음의 높낮이 차이를 알면, 메아리가 돌아오는 데 걸린 시간, 즉 거리를 알 수 있다.
flowchart LR
A["송신: 주파수가 직선으로 상승(chirp)"] --> B[물체에 반사]
B --> C["수신: 같은 모양, 시간만 늦음"]
C --> D["송신과 수신을 섞음(mixing)"]
D --> E["주파수 차이(비트 주파수)"]
E --> F["비트 주파수 → 거리"]
| 용어 | 뜻 | 비유 |
|---|---|---|
| chirp | 주파수가 오르는 한 번의 전파 신호 | 사이렌 한 번 |
| 대역폭 | chirp 가 훑는 주파수 범위 | 사이렌이 오르는 음역 폭 |
| 비트 주파수 | 송신과 수신 주파수의 차이 | 두 음의 높낮이 차이 |
핵심 관계는 다음 한 가지다.
- 대역폭이 넓을수록 가까이 붙은 두 물체를 구분하는 능력(거리 분해능)이 좋아진다.
LD2410C 소개 페이지는 거리 해상도가 0.75 m 라고 기재한다(6.1절). 이는 0.75 m 보다 가까이 붙은 두 사람을 구분하기 어렵다는 뜻으로 해석할 수 있다. 그래서 인원 계수에 부적합하다.
5.3 도플러: 지나가는 구급차의 소리 변화
구급차가 다가올 때는 사이렌이 높게, 멀어질 때는 낮게 들린다. 이것이 도플러 효과다. 전파도 마찬가지다. 움직이는 물체에 반사된 전파는 주파수가 미세하게 바뀐다.
- 다가오는 물체: 반사파 주파수 상승
- 멀어지는 물체: 반사파 주파수 하락
- 변화 크기: 속도에 비례
여러 chirp 를 연속으로 보내면, 같은 거리의 물체가 chirp 마다 위상이 조금씩 변하는 것으로부터 속도를 구할 수 있다.
정지한 사람은 어떻게 보이나
정지한 사람도 숨을 쉬고 가슴이 미세하게 움직인다. 일부 존재 감지 모듈은 이 미세 움직임을 별도로 분석해 "정지 표적(stationary target)"으로 보고한다. 이것이 PIR 과 가장 큰 차이다.
| 구분 | 움직임 표적 (moving) | 정지 표적 (stationary) |
|---|---|---|
| 신호 | 큰 도플러 변화 | 미세 움직임, 반사 에너지 |
| 특징 | 감지 쉬움 | 감도 설정에 민감, 감지 거리가 더 짧을 수 있음(제품별 확인) |
| 주 오탐 원인 | 선풍기, 커튼, 사람 지나감 | 벽·가구 반사 에너지 |
완전히 숨을 참고 정지한 물체는 없지만, 정지 표적 감지는 제품 알고리즘과 설정에 크게 좌우되므로 "정지한 사람은 항상 감지된다"고 가정하면 안 된다. 현장 실험이 필수다.
5.4 안테나 배열: 두 귀로 소리 방향 알기
사람은 귀가 두 개라서 소리가 어느 쪽에서 오는지 안다. 소리가 한쪽 귀에 아주 조금 먼저 도착하기 때문이다.
레이더도 수신 안테나(RX)를 여러 개 두면, 같은 반사파가 안테나마다 위상이 조금 다르게 도착한다. 이 위상 차이로 반사체의 방향(각도)을 추정한다.
flowchart TB
subgraph 안테나 배열
RX1[RX1]
RX2[RX2]
RX3[RX3]
RX4[RX4]
end
W[반사파] --> RX1 & RX2 & RX3 & RX4
RX1 & RX2 & RX3 & RX4 --> PH[안테나 간 위상차]
PH --> ANG[각도 추정]
- 안테나 수가 많을수록, 배열 간격이 적절할수록 각도 분해능이 좋아진다.
- 거리(FMCW) + 속도(도플러) + 각도(배열)를 합치면 점군(point cloud) 을 만들 수 있다. 점 하나는 "거리, 각도, 속도"를 가진 반사점이다.
- 송신 안테나(TX)와 수신 안테나(RX) 개수가 늘면 가상 안테나(virtual array)가 생겨 각도 해상도를 더 높일 수 있다. IWR6843ISK 는 TI 설명 기준 4 RX, 3 TX 구성이다(6.3절).
존재 감지 전용 모듈은 보통 안테나 수가 적거나 각도 정보를 외부에 노출하지 않는다. 그래서 "어디에 있는지"는 거리만 알려 주는 경우가 많다.
5.5 24GHz vs 60GHz
| 항목 | 24GHz 대역 | 60GHz 대역 |
|---|---|---|
| 파장(약) | 약 12.5 mm | 약 5 mm |
| 안테나 크기 | 상대적으로 큼 | 작게 만들 수 있음 |
| 대역폭 | 규제상 좁은 편(24GHz ISM 대역, 지역별 상이) | 넓게 쓸 수 있음(예: TI 60-64 GHz) |
| 거리 분해능 | 대역폭이 좁아 상대적으로 낮음 | 대역폭이 넓어 상대적으로 높음 |
| 미세 움직임 | 다소 둔감할 수 있음 | 파장이 짧아 미세 움직임에 민감 |
| 감쇠 | 대기·물체에 의한 감쇠 적음 | 대기 흡수가 커 장거리에는 불리(산소 흡수 대역) |
| 실내 사용 | 가정·소형 존재 감지 제품에 흔함 | 정밀 센싱·점군 제품에 흔함 |
| 제품 가격 경향 | 존재 감지 모듈은 저가 | 개발 보드는 고가인 경우가 많음 |
| 규제 | 지역별 인증 필요 | 지역별 인증 필요 |
위 표는 물리적 일반 경향이다. "60GHz 라서 항상 더 좋다"는 말은 틀리다. 같은 대역이라도 칩, 안테나, 펌웨어 알고리즘에 따라 성능이 크게 달라진다.
규제와 전파 인증(국내 KC 등)은 제품 구매 전에 확인해야 하며, 이 교재는 법무 자문을 대신하지 않는다. 판매용 상용 제품으로 만들 경우 인증 비용이 발생할 수 있다(미확인 — 구매 전 확인).
5.6 레이더의 흔한 오류 원인
| 원인 | 현상 | 대응 |
|---|---|---|
| 다중 경로 반사 | 벽·천장에 튕긴 전파가 "유령 표적"을 만듦 | 설치 각도 조정, 감지 범위 제한 |
| 움직이는 비인체 물체 | 선풍기, 커튼, 화분, 청소로봇 | 거리 구간 마스킹, 감도 조정 |
| 가림 | 사람 뒤의 사람, 가구 뒤의 사람 | 설치 위치 변경, 센서 수 증가 |
| 금속 근접 | 캐비닛·칠판 프레임 반사 | 금속에서 거리 확보 |
| 전원 노이즈 | 불안정한 USB 전원이 오동작 유발 | 안정적인 5V 어댑터 사용 |
| 설정 부적합 | 감도 임계값이 부적절 | 현장 튜닝, 문서화 |
5.7 "모든 mmWave 제품이 같은 정보를 출력하지는 않는다"
이 문장은 이 교재에서 가장 중요한 경고 중 하나다. 같은 "mmWave 레이더"라는 이름이어도 제품이 외부로 주는 정보는 완전히 다르다.
flowchart LR
ADC["원시 ADC 신호"] --> RP["거리-도플러 처리"]
RP --> PC["점군 point cloud"]
PC --> TR["표적 추적 tracking"]
TR --> PR["존재 / 인원 추정 / 상태"]
subgraph 개발 보드 영역
ADC
RP
PC
end
subgraph 가공 모듈 영역
TR
PR
end
| 구분 | 원시 point cloud 출력 보드 | 가공된 presence 출력 모듈 |
|---|---|---|
| 대표 예 | TI IWR6843ISK (개발 보드) | HLK-LD2410C (존재 감지 모듈) |
| 사용자가 받는 것 | 점군, (추가 장비 사용 시) 원시 신호까지 | 존재 여부, 움직임/정지 구분, 거리, 에너지 값 |
| 알고리즘 | 사용자가 구현하거나 TI 예제 사용 | 제조사 펌웨어가 내장 |
| 개발 난이도 | 높음 | 낮음 |
| 자유도 | 높음 | 낮음 (제조사가 허용한 설정 범위 내) |
| 인원 계수 | 알고리즘을 구현해야 가능성 있음 | 불가 (LD2410C) |
| 가격 | 높음 | 낮음 |
| 적합 단계 | 연구, 고급 확장 | MVP |
따라서 "mmWave 레이더로 인원수 센다"는 문장은 불완전하다. 어떤 제품이, 어떤 펌웨어로, 어떤 출력을 주는지를 반드시 확인해야 한다. 점군을 주는 보드는 인원 계수 알고리즘을 만들 수 있는 재료를 줄 뿐이고, 그 알고리즘의 정확도는 직접 검증해야 한다.
06레이더 장치 비교 및 학원 적합성
확인일: 2026-10-18. 제조사 공식 페이지를 우선하되, 공식 데이터시트 PDF 에 접근하지 못한 값은 출처를 구분해 표기한다. 접근 못 한 값은미확인으로 둔다.
사양은 펌웨어·판매처 버전에 따라 다를 수 있으므로 구매 전 최신 데이터시트를 직접 확인한다.
6.1 HLK-LD2410C — 존재 감지 전용
확인된 사양
| 항목 | 값 | 출처 구분 |
|---|---|---|
| 대역 | 24GHz ISM 대역 | 제조사 공식 소개 페이지 (확인 2026-10-18) |
| 공급 전압 | 5-12 V | 제조사 공식 소개 페이지 |
| 최대 감지 거리 | 5 m | 제조사 공식 소개 페이지 |
| 거리 해상도 | 0.75 m | 제조사 공식 소개 페이지 |
| 감지 각도 | 최대 ±60도 | 제조사 공식 소개 페이지 |
| 출력 | GPIO 및 UART | 제조사 공식 소개 페이지 |
| 감지 대상 | 움직이는 사람과 정지한 사람 | 제조사 공식 소개 페이지 |
| 크기 | 최소 16 mm x 22 mm | 제조사 공식 소개 페이지 |
| 설치 | 천장·벽 설치 옵션 언급 | 제조사 공식 소개 페이지 |
| 기본 UART 설정 | 256000 baud, 정지 비트 1, 패리티 없음 | 2차 출처(OpenELAB 안내 글), 공식 데이터시트 직접 확인 필요 |
| I/O 로직 레벨 | 3.3 V | 2차 출처(OpenELAB 안내 글), 공식 데이터시트 직접 확인 필요 |
| 권장 전원 용량 | 5V, 200 mA 초과 | 2차 출처(OpenELAB), 공식 확인 필요 |
| 동작 전류 | 79 mA 로 언급한 글 있음 | 2차 출처 일부 언어판에만 존재, 미확인 — 구매 전 확인 |
| 시리얼 프로토콜 문서 | Hi-Link 시리얼 통신 프로토콜 v1.00 PDF (제3자 호스팅) | 목차만 확인 |
출처 URL
- 제조사 소개 페이지: https://www.hlktech.net/index.php?id=1095 (확인 2026-10-18)
- 시리얼 프로토콜 PDF(제3자 호스팅): https://www.sudo.is/docs/esphome/components/ld2410/HLK-LD2410C_Serial_communication_protocol_v1.00.pdf (확인 2026-10-18, 목차 수준)
- 2차 출처: https://openelab.io/blogs/getting-started/how-to-use-the-hlk-ld2410c (확인 2026-10-18)
- 제조사 소개 페이지가 가리키는 공식 데이터시트 폴더: 해당 페이지에서 링크 확인 필요 (직접 열람하지 못함)
이 교재의 배선 전제와 확인 필요 사항
SPEC 의 배선은 모듈 전원을 ESP32 VIN(5V)에서 받고, UART 를 GPIO16/17 에 직결한다. 2차 출처 기준 I/O 가 3.3V 이므로 직결이 가능한 구성이지만, 공식 데이터시트의 로직 레벨 확인은 구매 후 배선 전에 반드시 수행한다. 확인되기 전에는 TX 신호를 5V 로 가정하고 레벨 변환기 사용을 고려한다. ESP32 GPIO 에 5V 신호를 직접 입력하지 마라.
기능 요약(존재 감지의 의미)
LD2410C 는 "사람이 있는가, 움직이는가 정지해 있는가, 대략 어느 거리인가"를 알려 주는 모듈이다. 거리 구간(게이트)별 감도를 설정할 수 있다고 제조사가 소개한다(range division). 관측 JSON 의 presence, moving_target, stationary_target, detect_distance_cm 가 이 출력에서 온다.
학원 적합성
| 장점 | 한계 |
|---|---|
| 정지한 사람을 감지하는 기능이 있다(제품 기능 범위) | 정확한 인원 계수를 할 수 없다 |
| 영상·음성이 없다 | 두 사람이 가까우면 하나로 보인다 (거리 해상도 0.75 m 기준) |
| 저가, UART 로 쉬운 연동 | 방향·좌표를 쓸 수 없다(거리 중심) |
ESP32 라이브러리 존재(ld2410, ncmreynolds) | 가림·다중 경로 반사에 취약 |
경고: LD2410C 로 정확한 인원 계수를 시도하지 마라. 이 모듈은 "있음/없음"과 "거리"를 보고하는 장치이며 사람 수를 세는 장치가 아니다. 표시되는 거리 하나를 "인원 1명"으로 해석하는 순간 모든 후속 분석이 오염된다. 인원이 필요하면 출결 기록(층3)을 쓰거나 다른 센서를 검토한다(18장).
6.2 HLK-LD2450 — 제한적 다중 표적 추적
확인된 사양
| 항목 | 값 | 출처 구분 |
|---|---|---|
| 최대 표적 수 | 3 | ESPHome 공식 문서: "A maximum of 3 targets can be detected" (확인 2026-10-18). 제조사 소개 페이지에는 최대 표적 수가 명시되지 않음 |
| 최대 감지 거리 | 6 m | 제조사 소개 페이지 (확인 2026-10-18) |
| 감지 각도 | 방위 ±60도, 피치 ±35도 | 제조사 소개 페이지 |
| 출력 | 시리얼 출력(감지 데이터) | 제조사 소개 페이지 |
| 기본 UART | 256000 baud, 패리티 없음, 정지 비트 1 | ESPHome 공식 문서 (제3자 문서) |
| 권장 펌웨어 | V2.02.23090617 이상 (ESPHome 통합 기준) | ESPHome 문서 |
| 공급 전압 | 5V (4.5-5.5V) 로 기재한 판매처 자료 있음 | 2차 출처(ShillehTek), 공식 미확인 — 구매 전 확인 |
| 로직 레벨 | 3.3V TTL 로 기재한 판매처 자료 있음 | 2차 출처, 공식 미확인 |
| 보고 주기 | 약 10Hz 로 기재한 판매처 자료 있음 | 2차 출처, 공식 미확인 |
| 평균 전류 | 약 90 mA 로 기재한 판매처 자료 있음 | 2차 출처, 공식 미확인 |
출처 URL
- 제조사 페이지: https://www.hlktech.net/index.php?id=1157 (확인 2026-10-18, 표적 수·전원 정보 없음)
- ESPHome 문서: https://esphome.io/components/sensor/ld2450/ (확인 2026-10-18)
- 2차 출처: https://shillehtek.com/blogs/shillehtek-product-manuals/hlk-ld2450-24ghz-mmwave-radar-human-body-tracking-sensor-module-manual (확인 2026-10-18)
- 공식 데이터시트: 제조사 페이지의 다운로드 폴더 링크에 있는 것으로 보이나 직접 열람하지 못함 (미확인)
학원 적합성
| 장점 | 한계 |
|---|---|
| 표적의 위치(좌표)와 속도를 받을 수 있다고 소개됨 | 최대 3 표적이면 20명 강의실의 인원 계수에는 부족 |
| 출입구 통과 방향 판단에 활용할 여지(실험 필요) | 표적 병합·소실이 일어날 수 있음 (가림, 근접) |
| 영상 없음 | 좌표 이력은 위치 정보이므로 보관 기간·목적을 제한해야 함 |
LD2450 은 "작은 공간의 소수 인원 추적"에 맞는 제품이다. 학원 강의실 전체 인원을 세는 용도는 아니며, 출입구·상담실처럼 인원이 적은 구역의 보조 센서로 검토할 수 있다. 이는 이 교재의 MVP 구매 대상이 아니다.
6.3 TI IWR6843ISK — 고급 개발 보드
확인된 사양
| 항목 | 값 | 출처 구분 |
|---|---|---|
| 대역 | 60-64 GHz | TI 공식 제품 페이지 (확인 2026-10-18) |
| 안테나 | 4 RX, 3 TX | TI 공식 페이지 |
| 시야각 | 방위 120도, 고도 30도 (ISK 공식 페이지 기준) | TI 공식 페이지 |
| 인터페이스 | 60핀 고속 인터페이스(호스트 제어), USB 를 통한 점군 데이터 접근 | TI 공식 페이지 |
| 전원 | USB 로 전원 공급, 보드 내 전력 소모 모니터링 | TI 공식 페이지 |
| 필수/관련 보드 | MMWAVEICBOOST(소프트웨어 개발·디버그용 캐리어), 원시 ADC 캡처에는 DCA1000EVM | TI 공식 페이지 |
| 소프트웨어 | mmWave Studio, mmWave SDK | TI 공식 페이지 |
| UART 상세, 정확한 전압·전류 | 미확인 — 구매 전 확인 | 사용자 가이드·회로도 필요 |
| 가격 | 미확인 — 구매 전 확인 | 조사 안 함 |
참고: TI 검색 결과 중에는 같은 칩 계열의 다른 보드(예: IWR6843ISK-ODS, IWR6843AOPEVM)도 나타났다. 이름이 비슷하므로 안테나 구성과 시야각이 다른 제품이라는 점을 구분해서 구매한다. 위 표의 값은 IWR6843ISK 페이지 기준이다. 검색 요약에서는 120도 고도 시야각이 보였으나 페이지 직접 열람에서는 30도로 확인되었으며, 제품 혼동 가능성이 있으므로 구매 전 TI 공식 페이지의 해당 제품 사양을 다시 확인한다.
출처 URL
- TI IWR6843ISK 제품 페이지: https://www.ti.com/tool/IWR6843ISK (확인 2026-10-18, 페이지 앞 절반만 열람)
- 관련 제품 목록: https://www.ti.com/tool/IWR6843ISK-ODS , https://www.ti.com/tool/IWR6843AOPEVM
학원 적합성
| 장점 | 한계 |
|---|---|
| 점군 접근이 가능해 인원 계수·추적 알고리즘 개발 재료가 됨 | 알고리즘을 직접 구현하거나 TI 예제를 학습해야 함(개발 난이도 높음) |
| 60GHz 대역폭 이점 | 가격이 높음 |
| 연구·성능 상한 탐색에 유용 | 신원·집중도 판단은 여전히 불가 |
| 개발 보드이므로 상시 운영용 장치로 쓰기에는 제품화 작업이 추가로 필요 |
MVP 구매 대상이 아니다. 1개 강의실 실증에서 LD2410C 의 한계가 명확히 확인된 뒤, 인원 계수가 정말 필요한지 사업적으로 판단하고 나서 도입을 검토한다(18장).
6.4 세 장치 비교
| 항목 | HLK-LD2410C | HLK-LD2450 | TI IWR6843ISK |
|---|---|---|---|
| 성격 | 존재 감지 모듈 | 다중 표적 추적 모듈 | 개발 보드 |
| 대역 | 24GHz | 24GHz(추정, 제조사 문서 확인 필요) | 60-64 GHz |
| 최대 표적 수 | 개수 보고 없음(존재/거리) | 3 (ESPHome 문서) | 점군(개수는 알고리즘에 의존) |
| 최대 거리 | 5 m | 6 m | 미확인 |
| 거리 해상도 | 0.75 m | 미확인 | 미확인 (설정 의존) |
| 각도 정보 | 없음(거리 중심) | 좌표 제공 | 점군에 각도 포함 |
| 인터페이스 | UART, GPIO | UART | USB, 60핀 |
| 전원 | 5-12V (제조사) | 5V 로 기재한 2차 자료, 미확인 | USB 전원(제조사), 상세 미확인 |
| 인원 계수 | 불가 | 제한적 (최대 3) | 알고리즘 구현 시 가능성 |
| 개발 난이도 | 낮음 | 중간 | 높음 |
| 가격 | 미확인 — 구매 전 확인 | 미확인 — 구매 전 확인 | 미확인 — 구매 전 확인 |
| MVP 채택 | 채택 | 확장 논의용 | 확장 논의용 |
flowchart TB
Q{필요한 정보는?}
Q -->|방에 사람이 있는가| A[LD2410C]
Q -->|소수 인원의 위치·방향| B[LD2450]
Q -->|다수 인원 계수·고급 연구| C[IWR6843ISK 등 + 알고리즘]
Q -->|정확한 출석| D[출결 시스템]
6.5 학원 적합성 결론
- MVP 는 LD2410C 로 "공간 사용 여부"만 다룬다. 이것이 센서가 책임질 수 있는 범위다.
- 인원 계수는 센서로 해결하기 전에 사업 가치를 따진다. 출결이 이미 사람 수를 알려 주고 있다면, 센서로 같은 정보를 다시 만들 필요가 없을 수 있다.
- 고급 레이더 도입 결정 근거는 1개 강의실 실증에서 얻은 오탐·미탐 데이터여야 한다(15장, 17장).
07학원 공간별 센싱 설계
이 장은 공간 유형별로 무엇을 측정하고 어디에 설치하며 센서를 몇 개 두는지 판단하는 방법을 다룬다. 공간 유형은 DB 의 spaces.type(classroom, entrance, hallway, office, facility)과 일치한다.
이 장의 판단은 설계 가이드이며 검증된 성능이 아니다. 현장마다 구조가 다르므로 각 설치 후 15장의 실험을 수행한다.
7.1 공통 설계 원칙
| 원칙 | 내용 |
|---|---|
| 목적 우선 | 공간마다 "무엇을 결정하려는가"를 한 문장으로 쓴다 |
| 최소 수집 | 존재와 환경만 수집하고 개인을 추적하지 않는다 |
| 사각지대 인정 | 센서는 모든 곳을 보지 못한다. 사각지대를 도면에 표시한다 |
| 측정으로 확인 | 설치 후 사람이 걸어 보며 감지 영역을 확인한다 |
| 수정 가능성 | 위치를 옮길 수 있게 임시 고정부터 시작한다 |
| 안내 | 센서 설치 사실과 수집 항목을 구성원에게 알린다(16장) |
flowchart LR
A[도면 확보] --> B[목적 정의]
B --> C[센서 후보 위치]
C --> D[감지 범위·사각지대 표시]
D --> E[임시 설치]
E --> F[걸어서 감지 확인]
F --> G{문제?}
G -->|예| C
G -->|아니오| H[고정 설치·기록]
7.2 출입구
목적
- 입퇴실 시점 추정, 사용 시작/종료 시각, 출입 통과 감지
센서 후보
| 센서 | 용도 | 비고 |
|---|---|---|
| 도어 마그네틱 센서 | 문 열림/닫힘 | 신뢰도 높은 접점 정보 |
| mmWave 존재 감지 | 출입구 앞 존재 | 움직임 방향·개수는 LD2410C 로 판별 불가 |
| ToF 다중 구역 | 통과 방향·계수 | 확장 논의. 동시 통과 오차 가능 |
| QR/NFC | 출결(층3) | 개인정보로 취급. 별도 설계 |
설계 포인트
- 문 근처는 문이 열릴 때 복도 쪽 움직임이 센서에 잡혀 오탐이 나기 쉽다. 센서를 문 바깥을 향하지 않게 하고 감지 거리를 제한한다.
- 입구 센서의 목적은 "누가 들어왔나"가 아니라 "언제 사람이 드나들었나"이다.
- 방향이 중요하면 LD2410C 로는 부족하다. 도어 센서와 존재 이벤트의 시간 순서로 추정하되 그것이 추론이라는 점을 표시한다.
7.3 강의실
강의실은 이 교재의 핵심 공간이다. 센서 설계의 어려움이 모두 모여 있다.
강의실 특유의 문제
| 문제 | 설명 | 영향 |
|---|---|---|
| 책상 밀집 | 학생들이 가까이 앉음 | 거리 해상도(예: LD2410C 0.75 m)보다 가까워 개별 구분 불가 → 인원 계수 불가 |
| 정지 학생 | 수업 중 대부분 앉아 거의 움직이지 않음 | 움직임 기반 센서(PIR) 미탐. 정지 표적 감도 설정이 핵심 |
| 벽·가구 가림 | 칠판, 사물함, 파티션 | 뒤쪽 학생이 안 보임, 사각지대 발생 |
| 다중 경로 반사 | 벽·칠판 프레임·유리에 반사 | 유령 표적, 거리 값 흔들림 |
| 움직이는 비인체 | 선풍기, 에어컨 바람, 커튼 | 오탐 |
| 불규칙 사용 | 쉬는 시간 대량 이동 | 상태 변화가 잦아 이벤트 규칙의 창 설정이 중요 |
flowchart TB
subgraph 강의실 평면 개념도
direction TB
T[칠판]
D1[책상 줄 1]
D2[책상 줄 2]
D3[책상 줄 3]
DR[출입문]
end
T --- D1 --- D2 --- D3 --- DR
설치 위치 비교
| 위치 | 장점 | 단점 | 적합 |
|---|---|---|---|
| 천장 중앙 아래 향함 | 가림이 적고 전체를 균등하게 봄 | 시공·전원 어려움, 높이에 따른 감지 영역 확인 필요 | 신축·정식 설치 |
| 벽 상단 모서리 | 전원 확보 쉬움, 대각선 시야 | 먼 쪽 사각지대, 뒤쪽 가림 | 시제품·MVP |
| 칠판 쪽 벽 | 학생을 정면으로 봄 | 칠판 금속 프레임 반사 가능, 교사 이동이 많이 잡힘 | 교사 활동 제외 필요 시 불리 |
| 뒷벽 | 교사 이동 영향 적음 | 앞줄이 먼 거리에 있어 감도 저하 가능 | 소형 강의실 |
| 책상 위/아래 | 수치 가까움 | 학생 접촉·훼손, 가림 | 비권장 |
LD2410C 는 소개 페이지 기준 최대 거리 5 m, 각도 ±60도다. 이 범위 안에서 실제 감지가 되는지는 설치 후 걸어서 확인한다. 정지 표적 감지 거리는 최대 거리보다 짧을 수 있다(제품별 확인, 실험 필요).
감지 범위와 사각지대
센서 위치를 정했다면 다음 순서로 확인한다.
- 도면에 센서 위치와 화각(±60도 부채꼴)을 그린다.
- 가구·칠판·사물함의 위치를 겹쳐 그려 가려지는 영역을 표시한다.
- 학생이 앉는 자리 중 부채꼴 밖 또는 가려진 자리를 사각지대로 표시한다.
- 임시 설치 후 각 자리에 사람이 앉아 정지 상태를 유지하며
stationary_target이 true 로 유지되는지 확인한다. - 결과를 표로 기록한다. 아래는 기록 양식 예시이며 실측 값이 아니다.
| 자리 | 거리(m, 줄자 측정) | 정지 감지 여부 | 비고 |
|---|---|---|---|
| (예시) A1 | (측정 후 기록) | (측정 후 기록) | |
| (예시) B3 | (측정 후 기록) | (측정 후 기록) |
공간당 센서 수 판단
| 조건 | 판단 |
|---|---|
| 한 개 센서의 유효 범위가 방 전체를 덮음 | 1개 |
| 방이 길거나 L자형, 뒤쪽 사각지대가 큼 | 2개 이상 검토 |
| 목적이 "방 사용 여부" | 1개로도 충분한 경우가 많음 (실험으로 확인) |
| 목적이 "구역별 사용" | 구역 수만큼 센서 필요 |
| 목적이 "정확한 인원" | 센서 수를 늘려도 LD2410C 로는 해결 안 됨. 다른 접근 필요 |
센서를 여러 개 둘 경우 서로의 전파가 간섭할 수 있는지 데이터시트와 실험으로 확인한다(미확인 — 구매 전 확인). 같은 방에 두 센서를 두면 장치 ID 를 구분한다(academy_room_A_01, academy_room_A_02).
CO₂ 센서 위치
- 학생 호흡 높이에 가깝고 공기가 순환하는 곳.
- 창문, 문, 에어컨 송풍구 바로 앞은 피한다.
- 사람의 입김이 직접 닿는 거리는 피한다.
- 한 방에서 위치마다 값이 다를 수 있다. 대표 위치 한 곳을 정하고 일관되게 쓴다.
7.4 복도
목적
- 이동 감지, 쉬는 시간 혼잡 정도(간접), 야간 침입 알림(옵션)
설계 포인트
| 항목 | 내용 |
|---|---|
| 센서 | mmWave 존재 감지, 도어 센서, PIR(이동 감지에는 적합) |
| 설치 | 천장 또는 복도 끝 벽. 직선 구조라 센서 축을 복도 방향으로 맞추면 먼 거리까지 볼 수 있음 |
| 특징 | 사람이 정지하지 않고 지나간다 → PIR 이 적합해지는 드문 공간 |
| 주의 | 벽 반대편 강의실의 움직임을 얇은 벽 너머로 감지할 수 있음 → 감지 거리 제한 필요 |
| 프라이버시 | 학생 동선 추적이 되지 않게 이력을 최소화 |
복도 센서는 MVP 필수 아님. 강의실 센서의 오탐 원인(벽 너머 복도 움직임)을 이해하는 대조군으로 쓸 수는 있다.
7.5 사무실
목적
- 직원 부재/재실, 상담실 사용 여부, 환기
설계 포인트
| 항목 | 내용 |
|---|---|
| 센서 | mmWave 존재 감지(앉아서 일하는 정지 상태 감지에 유리), CO₂ |
| 주의 | 직원은 근로자다. 재실 데이터를 근태 감시에 쓰면 노동 관련 문제와 신뢰 문제가 생긴다 |
| 원칙 | 개인별이 아니라 공간 단위로만 집계. 개인 근태 용도 금지를 문서화 |
| 상담실 | 학부모 상담 내용이 있으므로 마이크·카메라 금지, 사용 여부 Boolean 정도만 |
7.6 시설(화장실·탈의·휴게)
프라이버시 주의 (최우선)
| 공간 | 허용 가능한 수집 | 금지 |
|---|---|---|
| 화장실 | 문 잠금/열림, 환기·악취 지표(VOC), 사용 중 여부(최소) | 카메라, 마이크, 위치 좌표 이력, 사람 식별 |
| 탈의·샤워 | 설치하지 않는 것을 원칙으로 한다 | 모든 센서 (필요성이 분명하고 법적 검토를 마친 경우만 예외) |
| 휴게실 | CO₂, 온습도, 존재 여부(공간 단위) | 영상, 음성 |
| 서버실/창고 | 온습도, 도어, 침입 알림 | 해당 없음 |
화장실에 레이더를 설치하는 경우에도 다음을 지킨다.
- 설치 목적을 문서화하고 이용자(학생, 학부모, 직원)에게 안내한다.
- 저장 항목은 존재 여부 Boolean 으로 제한하고 좌표·거리 이력은 저장하지 않는다.
- 법적 검토를 거치고 가능하면 대안(도어 센서)을 먼저 쓴다.
spaces.type = facility 공간의 데이터는 일반 강의실보다 더 짧은 보관 기간을 기본값으로 한다(16장).
7.7 공간별 요약표
| 공간 | 주 목적 | 우선 센서 | 설치 위치 | 센서 수(초기 판단) | 주요 위험 |
|---|---|---|---|---|---|
| 출입구 | 출입 시점 | 도어 센서, 존재 감지 | 문 상단/옆 벽 | 1 | 문 열림 오탐, 방향 판별 한계 |
| 강의실 | 사용 여부, 환기 | mmWave 존재 + CO₂ | 벽 상단 모서리(MVP), 천장(정식) | 1에서 시작, 사각지대에 따라 증가 | 정지 학생, 가림, 다중 경로 |
| 복도 | 이동·야간 | PIR, 존재 감지 | 천장 또는 끝 벽 | 구간별 1 | 벽 너머 감지 |
| 사무실 | 재실, 환기 | mmWave 존재 + CO₂ | 천장 또는 벽 | 1 | 근태 감시로의 오용 |
| 시설 | 사용 중, 환기 | 도어, VOC | 문, 천장 | 최소 | 프라이버시 |
7.8 MVP 첫 설치 권장 절차
- 가장 단순한 강의실 하나를 고른다(가구가 적고 벽이 단단하며 전원이 가깝다).
- ESP32 + LD2410C + SCD41 을 브레드보드로 구성하고 벽 상단 모서리에 임시 고정한다(배선은 11장, 핀맵은 4번 문서에 따름).
- 사람이 직접 걸어 다니며 감지 영역을 확인한다.
- 정지 상태로 자리별 감지를 확인한다.
- 선풍기, 에어컨, 문 열림 같은 방해 요인을 의도적으로 만들어 오탐을 기록한다.
- 위치·감도를 조정하고 변경 내용을 기록한다.
- 2주 이상 데이터를 수집하고 사람이 센 기록과 비교한다(15장).
이 단계에서 얻은 오탐·미탐 데이터가 이후 센서 추가·교체 결정의 유일한 근거다.
08센서 퓨전과 공간 상태 추정
센서 하나가 보내는 값은 "그 순간의 관측"일 뿐이다. 학원 운영자가 알고 싶은 것은 "A강의실이 지금 수업 중인가", "환기가 필요한가" 같은 공간의 상태다. 이 장은 관측을 상태로 바꾸는 과정을 다룬다. 센서 종류와 원리는 3~5장, 공간별 배치는 7장을 참고한다.
8.1 세 가지를 구분한다
| 구분 | 의미 | 예 | 신뢰 수준 |
|---|---|---|---|
| 관측(Observation) | 센서가 보낸 원시 측정값 | presence: true, co2_ppm: 850 | 센서 오차를 포함 |
| 상태(State) | 규칙이 계산한 공간의 현재 상황 | "사용 중", "환기 필요" | 규칙과 임계값에 의존 |
| 사실(Fact) | 사람이 입력·검증한 기록 | 출결 기록, 시간표 | 가장 높음 |
세 가지를 섞으면 안 된다. 특히 다음 한계를 항상 기억한다.
- CO₂만으로 정확한 인원수를 계산할 수 없다.
- HLK-LD2410C로 정확한 인원 계수를 할 수 없다. 존재/움직임 감지 전용이다.
- 레이더만으로 신원이나 집중도를 판단할 수 없다.
8.2 처리 파이프라인
flowchart LR
A["센서 수집<br/>LD2410C, SCD41"] --> B["시간 동기화<br/>observed_at, received_at"]
B --> C["정규화<br/>단위, 필드명, 스키마"]
C --> D["품질 검증<br/>quality 플래그, 범위, 결측"]
D --> E["상태 추정<br/>규칙 + 시간표 + 출결"]
E --> F["이벤트 생성<br/>ROOM_OCCUPIED 등"]
F --> G["서비스 실행<br/>대시보드, 알림, AI 질의"]
단계별 역할은 다음과 같다.
- 센서 수집: ESP32가 LD2410C(UART)와 SCD41(I²C)을 읽는다. 구현은 12장.
- 시간 동기화: 장치가 찍은
observed_at과 서버가 찍은received_at을 둘 다 보존한다. 장치 시계가 틀리거나 전송이 지연되어도 추적할 수 있다. - 정규화: 단위(cm, ppm, ℃, %)와 필드명을 스키마(9장)에 맞춘다.
- 품질 검증:
quality플래그, 값 범위, 결측, 중복(seq)을 확인한다. 검증을 통과하지 못한 값은 상태 추정에서 제외하거나 가중치를 낮춘다. - 상태 추정: 규칙으로 현재 상태를 계산한다. 구현은 14장.
- 이벤트 생성: 상태가 바뀌거나 조건이 충족되면
events에 기록한다. - 서비스 실행: 대시보드 표시, 알림, AI 질의 응답에 쓴다. 구조는 10장.
8.3 예시: 강의실 상태 표현
네 가지 정보를 결합해 강의실 상태를 표현하는 예다. 아래 값은 모두 예시다.
| 입력 | 출처 | 종류 | 예시 값 |
|---|---|---|---|
| 레이더 존재 감지 | LD2410C | 관측 | 최근 5분 presence true 비율 90% |
| CO₂ | SCD41 | 관측 | 1,250 ppm, 12분 지속 |
| 강의실 시간표 | class_sessions | 사실(계획) | 16:00-17:30 수학 |
| 출결 기록 | 학원 시스템(MVP 범위 밖) | 사실(검증) | 출석 8명 |
결합 결과의 예시는 다음과 같다.
{
"space": "A강의실",
"state": "IN_USE",
"flags": ["VENTILATION_NEEDED"],
"confidence": "high",
"reasons": [
"radar: 최근 5분 presence 비율 >= 60% (관측)",
"schedule: 16:00-17:30 수학 수업 시간 (사실-계획)",
"co2: 1200 ppm 초과 10분 지속 (관측)"
],
"not_claimed": ["정확한 인원수", "학생 신원", "집중도"]
}
여기서 not_claimed 필드는 시스템이 주장하지 않는 것을 명시한다. 레이더가 "사람이 있다"고 말할 뿐 "8명이 있다"고 말하지 않는다는 점을 응답에 남겨 두면 오해를 줄인다. 출결 기록과 연결하는 것은 MVP 이후 확장이며 17장에서 다룬다.
8.4 규칙 기반 vs 추론
| 구분 | 규칙 기반 | 추론(통계·머신러닝) |
|---|---|---|
| 방식 | "CO₂ > 1200이 10분 지속이면 환기 필요" | 과거 데이터로 패턴을 학습해 확률 계산 |
| 설명 가능성 | 높음. 왜 그런지 말할 수 있다 | 낮을 수 있음 |
| 필요한 데이터 | 거의 없음 | 정답(라벨) 데이터가 충분히 필요 |
| 실패 양상 | 임계값 근처에서 경계 오류 | 학습 범위 밖에서 예측 불가 |
| 본 교재의 위치 | MVP의 기본 | 데이터가 쌓인 뒤 확장(17장) |
권장 순서는 이렇다. 먼저 규칙으로 시작한다. 2주 이상 데이터를 모으고 오탐·미탐을 측정한 뒤(15장), 규칙으로 부족한 부분에만 추론을 검토한다. 사람 수 추정 같은 추론 결과는 반드시 "추정"으로 표시하고, 출결 기록과 같은 등급으로 취급하지 않는다.
8.5 신뢰도 개념
신뢰도는 "이 상태를 얼마나 믿어도 되는가"를 나타낸다. 본 교재는 복잡한 확률 대신 세 단계 라벨을 쓴다.
| 신뢰도 | 조건(초기 설계) |
|---|---|
| high | 필요한 센서 모두 quality 정상 + 서로 다른 근거 2개 이상이 일치 |
| medium | 근거 1개만 있거나, 근거끼리 부분적으로 어긋남 |
| low | 센서 일부 오류, 데이터 지연, 근거가 서로 충돌 |
예를 들어 시간표상 수업 시간인데 레이더가 "비어 있음"을 보고하면 근거가 충돌한다. 이때 시스템은 어느 한쪽을 임의로 골라 확정하지 않고 low로 표시하고 사람이 확인하게 한다. 이 라벨의 기준도 초기값이며 실측 후 조정한다.
09데이터 아키텍처와 표준 스키마
9.1 테이블 관계
7개 테이블의 관계는 다음과 같다. 개념 이름(Academy, Space, Device, Person, ClassSession, Observation, Event)은 각각 academies, spaces, devices, persons, class_sessions, observations, events에 대응한다. 상세 DDL은 database/schema.sql을 참고한다.
erDiagram
academies ||--o{ spaces : "has"
academies ||--o{ persons : "has"
spaces ||--o{ devices : "hosts"
spaces ||--o{ class_sessions : "schedules"
spaces ||--o{ events : "has"
devices ||--o{ observations : "sends"
academies {
int id PK
text name
timestamptz created_at
}
spaces {
int id PK
int academy_id FK
text name
text type "classroom|entrance|hallway|office|facility"
numeric area_m2 "null 허용"
}
devices {
int id PK
int space_id FK
text device_id UK
text model
text firmware_version
timestamptz installed_at
boolean active
}
persons {
int id PK
int academy_id FK
text role "student|teacher|staff"
text name_masked
timestamptz consent_at
}
class_sessions {
int id PK
int space_id FK
text name
timestamptz starts_at
timestamptz ends_at
int expected_headcount
}
observations {
bigserial id PK
int device_id FK
int seq
timestamptz observed_at
timestamptz received_at
jsonb measurements
jsonb quality
text schema_version
}
events {
int id PK
int space_id FK
text type
text severity
timestamptz started_at
timestamptz ended_at "null 허용"
text source
jsonb evidence
timestamptz created_at
}
observations에는UNIQUE(device_id, seq)제약이 있다. 같은 장치가 같은seq를 다시 보내도 한 번만 저장된다(13장).persons는 MVP에서 테이블만 두고 센서 데이터와 연결하지 않는다. 개인정보 최소 수집이 기본값이다(16장).events.source는 이벤트를 만든 주체(예: 규칙 엔진)를 기록한다.
9.2 관측 JSON 표준
장치가 서버로 보내는 JSON(POST /api/v1/observations)은 다음과 같다. 필드명과 구조는 고정이다.
{
"device_id": "academy_room_A_01",
"seq": 1024,
"observed_at": "2026-10-18T16:30:00+09:00",
"firmware_version": "0.1.0",
"schema_version": "1.0",
"measurements": {
"presence": true,
"moving_target": false,
"stationary_target": true,
"detect_distance_cm": 210,
"co2_ppm": 850,
"temperature_c": 24.5,
"humidity_percent": 48.2
},
"quality": {
"radar_valid": true,
"co2_valid": true
}
}
규칙은 다음과 같다.
seq는 장치가 증가시키는 번호다. 재전송 시 중복 제거에 쓴다.received_at은 서버가 기록한다. 장치는 보내지 않는다.- 측정에 실패한 필드는
null로 보내고, 해당quality플래그를false로 둔다. - 전송 주기는 30초 고정이며, presence 상태가 바뀌면 즉시 1회 더 보낸다.
9.3 보존하는 항목과 그 이유
| 항목 | 보존 이유 |
|---|---|
device_id (source) | 어느 장치가 보냈는지 추적. 장치 교체·고장 분석에 필요 |
observed_at | 측정 시각. 상태 계산의 시간 기준 |
received_at | 수신 시각. 지연·시계 오차·미수신 판단(SENSOR_FAULT)에 필요 |
| 단위 | 필드명에 단위를 넣는다(_cm, _ppm, _c, _percent). 나중에 단위가 헷갈려 잘못 계산하는 일을 막는다 |
quality | 값을 믿어도 되는지의 표시. 오류 값이 상태 계산에 섞이지 않게 한다 |
schema_version | 필드가 바뀌어도 과거 데이터를 올바르게 해석 |
firmware_version | 동작 차이가 생겼을 때 원인을 펌웨어까지 좁힘 |
오류 상태(null + 플래그) | "값이 0"과 "측정 실패"를 구별. 실패를 0으로 저장하면 평균·임계값이 왜곡된다 |
원시 measurements (JSONB) | 규칙이 바뀌어도 과거 원자료로 재계산 가능 |
핵심 원칙은 원자료를 지우거나 고쳐 쓰지 않는다는 것이다. 정제와 해석은 상태·이벤트 계층에서 하고, 관측 테이블은 받은 그대로 둔다. 이벤트의 evidence(JSONB)에는 판단 근거가 된 관측 요약을 남겨, 나중에 "왜 이 이벤트가 생겼나"를 되짚을 수 있게 한다.
10통신·엣지·서버 아키텍처
10.1 UART와 I²C 기초
| 항목 | UART | I²C |
|---|---|---|
| 선 수 | 2개(TX, RX) + GND | 2개(SDA, SCL) + GND |
| 연결 대상 | 1:1 | 1:다 (주소로 구분) |
| 속도 | 설정값(baud)을 양쪽이 맞춰야 함. LD2410C 기본 256000 bps | 표준 100 kHz, 고속 400 kHz가 일반적 |
| 거리 | 짧은 배선(수십 cm~수 m 수준). 길어지면 노이즈 | 기판·브레드보드 수준(수십 cm 이내 권장) |
| 본 실습 용도 | LD2410C ↔ ESP32 (GPIO16/GPIO17) | SCD41 ↔ ESP32 (GPIO21/GPIO22, 주소 0x62) |
- UART는 TX와 RX를 교차 연결한다. 장치의 TX가 ESP32의 RX로 간다.
- I²C 선 길이·풀업 저항 등 세부 한계는 데이터시트 확인 후 설치한다(04_wiring_and_installation.md).
- 두 방식 모두 3.3V 로직 기준으로 쓴다. 5V 신호를 ESP32 GPIO에 직접 넣지 않는다(11장).
10.2 Wi-Fi
ESP32 DevKit V1(ESP32-WROOM-32)은 Wi-Fi를 내장한다. 일반적으로 2.4GHz 대역을 쓴다. 학원 공유기의 SSID·암호는 config.h에 분리해 두고 소스 저장소에 올리지 않는다. 신호 세기, 공유기 위치, 벽 재질에 따라 끊길 수 있으므로 재접속 로직이 필수다(12장).
10.3 HTTP vs MQTT
| 항목 | HTTP POST (기본) | MQTT (확장) |
|---|---|---|
| 방식 | 요청-응답 | 브로커 경유 발행-구독 |
| 구성 요소 | 장치 + 서버 | 장치 + 브로커 + 구독 서비스 |
| 구현 난이도 | 낮음 | 중간(브로커 운영 필요) |
| 응답 확인 | 즉시(상태 코드) | QoS 설정에 따름 |
| 적합한 규모 | 장치 수 적고 30초 주기 | 장치 수 많거나 양방향 제어·실시간성 필요 |
기본은 HTTP다. 30초 주기로 JSON 한 건을 보내는 구조에는 HTTP로 충분하고, 디버깅이 쉽고, 서버 코드도 단순하다. 다음 신호가 보이면 MQTT를 검토한다.
- 장치가 수십 대로 늘어 서버 연결 관리가 부담될 때
- 서버가 장치에 명령을 내려야 할 때(설정 변경, 재시작)
- 전송 주기를 크게 줄여야 할 때
- 네트워크가 불안정해 재전송·보장 수준(QoS) 제어가 필요할 때
전환 시점은 12주 로드맵의 확장 단계(9~12주)에서 판단한다(17장).
10.4 Edge Gateway의 역할
Edge Gateway는 센서 노드와 서버 사이에 놓이는 중간 장치(예: 학원 내 소형 PC)다. 본 MVP는 ESP32가 서버로 직접 보내므로 게이트웨이를 쓰지 않는다. 확장 시 맡을 역할은 다음과 같다.
- 여러 장치 데이터를 모아 한 번에 전송
- 인터넷 단절 시 임시 저장 후 재전송
- 학원 내부에서만 쓰는 데이터와 외부로 보낼 데이터의 분리(개인정보 최소화)
- 로컬 규칙 실행(서버 단절 시에도 동작)
10.5 전체 아키텍처
flowchart TB
subgraph L1["센서 계층"]
S1["HLK-LD2410C<br/>존재 감지"]
S2["SCD41<br/>CO2, 온습도"]
end
subgraph L2["엣지 계층"]
E1["ESP32 DevKit V1<br/>수집, JSON 생성, Wi-Fi 전송"]
E2["Edge Gateway<br/>(확장 단계, 선택)"]
end
subgraph L3["서버 계층"]
B1["FastAPI<br/>수신, 검증"]
B2[("PostgreSQL 16<br/>observations, events")]
B3["상태·이벤트 엔진<br/>규칙 기반"]
end
subgraph L4["서비스 계층"]
V1["대시보드<br/>Chart.js"]
V2["상태 조회 API<br/>이벤트 이력 API"]
V3["AI 질의 응답"]
end
S1 -- UART --> E1
S2 -- "I2C" --> E1
E1 -- "HTTP POST JSON" --> B1
E1 -.-> E2
E2 -.-> B1
B1 --> B2
B2 --> B3
B3 --> B2
B2 --> V1
B2 --> V2
V2 --> V3
실선이 MVP 경로, 점선이 확장 경로다. 서비스 계층의 AI는 데이터베이스를 직접 읽지 않고 API를 통해서만 접근한다.
10.6 AI가 검증된 데이터로만 답하는 구조
LLM에게 원시 센서 로그를 통째로 읽히고 "지금 강의실에 사람 있어?"라고 물으면 위험하다. 결측값, 중복, 오류 값을 모델이 스스로 걸러내지 못할 수 있고, 근거 없이 그럴듯한 답을 만들 수 있다. 그래서 구조를 다음과 같이 나눈다.
flowchart LR
Q["사용자 질문"] --> AI["AI"]
AI -- "도구 호출" --> API["상태 조회 API<br/>이벤트 이력 API"]
API --> ST["검증된 상태<br/>quality, 신뢰도 포함"]
API --> EV["이벤트 이력<br/>evidence 포함"]
ST --> AI
EV --> AI
AI --> A["답변<br/>근거와 한계 함께 제시"]
- 품질 검증과 상태 계산은 결정적인 코드(14장)가 한다. AI는 계산하지 않는다.
- AI는 API가 돌려준 상태·이벤트·
evidence·신뢰도를 문장으로 옮기는 역할만 한다. - 답변에는 근거(어느 시각의 어떤 이벤트인지)와 한계("정확한 인원수는 알 수 없음")를 함께 적는다.
- API가
SENSOR_FAULT상태를 돌려주면 AI는 "현재 센서 오류로 판단 불가"라고 답해야 한다. 추측하지 않는다.
질의 예시는 14.6절에 있다.
11실습 환경 준비 및 하드웨어 배선
이 장은 흐름만 요약한다. 부품 목록·호환성·구매 시 확인 사항은 03_bom_and_compatibility.md, 배선 순서·설치 위치·주의 사항 상세는 04_wiring_and_installation.md를 참고한다.
11.1 준비물
| 역할 | 부품 | 비고 |
|---|---|---|
| MCU | ESP32 DevKit V1 (ESP32-WROOM-32) | Wi-Fi 내장, 3.3V 로직 |
| 존재 감지 | HLK-LD2410C | 24GHz mmWave, UART, 존재/움직임 감지 전용(인원 계수 아님) |
| CO₂·온습도 | Sensirion SCD41 | NDIR CO₂ + 온도 + 습도, I²C(주소 0x62) |
| 기타 | 브레드보드, 점퍼선(M-F), USB 케이블, 5V USB 전원 어댑터 |
개발 PC에는 Arduino IDE 2.x(12장), Python 3.11+와 PostgreSQL 16(13장)을 설치한다.
11.2 작업 흐름
- 부품 도착 후 외관과 핀 표기를 확인한다.
- 전원을 끈 상태에서 아래 핀맵대로 배선한다.
- 배선을 한 번 더 눈으로 대조한다(전원 연결 전).
- USB로 ESP32만 먼저 연결해 PC가 포트를 인식하는지 확인한다.
- 센서를 하나씩 확인한다: SCD41 먼저, 이후 LD2410C.
- 12장의 펌웨어 업로드 후 직렬 모니터에서 값을 확인한다.
11.3 핀맵
| 연결 | ESP32 핀 | 상대 핀 | 주의 |
|---|---|---|---|
| LD2410C 전원 | VIN(5V) | VCC | 모듈 공급 전압은 데이터시트로 확인 후 기재 |
| LD2410C GND | GND | GND | |
| LD2410C UART | GPIO16 (RX2) | TX | LD2410C 로직은 3.3V TTL — 직결 가능 여부 데이터시트 확인 |
| LD2410C UART | GPIO17 (TX2) | RX | |
| SCD41 전원 | 3V3 | VDD | SCD41은 3.3V. 5V 금지 |
| SCD41 GND | GND | GND | |
| SCD41 I²C | GPIO21 (SDA) | SDA | |
| SCD41 I²C | GPIO22 (SCL) | SCL |
경고: ESP32 GPIO에 5V 신호를 직접 입력하지 않는다. ESP32 GPIO는 3.3V 로직이다. 5V 신호가 들어가면 칩이 손상될 수 있다. LD2410C의 UART 출력 레벨이 3.3V인지는 구매한 모듈의 데이터시트로 반드시 확인한다. 확인되지 않으면 레벨 변환기를 쓴다. SCD41 전원은 3V3에만 연결한다.
11.4 흔한 배선 실수
- TX-TX, RX-RX 직결(교차해야 한다)
- SCD41 전원을 VIN(5V)에 연결
- SDA/SCL 바꿔 연결
- GND 공통 누락
- 전원이 켜진 상태에서 점퍼선 이동
설치 위치(천장·벽 방향, 금속·유리 반사 등)는 7장과 04_wiring_and_installation.md를 참고한다.
12ESP32 펌웨어 실습
전체 코드는 firmware/를 참고한다(firmware/academy_sensor_node/academy_sensor_node.ino, 같은 폴더의 config.h). 여기서는 흐름과 핵심 조각만 다룬다. ESPHome으로도 비슷한 구성이 가능하나(대안), 이 교재는 Arduino IDE 2.x를 기본으로 한다.
12.1 개발 환경 설치
1단계. Arduino IDE 2.x 설치
Arduino 공식 사이트에서 IDE 2.x를 내려받아 설치한다. 버전은 설치 시점의 최신 2.x를 쓴다(실습 전 확인).
2단계. esp32 보드 패키지 추가
파일 > 환경설정 > 추가 보드 매니저 URLs에 Espressif의 ESP32 패키지 인덱스 URL을 추가한다. URL은 Espressif 공식 문서에서 확인해 붙여 넣는다(실습 전 확인).도구 > 보드 > 보드 매니저에서esp32(by Espressif Systems)를 검색해 설치한다.도구 > 보드에서ESP32 Dev Module을 선택한다(DevKit V1에 일반적으로 쓰는 선택지. 문제가 있으면board문서 확인).
3단계. 라이브러리 설치 (스케치 > 라이브러리 포함하기 > 라이브러리 관리)
ld2410(by ncmreynolds)Sensirion I2C SCD4x(의존성인Sensirion Core가 함께 설치되는지 확인, 없으면 직접 설치)
버전은 라이브러리 매니저의 최신 버전을 쓰며, 실습 전 확인한다. 라이브러리 버전에 따라 함수 이름이 다를 수 있다. 아래 코드가 컴파일되지 않으면 설치한 버전의 예제 스케치(파일 > 예제)와 대조한다.
4단계. 포트 선택
USB로 연결한 뒤 도구 > 포트에서 ESP32 포트(Windows는 COM 번호)를 고른다.
12.2 구현 흐름
flowchart TD
A["setup: 직렬, Wi-Fi, 센서 초기화"] --> B["loop 시작"]
B --> C["LD2410C 읽기"]
C --> D["SCD41 읽기"]
D --> E["quality 플래그 계산"]
E --> F["직렬 모니터 출력"]
F --> G{"30초 경과 또는<br/>presence 변화?"}
G -- "아니오" --> B
G -- "예" --> H{"Wi-Fi 연결?"}
H -- "아니오" --> I["재접속 시도"] --> B
H -- "예" --> J["HTTP POST JSON"]
J --> K{"2xx 응답?"}
K -- "예" --> L["seq 증가"] --> B
K -- "아니오" --> M["오류 로그, 다음 주기에 재시도"] --> B
12.3 단계 1: 센서 읽기
UART는 LD2410C 기본 속도 256000 bps로 연다. 핀은 GPIO16(RX2), GPIO17(TX2)이다.
#include <Wire.h>
#include <ld2410.h>
#include <SensirionI2cScd4x.h>
#define RADAR_RX_PIN 16 // ESP32 RX2 <- LD2410C TX
#define RADAR_TX_PIN 17 // ESP32 TX2 -> LD2410C RX
#define I2C_SDA_PIN 21
#define I2C_SCL_PIN 22
ld2410 radar;
SensirionI2cScd4x scd4x;
void setup() {
Serial.begin(115200); // 직렬 모니터 속도
Serial2.begin(256000, SERIAL_8N1, RADAR_RX_PIN, RADAR_TX_PIN);
if (!radar.begin(Serial2)) {
Serial.println("LD2410C 초기화 실패: 배선 또는 baud 확인");
}
Wire.begin(I2C_SDA_PIN, I2C_SCL_PIN);
scd4x.begin(Wire, SCD41_I2C_ADDR_62); // 주소 0x62
scd4x.stopPeriodicMeasurement(); // 이전 상태 정리
scd4x.startPeriodicMeasurement(); // 약 5초 주기 측정 시작
}
SCD41 첫 측정값은 시작 후 수 초가 지나야 나온다. 값이 준비되었는지 확인한 뒤 읽는다. (준비 확인 함수 이름은 라이브러리 버전에 따라 다르다. 설치한 버전의 예제를 따른다.)
struct Reading {
bool presence, moving, stationary;
int distance_cm;
bool radar_valid;
float co2, temp, hum;
bool co2_valid;
};
Reading readSensors() {
Reading r = {};
radar.read(); // 라이브러리가 프레임을 파싱
r.radar_valid = radar.isConnected();
if (r.radar_valid) {
r.presence = radar.presenceDetected();
r.moving = radar.movingTargetDetected();
r.stationary = radar.stationaryTargetDetected();
r.distance_cm = radar.detectedDistance();
}
uint16_t co2 = 0; float t = 0, h = 0;
uint16_t err = scd4x.readMeasurement(co2, t, h);
r.co2_valid = (err == 0 && co2 != 0); // 0 ppm 또는 오류면 무효
if (r.co2_valid) { r.co2 = co2; r.temp = t; r.hum = h; }
return r;
}
주의: radar.read()는 자주 호출해야 프레임을 놓치지 않는다. loop() 안에서 delay()를 길게 쓰지 말고, 시간 비교(millis())로 전송 주기를 관리한다.
12.4 단계 2: 직렬 출력 확인
Wi-Fi를 붙이기 전에 센서 값만 직렬 모니터(115200)에 출력해 확인한다.
void printReading(const Reading& r) {
Serial.printf("presence=%d moving=%d stationary=%d dist=%dcm radar_valid=%d | "
"co2=%.0fppm temp=%.1fC hum=%.1f%% co2_valid=%d\n",
r.presence, r.moving, r.stationary, r.distance_cm, r.radar_valid,
r.co2, r.temp, r.hum, r.co2_valid);
}
예상 시리얼 출력 (예시, 실제 값은 환경에 따라 다름):
presence=1 moving=0 stationary=1 dist=210cm radar_valid=1 | co2=850ppm temp=24.5C hum=48.2% co2_valid=1
presence=1 moving=1 stationary=0 dist=180cm radar_valid=1 | co2=853ppm temp=24.5C hum=48.1% co2_valid=1
presence=0 moving=0 stationary=0 dist=0cm radar_valid=1 | co2=848ppm temp=24.4C hum=48.3% co2_valid=1
확인 항목: 센서 앞에서 움직이면 moving=1, 가만히 앉으면 stationary=1로 바뀌는가. 방을 비우면 presence=0이 되는가. CO₂ 값이 출력되는가. 이 단계에서 문제가 있으면 Wi-Fi 이전에 해결한다.
12.5 단계 3: Wi-Fi 접속과 HTTP 전송
Wi-Fi 이름·암호와 서버 주소는 config.h에 둔다.
// config.h (저장소에 올리지 않는다. 값은 예시)
#define WIFI_SSID "your-wifi-ssid"
#define WIFI_PASS "your-wifi-password"
#define SERVER_URL "http://192.168.0.10:8000/api/v1/observations"
#define DEVICE_ID "academy_room_A_01"
#define FW_VERSION "0.1.0"
#include <WiFi.h>
#include <HTTPClient.h>
#include <ArduinoJson.h> // JSON 생성용. 라이브러리 관리자에서 설치
#include "config.h"
uint32_t seq = 0;
String buildJson(const Reading& r, const String& observedAt) {
JsonDocument doc;
doc["device_id"] = DEVICE_ID;
doc["seq"] = seq;
doc["observed_at"] = observedAt; // 시각 동기화 필요(NTP)
doc["firmware_version"] = FW_VERSION;
doc["schema_version"] = "1.0";
JsonObject m = doc["measurements"].to<JsonObject>();
if (r.radar_valid) {
m["presence"] = r.presence;
m["moving_target"] = r.moving;
m["stationary_target"] = r.stationary;
m["detect_distance_cm"] = r.distance_cm;
} else {
m["presence"] = nullptr; m["moving_target"] = nullptr;
m["stationary_target"] = nullptr; m["detect_distance_cm"] = nullptr;
}
if (r.co2_valid) {
m["co2_ppm"] = (int)r.co2;
m["temperature_c"] = r.temp;
m["humidity_percent"] = r.hum;
} else {
m["co2_ppm"] = nullptr; m["temperature_c"] = nullptr; m["humidity_percent"] = nullptr;
}
JsonObject q = doc["quality"].to<JsonObject>();
q["radar_valid"] = r.radar_valid;
q["co2_valid"] = r.co2_valid;
String out; serializeJson(doc, out); return out;
}
bool postJson(const String& body) {
HTTPClient http;
http.begin(SERVER_URL);
http.addHeader("Content-Type", "application/json");
int code = http.POST(body);
http.end();
Serial.printf("POST seq=%u -> HTTP %d\n", seq, code);
return code >= 200 && code < 300;
}
observed_at은 +09:00 오프셋이 붙은 ISO 8601 문자열이어야 한다. ESP32는 전원이 켜질 때 시각을 모르므로, Wi-Fi 연결 후 NTP로 시각을 맞춘다(configTime(...)). NTP 동기화 전의 시각은 보내지 않는다. observed_at 생성 방법의 전체 구현은 firmware/를 참고한다.
전송 시점은 두 조건이다: 30초마다, 그리고 presence 값이 바뀔 때 즉시 1회.
const uint32_t SEND_INTERVAL_MS = 30000;
uint32_t lastSend = 0;
bool lastPresence = false;
void loop() {
Reading r = readSensors();
bool changed = (r.radar_valid && r.presence != lastPresence);
if (changed || millis() - lastSend >= SEND_INTERVAL_MS) {
printReading(r);
sendWithRetry(r);
lastSend = millis();
if (r.radar_valid) lastPresence = r.presence;
}
}
12.6 단계 4: 재접속과 오류 처리
현장에서는 Wi-Fi가 끊기고 서버가 잠시 꺼지는 일이 생긴다. 기본 대응 원칙은 다음과 같다.
- 전송 전에
WiFi.status()를 확인하고, 끊겨 있으면 재접속한다. - 전송 실패 시 같은
seq를 유지해 다음 주기에 다시 보낸다. 성공했을 때만seq를 증가시킨다. 서버가UNIQUE(device_id, seq)로 중복을 걸러낸다. - 센서 읽기 실패는
null+quality플래그false로 보낸다. 값을 0으로 채우지 않는다. - 오랫동안 연결이 복구되지 않으면 재시작(
ESP.restart())을 고려한다.
bool ensureWifi() {
if (WiFi.status() == WL_CONNECTED) return true;
WiFi.disconnect();
WiFi.begin(WIFI_SSID, WIFI_PASS);
uint32_t t0 = millis();
while (WiFi.status() != WL_CONNECTED && millis() - t0 < 10000) delay(200);
Serial.println(WiFi.status() == WL_CONNECTED ? "Wi-Fi 재접속 성공" : "Wi-Fi 재접속 실패");
return WiFi.status() == WL_CONNECTED;
}
void sendWithRetry(const Reading& r) {
if (!ensureWifi()) return; // 실패 시 seq 유지, 다음 주기에 재시도
String body = buildJson(r, nowIso8601()); // nowIso8601(): NTP 기반 시각 문자열
if (postJson(body)) seq++;
}
예상 시리얼 출력 (예시):
Wi-Fi 연결됨, IP=192.168.0.37
POST seq=1024 -> HTTP 201
presence=1 moving=0 stationary=1 dist=210cm radar_valid=1 | co2=850ppm temp=24.5C hum=48.2% co2_valid=1
POST seq=1025 -> HTTP 201
Wi-Fi 재접속 실패
POST seq=1026 -> HTTP -1
HTTP 코드는 서버 구현(13장)에 따라 다를 수 있다. 음수 값은 연결 자체가 실패했다는 뜻이다.
12.7 흔한 오류와 해결
| 증상 | 원인 후보 | 해결 |
|---|---|---|
| 포트가 보이지 않음 | 충전 전용 USB 케이블, USB-UART 드라이버 미설치 | 데이터 케이블로 교체. 보드에 쓰인 USB-UART 칩(CP210x 또는 CH340 등)에 맞는 드라이버 설치. 다른 USB 포트 시도 |
업로드 실패 (Failed to connect to ESP32) | 부트로더 진입 실패 | 업로드 시작 메시지(Connecting...)가 나올 때 보드의 BOOT 버튼을 누른 채 있다가 연결되면 놓는다. 일부 보드는 EN 버튼 조합 필요 |
| 업로드는 되나 실행 안 됨, 계속 재부팅 | 전원 부족, GPIO에 부하 | 전원 어댑터 용량 확인, 센서 일부를 분리해 원인 격리 |
| 컴파일 오류: 헤더 없음 | 라이브러리 미설치 | 라이브러리 관리에서 설치, Sensirion Core 의존성 확인 |
| 컴파일 오류: 함수 없음/인자 불일치 | 라이브러리 버전 차이 | 설치한 버전의 예제 스케치와 대조해 함수 이름 수정 |
| 같은 기능의 라이브러리가 둘 이상 설치되어 충돌 | 구버전 Sensirion I2C SCD4x와 신버전 클래스명 혼재, 수동 설치본과 매니저 설치본 중복 | 하나만 남기고 삭제(문서 폴더의 Arduino/libraries 확인), IDE 재시작 |
| LD2410C 값이 항상 0/미연결 | TX/RX 교차 안 됨, baud 불일치, 전원 부족 | 11장 핀맵 재확인, 256000 확인, 5V 전원 확인 |
| SCD41 읽기 실패 | I²C 주소/배선, 3V3 미연결 | SDA(GPIO21)/SCL(GPIO22) 확인, I²C 스캐너 스케치로 0x62 응답 확인 |
| 업로드 후 직렬 모니터 글자 깨짐 | 직렬 모니터 속도 불일치 | 모니터를 115200으로 맞춘다 |
| Wi-Fi 접속 안 됨 | 5GHz 전용 SSID, 암호 오타 | 2.4GHz SSID 사용, config.h 확인 |
13백엔드·DB·대시보드 실습
전체 코드는 backend/(app/main.py, app/models.py, app/schemas.py, app/dashboard.html, requirements.txt)와 database/(schema.sql, sample_data.json)를 참고한다. 여기서는 설치 흐름과 핵심 조각만 다룬다.
13.1 설치 흐름
| 구성 요소 | 버전 | 용도 |
|---|---|---|
| Python | 3.11+ | 서버 언어 |
| FastAPI | 최신(requirements.txt 확인) | 웹 API |
| uvicorn | 최신 | ASGI 서버 |
| Pydantic | v2 | 입력 검증 |
| SQLAlchemy | 2.0 | DB 접근 |
| PostgreSQL | 16 | 저장소 |
- PostgreSQL 16을 설치하고 데이터베이스와 계정을 만든다(예: DB명
academy_sensor. 이름은 자유). database/schema.sql을 실행해 테이블을 만든다.- 가상환경을 만들고
backend/requirements.txt를 설치한다. - 접속 문자열을 환경변수로 지정하고 uvicorn으로 실행한다.
# 예시 명령 (Windows PowerShell 기준 환경변수 문법은 다를 수 있음)
cd backend
python -m venv .venv
.venv\Scripts\activate
pip install -r requirements.txt
psql -U postgres -c "CREATE DATABASE academy_sensor;"
psql -U postgres -d academy_sensor -f ../database/schema.sql
# DATABASE_URL 변수명은 backend/README.md 확인
uvicorn app.main:app --host 0.0.0.0 --port 8000
--host 0.0.0.0은 같은 네트워크의 ESP32가 접속할 수 있게 한다. 외부 인터넷에 그대로 노출하지 않는다(16장).
예상 출력 (예시):
INFO: Started server process [12345]
INFO: Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)
13.2 수신 → 검증 → 저장
검증 스키마 (Pydantic v2). 관측 JSON(9장)과 필드를 일치시킨다. 측정 필드는 null을 허용한다.
from datetime import datetime
from pydantic import BaseModel
class Measurements(BaseModel):
presence: bool | None = None
moving_target: bool | None = None
stationary_target: bool | None = None
detect_distance_cm: int | None = None
co2_ppm: int | None = None
temperature_c: float | None = None
humidity_percent: float | None = None
class Quality(BaseModel):
radar_valid: bool
co2_valid: bool
class ObservationIn(BaseModel):
device_id: str
seq: int
observed_at: datetime # 오프셋 포함 ISO 8601
firmware_version: str
schema_version: str
measurements: Measurements
quality: Quality
형식이 틀린 요청(필수 필드 누락, 시각 형식 오류)은 FastAPI가 자동으로 422로 거부한다.
저장 모델 (SQLAlchemy 2.0). UNIQUE(device_id, seq)를 모델에도 선언한다.
from sqlalchemy import BigInteger, ForeignKey, UniqueConstraint, DateTime, String, Integer
from sqlalchemy.dialects.postgresql import JSONB
from sqlalchemy.orm import Mapped, mapped_column, DeclarativeBase
class Base(DeclarativeBase): pass
class Observation(Base):
__tablename__ = "observations"
__table_args__ = (UniqueConstraint("device_id", "seq", name="uq_observations_device_seq"),)
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
device_id: Mapped[int] = mapped_column(ForeignKey("devices.id"))
seq: Mapped[int] = mapped_column(Integer)
observed_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
received_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
measurements: Mapped[dict] = mapped_column(JSONB)
quality: Mapped[dict] = mapped_column(JSONB)
schema_version: Mapped[str] = mapped_column(String)
수신 엔드포인트. device_id 문자열(예: academy_room_A_01)로 devices 행을 찾고, 없으면 거부한다. received_at은 서버가 채운다. 중복(device_id, seq)은 오류가 아니라 "이미 저장됨"으로 응답한다.
from datetime import datetime, timezone
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy import select
from sqlalchemy.dialects.postgresql import insert
app = FastAPI()
@app.post("/api/v1/observations", status_code=201)
def receive(obs: ObservationIn, db=Depends(get_db)):
dev = db.scalar(select(Device).where(Device.device_id == obs.device_id))
if dev is None or not dev.active:
raise HTTPException(status_code=404, detail="알 수 없는 장치")
stmt = insert(Observation).values(
device_id=dev.id,
seq=obs.seq,
observed_at=obs.observed_at,
received_at=datetime.now(timezone.utc), # 서버가 기록
measurements=obs.measurements.model_dump(),
quality=obs.quality.model_dump(),
schema_version=obs.schema_version,
).on_conflict_do_nothing(index_elements=["device_id", "seq"])
result = db.execute(stmt)
db.commit()
if result.rowcount == 0:
return {"status": "duplicate"} # 이미 받은 seq
return {"status": "stored"}
위 조각은 흐름을 보이기 위한 요약이다. 중복 시 응답 코드(200/201)는 backend/app/main.py의 정의를 따른다. 12장 펌웨어는 2xx 응답을 성공으로 본다.
13.3 중복 제거
ESP32는 응답을 받지 못하면 같은 seq를 다시 보낸다. 서버가 이미 저장했는데 응답만 유실된 경우 중복 행이 생길 수 있다. UNIQUE(device_id, seq) 제약과 ON CONFLICT DO NOTHING으로 DB가 직접 막는다. 코드에서 "먼저 조회 후 저장"으로 처리하면 동시 요청에서 틈이 생기므로, 제약을 최종 방어선으로 둔다.
중복 전송 확인 (예시):
curl -X POST http://localhost:8000/api/v1/observations \
-H "Content-Type: application/json" \
-d @sample_observation.json
# {"status":"stored"}
curl -X POST http://localhost:8000/api/v1/observations \
-H "Content-Type: application/json" \
-d @sample_observation.json
# {"status":"duplicate"}
13.4 시계열 조회 API
대시보드와 상태·이벤트 엔진이 쓰는 조회 API다. 경로 이름은 backend/app/main.py를 따르며, 아래는 예시 형태다.
from datetime import datetime
from fastapi import Query
@app.get("/api/v1/spaces/{space_id}/observations")
def list_observations(space_id: int,
since: datetime,
until: datetime | None = None,
limit: int = Query(500, le=5000),
db=Depends(get_db)):
q = (select(Observation)
.join(Device, Device.id == Observation.device_id)
.where(Device.space_id == space_id, Observation.observed_at >= since)
.order_by(Observation.observed_at.desc())
.limit(limit))
if until:
q = q.where(Observation.observed_at < until)
rows = db.scalars(q).all()
return [{"observed_at": r.observed_at, "measurements": r.measurements,
"quality": r.quality} for r in reversed(rows)]
limit 상한을 두어 한 번에 너무 많은 행을 반환하지 않게 한다. 조회가 느려지면 (device_id, observed_at) 인덱스를 검토한다(database/schema.sql에 정의된 인덱스를 확인).
호출 예시와 응답 (예시 데이터):
curl "http://localhost:8000/api/v1/spaces/1/observations?since=2026-10-18T16:00:00%2B09:00&limit=2"
[
{"observed_at": "2026-10-18T16:30:00+09:00",
"measurements": {"presence": true, "co2_ppm": 850},
"quality": {"radar_valid": true, "co2_valid": true}},
{"observed_at": "2026-10-18T16:30:30+09:00",
"measurements": {"presence": true, "co2_ppm": 855},
"quality": {"radar_valid": true, "co2_valid": true}}
]
13.5 Chart.js 대시보드
복잡한 프런트엔드 프레임워크는 쓰지 않는다. FastAPI가 단일 HTML(app/dashboard.html)을 서빙하고, 이 파일이 Chart.js(cdnjs CDN)로 시계열을 그린다.
<canvas id="co2Chart" height="120"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
<script>
async function load() {
const since = new Date(Date.now() - 3 * 3600 * 1000).toISOString();
const res = await fetch(`/api/v1/spaces/1/observations?since=${encodeURIComponent(since)}`);
const rows = await res.json();
new Chart(document.getElementById("co2Chart"), {
type: "line",
data: {
labels: rows.map(r => r.observed_at.slice(11, 16)),
datasets: [{ label: "CO2 (ppm)",
data: rows.map(r => r.measurements.co2_ppm), // null은 끊김으로 표시
spanGaps: false }]
}
});
}
load();
</script>
- 결측(
null)은 선을 이어 붙이지 않고 끊어서 보여 준다(spanGaps: false). 센서 오류를 눈에 보이게 하기 위해서다. - 차트 라이브러리 버전은 실습 전 cdnjs에서 확인한다.
- 대시보드는 presence 타임라인, CO₂/온습도 선 그래프, 최근 이벤트 목록을 최소 구성으로 한다.
브라우저에서 http://localhost:8000/(경로는 main.py 확인)로 접속해 그래프가 표시되면 성공이다.
14센서 퓨전·이벤트 엔진 구현
규칙의 정식 정의는 08_event_rules.md에 있다. 이 장은 구현 흐름과 핵심 조각을 보인다.
임계값에 대한 주의. 아래 시간 창과 임계값(5분, 10분, 60%, 1200 ppm, 1000 ppm)은 초기값이며 실측 후 조정한다. 학원의 강의실 크기, 환기 방식, 설치 위치에 따라 적절한 값이 다르다. 근거 없이 고정하지 말고 15장의 실험 설계로 오탐·미탐을 측정해 바꾼다.
14.1 규칙 4종
| 이벤트 | 조건(요약) | 해제 |
|---|---|---|
ROOM_OCCUPIED | 최근 5분 창에서 presence true 비율 ≥ 60% | 아래 EMPTY 성립 시 |
ROOM_EMPTY | 최근 10분 presence true 없음 | OCCUPIED 성립 시 |
VENTILATION_NEEDED | co2_ppm > 1200 상태 10분 지속 | co2_ppm < 1000 10분 지속 |
SENSOR_FAULT | 장치 데이터 5분 이상 미수신 또는 quality false 지속 | 정상 수신 재개 |
14.2 데이터 품질 플래그 처리
규칙을 적용하기 전에 관측을 걸러 낸다. 원칙은 다음과 같다.
quality.radar_valid == false인 관측은 presence 계산에서 제외한다(false로 취급하지 않는다).quality.co2_valid == false인 관측은 CO₂ 계산에서 제외한다.- 제외된 비율이 높으면 해당 규칙을 판단 불가로 두고,
SENSOR_FAULT조건을 따로 평가한다. - 레이더가 무효인 동안에 "비어 있음"으로 확정하지 않는다. 측정 실패와 빈 방은 다르다.
def valid_presence(rows):
"""radar_valid 인 관측의 presence 값만 반환 (True/False 리스트)"""
return [r.measurements["presence"] for r in rows
if r.quality.get("radar_valid") and r.measurements.get("presence") is not None]
def valid_co2(rows):
"""co2_valid 인 관측의 (observed_at, co2_ppm) 반환"""
return [(r.observed_at, r.measurements["co2_ppm"]) for r in rows
if r.quality.get("co2_valid") and r.measurements.get("co2_ppm") is not None]
14.3 ROOM_OCCUPIED / ROOM_EMPTY
의사코드:
입력: 공간의 최근 관측 목록 (observed_at 순)
window5 = 현재 시각 기준 최근 5분 관측 중 radar_valid 인 것
window10 = 현재 시각 기준 최근 10분 관측 중 radar_valid 인 것
if window5 가 비어 있으면: 판단 불가 (SENSOR_FAULT 평가로 넘김)
ratio = window5 에서 presence == true 인 개수 / window5 개수
if ratio >= 0.60: 상태 = OCCUPIED (ROOM_OCCUPIED 시작, 열려 있는 ROOM_EMPTY 종료)
elif window10 에 presence == true 가 하나도 없음:
상태 = EMPTY (ROOM_EMPTY 시작, 열려 있는 ROOM_OCCUPIED 종료)
else: 상태 유지 (히스테리시스: 경계에서 흔들리지 않게)
from datetime import timedelta
OCC_WINDOW = timedelta(minutes=5) # 초기값: 실측 후 조정
OCC_RATIO = 0.60 # 초기값: 실측 후 조정
EMPTY_WINDOW = timedelta(minutes=10) # 초기값: 실측 후 조정
def evaluate_occupancy(rows, now, current_state):
"""반환: 'OCCUPIED' | 'EMPTY' | current_state(유지) | None(판단 불가)"""
w5 = [r for r in rows if now - r.observed_at <= OCC_WINDOW and r.quality.get("radar_valid")
and r.measurements.get("presence") is not None]
w10 = [r for r in rows if now - r.observed_at <= EMPTY_WINDOW and r.quality.get("radar_valid")
and r.measurements.get("presence") is not None]
if not w5:
return None # 판단 불가
ratio = sum(1 for r in w5 if r.measurements["presence"]) / len(w5)
if ratio >= OCC_RATIO:
return "OCCUPIED"
if w10 and not any(r.measurements["presence"] for r in w10):
return "EMPTY"
return current_state # 경계 구간: 이전 상태 유지
주의: 처음 실행해 이전 상태가 없으면 current_state가 None이다. 이 경우 경계 구간에서는 상태를 만들지 않는다. 또한 OCCUPIED는 "사람이 있다"일 뿐 몇 명인지는 말해 주지 않는다. 가만히 앉은 사람의 정지 감지는 LD2410C 설정과 환경에 따라 놓칠 수 있다(미탐 가능). 이 위험은 15장에서 측정한다.
14.4 VENTILATION_NEEDED
의사코드:
CO2 유효 관측만 사용
시작: co2_ppm > 1200 이 연속으로 10분 이상 지속 → VENTILATION_NEEDED 시작
해제: co2_ppm < 1000 이 연속으로 10분 이상 지속 → 종료
(1000~1200 사이는 현재 상태 유지)
VENT_HIGH = 1200 # ppm, 초기값: 실측 후 조정
VENT_LOW = 1000 # ppm, 초기값: 실측 후 조정
VENT_HOLD = timedelta(minutes=10)
def evaluate_ventilation(co2_series, now, active):
"""co2_series: [(observed_at, ppm)] 시간순. 반환: True(필요) / False / None(판단 불가)"""
if not co2_series:
return None
recent = [(t, v) for t, v in co2_series if now - t <= VENT_HOLD]
if not recent or (now - recent[0][0]) < VENT_HOLD * 0.9: # 10분 분량 데이터가 없음
return active if active is not None else False
if not active and all(v > VENT_HIGH for _, v in recent):
return True
if active and all(v < VENT_LOW for _, v in recent):
return False
return active
CO₂는 환기 필요 여부의 참고 지표일 뿐 인원수를 알려 주지 않는다. 사람이 적어도 밀폐되어 있으면 올라가고, 사람이 많아도 환기가 잘 되면 낮을 수 있다.
14.5 SENSOR_FAULT
의사코드:
조건 A: 장치의 마지막 received_at 이 현재보다 5분 이상 과거 → 미수신
조건 B: 최근 5분 관측 중 radar_valid == false 또는 co2_valid == false 가 계속 이어짐
A 또는 B → SENSOR_FAULT 시작
정상 수신이 재개되고 quality 가 true 로 돌아오면 종료
FAULT_SILENCE = timedelta(minutes=5) # 초기값: 실측 후 조정
def evaluate_fault(rows, last_received_at, now):
if now - last_received_at >= FAULT_SILENCE:
return "NO_DATA"
recent = [r for r in rows if now - r.observed_at <= FAULT_SILENCE]
if recent and all(not r.quality.get("radar_valid") for r in recent):
return "RADAR_INVALID"
if recent and all(not r.quality.get("co2_valid") for r in recent):
return "CO2_INVALID"
return None
미수신 판단에는 장치 시계가 아니라 서버가 기록한 received_at을 쓴다. 장치 시계가 틀려도 판단이 흔들리지 않는다. 이 이벤트는 다른 규칙의 신뢰도에 영향을 준다. SENSOR_FAULT가 열려 있는 동안 상태 API는 해당 공간의 신뢰도를 low로 반환하거나 "판단 불가"로 응답한다.
14.6 이벤트 생성과 이력 기록
이벤트는 상태가 바뀔 때 만든다. 매 평가마다 새 행을 쓰면 이력이 쓰레기로 가득 찬다.
def apply_state(db, space_id, event_type, active, now, evidence):
"""event_type 에 대해 열린 이벤트가 있는지 보고 시작/종료를 기록"""
open_ev = db.scalar(select(Event).where(Event.space_id == space_id,
Event.type == event_type,
Event.ended_at.is_(None)))
if active and open_ev is None:
db.add(Event(space_id=space_id, type=event_type, severity=SEVERITY[event_type],
started_at=now, source="rule_engine", evidence=evidence,
created_at=now))
elif not active and open_ev is not None:
open_ev.ended_at = now
db.commit()
evidence에는 판단 근거를 요약해 남긴다(예시).
{ "rule": "ROOM_OCCUPIED", "window_minutes": 5, "presence_ratio": 0.9,
"samples": 10, "excluded_invalid": 0, "thresholds_version": "initial-v1" }
thresholds_version처럼 임계값 버전을 함께 기록하면, 나중에 임계값을 바꿨을 때 과거 이벤트를 어느 기준으로 만들었는지 알 수 있다.
엔진은 주기적으로(예: 30초~1분 간격, 간격은 초기값) 각 공간에 대해 14.2~14.5의 평가를 순서대로 실행한다. 실행 방식(백그라운드 스레드, 별도 프로세스, cron)은 MVP에서는 단순한 쪽을 택한다. 구현은 backend/를 참고한다.
14.7 시간표·출결과 결합하기 (확장)
규칙 4종만으로 8장의 "IN_USE" 같은 종합 상태를 만들 수 있다. 시간표(class_sessions)를 결합하면 "수업 시간에 비어 있음", "수업 시간이 아닌데 사용 중" 같은 의미 있는 불일치를 찾는다. 출결 기록 연동은 MVP 이후 확장이며 17장에서 다룬다. 이 경우에도 persons와 센서 데이터를 개별 학생 단위로 연결하지 않는 것이 기본값이다(16장).
14.8 검증 가능한 AI 질의 예시
10장의 구조에서 AI는 아래 API를 호출하고 그 결과로만 답한다. 아래 응답은 모두 예시 데이터다.
질문: "지금 A강의실 비어 있어?"
- AI가 상태 조회 API를 호출한다.
curl "http://localhost:8000/api/v1/spaces/1/state"
(상태 API의 정확한 경로·응답 필드는 backend/를 참고한다. 아래는 형태 예시다.)
{
"space": "A강의실",
"as_of": "2026-10-18T16:40:00+09:00",
"occupancy": "OCCUPIED",
"open_events": ["ROOM_OCCUPIED", "VENTILATION_NEEDED"],
"confidence": "high",
"last_received_at": "2026-10-18T16:39:45+09:00",
"limitations": "사람의 존재 여부만 판단. 정확한 인원수는 알 수 없음"
}
- 필요하면 이벤트 이력 API로 근거를 확인한다.
curl "http://localhost:8000/api/v1/spaces/1/events?since=2026-10-18T16:00:00%2B09:00"
- AI의 답변 예시:
현재 A강의실은 사용 중입니다(16:40 기준, 신뢰도 높음). 16:21부터 사람이 있는 상태로 기록되어 있고, CO₂가 기준을 넘어 환기 필요 이벤트도 열려 있습니다. 다만 센서는 사람의 존재 여부만 판단하므로 몇 명인지는 알 수 없습니다.
(위 시각과 내용은 예시다.)
센서 오류 시의 답변 예시. 상태 API가 SENSOR_FAULT를 돌려주면:
A강의실은 센서 데이터가 5분 이상 들어오지 않아 현재 상태를 판단할 수 없습니다. 마지막으로 확인된 시각은 16:31입니다. 직접 확인이 필요합니다.
이 구조가 주는 이점은 다음과 같다.
- 답변의 근거를 추적할 수 있다(이벤트 ID,
evidence, 시각). - 오류일 때 "모르겠다"고 답하게 강제할 수 있다.
- 규칙을 바꿔도 AI 프롬프트를 고칠 필요가 없다. 규칙은 코드에, AI는 설명에 둔다.
AI가 인원수, 학생 신원, 집중도를 답하려 하면 안 된다. 센서가 제공하지 않는 정보이기 때문이다. API 응답의 limitations 필드를 답변에 반영하게 한다.
14.9 다음 단계
- 규칙 정식 정의:
08_event_rules.md - 오탐·미탐 측정과 임계값 조정: 15장,
09_test_protocol.md - 개인정보·보안 점검: 16장,
11_privacy_security_checklist.md - 출결 연동과 다중 장치 확장: 17장,
10_roadmap.md
15실험 설계와 성능 검증
센서를 붙였다고 끝이 아니다. "붙였더니 되더라"는 검증이 아니다. 이 장은 요청서의 필수 실험 7종을 정리한다. 각 실험의 상세 프로토콜(체크 항목, 양식 원본)은 09_test_protocol.md 에 둔다. 여기서는 설계 논리와 기록 양식을 다룬다.
원칙. 이 장의 모든 결과 수치는 "예상/가설"이다. 실제 값은 직접 측정해서 기록표에 채운다. 측정하지 않은 수치를 보고서에 쓰지 않는다.
15.1 공통 개념
관측, 추론, 사실을 섞지 않는다
| 구분 | 예 | 실험에서의 역할 |
|---|---|---|
| 관측 | LD2410C 의 presence=true, SCD41 의 co2_ppm | 평가 대상 |
| 추론 | 규칙이 계산한 ROOM_OCCUPIED, 인원수 추정 | 평가 대상 |
| 사실(ground truth) | 사람이 직접 센 인원, 스톱워치 시각, 출결부 | 정답 기준 |
센서 출력끼리 비교하면 검증이 아니다. 반드시 센서와 독립적인 정답 기준을 따로 확보한다.
평가 지표 정의
시간 단위를 1분 슬롯(또는 30초 관측 1건)으로 나누고, 슬롯마다 정답과 센서 판정을 비교한다.
| 지표 | 정의 | 계산 |
|---|---|---|
| 감지율(재현율) | 실제 사람이 있는 슬롯 중 센서가 있다고 한 비율 | TP / (TP + FN) |
| 미탐률 | 실제 사람이 있는데 센서가 없다고 한 비율 | FN / (TP + FN) |
| 오탐률 | 실제 빈 공간인데 센서가 있다고 한 비율 | FP / (FP + TN) |
| 지연 | 실제 변화 시각과 센서·규칙이 변화를 반영한 시각의 차 | 반영 시각 - 정답 시각 (초) |
| 인원수 추정 오차 | 추정 인원 - 실제 인원 | 절대오차 평균(MAE) 등 |
- TP: 사람 있음·센서 있음 / FP: 사람 없음·센서 있음 / FN: 사람 있음·센서 없음 / TN: 사람 없음·센서 없음
- 인원수 추정 오차는 LD2410C 단독으로는 계산 대상이 아니다. 인원 계수 기능이 없기 때문이다(실험 3 참고).
실험 환경 기록 (모든 실험 공통)
실험마다 다음을 기록표 머리에 적는다. 이 정보가 없으면 나중에 결과를 재현할 수 없다.
| 항목 | 기록 내용 |
|---|---|
| 일시·실험자 | |
| 장치 ID·펌웨어 버전 | 예: academy_room_A_01, 0.1.0 |
| 설치 높이·방향·각도 | 줄자·각도기 값 |
| 강의실 크기·가구 배치 | 약식 도면 사진 |
| 문·창문 상태 | 열림/닫힘 |
| 에어컨·환기 장치 | 켜짐/꺼짐, 설정 온도·풍량 |
| 센서 설정 | 감도·최대 거리 게이트 설정 값 |
정답 확보의 공통 방법
- 사람이 직접 센다. 실험자 1명이 문 앞 또는 구석에서 인원과 상태(앉음·걸음·퇴실)를 스톱워치와 함께 기록한다.
- 시각 동기화. 기록자 스마트폰 시각과 장치 시각(NTP 동기)의 차이를 실험 시작 전에 확인한다.
- 영상 녹화는 쓰지 않는다. 학생이 있는 환경에서는 영상 정답을 확보하지 않는다(16장). 실험 참여자가 성인 스태프·동의한 지원자인 경우에 한해, 별도 동의 하에 제한적으로 쓸 수 있는지는 법률 검토 필요 항목이다.
- 미성년자는 실험 참여자로 쓰지 않는 것을 기본값으로 한다. 정규 수업 중 측정이 필요하면 기관장 승인과 보호자 고지 후 인원 수만 수기로 기록한다(16장).
15.2 실험 1. 빈 강의실 오탐률 측정
| 항목 | 내용 |
|---|---|
| 목적 | 아무도 없을 때 센서가 "있음"이라고 잘못 보고하는 빈도와 원인 파악 |
| 환경 통제 | 문 닫기, 잠금. 실험자 퇴실. 에어컨·선풍기·환풍기·블라인드 등 움직이는 물체 상태를 고정하거나 조건별로 구분 |
| 절차 | 1) 센서 설치·설정 기록 2) 실험자가 퇴실 후 문 닫음 3) 일정 시간 연속 기록 4) 조건(에어컨 켬/끔 등)을 바꿔 반복 |
| 반복 횟수 | 조건별 최소 3회(권장). 시간 길이는 조건마다 최소 1시간 이상, 야간 포함 1회 이상 권장. 초기 계획값이며 상황에 따라 조정 |
| 정답 확보 | "빈 방"이라는 사실 자체가 정답. 실험 중 출입이 없었음을 문 앞 확인(출입 전 사진 또는 도어 봉인 메모)으로 보장 |
| 지표 | 오탐률(FP / (FP + TN)), 오탐 지속 시간, 오탐 거리값 분포 |
| 예상/가설 | 에어컨 바람·커튼·팬 등 움직이는 물체가 있으면 오탐이 늘어날 수 있다(가설). 정지 물체만 있는 구성에서는 오탐이 적을 것으로 예상(가설). 실제 값은 측정으로 확인 |
기록표 양식:
| 일시 | 조건(에어컨/팬/창) | 기록 시간(분) | 전체 슬롯 수 | presence=true 슬롯 수 | 오탐률 | 오탐 시 거리(cm) | 비고 |
|---|---|---|---|---|---|---|---|
15.3 실험 2. 1명 보행 vs 정지 착석 존재 감지 비교
| 항목 | 내용 |
|---|---|
| 목적 | 움직이는 사람과 가만히 앉은 사람의 감지 차이 확인. 정지 상태 감지가 유지되는 시간·거리 한계 파악 |
| 환경 통제 | 실험자 1명만 입실. 기타 조건은 실험 1과 동일하게 고정 |
| 절차 | A) 보행: 센서 앞 일정 거리를 오가며 걷기 B) 착석: 책상에 앉아 거의 움직이지 않기(필기·독서 자세) C) 완전 정지: 눈 감고 최대한 움직이지 않기. 각 자세를 거리별(예: 1m, 3m, 5m)로 반복 |
| 반복 횟수 | 자세 x 거리 조합마다 최소 3회. 1회 길이는 5분 이상 권장(초기 계획값) |
| 정답 확보 | 실험자 본인이 자세 전환 시각을 스톱워치 기록. 별도 기록자가 있으면 더 좋다 |
| 지표 | 자세별 감지율, 미탐률, 감지 지연(입실부터 첫 presence=true 까지 초), 거리 추정값과 실제 거리의 차 |
| 예상/가설 | 보행은 moving_target 으로 쉽게 잡히고, 정지 착석은 stationary_target 으로 잡히되 에너지가 약해 먼 거리에서 미탐이 늘 수 있다(가설) |
기록표 양식:
| 일시 | 자세(보행/착석/완전정지) | 실제 거리(cm) | 지속(분) | 감지 슬롯/전체 슬롯 | 감지율 | 미탐률 | 첫 감지까지 지연(초) | moving/stationary 구분 | 센서 거리값(cm) |
|---|---|---|---|---|---|---|---|---|---|
15.4 실험 3. 2-5명 감지 변화 관찰
명시. LD2410C 로는 정확한 인원 계수를 할 수 없다. 이 실험의 목적은 인원수를 맞히는 것이 아니다. 사람이 늘어날 때 센서 출력(거리값, 에너지, 이동/정지 구분)이 어떻게 달라지는지 관찰하는 것이다. 정확한 다중 인원 추적이 필요하면 LD2450 등 다른 센서를 검토한다(6장).
| 항목 | 내용 |
|---|---|
| 목적 | 인원 증가에 따라 존재 판정, 에너지 값, 거리값, CO₂ 가 어떻게 변하는지 관찰하고, "인원수 추정에는 부적합"하다는 근거를 직접 확인 |
| 환경 통제 | 성인 지원자(스태프)만 참여. 좌석 위치 고정. 1명, 2명, 3명, 4명, 5명 순서로 입실 |
| 절차 | 1) 인원별로 착석 상태 유지 2) 단계당 일정 시간 기록 3) 일부 단계에서는 보행·이동 포함 4) 단계 사이에 빈 방 구간 확보 |
| 반복 횟수 | 인원 단계별 최소 2회. 좌석 배치를 바꿔 1회 추가 권장(초기 계획값) |
| 정답 확보 | 실험자가 인원 변화 시각과 인원수 기록 |
| 지표 | 단계별 presence 유지율, 에너지·거리값 분포 변화 요약. 인원수 추정 오차는 "센서 단독 판정이 가능한가"가 아니라 "SCD41 CO₂ 증가 폭과 인원수의 관계 참고용"으로만 본다 |
| 예상/가설 | presence 는 인원이 늘어도 true 로 유지되고 인원수는 구분되지 않을 것(가설). CO₂ 는 인원·환기 상태에 따라 증가하지만 CO₂ 만으로 인원수를 정확히 계산할 수 없다(방 크기·환기·시간에 따라 달라짐) |
기록표 양식:
| 일시 | 실제 인원 | 좌석 배치 | 지속(분) | presence 유지 슬롯 비율 | 센서 거리값 범위(cm) | 에너지 값 범위 | CO₂ 시작→끝(ppm) | 비고 |
|---|---|---|---|---|---|---|---|---|
15.5 실험 4. 출입문·창문·에어컨·금속·가구의 영향
| 항목 | 내용 |
|---|---|
| 목적 | 환경 요인이 mmWave 판정과 CO₂ 측정에 주는 영향 파악. 설치 위치 선정 근거 확보 |
| 환경 통제 | 한 번에 한 요인만 바꾼다(변수 통제). 빈 방 기준(실험 1)과 1명 착석 기준(실험 2) 두 가지에서 비교 |
| 절차 | 기준 상태 기록 → 요인 하나 변경 → 기록 → 원복 후 재기록. 요인: 문 열림/닫힘, 문 밖 복도 보행, 창문 열림, 에어컨/팬 켬, 금속 캐비닛·화이트보드 근접 배치, 책상·파티션 배치 변경, 센서 방향 변경 |
| 반복 횟수 | 요인 상태당 최소 3회(초기 계획값) |
| 정답 확보 | 방 안 사람 유무는 실험자가 통제. 요인 변경 시각을 수기 기록 |
| 지표 | 요인별 오탐률 변화, 감지율 변화, 거리값 이동, CO₂ 안정화 시간(창문 열림 시) |
| 예상/가설 | 얇은 벽·문 너머 이동이 감지될 수 있고 금속 반사로 거리값이 달라질 수 있다(가설). 확인은 측정으로 |
기록표 양식:
| 일시 | 변경 요인 | 변경 시각 | 기준 상태 | 변경 후 오탐률 | 변경 후 감지율 | 거리값 변화(cm) | 비고(사진 번호) |
|---|---|---|---|---|---|---|---|
15.6 실험 5. CO₂ 환기 반응·안정화 시간
| 항목 | 내용 |
|---|---|
| 목적 | 환기 조치 후 SCD41 값이 얼마나 빨리 반응하고 안정되는지 확인. VENTILATION_NEEDED 임계값(1200/1000 ppm, 10분 지속)을 현장에 맞게 조정할 근거 확보 |
| 환경 통제 | 인원·환기 설정을 기록. 센서를 공기 순환이 있는 위치에 설치하고 사람 호흡이 직접 닿지 않게 한다 |
| 절차 | 1) 문·창문 닫고 인원 입실, CO₂ 상승 기록 2) 목표 농도에 도달하면 창문·문 열기 또는 환기 장치 가동 3) 하락 곡선과 안정 시점 기록 4) 같은 절차를 인원·시간대를 바꿔 반복 |
| 반복 횟수 | 시나리오당 최소 3회(초기 계획값) |
| 정답 확보 | 별도 기준 CO₂ 측정기가 있으면 병행 비교. 없으면 "기준 없음"으로 기록하고 절대 정확도는 주장하지 않는다. SCD41 데이터시트 정확도는 공식 문서 확인 후 인용 |
| 지표 | 환기 조치 후 하락 시작까지 지연(초), 목표 농도 도달 시간(분), 안정화 시간(변화가 일정 폭 이하로 유지된 시점) |
| 예상/가설 | 센서 응답 자체의 시간 상수와 방 공기 혼합 시간이 합쳐져 수 분 단위의 지연이 있을 것으로 예상(가설) |
기록표 양식:
| 일시 | 인원 | 환기 조치(종류·시각) | 조치 시점 CO₂ | 하락 시작 시각 | 목표(예: 1000ppm) 도달 시각 | 안정화 시각 | 기준 측정기 값 | 비고 |
|---|---|---|---|---|---|---|---|---|
15.7 실험 6. Wi-Fi 단절 후 재전송·누락 확인
| 항목 | 내용 |
|---|---|
| 목적 | Wi-Fi 가 끊겼다 복구될 때 데이터가 누락되는지, 중복되는지, 순서가 맞는지 확인 |
| 환경 통제 | 서버는 로컬 네트워크에서 가동. 장치 seq 증가를 기준으로 비교 |
| 절차 | 1) 정상 전송 확인 2) 공유기 전원 차단 또는 Wi-Fi 해제로 단절 3) 일정 시간 후 복구 4) 서버 observations 의 seq 연속성·중복 확인 5) 서버 중단 후 복구 시나리오도 별도 수행 |
| 반복 횟수 | 단절 길이별(예: 1분, 10분, 1시간) 최소 3회(초기 계획값) |
| 정답 확보 | 장치가 단절 시간 동안 만든 seq 범위. 시리얼 모니터 로그와 서버 저장 건수 비교 |
| 지표 | 누락률 = 누락 seq 수 / 장치 생성 seq 수, 중복 건수(UNIQUE(device_id, seq) 위반 거부 횟수), 복구 후 첫 정상 전송까지 시간, 시간 순서 역전 여부 |
| 주의 | 현재 펌웨어가 오프라인 큐(재전송 버퍼)를 구현했는지에 따라 결과가 달라진다. 구현하지 않았다면 단절 구간은 누락되며, 이것을 "한계"로 기록한다. 버퍼 크기·구현 여부는 펌웨어 문서(05)에서 확인 |
| 예상/가설 | 버퍼가 없으면 단절 시간만큼 데이터가 누락될 것(가설). 버퍼가 있으면 누락이 줄지만 메모리 한도를 넘으면 오래된 것부터 버려질 수 있다(가설) |
기록표 양식:
| 일시 | 단절 방식 | 단절 길이 | 장치 생성 seq 범위 | 서버 저장 건수 | 누락 seq | 중복 시도 | 복구 후 첫 전송 지연(초) | 비고 |
|---|---|---|---|---|---|---|---|---|
15.8 실험 7. 무인 상태에서 센서 오류를 정상 이벤트로 오인하지 않는지
| 항목 | 내용 |
|---|---|
| 목적 | 센서 고장·배선 불량·통신 끊김이 "빈 방(ROOM_EMPTY)" 또는 "사용 중"이라는 정상 이벤트로 기록되지 않고 SENSOR_FAULT 로 구분되는지 확인 |
| 환경 통제 | 빈 방. 고장을 인위적으로 주입하되 안전을 지킨다(전원 연결 상태에서 배선을 만지지 않는다. 항상 전원 차단 후 작업) |
| 절차 | 1) LD2410C UART 선 분리 2) SCD41 I²C 선 분리 3) 장치 전원 차단 4) 서버 중단 5) 각 조건에서 quality 플래그, null 값, 이벤트 테이블 확인 |
| 반복 횟수 | 고장 유형별 최소 3회(초기 계획값) |
| 정답 확보 | 고장 주입 시각을 기록. 정상 동작 시 기대되는 결과를 사전에 표로 적어 둔다 |
| 지표 | SENSOR_FAULT 발생 여부, 발생까지 지연(5분 미수신 규칙 기준, 초기값), 오인 건수(고장인데 ROOM_EMPTY/ROOM_OCCUPIED 이벤트가 생긴 건수) |
| 예상/가설 | quality 플래그를 규칙이 확인하지 않으면 고장이 "빈 방"으로 오인될 수 있다(가설). 규칙 설계 시 quality false 는 퓨전 입력에서 제외한다(8장·08_event_rules.md) |
기록표 양식:
| 일시 | 주입한 고장 | 주입 시각 | quality 값 | measurements 값 | 생성된 이벤트 | SENSOR_FAULT 지연 | 오인 여부(O/X) | 비고 |
|---|---|---|---|---|---|---|---|---|
15.9 실험 결과를 보고하는 방법
- 원시 기록표를 먼저 첨부한다.
- 지표는 정의대로 계산하고 분모를 같이 적는다(예: 12 / 480 슬롯).
- 표본이 작으면 "표본 부족, 경향 참고용"이라고 쓴다.
- 실험 조건을 벗어난 일반화를 하지 않는다. 한 강의실의 결과를 모든 학원에 적용하지 않는다.
- 임계값 조정은 실험 결과에 근거해서만 하고, 조정 이력을 남긴다.
상세 프로토콜: 09_test_protocol.md
16보안·개인정보·운영 안전
이 교재의 시스템은 미성년자가 있는 공간에 설치된다. 기술보다 규칙이 먼저다. 이 장은 설계 원칙과 체크 항목을 안내하며, 법적 판단은 제공하지 않는다. 법령 해석이 필요한 부분은 법률 검토 필요로 표시한다. 세부 체크리스트는 11_privacy_security_checklist.md 를 따른다.
16.1 기본 전제
- 대상 공간에 미성년자가 있다. 학생과 보호자의 신뢰가 사업의 기반이다.
- MVP 에서 수집하는 것은 공간의 존재 여부, 이동/정지 여부, 거리, CO₂, 온도, 습도다. 개인 식별 정보는 센서 데이터에 포함하지 않는다.
- 레이더만으로 신원이나 집중도를 판단하지 않는다. 그런 용도로 쓰지 않는다.
- 학생 영상·음성의 상시 수집, 개인 행동 감시는 MVP 범위에서 제외한다.
16.2 개인정보 최소 수집 원칙
| 원칙 | 적용 |
|---|---|
| 목적 한정 | 수집 목적을 "강의실 사용 상태와 실내 공기질 관리"로 한정하고 문서화 |
| 최소 수집 | 필요 없는 필드는 수집하지 않는다. 센서 관측 JSON 에 이름·학번 등 개인 식별 필드를 넣지 않는다 |
| 분리 | persons 테이블은 MVP 에서 만들어 두기만 하고 센서 데이터와 연결하지 않는다 |
| 마스킹 | 이름이 필요한 경우에도 name_masked 형태로만 저장 |
| 집계 우선 | 가능하면 원시 관측보다 집계(시간대별 점유율)를 보고용으로 쓴다 |
법률 검토 필요: 센서 데이터가 개인정보에 해당하는지 여부, 센서 데이터와 출결·수강 정보를 결합할 때의 법적 지위, 14세 미만 아동 관련 별도 의무 여부는 법무 전문가 검토가 필요하다. 이 문서는 단정하지 않는다.
16.3 동의와 고지
- 현장 고지: 센서 설치 공간 입구에 안내문을 게시한다. 내용: 설치 사실, 측정 항목(존재 여부·CO₂·온습도), 영상·음성은 수집하지 않음, 문의처.
- 보호자 고지: 학원 등록·안내 채널로 목적, 수집 항목, 보관 기간, 문의 방법을 알린다. 고지 문안은 짧고 평이하게 쓴다.
- 동의 기록: 필요하다고 판단되는 경우 동의 일시와 방법을 기록한다. 미성년자의 동의 방식(법정대리인 동의 등)은 법률 검토 필요.
- 거부·철회 대응: 보호자나 학생이 불편을 제기할 때 응대 절차(담당자, 응답 기한)를 사전에 정한다.
- 센서 설치 전 학원장 서면 승인을 받는다. 건물주·임대차 계약상 제한이 있는지도 확인한다.
법률 검토 필요: 고지만으로 충분한지, 별도 동의서가 필요한지, 정보주체별(학생·교사·직원) 처리 근거를 어떻게 정리할지는 전문가 검토 대상이다. 교사·직원에 대한 근로 감시 해석 가능성도 점검한다.
16.4 접근 통제 (역할별)
| 역할 | 볼 수 있는 것 | 할 수 있는 것 |
|---|---|---|
| 원장 | 강의실 상태·이벤트 요약, 보고서 | 열람, 임계값 조정 요청 |
| 교사 | 담당 강의실의 실시간 상태(점유, CO₂) | 열람만 |
| 운영 담당(스태프) | 모든 강의실 상태, 장치 상태, 장애 알림 | 장치 등록·교체 보고 |
| 개발·관리자 | 원시 관측, DB, 로그 | 설정 변경, 배포. 계정 최소화, 개인 계정 사용 |
| 외부(보호자·방문자) | 없음 | MVP 에서는 대시보드 접근 불가 |
- 공유 계정 금지. 계정마다 비밀번호를 발급하고 퇴사·역할 변경 시 즉시 회수한다.
- 관리자 접근 기록(누가 언제 무엇을 조회·변경했는지)을 남긴다.
- DB 는 외부 인터넷에 직접 노출하지 않는다. 서버 포트는 필요한 것만 연다.
- 실습용 서버의 기본 계정·기본 비밀번호는 즉시 변경한다.
16.5 보관 기간과 삭제
- 보관 기간은 목적에 필요한 최소로 정한다. 값은 이 교재가 정하지 않는다. 초기 가이드 후보: 원시 관측은 짧게(예: 수 주), 집계는 더 길게. 이 값은 초기안이며 학원 정책과 법률 검토로 확정한다.
- 자동 삭제 작업(예약 작업)을 두고, 삭제 로그를 남긴다.
- 백업에도 같은 보관 정책을 적용한다.
- 학원 계약 종료·장치 폐기 시 데이터 반환·삭제 절차를 계약서에 넣는다.
법률 검토 필요: 법정 보관 의무가 있는 정보와의 관계, 삭제 요청 처리 기한은 단정하지 않는다.
16.6 전송 보안
| 구분 | 방식 | 사용처 | 주의 |
|---|---|---|---|
| 실습용 | 평문 HTTP (POST /api/v1/observations) | 실습실 내 사설망, 학습·프로토타입 | 도청·변조 가능. 인터넷 구간 통과 금지. 학원 운영망과 분리된 별도 SSID/공유기 권장 |
| 실서비스 | HTTPS(TLS) | 외부 서버·클라우드 | 서버 인증서 발급·갱신 필요. ESP32 에서 인증서 검증 설정이 필요하며 구현은 별도 작업 |
추가 항목:
- 장치 인증: 장치별 API 키(토큰)를 두고 서버가 검증한다. 키를 소스에 하드코딩하지 않고
config.h로 분리하며, 저장소에 올리지 않는다. - Wi-Fi 는 WPA2 이상. 학원 직원·학생용 Wi-Fi 와 분리한다.
- 평문 HTTP 로 수집하는 데이터라도 개인 식별 정보는 포함하지 않는다는 원칙을 유지한다.
16.7 장치 물리 보안
- 천장·벽의 손이 닿지 않는 위치에 설치하고 케이블을 정리한다.
- 장치 케이스에 장치 ID 라벨을 붙이되 비밀번호·키는 적지 않는다.
- ESP32 시리얼 포트(USB)는 외부에서 접근하기 어렵게 한다. 시리얼 접속으로 Wi-Fi 설정이 노출될 수 있다.
- 전원 어댑터는 인증받은 제품을 쓰고 과열·합선 위험을 점검한다. 5V 와 3.3V 를 혼동하지 않는다(SCD41 은 3.3V, ESP32 GPIO 에 5V 신호 직접 입력 금지).
- 장치 분실·교체 시 해당 장치 키를 폐기하고
devices.active를 비활성화한다.
16.8 운영 안전
- 임시 배선은 사람이 지나가는 곳에 두지 않는다. 걸림·감전 위험을 줄인다.
- 장비 설치 작업은 전원 차단 후 한다. 천장 작업은 안전 수칙을 지킨다.
- 24GHz 레이더 모듈의 인증·전파 관련 요건은 국가별로 다르다. 국내 사용 가능 여부와 인증 상태는 미확인 — 구매 전 확인, 법률 검토 필요(전파 인증 포함).
- 사고·민원·유출 의심 시 대응 절차(중단, 기록, 통지 담당)를 사전에 문서화한다. 통지 의무 요건은 법률 검토 필요.
체크리스트 원본: 11_privacy_security_checklist.md
174주 학습 및 12주 학원 MVP 로드맵
17.1 전체 구조
| 구간 | 기간 | 목표 | 결과물 |
|---|---|---|---|
| 학습·프로토타입 | 1-4주 | 개념 학습, 1개 강의실 프로토타입(디지털 트윈 V0.1) | 센서 비교표, 로그, 규칙, 대시보드 |
| 실증 | 5-8주 | 실제 학원 1개 강의실 | 설치, 2주 이상 수집, 오탐·미탐 측정, 원장 보고 1회 |
| 확장 | 9-12주 | 3-5개 강의실 | 다중 장치 운영, 장애 대응, 대시보드 공유, 다음 단계 의사결정 |
flowchart LR A[1주 원리·통신·데이터] --> B[2주 LD2410C+ESP32 측정] B --> C[3주 SCD41·퓨전] C --> D[4주 API·DB·대시보드 V0.1] D --> E[5-8주 1개 강의실 실증] E --> F[9-12주 3-5개 강의실 확장]
각 주차는 "통과 조건"을 만족해야 다음으로 넘어간다. 통과하지 못하면 다음 주로 가지 않고 원인을 해결한다. 일정이 늘어나는 것은 실패가 아니다. 통과 조건 없이 넘어가는 것이 실패다.
17.2 1-4주 상세
1주. 센서 원리·통신·데이터 구조
- 목표: mmWave·NDIR CO₂ 센서 원리, UART·I²C·Wi-Fi·HTTP 이해, 관측-상태-이벤트 구분, 스키마 읽기
- 선행조건: 파이썬·아두이노 기초
- 결과물: 센서 비교표(
02_sensor_selection_matrix.csv기반), 선정 근거 1쪽 - 통과 조건:
- 관측/상태/이벤트를 자기 말로 설명할 수 있다
- LD2410C 가 할 수 있는 것과 없는 것(인원 계수 불가)을 설명할 수 있다
- 부품 주문이 완료되었거나 수령 일정이 확정되었다
- 미통과 시: 용어 복습 후 재평가. 부품 지연이면 시뮬레이션 데이터로 2-4주 준비 일부 병행
2주. LD2410C + ESP32 연결·측정
- 목표: 배선, 펌웨어 업로드, 시리얼로 존재 값 확인, 시간별 로그 수집
- 선행조건: 1주 통과, 부품 수령, Arduino IDE 2.x 설정
- 결과물: 시간별 존재 감지 로그(CSV 또는 시리얼 캡처)
- 통과 조건:
- 핀맵(GPIO16/17, VIN, GND)대로 배선하고 배선 확인을 두 번 했다
- 빈 방·보행·착석 상태에서 값이 구분되어 기록된다
- 실험 1(빈 강의실 오탐)의 예비 측정을 1회 이상 했다
- 미통과 시: UART 속도·배선·전원 용량 점검. 5V 신호가 GPIO 에 들어가지 않는지 재확인
3주. SCD41 추가·센서 퓨전
- 목표: I²C 연결(GPIO21/22, 3V3), CO₂·온습도 수집, 규칙으로 강의실 상태 계산
- 선행조건: 2주 통과
- 결과물: 강의실 상태 데이터, 이벤트 규칙(ROOM_OCCUPIED, ROOM_EMPTY, VENTILATION_NEEDED, SENSOR_FAULT) 초기 구현
- 통과 조건:
- SCD41 값이 안정적으로 수신된다(주소 0x62 인식)
- 4개 이벤트가 시나리오 시험에서 의도대로 발생한다
- 센서 한 쪽 분리 시 SENSOR_FAULT 판정 동작을 확인했다(실험 7 예비)
- 미통과 시: 규칙 임계값을 바꾸기 전에 원시 값부터 확인
4주. API·DB·대시보드 (디지털 트윈 V0.1)
- 목표: 장치 → HTTP POST → FastAPI → PostgreSQL → 단일 HTML 대시보드
- 선행조건: 3주 통과, Python 3.11+, PostgreSQL 16
- 결과물: 디지털 트윈 V0.1(1개 강의실 현재 상태·최근 추이·이벤트 목록)
- 통과 조건:
- 24시간 연속 수집에서 서버가 중단되지 않았다
- seq 기준 누락·중복이 기록표로 확인된다(실험 6 예비)
- 대시보드에서 현재 상태와 최근 이벤트가 보인다
- 16장 보안 최소 항목(기본 비밀번호 변경, 별도 네트워크)을 적용했다
- 의사결정: 4주 말에 "실제 학원 실증으로 간다/보완한다"를 결정한다. 통과 조건 미달 항목이 있으면 보완 후 진행
17.3 5-8주 실제 학원 1개 강의실 실증
- 목표: 현장 조건에서 성능 측정
- 선행조건: 원장 승인, 현장 고지(16.3), 보안 점검, 장치 안전 점검
- 활동: 설치 → 2주 이상 연속 데이터 수집 → 정답 기준(수기 기록) 병행 샘플링 → 오탐·미탐 계산 → 임계값 조정 → 원장 보고 1회
- 통과 조건:
- 2주 이상 연속 수집했고 누락률을 계산했다
- 오탐률·미탐률을 정의대로 산출하고 근거 기록을 남겼다
- 원장 보고에서 "무엇이 되고 안 되는지"를 한계 포함으로 설명했다
- 보호자·학생 민원이 없거나 대응 절차대로 처리했다
- 의사결정: 확장하려면 원장의 지속 의사와 성능이 사용 목적에 충분한지(환기 알림, 공실 확인 등 목적별) 판단한다. 기준 수치는 이 교재가 정하지 않고 실증 결과로 정한다
17.4 9-12주 3-5개 강의실 확장
- 목표: 다중 장치 운영 안정화
- 선행조건: 5-8주 통과
- 활동: 장치 추가 설치(스키마 변경 없이 devices·spaces 등록), 장애 대응 절차 운영, 대시보드 공유 범위 설정, 비용 정리(18장), 다음 단계(출결 연동 등) 의사결정
- 통과 조건:
- 3개 이상 강의실이 동시에 동작하고 장치별 상태를 확인할 수 있다
- 장애 알림과 복구 절차를 1회 이상 실제로 수행했다
- 장치 교체를 문서만 보고 수행할 수 있다
- 다음 단계 결정 문서(진행/보류/중단과 근거)를 작성했다
- 출결 연동은 개인정보 영향이 크므로 별도 검토를 거친다(16장, 법률 검토 필요)
17.5 위험 신호 (중단·재검토 기준)
- 현장 오탐·미탐이 사용 목적에 비해 너무 커서 원장이 신뢰하지 않는 경우
- 민원이 반복되는 경우
- 전파·안전 관련 문제 제기가 있는 경우
- 운영 부담이 이익을 초과하는 경우
상세는 10_roadmap.md.
18비용·유지보수·확장 전략
18.1 비용 시나리오
부품 가격은 변동이 크고 조사일에 따라 달라진다. 이 교재에서는 BOM 기준 부품 목록만 확정하고, 가격은 구매 전에 확인한다. 가격 조사가 되지 않은 항목은 미확인으로 둔다. 가격 근거 문서는 03_bom_and_compatibility.md.
노드 1개당 부품
| 항목 | 수량 | 단가 | 비고 |
|---|---|---|---|
| ESP32 DevKit V1 | 1 | 미확인 — 구매 전 확인 | BOM 범위는 03 문서 참조 |
| HLK-LD2410C | 1 | 미확인 — 구매 전 확인 | |
| Sensirion SCD41 모듈 | 1 | 미확인 — 구매 전 확인 | 센서 모듈 형태(브레이크아웃)로 구매. 정품 여부 확인 |
| 브레드보드·점퍼선·USB 케이블·5V 어댑터 | 1 세트 | 미확인 — 구매 전 확인 | |
| 케이스·고정 부품 | 1 | 미확인 | 프로토타입 이후 필요 |
시나리오 비교
| 항목 | 1개 강의실 | 5개 강의실 |
|---|---|---|
| 노드 수 | 1 | 5 (예비 1-2개 별도 권장) |
| 부품비 | 노드 단가 x 1 | 노드 단가 x 5, 단가 합계 = 미확인 |
| 서버 | 노트북·미니 PC 또는 저가 클라우드. 비용 미확인 | 동일하나 데이터 증가 고려. 비용 미확인 |
| 네트워크 | 기존 공유기 활용 또는 별도 공유기 1대 | 공유기 추가 또는 AP 확장 필요 여부 점검 |
| 설치 인건비 | 직접 수행 시 시간 비용 | 현장 방문 횟수 증가 |
| 운영 인력 | 장애 대응 겸임 | 담당 지정 필요 |
| 합계 | 미확인 | 미확인 |
- 개발 인력 시간(펌웨어·백엔드·대시보드)은 가장 큰 비용이지만 이 교재가 추정하지 않는다.
- 창업자가 직접 구축하면 현금 비용은 작아도 시간 비용이 크다. 시간 비용도 계산해야 한다.
- 가격은 알리익스프레스·국내 오픈마켓마다 크게 다르다. 세금·배송·전파 인증 관련 비용은 구매 전 확인한다.
18.2 유지보수와 운영
장애 유형과 대응
| 유형 | 징후 | 1차 조치 |
|---|---|---|
| 장치 전원·통신 단절 | 5분 이상 미수신 → SENSOR_FAULT | 전원·Wi-Fi 확인, 재부팅 |
| 센서 값 이상 | quality false, null 값 지속 | 배선 점검, 센서 교체 |
| 서버 중단 | 모든 장치 동시 미수신 | 서버 프로세스·DB 확인 |
| 오탐 증가 | 빈 방에서 점유 판정 | 설치 환경 변경 확인, 감도 재조정 |
| CO₂ 값 드리프트 | 환기된 방에서도 기준 이상으로 높음 | SCD41 보정 옵션 점검(공식 문서 확인 후 수행) |
교체 절차 (요약)
- 새 장치에
device_id부여(규칙academy_room_{공간}_{번호}) - 서버
devices에 등록, 이전 장치는active=false - 설치 후 시리얼·서버에서 값 확인
- 교체 일시를 기록하고 이전 장치 키 폐기
펌웨어 업데이트
- 버전 번호를
firmware_version으로 보고하고 서버에서 확인한다. - 업데이트는 1대에 먼저 적용하고 24시간 관찰 후 확대한다(카나리 방식).
- OTA 업데이트는 보안 위험이 있어 MVP 에서는 USB 수동 업데이트를 기본으로 한다. OTA 는 확장 단계 검토 사항이다.
- 업데이트 전후
schema_version호환성을 확인한다.
정기 점검 (초기 권장 주기, 조정 가능)
- 매일: 장치 미수신 알림 확인
- 매주: 누락률·오탐 의심 구간 점검
- 분기: 센서 위치·고정 상태, 먼지, 전원 어댑터 점검
18.3 센서 추가 시 확장 방법
핵심은 스키마는 그대로 두고 장치만 등록한다.
spaces에 새 공간 등록 (type지정)devices에 장치 등록 (space_id,device_id,model)- 장치는 같은 JSON 형식으로
/api/v1/observations에 전송 - 대시보드는 공간 목록을
spaces에서 읽어 자동 표시
새 센서 종류 추가(예: 출입구용 센서, LD2450):
measurements는 JSONB 이므로 새 필드를 추가해도 테이블 구조를 바꾸지 않는다.- 다만
schema_version을 올리고 필드 의미를 문서화한다. - 이벤트 규칙은 새 필드를 쓰도록 별도 추가한다. 기존 규칙에 영향이 없는지 시험한다.
- LD2450 은 확장 논의용이며 구매 대상이 아니다. TI IWR6843ISK 는 고급 개발 보드로 이 교재 범위를 벗어난다.
18.4 직접 구축 vs 외주 판단
| 판단 기준 | 직접 구축 | 외주 |
|---|---|---|
| 학습 목적 | 적합. 이해도가 쌓인다 | 부적합 |
| 시간 | 느림 | 빠름(일정은 계약으로 관리) |
| 현금 비용 | 낮음 | 높음(견적 미확인) |
| 유지보수 | 직접 대응 | 유지보수 계약 필요 |
| 보안·법적 책임 | 직접 책임 | 계약으로 분담해도 최종 책임은 사업자에게 남을 수 있음 — 법률 검토 필요 |
| 맞춤 요구 | 유연 | 변경 비용 발생 |
권장 판단 순서:
- 1-4주(프로토타입)는 직접 구축한다. 이해 없이 외주하면 요구사항을 정의할 수 없다.
- 5-8주 실증에서 요구사항이 안정되면, 반복 설치·대량 하드웨어·인증이 필요한 부분만 외주를 검토한다.
- 외주 시 소스 코드·스키마·데이터 소유권, 보안 요건, 유지보수 범위를 계약에 명시한다.
19프로토타입 최종 검수 체크리스트
4주 프로토타입(디지털 트윈 V0.1) 완료 시점의 검수 항목이다. 모든 항목은 "확인 방법"과 함께 기록한다. 체크만 하고 근거를 남기지 않으면 검수가 아니다.
19.1 기능
존재 감지
- 빈 방에서 presence=false 로 기록된다
- 보행 시 moving_target=true 가 기록된다
- 착석 시 stationary_target 이 기록되고 거리값이 있다
- 센서 값이 없을 때
null+radar_valid=false가 기록된다 - 인원수를 표시하지 않는다(LD2410C 로 인원 계수 불가를 UI에도 명시)
환경 측정
- co2_ppm, temperature_c, humidity_percent 가 주기적으로 기록된다
- SCD41 3.3V 연결을 확인했다(5V 금지)
- SCD41 실패 시
co2_valid=false가 기록된다
저장
- 30초 주기 + presence 변화 시 즉시 1회 전송이 확인된다
observations에received_at이 서버 시각으로 기록된다- UNIQUE(device_id, seq) 로 중복이 방지된다
- 이벤트 4종(ROOM_OCCUPIED, ROOM_EMPTY, VENTILATION_NEEDED, SENSOR_FAULT)이
events에 기록된다
대시보드
- 현재 상태가 표시된다
- 최근 CO₂·온습도 추이가 표시된다
- 최근 이벤트 목록이 표시된다
- 장치 마지막 수신 시각이 표시된다
19.2 데이터 품질
- 24시간 이상 수집에서 누락률을 계산했다 (누락 seq / 생성 seq): 값 기록 ______
- 중복 시도가 서버에서 거부된다(건수 기록 ______)
- 장치 시각과 서버 시각의 차이를 측정했다 (차이 ______ 초). 시각 동기화 방식(NTP)을 확인했다
observed_at이 시간대 +09:00 로 기록된다- 이상값(예: 비현실적 CO₂, 온도)을 걸러내는 규칙을 정했다
- 센서 설정 값(감도, 게이트)이 문서화되어 있다
- 실험 1, 2, 6, 7 의 기록표가 채워져 있다
19.3 보안
- 서버·DB·대시보드의 기본 비밀번호를 변경했다
- 실습/운영 네트워크를 학원 직원·학생용 Wi-Fi 와 분리했다
- DB 포트가 외부에 노출되지 않는다
- 장치 API 키가 소스 저장소에 포함되어 있지 않다
- 역할별 접근 권한이 설정되어 있다(16.4)
- 평문 HTTP 는 사설망에서만 쓰고, 인터넷 구간은 HTTPS(TLS) 계획이 있다
- 센서 데이터에 개인 식별 정보가 포함되지 않는다
- 현장 고지문이 준비되어 있다
- 보관 기간 초안이 있다(법률 검토 필요 항목은 표시됨)
19.4 운영
- 장치 전원을 껐다 켰을 때 자동으로 Wi-Fi 에 재접속하고 전송을 재개한다
- 서버 재시작 후 정상 수신을 재개한다
- 5분 이상 미수신 시 SENSOR_FAULT 가 생성된다
- 장애 알림 수단(이메일, 메신저 등)과 담당자가 정해져 있다
- 장치 교체 절차를 한 번 실제로 수행했다
- 설치 위치·배선 사진과 장치 ID 목록이 문서로 정리되어 있다
19.5 미해결 위험 목록
검수 시점에 남아 있는 위험을 숨기지 않고 적는다. 아래는 이 교재 기준으로 기본 포함되는 항목이다. 현장 상황에 맞게 추가·삭제한다.
| 번호 | 위험 | 상태 | 담당 | 조치 계획 |
|---|---|---|---|---|
| 1 | LD2410C 정지 사람 미탐 가능성 | 실측 필요 | 실험 2 | |
| 2 | 에어컨·팬·커튼에 의한 오탐 가능성 | 실측 필요 | 실험 1, 4 | |
| 3 | 인원 계수 불가 — 이용자의 오해 가능성 | 안내 필요 | UI 문구, 원장 교육 | |
| 4 | CO₂ 센서 정확도·보정 상태 | 미확인 | 공식 문서 확인, 기준 측정기 비교 | |
| 5 | Wi-Fi 단절 시 데이터 누락(오프라인 버퍼 유무) | 실측 필요 | 실험 6 | |
| 6 | 평문 HTTP 의 도청·변조 | 알려진 한계 | 실서비스 전 TLS 적용 | |
| 7 | 24GHz 모듈의 국내 전파 인증 | 미확인 | 법률 검토 필요, 구매 전 확인 | |
| 8 | 미성년자 관련 고지·동의 방식 | 법률 검토 필요 | 전문가 검토 | |
| 9 | 시계 동기 오차 | 실측 필요 | NTP 점검 | |
| 10 | 단일 장애점(서버 1대) | 알려진 한계 | 확장 단계에서 검토 |
20참고자료 및 용어집
20.1 제조사 공식 문서
확인일: 2026-10-18 (웹 검색으로 확인). 문서는 개정되므로 사용 전에 최신 개정판 여부를 확인한다. 아래에 URL 이 없는 항목은 이번 검색에서 공식 URL 을 확인하지 못한 것이다.
| 대상 | 문서 | 링크 | 비고 |
|---|---|---|---|
| Hi-Link HLK-LD2410C | 데이터시트·사용자 매뉴얼 | 공식 사이트(Hi-Link, hlktech.com 등)에서 "LD2410C" 검색 | 이번 검색에서 공식 URL 미확인. 타사 판매 페이지(예: OpenELAB)는 2차 자료이므로 사양은 공식 문서로 재확인. 5V 전원, 3.3V IO, 기본 UART 256000 bps 는 2차 자료 기준 — 공식 문서 확인 전까지 추정으로 취급 |
| Hi-Link HLK-LD2450 | 데이터시트 | 공식 사이트(Hi-Link)에서 "LD2450" 검색 | 공식 URL 미확인. ESPHome 문서(https://esphome.io/components/sensor/ld2450/)는 통합 방법 참고용이며 제조사 문서가 아니다. 최대 감지 거리는 판매처마다 6m/8m 로 달라 미확인 |
| Sensirion SCD41 | SCD4x 데이터시트 | https://sensirion.com/media/documents/E0F04247/631EF271/CD_DS_SCD40_SCD41_Datasheet_D1.pdf | 검색 결과(판매처 목록)에 인용된 sensirion.com 링크. 최신 개정판은 Sensirion 공식 사이트(sensirion.com)의 SCD4x 제품 페이지에서 확인 |
| Espressif ESP32 | ESP32-WROOM-32 데이터시트 | https://documentation.espressif.com/esp32-wroom-32_datasheet_en.html | 확인일 기준 NRND(신규 설계 비권장) 표기. 학습용으로는 사용 가능하나 신규 제품 설계 전 최신 권장 모듈 확인 |
| Espressif ESP32 | 데이터시트 PDF | https://www.espressif.com/sites/default/files/documentation/esp32-wroom-32_datasheet_en.pdf | 개정 번호가 문서마다 다르므로 Espressif 다운로드 페이지(https://www.espressif.com/en/support/download/documents)에서 최신 확인 |
| TI IWR6843ISK | 평가 모듈 제품 페이지 | https://www.ti.com/tool/iwr6843isk | 60GHz 평가 보드. 이 교재의 구매 대상 아님(확장 논의용) |
참고: 라이브러리 ld2410(ncmreynolds), Sensirion I2C SCD4x + Sensirion Core 는 Arduino 라이브러리 매니저 최신 버전을 쓰며, 실습 전 버전을 확인한다.
20.2 용어집
| 용어 | 한 줄 설명 |
|---|---|
| 관측(Observation) | 센서가 보낸 원시 측정값 |
| 상태(State) | 규칙이 계산한 공간의 현재 상황 |
| 이벤트(Event) | 상태 변화나 조건 충족의 기록 |
| 존재 감지(Presence) | 공간에 사람이 있는지 여부 판정. 인원 계수가 아님 |
| mmWave | 밀리미터파 대역의 전파를 쓰는 레이더 방식. 이 교재의 LD2410C 는 24GHz |
| 정지 목표(Stationary target) | 움직이지 않지만 호흡·미세 움직임 등으로 감지되는 대상 |
| 오탐(False positive) | 실제로는 없는데 있다고 판단한 오류 |
| 미탐(False negative) | 실제로는 있는데 없다고 판단한 오류 |
| 정답 기준(Ground truth) | 센서와 독립적으로 확보한 실제 값 |
| NDIR | 비분산 적외선 방식. 가스가 흡수하는 적외선 양으로 CO₂ 를 측정 |
| ppm | 100만분율 농도 단위. CO₂ 농도 표기에 사용 |
| 센서 퓨전 | 여러 센서 값을 합쳐 상태를 추정하는 방법 |
| UART | 2선 직렬 통신. LD2410C 와 ESP32 연결에 사용 |
| I²C | 2선 버스 통신. SCD41 연결에 사용 (SDA/SCL, 주소 0x62) |
| ESP32 | Wi-Fi 내장 마이크로컨트롤러. 이 교재의 장치 두뇌 |
| seq | 장치가 증가시키는 전송 번호. 중복 제거와 누락 확인에 사용 |
| UNIQUE(device_id, seq) | 같은 장치의 같은 seq 가 두 번 저장되지 않게 하는 DB 제약 |
| HTTP POST | 장치가 서버로 JSON 을 보내는 기본 전송 방식 |
| TLS(HTTPS) | 전송 구간 암호화. 실서비스에서 필요 |
| 디지털 트윈 | 실제 공간의 상태를 데이터로 반영한 가상 모델. V0.1 은 대시보드 수준 |
| SENSOR_FAULT | 장치 미수신 또는 품질 불량 지속 시 기록하는 이벤트 |
| 법률 검토 필요 | 이 교재가 법적 판단을 하지 않고 전문가 검토를 요구하는 표시 |