A fully autonomous, low-cost insect monitoring system that runs a complete camera → AI inference → logging → remote-streaming pipeline entirely on-device, on a Raspberry Pi 3 B+ (1 GB RAM, CPU-only). The system detects and classifies insects with a custom-trained YOLOv5n model (33 classes, based on the IP102 benchmark dataset), logs annotated images and a CSV detection record to the local MicroSD card, and serves a live MJPEG video stream over Wi-Fi.
Developed by Sudipto Chakraborty — Master's Programme in Aerospace Informatics, Julius-Maximilians-Universität Würzburg, under the supervision of Prof. Dr. Marco Schmidt (ESSEO Laboratory, JMU Würzburg).
- Project Overview
- System Architecture
- Repository Structure
- Hardware
- Getting Started
- Configuration Reference
- Module Documentation
- Detection Output and Analytics
- Field Validation Results
- Known Hardware Constraints
- Troubleshooting
- Roadmap
- References
- License
| Primary objective | Fully autonomous, low-cost insect monitoring with embedded AI running entirely on-device |
| Detection model | Custom YOLOv5n, 33 insect classes, trained on the IP102 benchmark dataset (500 epochs) |
| Target hardware | Raspberry Pi 3 B+ — 1 GB RAM, CPU-only |
| Storage | Annotated JPEG images + per-class cropped insect images + structured CSV detection log |
| Remote monitoring | Live MJPEG stream via Flask web server (:8080), plus a lightweight /snap fallback |
| Enclosure | Custom 3D-printed PLA enclosure, designed in SolidWorks (hardware/insect_detect.stl) |
| Effective throughput | ~0.3–0.6 fps — sufficient for static-surface monitoring of stationary insects |
The full processing pipeline runs locally on the Pi with no cloud or internet dependency once the model weights are deployed.
flowchart LR
subgraph Capture["Capture / Output"]
CAM["Camera Module 3\n12 MP, BGR888 640x640"]
end
subgraph Pre["Preprocessing"]
BGR["BGR -> RGB\n(OpenCV)"]
SHARP["Laplacian Sharpening\n3x3 kernel"]
end
subgraph AI["AI Inference"]
YOLO["YOLOv5n Inference\nPyTorch CPU, conf >= 0.15"]
FILTER["Two-Stage Filter\nconfidence + bbox area"]
end
subgraph Data["Data & Monitoring"]
STORE["Storage & CSV Log\nMicroSD"]
DASH["Analytics Dashboard\nmatplotlib"]
FLASK["Flask Stream\nMJPEG :8080"]
end
CAM --> BGR --> SHARP --> YOLO --> FILTER
FILTER --> STORE --> DASH
FILTER --> FLASK
All operations execute locally on the Raspberry Pi 3 B+.
- Image Acquisition —
camera/captures a 640×640 BGR888 frame via PiCamera2 (primary) orrpicam-jpeg/libcamera-jpegsubprocess (fallback). - BGR → RGB Conversion — OpenCV channel swap; YOLOv5 expects RGB.
- Laplacian Sharpening — 3×3 kernel via
cv2.filter2D(), enhances edge contrast for close-up macro images. - YOLOv5n Inference —
torch.no_grad()forward pass, 33 classes,conf_thres = 0.15, loaded fully offline via PyTorch Hub local source mode. - Two-Stage Filter — Stage 1: confidence ≥ 0.15. Stage 2: bounding box area ≥ 100 px².
- Annotation & Storage — annotated JPEG + per-class cropped images saved to the MicroSD card; CSV log flushed every 20 events.
- Flask Live Stream — MJPEG on
/, single-frame fallback on/snap.
detections/
<YYYY-MM-DD>/
<HH-MM-SS>/ # one directory per session
annotated/ # full frames with bounding boxes
frame_7.jpg
frame_11.jpg
...
cropped/ # per-class insect crops (no boxes)
Parasa indetermina/
7.jpg
Torpedo bug/
7.jpg
detections.csv # frame, timestamp, class, confidence, x1,y1,x2,y2
insect_detect/
├── main.py # Entry point — orchestrates the full pipeline
├── requirements.txt
├── LICENSE
├── .gitignore
├── camera/
│ ├── __init__.py
│ ├── base.py # CameraBase abstract interface
│ ├── factory.py # open_camera() — backend selection
│ ├── picamera2_backend.py # Primary backend (PiCamera2)
│ └── subprocess_backend.py # Fallback backend (rpicam-jpeg / libcamera-jpeg)
├── config/
│ ├── __init__.py
│ └── settings.py # ALL tunable parameters live here
├── detection/
│ ├── __init__.py
│ ├── model.py # load_model() — PyTorch Hub local source mode
│ ├── preprocessor.py # BGR->RGB + Laplacian sharpening
│ └── inference.py # run_inference() — forward pass + filtering
├── storage/
│ ├── __init__.py
│ ├── session.py # Session — per-run output directory layout
│ └── writer.py # DetectionWriter — JPEG + CSV logging
├── stream/
│ ├── __init__.py
│ └── flask_server.py # MJPEG stream (/) + snapshot (/snap)
├── utils/
│ ├── __init__.py
│ ├── env.py # SSH / headless detection
│ └── paths.py # Startup path validation
├── scripts/
│ └── plot_detections.py # Post-session analytics dashboard generator
├── hardware/
│ └── insect_detect.stl # 3D-printable enclosure (SolidWorks export)
└── docs/
└── images/ # Reference photos used in this README
Not included in this repository (too large / environment-specific — see Deployment):
- The
yolov5/source tree (clone from ultralytics/yolov5)- The trained model weights
best.pt- Runtime output (
detections/)
| Component | Specification | Notes |
|---|---|---|
| Raspberry Pi 3 B+ | ARM Cortex-A53 @ 1.4 GHz, 1 GB LPDDR2 RAM | Software is compatible with Pi 4/5 as-is |
| Camera Module 3 | Sony IMX708, 12 MP, motorised autofocus, CSI-2 | ~15 cm camera-to-tray distance |
| MicroSD card | ≥ 32 GB, high-endurance / dashcam-rated | Standard cards degrade under continuous small-file writes |
| Power supply | 5 V / 3 A, short 26 AWG cable | Peak draw 7.5 W; under-spec supplies cause CSI camera corruption |
| Enclosure | Custom SolidWorks design, green PLA, 3-part assembly | hardware/insect_detect.stl |
The enclosure is a 3-part parametric assembly: main body, removable base cover (for tool-free access to the Pi and MicroSD card), and the camera mount / monitoring tray. Ventilation slots are cut into the side walls for passive cooling — see Known Hardware Constraints for why this matters.
This section reproduces a working deployment from a blank Raspberry Pi.
- Flash Raspberry Pi OS Lite (64-bit) — not the Desktop image — using Raspberry Pi Imager. The headless Lite image saves ~200–300 MB of RAM that the inference pipeline needs.
- In the imager's advanced options, enable SSH and configure Wi-Fi.
- Boot the Pi and connect via SSH:
ssh pi@<hostname>.local
# Reduce GPU memory split — gives the CPU pipeline more RAM
sudo nano /boot/firmware/config.txt
# add: gpu_mem=16
# Disable Wi-Fi power management (prevents SSH/MJPEG stalls)
sudo iwconfig wlan0 power off
# (Recommended) increase swap as an OOM safety net
sudo nano /etc/dphys-swapfile
# set: CONF_SWAPSIZE=512
sudo dphys-swapfile setup && sudo dphys-swapfile swaponsudo apt update && sudo apt upgrade -y
sudo apt install -y python3-pip python3-opencv libopencv-dev python3-picamera2
pip3 install -r requirements.txt --break-system-packagesPyTorch installation on ARM can take 20–60 minutes on a Pi 3 B+.
The detection model is loaded via PyTorch Hub local source mode
(detection/model.py), which requires a local clone of the YOLOv5
repository plus the trained weights file. Both must sit next to this
repository as configured in config/settings.py:
cd /home/<username>/insect_detect
git clone https://github.com/ultralytics/yolov5.git
cd yolov5 && pip3 install -r requirements.txt --break-system-packages
# Copy the trained weights onto the Pi (from your training machine)
scp best.pt pi@<hostname>.local:/home/<username>/insect_detect/yolov5/best.ptEdit config/settings.py and set HOME_DIR to match your actual install
path (default is /home/sudipto/insect_detect):
HOME_DIR = "/home/<username>/insect_detect"All other paths (YOLOV5_PATH, MODEL_WEIGHTS, BASE_DIR) derive from
HOME_DIR automatically.
cd /home/<username>/insect_detect
python3 main.pyExpected startup log:
Path validation passed.
SSH mode : True
GUI preview: False
Session output : /home/<username>/insect_detect/detections/2026-06-15/10-30-00
Loading YOLOv5 model ...
YOLOv5 model loaded ✓
Camera backend : picamera2
Live stream -> http://<pi-ip>:8080/
Snapshot -> http://<pi-ip>:8080/snap
SSH tunnel -> ssh -L 8080:localhost:8080 pi@<pi-ip>
Detection running - press Ctrl+C to stop
Open http://<pi-ip>:8080/ in a browser on the same network to view the
live MJPEG stream. Stop the system with Ctrl+C — this triggers a clean
shutdown (camera release + final CSV flush).
# /etc/systemd/system/insect-detect.service
[Unit]
Description=Insect Detection Service
After=network.target
[Service]
Type=simple
User=<username>
WorkingDirectory=/home/<username>/insect_detect
ExecStart=/usr/bin/python3 main.py
Restart=on-failure
RestartSec=10
StandardOutput=append:/home/<username>/insect_detect/service.log
StandardError=append:/home/<username>/insect_detect/service.log
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable --now insect-detectAll tunables live in config/settings.py. Do not hardcode parameters
elsewhere in the codebase.
| Parameter | Default | Effect |
|---|---|---|
HOME_DIR |
/home/sudipto/insect_detect |
Project root — must be updated per deployment |
YOLOV5_PATH |
HOME_DIR/yolov5 |
Path to the local YOLOv5 clone (must contain hubconf.py) |
MODEL_WEIGHTS |
YOLOV5_PATH/best.pt |
Trained model weights |
IMG_WIDTH / IMG_HEIGHT |
640 / 640 |
Capture resolution — must match training resolution |
CONF_THRESHOLD |
0.15 |
Stage-1 detection filter (confidence) |
MIN_BOX_AREA |
100 |
Stage-2 detection filter (bounding box area, px²) |
INFER_EVERY |
1 |
Run inference on every Nth captured frame |
FRAME_DELAY |
0.0 |
Idle sleep (s) per loop — raise to reduce thermal load |
CAPTURE_TIMEOUT |
12 |
Timeout (s) for the subprocess camera fallback |
JPEG_QUALITY |
60 |
JPEG compression quality for saved images (0–100) |
CSV_FLUSH_EVERY |
20 |
Write detections.csv every N detection events |
WEB_PORT |
8080 |
Flask stream server port |
- Too many false positives (debris detected as insects) → raise
CONF_THRESHOLDto 0.25–0.35 and/or raiseMIN_BOX_AREA. - Missing genuine insects → lower
CONF_THRESHOLD(already biased low by design — see Field Validation Results). - Thermal throttling during long sessions → raise
FRAME_DELAYto 1.0–2.0 s. - MicroSD filling too quickly → lower
JPEG_QUALITYto 40–50.
Defines CameraBase (abstract read() / release() interface) and two
implementations: PiCamera2Capture (primary, uses the official
picamera2 library) and SubprocessCapture (fallback, shells out to
rpicam-jpeg/libcamera-jpeg). factory.open_camera() tries backends in
priority order and exits only if both fail.
Single source of truth for every path and tunable constant. See Configuration Reference.
model.load_model()— loads YOLOv5n fully offline viatorch.hub.load(..., source="local"), setsmodel.conf = CONF_THRESHOLD, and puts the model ineval()mode on CPU.preprocessor.preprocess()— BGR→RGB conversion followed by a 3×3 Laplacian sharpening kernel ([[0,-1,0],[-1,5,-1],[0,-1,0]]).inference.run_inference()— runs the forward pass insidetorch.no_grad(), returns a detectionsDataFrameand the rendered annotated frame, and applies the Stage-2 bounding-box-area filter.
session.Session— creates thedetections/<date>/<time>/{annotated,cropped}/directory tree anddetections.csvpath for one run.writer.DetectionWriter— saves per-class crops (from the raw, not annotated, frame), saves the annotated full frame, and appends to an in-memory log that is flushed to CSV everyCSV_FLUSH_EVERYevents and on shutdown.
Runs Flask on a daemon thread so the inference loop is never blocked. Exposes:
/— MJPEG multipart stream of the latest annotated frame./snap— single JPEG snapshot, useful when the MJPEG stream stalls on unstable Wi-Fi.
If Flask is not installed, stream.start() returns False and the
pipeline continues without streaming — it never crashes the main loop.
env.is_ssh()/env.show_preview()— detect headless operation socv2.imshow()is never called without a display.paths.validate()— pre-flight check forYOLOV5_PATH,hubconf.py, andMODEL_WEIGHTS; exits with a clear diagnostic before any expensive initialization if anything is missing.
Orchestrates startup (validation → session → model → camera → stream),
runs the main capture/inference/save loop, and guarantees clean shutdown
(cam.release(), writer.flush()) via a finally block and SIGINT/SIGTERM
handlers.
Each session produces detections.csv with one row per detected insect:
| Column | Description |
|---|---|
frame |
Frame counter from session start |
timestamp |
Wall-clock time (YYYY-MM-DD HH:MM:SS) |
class |
Detected species name |
confidence |
Detection confidence (0–1) |
x1, y1, x2, y2 |
Bounding box coordinates (pixels) |
Run the analytics script on a desktop machine (requires matplotlib +
pandas):
python scripts/plot_detections.py path/to/detections.csv
# -> writes detection_plots.png in the same directoryThis produces a four-panel "Insect Detection Summary" dashboard: detections per class, confidence score distribution, detections over time (per minute), and bounding-box-area distribution.
Field test conducted 27 April 2026, 08:57:26, with live specimens of Parasa indetermina (moth, family Limacodidae) and Torpedo bug (Siphanta acuta, planthopper) placed on the monitoring tray. The session produced 11 annotated frames.
Detection Summary dashboard generated with scripts/plot_detections.py from the 2026-04-27 session log.
| Metric | Value |
|---|---|
| Inference time (normal) | 1.5 – 3.5 s/frame |
| Inference time (thermally throttled) | 4 – 6 s/frame |
| Effective frame rate | ~0.3 – 0.6 fps |
| Peak RAM during inference | ~700 MB / 1024 MB |
| Peak detection confidence | 0.85 (Parasa indetermina, frame 14) |
| Annotated frames produced | 11 |
The ~0.3–0.6 fps throughput is sufficient for static-surface monitoring: insects on the tray remain stationary for seconds to minutes. It is not sufficient for fast-flying insects — see Roadmap.
Empirically observed constraints and their mitigations. See the full project report for detailed root-cause analysis.
| # | Constraint | Severity | Mitigation |
|---|---|---|---|
| 1 | Voltage instability — CSI camera corrupts below ~4.65 V | HIGH | Dedicated 5 V/3 A supply, short 26 AWG cable |
| 2 | CPU thermal throttling — clock drops 1.4 GHz → 600 MHz above 80°C | HIGH | Heatsinks, ventilation slots, FRAME_DELAY |
| 3 | RAM exhaustion — OOM kills / swap thrashing near 700–750 MB | HIGH | OS Lite, gpu_mem=16, YOLOv5n, explicit gc.collect() |
| 4 | Wi-Fi / SSH instability — periodic 3–10 s freezes | MEDIUM | iwconfig wlan0 power off, ServerAliveInterval=30, /snap fallback |
| 5 | MicroSD I/O degradation under continuous small-file writes | MEDIUM | High-endurance (dashcam-rated) card, batched CSV writes |
| 6 | Inference latency — precludes fast-flying insect detection | LOW (accepted) | Acceptable for static tray monitoring; Coral USB Accelerator planned |
| Symptom | Likely cause | Fix |
|---|---|---|
PATH ERRORS on startup, "YOLOv5 directory not found" |
HOME_DIR in config/settings.py doesn't match the actual install path |
Update HOME_DIR to /home/<username>/insect_detect |
Model weights not found |
best.pt not copied to the Pi |
scp best.pt into <HOME_DIR>/yolov5/ |
All camera backends failed |
CSI cable not seated / camera not enabled | Check ribbon cable orientation at both ends; vcgencmd get_camera; enable camera in raspi-config |
| Frames are black | Camera lens cap on, or AEC warm-up too short in low light | Increase the 1 s time.sleep() in picamera2_backend.py to 2 s |
| Corrupted / scrambled frames | Under-voltage | Use a 5 V/3 A supply with a short 26 AWG cable |
| All frames show "no detections" | CONF_THRESHOLD too high, or model not trained on the target species |
Lower CONF_THRESHOLD; verify the species is in the 33-class set |
| MJPEG stream loads once then freezes | Wi-Fi power management | sudo iwconfig wlan0 power off; use /snap as a fallback |
detections.csv missing/incomplete after shutdown |
Process killed with kill -9 instead of Ctrl+C, bypassing the final flush |
Always stop with Ctrl+C (SIGINT) or systemctl stop (SIGTERM) |
| Phase | Focus | Planned work |
|---|---|---|
| 1 | Power Autonomy | Solar panel + LiPo battery, multi-week deployment, DC-DC power budget |
| 2 | AI Acceleration | Pi 4/5 upgrade, Google Coral USB Accelerator, sub-200 ms inference |
| 3 | Wide-Area Connectivity | LoRaWAN / GSM-LTE uplink, central biodiversity server |
| 4 | Physical Robustness | IP65-rated weatherproof housing, condensation management, active cooling |
| 5 | Dataset Expansion | European species (bees, hoverflies, butterflies), sticky-trap validation |
- Sánchez-Bayo, F. & Wyckhuys, K.A.G. (2019). Worldwide decline of the entomofauna: A review of its drivers. Biological Conservation, 232, 8–27.
- Geissmann, Q. et al. (2023). Insect Detect: An open-source DIY camera trap for automated insect monitoring. bioRxiv.
- Wu, X. et al. (2019). IP102: A Large-Scale Benchmark Dataset for Insect Pest Recognition. IEEE/CVF CVPR.
- Jocher, G. et al. (2020). ultralytics/yolov5. Zenodo.
This project is licensed under the MIT License.





