To run real-time machine learning in Raspberry Pi 5, use the Raspberry Pi 5 8GB (SKU: SC1112) paired with the official Hailo-8L AI Kit (13 TOPS) and the IMX708 Camera Module 3. This specific hardware combination achieves 30+ FPS on YOLOv8n at 640x640 resolution, vastly outperforming native CPU inference and eliminating the thermal throttling issues common in older Pi 4 setups. This guide covers the exact M.2 HAT+ wiring, PCIe configuration, and a complete Python inference script using picamera2 and the Hailo/TFLite delegates.
Edge AI Performance Matrix: Pi 5 vs Legacy Setups
Before ordering parts, it is critical to understand where the Pi 5 + Hailo-8L sits in the current embedded ML landscape. The table below benchmarks inference performance for a standard YOLOv8n (640x640) object detection model across common Raspberry Pi configurations.
| Hardware Configuration | Compute (TOPS) | YOLOv8n FPS (640x640) | System Power Draw (Load) | Approx. Cost (2026) |
|---|---|---|---|---|
| Pi 4 (8GB) Native CPU | 0.1 (Est) | 1.2 FPS | 6.5W | $75 |
| Pi 5 (8GB) Native CPU (XNNPACK) | 0.5 (Est) | 4.5 FPS | 11.0W | $80 |
| Pi 4 + Coral USB Accelerator | 4.0 | 12.0 FPS | 8.5W | $140 (Used/Scalped) |
| Pi 5 + Hailo-8L AI Kit | 13.0 | 32.0 FPS | 13.5W | $150 |
rpicam-apps post-processing support.
Parts List & M.2 HAT+ Pin Mapping
This build targets the exact SKUs below. Do not substitute the M.2 HAT+ with a generic third-party PCIe breakout unless it explicitly supports the Pi 5's 16-pin PCIe FPC connector and 40-pin GPIO passthrough.
- Compute: Raspberry Pi 5 8GB (SC1112)
- Accelerator: Raspberry Pi AI Kit (Hailo-8L M.2 HAT+ bundle, SC1145)
- Sensor: Raspberry Pi Camera Module 3 (IMX708, SC1220)
- Power: 27W USB-C PD Power Supply (SC1098) - Crucial: The Hailo-8L draws up to 5W; standard 15W phone chargers will cause brownouts.
- Storage: 64GB NVMe SSD (Optional, but recommended over microSD for ML model I/O)
Hardware Pin & Lane Mapping
The Pi 5 routes high-speed interfaces differently than the Pi 4. Below is the physical mapping for the accelerator and camera.
| Interface | Pi 5 Physical Connector | Module / Peripheral | Signal / Lane Assignment |
|---|---|---|---|
| PCIe Gen 2 x1 | 16-pin FPC (J4) | M.2 HAT+ to Hailo-8L | TX+/TX-, RX+/RX-, CLK+/CLK- |
| CSI-2 (Camera 0) | 22-pin FPC (J1) | IMX708 (via 15-to-22 adapter) | 2x Data Lanes, I2C (SDA1/SCL1) |
| I2C Sensor ID | GPIO Header (Pins 3, 5) | IMX708 EEPROM | Address 0x1A (Auto-detected by libcamera) |
Assembly & PCIe Configuration Steps
- ESD Precaution: Ground yourself before handling the Hailo-8L M.2 module. The exposed NAND/Logic dies are highly sensitive to static discharge.
- Mount the HAT+: Secure the M.2 HAT+ to the Pi 5 using the provided 8mm standoffs. Connect the 16-pin PCIe FPC cable. Ensure the blue tab faces outward (away from the SoC).
- Seat the Hailo Module: Insert the Hailo-8L into the M.2 Key M slot at a 30-degree angle, press down, and secure with the M2 screw. Apply the included thermal pad and heatsink.
- Enable PCIe Probe: Flash Raspberry Pi OS (Bookworm or newer). Open a terminal and edit the boot configuration:
sudo nano /boot/firmware/config.txt
Add the following line to the bottom to force PCIe Gen 2 enumeration (required for Hailo bandwidth):
dtparam=pciex1_gen=2 - Install HailoRT: Run
sudo apt update && sudo apt install hailo-all. This pulls the Hailo Runtime, GStreamer elements, and TFLite delegates. - Verify Enumeration: Reboot and run
hailortcli fw-control identify. You should seeHailo-8Land firmware version output.
Python Inference Code: Picamera2 + TFLite
This script targets the Raspberry Pi 5 8GB. It initializes the IMX708 camera via picamera2, loads a quantized TFLite model, and attempts to attach the Hailo delegate. If the Hailo hardware is unavailable, it gracefully falls back to the Pi 5's native XNNPACK CPU delegate.
import time
import cv2
import numpy as np
from picamera2 import Picamera2
import tflite_runtime.interpreter as tflite
# --- Hardware & Pin Definitions ---
# IMX708 I2C Address: 0x1A (Handled internally by libcamera)
# Hailo-8L PCIe: Gen 2 x1 (Handled by HailoRT delegate)
CAMERA_RESOLUTION = (640, 640)
MODEL_PATH = "yolov8n_full_integer_quant.tflite"
def load_delegate():
"""Attempts to load Hailo delegate, falls back to XNNPACK."""
try:
# Hailo delegate path for Pi AI Kit
delegate = tflite.load_delegate('libhailo_tf_lite.so')
print("[INFO] Hailo-8L Delegate loaded successfully.")
return [delegate]
except ValueError:
print("[WARN] Hailo delegate not found. Falling back to Pi 5 CPU (XNNPACK).")
# XNNPACK delegate for native ARM Cortex-A76 acceleration
delegate = tflite.load_delegate('libtensorflowlite.so',
options={'num_threads': 4})
return [delegate]
def main():
# 1. Initialize Camera
picam2 = Picamera2()
config = picam2.create_preview_configuration(main={"size": CAMERA_RESOLUTION, "format": "RGB888"})
picam2.configure(config)
picam2.start()
time.sleep(2) # Allow IMX708 AGC/AWB to settle
# 2. Initialize Interpreter
delegates = load_delegate()
interpreter = tflite.Interpreter(model_path=MODEL_PATH, experimental_delegates=delegates)
interpreter.allocate_tensors()
input_details = interpreter.get_input_details()
output_details = interpreter.get_output_details()
input_shape = input_details[0]['shape']
print(f"[INFO] Model input shape: {input_shape}")
try:
while True:
# Capture frame
frame = picam2.capture_array()
# Preprocess: Resize and normalize if required by model
input_data = cv2.resize(frame, (input_shape[1], input_shape[2]))
input_data = np.expand_dims(input_data, axis=0).astype(np.uint8)
# Inference
start_time = time.time()
interpreter.set_tensor(input_details[0]['index'], input_data)
interpreter.invoke()
inference_time = time.time() - start_time
# Post-process (Placeholder for bounding box parsing)
output_data = interpreter.get_tensor(output_details[0]['index'])
fps = 1.0 / inference_time
cv2.putText(frame, f"FPS: {fps:.1f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)
# cv2.imshow("Edge AI", frame) # Uncomment if running with desktop GUI
# if cv2.waitKey(1) & 0xFF == ord('q'): break
print(f"Inference: {inference_time*1000:.2f} ms | FPS: {fps:.1f}")
except KeyboardInterrupt:
print("[INFO] Stopping capture.")
finally:
picam2.stop()
if __name__ == "__main__":
main()
Debugging: Exact Errors & The First 3 Checks
Embedded ML fails at the intersection of hardware enumeration and software allocation. If your script crashes, look for these exact error strings.
Error 1: RuntimeError: Failed to allocate VDevice
This is the most common Hailo-specific error. It means the TFLite interpreter found the delegate library, but the HailoRT driver cannot communicate with the M.2 module over PCIe.
Ranked Causes:
- PCIe Gen 2 not forced: The Pi 5 defaults to Gen 1 for stability. The Hailo-8L requires Gen 2 bandwidth. Ensure
dtparam=pciex1_gen=2is inconfig.txt. - M.2 Seating Issue: The M.2 connector is shallow. If the thermal pad is too thick, the gold fingers lose contact with the PCIe lanes. Reseat the module and ensure the screw is tight.
- HailoRT Service Crash: Run
sudo systemctl restart hailoto reset the kernel driver state.
Error 2: picamera2.configuration_error: [Errno 5] Input/output error
This occurs when libcamera cannot initialize the IMX708 sensor over the I2C bus.
Ranked Causes:
- CSI Ribbon Backwards: The 15-pin to 22-pin adapter cable has a specific orientation. The blue stiffener tab must face the same direction as the Pi 5's USB ports.
- I2C Bus Collision: If you have other I2C devices on GPIO 2/3, they may be pulling the bus low. Disconnect them during camera init.
- Verify PCIe Probe: Run
vcgencmd bootloader_config | grep PCIE_PROBE. It must returnPCIE_PROBE=1. - Check Physical Seating: Power down, remove the HAT+, and inspect the M.2 gold fingers for scratches or incomplete insertion.
- Verify Camera I2C: Run
i2cdetect -y 1. You should see1ain the grid. If the grid is empty, your CSI cable is backwards or damaged.
Extending and Simplifying the Build
Depending on your deployment environment, you may need to scale this architecture up or down.
How to Simplify (Headless / Low Power)
If you do not need 30 FPS and want to reduce power draw for a solar-powered remote camera:
- Drop the Hailo-8L M.2 module entirely.
- Remove the
dtparam=pciex1_gen=2line fromconfig.txt. - Rely on the Pi 5's native XNNPACK delegate (already handled in the fallback logic of the Python script above).
- Reduce the camera resolution to 320x320 and drop the framerate to 5 FPS using
picam2.set_controls({"FrameRate": 5}). This drops system power draw to under 4W.
How to Extend (Multi-Camera / Fleet)
For industrial inspection or multi-angle tracking:
- Dual Camera: The Pi 5 has two 22-pin CSI connectors (CAM0 and CAM1). You can run two IMX708 sensors simultaneously. Use
Picamera2(camera_num=1)to instantiate the second stream. - Model Swapping: Instead of standard YOLO, compile custom Hailo HEF (Hailo Executable Format) models using the Hailo Dataflow Compiler. This allows you to run specialized defect-detection models at the full 13 TOPS capacity.
- Thermal Management: If running 24/7 in an enclosed NEMA box, replace the stock Hailo heatsink with a 5V 30mm fan blowing directly across the M.2 HAT+ fins. The Hailo-8L will thermal throttle at 85°C, dropping FPS by 40%.
For deeper integration with GStreamer pipelines, refer to the official Picamera2 Python Documentation and the TensorFlow Lite Edge AI guides.






