8장: 얼굴 인식
이번 장의 목표: 얼굴 특징을 등록하여 NanoCam이 "당신이 누구인지" 알아보게 하고, 완전한 출입 통제 솔루션을 구축합니다.
원리
얼굴 인식 = 얼굴 검출(MSR01+MNP01 2단계 파이프라인) + 특징 추출(FaceRecognition112V1S8 MFN 신경망) + 코사인 유사도 비교.
카메라 RGB565 프레임
→ MSR01 1차 검출(320×240, 0.3F 임계값)
→ MNP01 정밀 검출(1차 검출 후보 상자 기반, 0.4F 임계값)
→ 10개 얼굴 키포인트 추출(양눈/코끝/입꼬리)
→ 키포인트 정렬 → 112×112 얼굴 크롭
→ MFN 컨볼루션 네트워크 → 512차원 특징 벡터
→ L2 정규화
→ Flash에 등록된 모든 ID의 벡터와 하나씩 코사인 거리 계산
→ 최대 코사인 유사도 > 임계값(0.55) → 매칭 성공 → ID 출력
→ 모든 유사도 < 임계값 → 낯선 사람 → "who?" 출력성능 최적화
MFN 특징 추출과 전체 데이터베이스 비교는 연산량이 커서 매 프레임 실행하면 화면이 끊깁니다. 현재 구현은 프레임 건너뛰기 전략을 사용합니다: 얼굴 검출은 매 프레임 실행(저비용)하고, MFN 인식은 10프레임마다 한 번 실행(고비용)하며, 라벨은 이전 인식 결과를 계속 오버레이 표시합니다. 이렇게 하면 화면이 부드럽게 유지되고 ID 라벨이 깜빡이지 않습니다.
얼굴 특징 저장
등록된 얼굴 특징(id + 512차원 embedding)은 Flash의 fr 파티션(96 KB, 최대 47개 얼굴 ID)에 영구 저장됩니다. 전원이 꺼져도 유지됩니다.
하드웨어 준비
NanoCam 코어 보드 + 베이스 보드
USB-C 데이터 케이블(컴퓨터 연결로 전원+시리얼)
시리얼 터미널(보레이트 115200)
단계
8.1 얼굴 인식 모드 진입
ai_mode:4기기가 자동으로 재부팅되어 FaceID 모드로 진입하며, WS2812 RGB LED (GPIO18 DIN, VDD50 전원)가 보라색으로 표시됩니다. 재부팅 후 시리얼에서 다음이 표시되어야 합니다:
I (5526) MFN: fr partition size: 98304 bytes, maxminum 47 IDs can be stored
I (5526) MFN: No face ID in flashNo face ID in flash는 아직 등록된 얼굴이 없다는 의미이며, 정상입니다.
8.2 얼굴 등록
얼굴을 카메라 정면에 맞추고(거리 30-50cm, 조명 균일), 화면에 얼굴이 하나만 있도록 합니다. 시리얼에서 전송합니다:
face_eril기기가 얼굴을 검출하면 자동으로 특징을 추출하여 Flash에 등록합니다:
I (xxxx) ENROLL: ID 1 is enrolled화면에 파란색 텍스트 Enroll: ID 1이 약 0.5초 동안 오버레이 표시된 후 사라집니다.
주의: 명령은
face_eril(enroll 약어)이며,face_enroll이 아닙니다.fail: unknown command가 보이면 철자를 확인하세요.
8.3 얼굴 식별
등록 완료 후 인식 명령을 전송합니다:
face_rz시스템이 지속 인식 모드로 진입합니다. 현재 얼굴을 Flash에 등록된 모든 ID와 비교합니다:
매칭 성공: 시리얼로
Similarity: 0.85, Match ID: 1출력, 화면에 녹색ID: 1지속 오버레이낯선 사람: 시리얼로
Similarity: 0.32, Match ID: 0출력, 화면에 빨간색who?지속 오버레이
라벨은 지속 표시되며 사라지지 않습니다. 인식 모드를 종료하려면
face_detect를 전송하여 순수 검출 모드로 돌아갑니다.
8.4 얼굴 삭제
face_del마지막으로 등록한 얼굴 ID를 삭제하며, 시리얼로 N IDs left가 반환되고 화면에 남은 ID 수가 잠시 표시됩니다. Flash의 특징도 함께 삭제됩니다.
8.5 인식 모드 종료
face_detect순수 얼굴 검출 모드로 돌아가며(상자+키포인트만 그리고 인식하지 않음), ID 라벨이 제거됩니다.
DETECT 모드 관련: ESP32-S3에서 순수 얼굴 검출 모드의 시리얼 좌표 출력은 비활성화되어 있습니다(
#if !CONFIG_IDF_TARGET_ESP32S3). 이는 검출 로그로 시리얼이 도배되는 것을 방지하기 위함입니다. 인식 모드(face_rz)에 진입한 후에야detection_result좌표 로그가 출력됩니다.
전체 명령 빠른 참조
| 명령 | 기능 | 라벨 동작 | 지속 여부 |
|---|---|---|---|
face_eril | 현재 검출된 얼굴 등록 | 파란색 "Enroll: ID N" | 0.5초 깜빡임 |
face_rz | 지속 인식 모드 진입 | 녹색 "ID: N" / 빨간색 "who?" | ✅ 지속 |
face_del | 마지막으로 등록한 ID 삭제 | 빨간색 "N IDs left" | 0.5초 깜빡임 |
face_detect | 인식 종료, 순수 검출로 복귀 | 모든 라벨 제거 | — |
전체 명령은 시리얼 프로토콜 매뉴얼을 참조하세요.
조작 흐름 예시
ai_mode:4 # 얼굴 인식 모드 진입
[기기 재부팅, LED 보라색]
face_eril # 첫 번째 얼굴 등록(철수)
→ ID 1 is enrolled
face_eril # 두 번째 얼굴 등록(영희)
→ ID 2 is enrolled
face_rz # 지속 인식 시작
→ 철수가 카메라 앞에 섬: 화면에 "ID: 1" 지속 표시
→ 영희가 카메라 앞에 섬: 화면에 "ID: 2" 지속 표시
→ 낯선 사람이 카메라 앞에 섬: 화면에 "who?" 지속 표시
face_detect # 인식 모드 종료
→ 라벨이 사라지고 검출 상자만 그려짐
face_del # 영희 삭제 (ID 2)
→ 1 IDs left
face_rz # 다시 인식
→ 철수가 카메라 앞에 섬: "ID: 1"
→ 영희가 카메라 앞에 섬: "who?" (삭제됨)얼굴 인식 모드는 메모리 사용량이 큽니다(MFN 모델 + 얼굴 검출 이중 모델). Type-C 시리얼(UART0)은 정상 작동합니다. 시리얼이 응답하지 않으면 먼저 보레이트가 115200인지 확인하세요.
코드
핵심 인식 로직
components/modules/ai/who_human_face_recognition.cpp — 프레임 건너뛰기 인식 전략:
case RECOGNIZE:
{
// 프레임 건너뛰기: 검출 10회마다 MFN 인식 1회 실행
static int recog_skip = 0;
if (recog_skip <= 0) {
recognize_result = recognizer->recognize(
(uint16_t *)frame->buf,
{(int)frame->height, (int)frame->width, 3},
detect_results.front().keypoint);
recog_skip = 10;
}
recog_skip--;
frame_show_state = SHOW_STATE_RECOGNIZE;
break;
}문제 해결
| 증상 | 가능한 원인 | 해결 |
|---|---|---|
No face ID in flash | 정상, 아직 등록한 적 없음 | face_eril 전송하여 등록 |
인식 결과가 항상 who? | 조명 부족/각도 기울어짐/유사도가 임계값 미만 | 재등록, 카메라 정면, 조도 균일 |
| 등록 시 반응 없음 | 화면 속 얼굴 ≠ 1장 | 얼굴이 하나만 있도록 하고 거리 30-50cm 유지 |
| 인식 시 화면 끊김 | 정상, MFN 추론에 시간 필요 | 프레임 건너뛰기로 최적화됨, 10프레임마다 1회 실행 |
| 라벨 깜빡임 | — | 수정됨, 라벨 지속 표시되어 사라지지 않음 |
fail: unknown command | 명령 철자 오류 | 명령 확인: face_eril이며 face_enroll이 아님 |
결과
얼굴 등록 → 지속 인식하여 ID 표시 → I2C/시리얼로 결과 출력 → 릴레이/서보 제어, 완전한 출입 통제 솔루션.
다음 장: 9장: 음성 대화

