write: 29/10/2025 / modified: 30/10/2025
«бёрдвотчинг», англ. birdwatching или «бёрдинг», англ. birding или twitching) — любительскoe (в отличие от профессиональной орнитологии) наблюдение за птицами и изучение их при помощи бинокля, зрительных труб, фото- и видеоаппаратуры в естественной среде обитания. Помимо визуального наблюдения, эта деятельность включает прослушивание пения птиц, поскольку многие виды бывает легче распознать по издаваемым звукам. Взято из википедии.
Сам этот проект наврятли можно отнести к прямому бёрдвотчингу. Но параллели есть.
В том году жена купила на вб прозрачную кормушку, которую мы прикрепили присосками на окно балкона и всю зиму подкармливали синичек. Тогда и родилась идея установить камеру для фотофиксации пернатых. Проектов подобного плана на просторах интернета уже много кто реализовал, хороший пример у Andrew Evans, ну и я решил не отставать.
Но мало просто их фотографировать, я хотел большего, знать статистику посещения кормушки, статистику посещения в зависимости от погоды, атмосферного давления и влажности и все это лицезреть в любой момент времени не только в табличном виде, но и в виде красочных графиков! Обожаю графики.
И даже этого мне оказалось мало, нужно определять породу птиц!

Bird Feeder - Система использует микроконтроллер ESP32-S3 с камерой для фиксации изображений птиц, сбора данных о погоде (температура, давление, влажность) и отправки их на сервер. На сервере происходит обработка изображений с помощью моделей машинного обучения для детекции и классификации птиц, хранение данных в базе данных SQLite и предоставление веб-интерфейса для просмотра статистики.
WORK IN PROGRESS
В качестве сервера выступает приложение на Python (FastAPI, Celery, redis, sqlalchemy, pydantic, ultralytics, transformers) обернутое в докер контейнер.
birdfeeder
│ bird_data.db # БД
│ Dockerfile
│ docker-compose.yml
├───app
│ ├───ml
│ │ __init__.py
│ │ classification.py # Модель классификации птиц
│ │ detection.py # Модель детектирования птиц на фото
│ └───routers #
│ __init__.py
│ api.py # API Отвечающий за все эндпоинты
│ __init__.py
│ auth.py # Модуль аутентификации
│ crud.py # CRUD
│ database.py # Модуль инициализации БД
│ main.py # Модуль запуска приложения
│ models.py # Модель записи о посещении птиц и статуса ESP32
│ schemas.py # Схемы БД
├───static #
│ └───photos # Папка для хранения фото
└───frontend # Собственно фронт
index.php
scripts.js
style.css
Остановился на следующих моделях:
YOLOv8n (yolov8n.pt) с пониженным порогом (0.1). Возвращает обрезанные изображения и bounding boxes птицы.Birds-Classifier-EfficientNetB2 (Hugging Face). Изображения ресайзятся до 224x224.Модели легковесные, шустро работают на CPU.
Так же в закладках оставил эти:
На 29.10.2025 тестирую и выбираю лучшую модель классификатор. Но видимо для исключения Ацтекских чаек нужно дообучать на своих данных. Скорее всего кормушка будет висеть для сбора датасета. Классификатор плохо определяет по попкам синичек (кушают отвернувшись).
Backend предоставляет REST API. Все GET-запросы поддерживают фильтры (start_date, end_date, hour, min_confidence, species, min_humidity, max_humidity).
/upload/: Загрузка от ESP (фото + JSON). Требует multipart/form-data. (требует JWT в Authorization: Bearer /visits/: Список посещений (с пагинацией: skip, limit)./visits/{id}/: Детали посещения по ID./total_visits/: Общее количество посещений./unique_species/: Уникальные породы./stats/: Статистика по породам (кол-во, avg_temp, avg_pressure, avg_humidity)./heatmap/: Данные для тепловой карты (bins по temp/pressure)./time_distribution/: Распределение по часам./esp_status/{device_id}: Статус устройства./export_csv/: Экспорт в CSV (требует JWT в Authorization: Bearer От ESP прилетают фото и JSON с датой, температурой, влажностью и давлением, Celery берёт task из queue передает YOLO: "Accepted (202)" → "Task ID: xxx" → "Poll 1, code: 200" → "Status: processing" → ... → "Task ready — success!".
Новые:
/set_command/?device_id=esp32_1&command=enter_config: Для установки/выключения команды. (требует JWT в Authorization: Bearer /commands/?device_id=esp32_1: Для получения команды (если установлена). (требует JWT в Authorization: Bearer Ошибки логируются в logs/api_errors.log.
Фронтенд реализован на PHP с Chart.js для графиков. Реализована поддержка русского и английского языков. Темы: светлая/темная (сохраняется в localStorage).

Таб Посещения: Таблица с сортировкой (по timestamp, species, temp, etc.). Модальное окно для деталей (фото с BBoxes, порода, давление, влажность, температура).
Таб Графики:

Таб Статистика: Табличная статистика по породам (посещения, avg погода).
Дата (с/по), час, мин. уверенность модели классификатора, мин/макс влажность, порода (авто дополнение). Фильтры глобальные, применяются ко всем табам.
Модель классификатор выдает предсказания на английском языке, поэтому для русификации был скачан эксель на 11250 имен птиц IOC World Bird Master List, я не стал убирать лишнее, мало ли, в мою кормушку залетит "Сомалийский страус" или "Большой желтохохлый какаду". Поэтому оставил только нужные столбцы и загрузил это в БД SQLite такого содержания:
| seq | Order | Family | IOC_15.1 | English | Russian |
|---|---|---|---|---|---|
| 1 | STRUTHIONIFORMES | Struthionidae | Struthio camelus | Common Ostrich | Африканский страус |
| 2 | STRUTHIONIFORMES | Struthionidae | Struthio molybdophanes | Somali Ostrich | Сомалийский страус |
| 3 | RHEIFORMES | Rheidae | Rhea americana | Greater Rhea | Нанду |
| 4 | RHEIFORMES | Rheidae | Rhea pennata | Lesser Rhea | Дарвинов нанду |
| 5 | APTERYGIFORMES | Apterygidae | Apteryx australis | Southern Brown Kiwi | Бурый киви |
| 6 | APTERYGIFORMES | Apterygidae | Apteryx mantelli | North Island Brown Kiwi | Северный киви |
Translated species for ID 1: PARUS MAJOR -> Большая синица
Translated species for ID 2: EURASIAN BULLFINCH -> Снегирь
Translated species for ID 3: ROCK DOVE -> Сизый голубь
Крутых голубей на чистом CSS нашел у Julia Miocene. Идеальная сцена вписалась в пустое пространство страницы. При желании можно отключить анимацию (сохраняется в localStorage).
В качестве папарацци выступает малыш Seeed Studio ESP32-S3 Sense, HC-SR501 (датчик движения), AHT20+BMP280 высокоточный метео датчик, измеряющий температуру, влажность и атмосферное давление, а так же модуль часов реального времени DS3231. Корпус был напечатан на 3Д принтере. Часть прошивки была позаимствована у выше упомянутого Andrew Evans. Были внесены корректировки, а именно:
BMP280 и отправляет JSON на эндпоинт сервера /upload/ такого содержания: POST http://server-ip:8000/upload/ -F "files=@photo1.jpg" -F "files=@photo2.jpg" -F "files=@photo3.jpg" -F "json_data={\"timestamp\": \"2025-09-08T05:33:00\", \"temperature\": 26.5, \"pressure\": 980.2, \"humidity\": 55.5}"

Ну а дальше уже все понятно, модельки детектят, классифицируют и записывают в БД нового посетителя. Вуаля!
Так как железяка все время спит глубоким сном да и работает всегда в режиме Wi-Fi клиента, уже позже были созданы два эндпоинта, /set_command/ и /commands/.
Когда нужно попасть на веб страницу настроек и диагностики ESP32-S3, я через POST запрос устанавливаю команду:
curl -X POST "http://<server-ip>:<port>/set_command/?device_id=esp32_1&command=enter_config" \
-H "Authorization: Bearer <token>"
При следующем пробуждении железка обращается по GET к серверу:
curl -X GET "http://<server-ip>:<port>/commands/?device_id=esp32_1" \
-H "Authorization: Bearer <token>"
понимает, что от нее хотят и перезагружается в режиме AP. После чего я спокойно могу зайти на внутреннюю страничку, применить необходимые мне настройки, проверить работу датчиков и перезагрузить в ее рабочий режим.




Да, можно было приделать кнопку, хоть сенсорную, но во первых это еще одни провода, а во вторых мне такой способ показался более элегантным.
OV5640 - Разрешение: 5МП, Поле зрения объектива: 120°, разрешение: 2592x1944.- Адаптивный и отзывчивый дизайн для мобильных устройств (подправить)
- Оповещения в телеграм (мб редких птиц)
- Детекция пополнения кормушки (сколько наели синички за определенный промежуток времени)
- Дообучение модели на своих данных
tags: python php programming javascript sqlite web esp32