STM32 з нуля без HAL: Luckfox думає, STM32 діє: face detection від камери до реле. Частина 10.

Luckfox думає, STM32 діє: face detection від камери до реле

Серія «STM32 з нуля без HAL» · Частина 10 · Місяць 3


У Статті 9 ми змусили NPU видавати координати обличчя в консоль. Красиво — але рядки в терміналі це не система. Система — коли хтось підходить до камери і фізично клацає реле. Саме це ми зробимо сьогодні.

Фінальний результат: обличчя в кадрі → Luckfox детектує через NPU → фото летить на сервер → DeepFace звіряє з базою → SSH команда на Luckfox → STM32 отримує on по UART → реле спрацьовує.

П’ять стрибків. Розберемо кожен.


◆ Архітектура — три вузли

Чому саме так розподілені ролі:

Luckfox детектує — NPU апаратно, швидко, в реальному часі. CPU на це не витрачається.

Сервер розпізнає — база облич може рости, моделі змінюватись, поріг підлаштовуватись. Все це без перезбірки прошивки.

STM32 діє — надійний, детермінований. Отримав команду — виконав. Без Linux overhead.

До речі, поки налаштовував ethernet між Luckfox і сервером — задумався: а як в реальних автономних системах з’єднують vision модулі з мозком? CAN? Automotive Ethernet? Luckfox не має WiFi — тільки ethernet і UART. Для стаціонарної системи це нормально, для мобільного робота — обмеження. Якщо є досвід з robotics або automotive — цікаво почути в коментарях.


◆ Модифікація main.cc — мінімально і чисто

Оригінальний main.cc виводить координати в консоль і все. Нам треба додати два блоки: зберегти ROI обличчя як JPEG і відправити на сервер через curl.

Повний код вже готовий — зупинимось на ключових рішеннях.

IP сервера — в аргументи, не в код:

Міняєш IP — просто інший аргумент при запуску. Без make.

Throttling — не більше одного запиту на 5 секунд:

Без throttling — при кожному кадрі де є обличчя летить запит. Це 10-15 POST на секунду. Плата гріється, сервер задихається.

Відправка — асинхронно:

& в кінці — curl іде у фоновий процес. Детекція не зупиняється поки curl чекає відповідь.

system(curl ...) — не найелегантніше рішення з точки зору C++. Правильніше використовувати libcurl API напряму. Але для статті це чесно і читабельно — кожен розуміє що відбувається без знання libcurl.

Збірка — підводний камінь з file(GLOB):

CMakeLists використовує file(GLOB SRC_FILES "${SRC_DIR}/*.cc") — бере всі .cc файли в директорії. Якщо перейменував старий main.cc замість видалення — CMake підхопить обидва файли і отримаєш конфлікт символів:

Рішення: видаляти старий файл, не перейменовувати.


◆ Сервер — три варіанти і що з них вийшло

Для розпізнавання облич по базі є кілька Python бібліотек. Спробував усі три — ось чесний результат.

Варіант 1 — face_recognition + dlib

Найпопулярніший на GitHub, найбільше туторіалів.

dlib — це C++ бібліотека яка компілюється при встановленні. Потребує:

Якщо і після цього не збирається — залежить від версії gcc і cmake. На Ubuntu 22.04 зазвичай проходить після встановлення залежностей.

Варіант 2 — DeepFace + TensorFlow

Встановлюється без компіляції. Під капотом може використовувати VGG-Face, FaceNet, ArcFace — вибираєш модель. Ми використовуємо FaceNet + cosine similarity.

⚠ TensorFlow 2.21 відокремив Keras в окремий пакет. deepface ще не оновив залежності автоматично:

Після цього запускається. Перший виклик DeepFace.verify() автоматично скачує модель (~92MB FaceNet weights) в ~/.deepface/weights/. Це один раз.

⚠ Перший POST запит може прийти поки модель ще качається. Flask обробляє його в окремому потоці — система не падає, але перший результат може бути повільним.

Варіант 3 — cosine similarity на numpy

Найлегший підхід — без важких бібліотек взагалі. FaceNet вже рахує 128-мірний embedding на Luckfox. Якщо відправляти цей вектор на сервер замість фото — порівняння це буквально 5 рядків numpy:

Потребує модифікації main.cc щоб відправляв вектор out_fp32[128] разом з фото. Залишаємо як вправу — хто хоче найлегше рішення без TensorFlow.

Обрали Варіант 2 — DeepFace встановлюється, працює одразу, база облич — просто папка з фото.


◆ Enrollment — як додати себе в базу

Найелегантніший момент системи — і я його не планував.

Щоб сервер тебе впізнав — потрібне еталонне фото. Де його взяти? Я просто підійшов до камери. Система сфоткала моє обличчя і зберегла в received/. Я взяв це фото і поклав в базу:

Перезавантажую базу без рестарту сервера:

Наступний раз — OK:Alex. Enrollment через саму систему.

Для кількох людей — та сама схема. Кожен підходить до камери, фото падає в received/, перекладаєш в known_faces/Ім'я.jpg, робиш /reload.


◆ SSH ключі на Buildroot — чому не спрацювало

Щоб сервер міг по SSH заходити на Luckfox без пароля — треба SSH ключ. Генеруємо і копіюємо:

Перевіряємо:

Дивимось на платі:

/root директорія належить uid 1000 а не root (uid 0). Buildroot створює її під іншим користувачем. SSH демон перевіряє власника home директорії — і якщо вона не належить користувачу який логіниться, відхиляє ключ з міркувань безпеки. Навіть якщо authorized_keys має правильні права.

Виправити можна через chown root:root /root — але на Buildroot це не персистентно між перезавантаженнями якщо /root монтується з tmpfs.

Практичне рішення — sshpass:

Так, передавати пароль через аргумент — не найкраща практика безпеки. Для домашньої лабораторії і статті — прийнятно. Для продакшн — виправляти через chown в init скрипті Buildroot.


◆ Реле спрацьовує

Сервер отримує фото → DeepFace порівнює з базою:

dist=0.073 при порозі 0.40 — DeepFace дуже впевнений. Два числа в логу:

  • norm від RKNN на Luckfox — детекція на NPU
  • dist від DeepFace на PC — розпізнавання по базі

Захист від паралельних викликів:

Luckfox відправляє фото раз на 5 секунд. Але Flask обробляє запити в кількох потоках — без захисту реле спрацьовувало б кілька разів підряд:

Тепер в логах:


◆ Клонування плати

Питання яке виникає коли система працює: як перенести на іншу плату?

update.img в ~/luckfox-pico/output/image/ — це Buildroot образ. Він містить все що зібрано через make: ядро, rootfs, наші пакети (go-dashboard, статичний IP). Прошивається одною командою:

⚠ Але файли які клали через scp — бінарник детекції, моделі, stm32_controller.py — в образі не збережені. Вони живуть в /root/ який не входить в rootfs образу.

Після прошивки нової плати треба скопіювати:

Щоб файли були в образі автоматично — треба Buildroot overlay або пакет. Це тема для окремої статті.


◆ Гайд-шпаргалка

Одноразово — збірка бінарника на PC:

Кожного разу — запуск:

Додати людину в базу:

Перевірити стан:

Перевірити STM32 окремо:


Блог серії: nodejs.jdymora.com/category/embedded/stm32 Репозиторій: github.com/pipicosim800-maker/stm32F103


◆ Бонус: SD карта, swap і додаткове сховище

Поки налаштовував систему — знайшов вільний SanDisk Ultra 32GB. Вставив в Luckfox і прийшла ідея: система працює, фото летять в received/ на PC — але що якщо перенести сховище прямо на плату?

Але спочатку важливіше — пам’ять.

Скільки RAM на RV1106:

53MB RAM і нуль swap. RetinaFace запускається — він відносно легкий. Але якщо захочеш YOLOv8 або важчі моделі:

Can't allocate memory — класична помилка RKNN при нестачі RAM. Автор статті на Habr про YOLOv8 на Luckfox Pico Mini зіткнувся з тим самим. Рішення — swap.

Перевіряємо SD карту:

Карта змонтована як /mnt/sdcard. Замість перерозбивки — простіший шлях через swap файл:

512MB swap — YOLOv8 тепер запуститься.

Автозапуск після перезавантаження:

Для Автобота — що це дає:

SD карта на мобільній платформі це зручно:

  • received/ з фото облич — прямо на карті, не на PC
  • логи детекції — зберігаємо локально
  • embeddings бази — переносити карту замість SSH

Карта виймається — переносиш дані на PC фізично. Без мережі, без SSH. Для польових умов Автобота це практично.

Практична ідея для Автобота:

Міняєш карту — міняєш конфігурацію системи. Без перепрошивки.

Опубліковано в Linux

Залишити відповідь

Ваша e-mail адреса не оприлюднюватиметься. Обов’язкові поля позначені *