Meet YOLO26: next-gen vision AI.

Link to this sectionXuất Hailo cho các model YOLO của Ultralytics#

Không phải định dạng xuất trực tiếp của Ultralytics

Hailo HEF hiện không được hỗ trợ như một mục tiêu xuất trực tiếp thông qua model.export(format="hailo") của Ultralytics. Quy trình dưới đây là giải pháp thủ công thay thế, trong đó model được xuất sang ONNX trước, sau đó sử dụng bộ công cụ Dataflow Compiler từ Hailo để tạo file .hef. Quy trình làm việc liền mạch của Ultralytics sẽ cần tích hợp Hailo thông qua cùng API xuất Python và CLI như các định dạng phần cứng khác.

Chuỗi công cụ Hailo sử dụng các tệp HEF cho các nền tảng nhúng bao gồm Raspberry Pi AI KitAI HAT+, camera công nghiệp, cổng kết nối edge và các máy tính AI.

Hướng dẫn này trình bày cách xuất các model Ultralytics YOLO đã chọn sang định dạng HEF (Hailo Executable Format) của Hailo bằng cách sử dụng SDK Hailo Dataflow Compiler (DFC). Quy trình bắt đầu từ một model YOLO .pt, xuất sang ONNX, biên dịch bằng các công cụ của Hailo và tạo ra file .hef sẵn sàng cho các bộ tăng tốc Hailo được hỗ trợ.

Link to this sectionKhi nào nên sử dụng Hailo HEF#

HEF là artifact đã biên dịch được tiêu thụ bởi HailoRT trên các thiết bị đích Hailo. Chỉ sử dụng hướng dẫn thủ công này khi phần cứng triển khai của bạn yêu cầu cụ thể Hailo HEF trước khi có hỗ trợ xuất trực tiếp từ Ultralytics sang Hailo.

HEF có vai trò triển khai tương tự như các định dạng dành riêng cho phần cứng như RKNN cho Rockchip NPU, IMX500 cho Raspberry Pi AI Camera và Qualcomm QNN cho Snapdragon NPU, nhưng hiện tại nó không được Ultralytics tạo trực tiếp.

Quy trình này phù hợp khi bạn cần:

  • Khả năng tương thích với Raspberry Pi AI Kit: Hailo-8L được sử dụng trong Raspberry Pi AI Kit và AI HAT+ chính thức.
  • Xử lý hậu kỳ bằng HailoRT: HailoRT có thể bao gồm non-maximum suppression (NMS) của YOLO trong pipeline suy luận đã biên dịch.
  • Biên dịch INT8: Hailo DFC thực hiện lượng tử hóa model với các hình ảnh hiệu chỉnh đại diện để tạo ra biểu đồ INT8 cho phần cứng Hailo. Tìm hiểu thêm về model quantization.

Link to this sectionĐịnh dạng xuất Hailo HEF#

HEF là một tệp thực thi dành riêng cho phần cứng do Hailo Dataflow Compiler tạo ra. Nó chứa biểu đồ model đã lượng tử hóa, phân bổ bộ nhớ, lập lịch và xử lý hậu kỳ tùy chọn được cấu hình cho kiến trúc Hailo mục tiêu. Không giống như các định dạng Export mode tiêu chuẩn của YOLO được tạo trực tiếp bởi model.export(format=...), việc biên dịch HEF hiện sử dụng quy trình hai giai đoạn:

  1. Xuất YOLO sang ONNX với Ultralytics.
  2. Sử dụng các công cụ Hailo DFC để phân tích, tối ưu hóa, lượng tử hóa và biên dịch model ONNX thành HEF.

Quy trình đầy đủ mở rộng thành pipeline sau:

YOLO (.pt) -> ONNX -> HAR (parse) -> HAR (optimize/quantize) -> HEF (compile)
  1. Xuất sang ONNX bằng Export mode của Ultralytics
  2. Phân tích (Parse) model ONNX thành định dạng trung gian HAR của Hailo
  3. Tải script model (.alls) với các chỉ thị chuẩn hóa và xử lý hậu kỳ
  4. Hiệu chỉnh và lượng tử hóa (Calibrate and quantize) bằng các hình ảnh đại diện
  5. Biên dịch (Compile) thành tệp HEF có thể triển khai

Link to this sectionCác tác vụ được hỗ trợ#

Ví dụ thủ công hiện tại tập trung vào phát hiện đối tượng YOLO11 vì script model Hailo và cấu hình hậu xử lý (post-processing) là dành riêng cho đầu ra phát hiện (detection-head). Việc triển khai model.export(format="hailo") trực tiếp trong tương lai sẽ giúp thao tác xuất sang Hailo trở nên đơn giản như các định dạng xuất khác của Ultralytics, với khả năng hỗ trợ tác vụ được quản lý bởi head của model và tính tương thích với trình biên dịch Hailo thay vì các bước quy trình bên ngoài.

Tác vụMục tiêu xuất trực tiếp sang HailoLưu ý
Phát hiện đối tượng✅ Mục tiêu chínhViệc phát hiện cho YOLOv8, YOLO11 và YOLO26 nên là con đường xuất trực tiếp đầu tiên.
Phân đoạn Instance✅ Mục tiêuPhân đoạn (segmentation) trên YOLOv8, YOLO11 và YOLO26 yêu cầu xử lý đầu ra mask và xác thực dành riêng cho tác vụ.
Phân đoạn ngữ nghĩa⚠️ Cần xác thựcPhân đoạn ngữ nghĩa trên YOLO26 cần một trình biên dịch và lộ trình xác thực đầu ra chuyên dụng.
Ước tính tư thế⚠️ Cần xác thựcPose (phân tích tư thế) yêu cầu xử lý đầu ra keypoint ngoài lộ trình NMS phát hiện thông thường.
OBB Detection⚠️ Cần xác thựcOBB yêu cầu xử lý đầu ra hộp xoay (rotated-box) ngoài lộ trình NMS phát hiện tiêu chuẩn.
Phân loại⚠️ Cần xác thựcClassification (phân loại) có đầu ra đơn giản hơn, nhưng vẫn cần quá trình biên dịch Hailo và xác thực thời gian chạy (runtime).

Cho đến khi việc xuất trực tiếp sang Hailo được triển khai trong Ultralytics, chỉ có quy trình thủ công từ ONNX sang HEF dưới đây là được ghi lại.

Link to this sectionCác phiên bản Hailo SDK#

Việc xuất trực tiếp sang Hailo phải tính đến sự phân tách phần cứng và thế hệ SDK của Hailo:

  • Hailo-8 và Hailo-8L: sử dụng Hailo Dataflow Compiler v3.x. Đây là lộ trình liên quan cho các triển khai trên Raspberry Pi AI Kit và 13 TOPS AI HAT+.
  • Hailo-10 và Hailo-15: sử dụng Hailo Dataflow Compiler v5.x.

Sự phân chia phiên bản này ảnh hưởng đến các API của trình biên dịch, các kiến trúc được hỗ trợ, tính tương thích của HEF được tạo ra và các giá trị hw_arch mà một bộ xuất trực tiếp cần cung cấp. Việc hỗ trợ tác vụ trên một thế hệ phần cứng Hailo không nên được coi là hỗ trợ trên thế hệ khác nếu chưa xác thực phiên bản DFC đích và hw_arch.

Link to this sectionGhi chú về khả năng tương thích#

Tính tương thích khi xuất sang Hailo phụ thuộc vào head của model, kích thước ảnh đầu vào, số lượng lớp (class count), kiến trúc Hailo, script model đã tạo (.alls) và cấu hình hậu xử lý. Các cấu hình tĩnh không phải là mẫu áp dụng chung. Ví dụ, một JSON NMS được tạo cho model YOLO11n 80 lớp COCO sẽ không chính xác cho một model tùy chỉnh 3 lớp hoặc cho một imgsz cố định khác.

Phạm viMức độ hỗ trợ dự kiếnLưu ý
Phát hiện trên YOLOv8 / YOLO11✅ TốtSử dụng chung detection head đã tách rời; các chỉ thị .alls, nút kết thúc (end nodes) và cấu hình NMS vẫn cần khớp với đồ thị đã xuất và imgsz cố định.
Phát hiện YOLOv8 / YOLO11 tùy chỉnh✅ Có thểYêu cầu cấu hình NMS cho mỗi model được tạo từ số lượng lớp, strides và bố cục của detection-head; JSON tĩnh sẽ không khớp.
Phát hiện trên YOLO26✅ Mục tiêuKiến trúc không dùng NMS cần một lộ trình biên dịch/hậu xử lý riêng; không sử dụng lại quy trình NMS của YOLO11/YOLOv8 dưới đây cho YOLO26.
Phân đoạn đối tượng trên YOLO26✅ Mục tiêuCần xử lý đầu ra mask dành riêng cho phân đoạn YOLO26 và xác thực độ chính xác.
Phân đoạn ngữ nghĩa, pose, OBB, phân loại trên YOLO26⚠️ Nghiên cứuCác tác vụ này cần trình biên dịch chuyên dụng và xác thực thời gian chạy trước khi có thể được công bố là hỗ trợ trực tiếp.
Kích thước ảnh động hoặc tùy ý❌ Không hỗ trợBiên dịch Hailo sử dụng hình dạng đầu vào cố định; các cài đặt .alls và hậu xử lý phải khớp với imgsz đã xuất.

Link to this sectionCài đặt#

Link to this sectionBước 1: Cài đặt Ultralytics#

pip install ultralytics

Link to this sectionBước 2: Cài đặt SDK Hailo DFC#

Hailo DFC là bắt buộc để phân tích cú pháp, tối ưu hóa và biên dịch. Tải xuống wheel Python từ Hailo Developer Zone (yêu cầu đăng ký miễn phí) và cài đặt nó:

pip install /path/to/hailo_dataflow_compiler-*.whl
Lưu ý

SDK Hailo DFC yêu cầu máy Linux x86_64. Việc xuất và biên dịch không thể thực hiện trên các thiết bị ARM như Raspberry Pi. Sao chép tệp .hef thu được vào thiết bị chạy Hailo của bạn để triển khai với HailoRT.

Link to this sectionVí dụ về xuất YOLO11n HEF#

Script bên dưới biên dịch một model phát hiện YOLO11n từ .pt sang .hef với kích thước đầu vào cố định 640 pixel. Nó xuất sang ONNX bằng Ultralytics, sau đó biên dịch với Hailo DFC bằng COCO128 làm tập dữ liệu hiệu chỉnh nhỏ.

Trước khi chạy script, hãy cung cấp một JSON NMS của Hailo khớp chính xác với đồ thị xuất của YOLO11n, số lượng lớp, strides và kích thước đầu vào cố định. Sử dụng lại script này như một điểm khởi đầu cho YOLO11n; các model tùy chỉnh cần các nút kết thúc, chỉ thị .alls và cài đặt NMS phù hợp.

YOLO26 sử dụng lộ trình Hailo khác

Các model YOLO26 không dùng NMS. Một bộ xuất trực tiếp Ultralytics sang Hailo cần một lộ trình biên dịch và hậu xử lý chuyên dụng cho YOLO26 đối với tác vụ phát hiện hoặc phân đoạn đối tượng thay vì ví dụ NMS của YOLO11 dưới đây.

Pipeline đầy đủ
import ast
import random
from pathlib import Path

import numpy as np
import onnx
from hailo_sdk_client import ClientRunner
from PIL import Image

from ultralytics import YOLO
from ultralytics.data.utils import check_det_dataset
from ultralytics.utils import DATASETS_DIR, YAML

# Configuration
MODEL = "yolo11n"
HW_ARCH = "hailo8"  # hailo8 | hailo8l | hailo15h
IMGSZ = 640
CALIB_IMAGES = 128
NMS_CONFIG = "yolo11n_nms_config.json"  # Download or generate for your exact model.
OUT_DIR = Path(f"{MODEL}_hailo_model")  # deploy folder (mirrors Ultralytics <model>_<format>_model exports)
OUT_DIR.mkdir(exist_ok=True)

# YOLO11 detection head end nodes. See "Supported Models and End Nodes" for YOLOv8 and other families.
END_NODES = [
    "/model.23/cv2.0/cv2.0.2/Conv",
    "/model.23/cv3.0/cv3.0.2/Conv",
    "/model.23/cv2.1/cv2.1.2/Conv",
    "/model.23/cv3.1/cv3.1.2/Conv",
    "/model.23/cv2.2/cv2.2.2/Conv",
    "/model.23/cv3.2/cv3.2.2/Conv",
]

# Step 1: Export to ONNX, then move it into the deploy folder to keep the working directory tidy
model = YOLO(f"{MODEL}.pt")
onnx_path = Path(model.export(format="onnx", imgsz=IMGSZ, opset=11))
onnx_path = onnx_path.rename(OUT_DIR / onnx_path.name)

# Copy the metadata Ultralytics embedded in the ONNX into the standard metadata.yaml sidecar.
# The HEF stores no class names, so inference reads them from this file.
meta = {p.key: p.value for p in onnx.load(onnx_path, load_external_data=False).metadata_props}
for k in ("stride", "batch", "channels"):
    if k in meta:
        meta[k] = int(meta[k])
for k in ("imgsz", "names", "args", "end2end"):
    if k in meta:
        meta[k] = ast.literal_eval(meta[k])
YAML.save(OUT_DIR / "metadata.yaml", meta)

# Step 2: Parse ONNX with Hailo DFC
# The DFC prints the detected end nodes after parsing; use them if unsure.
runner = ClientRunner(hw_arch=HW_ARCH)
runner.translate_onnx_model(str(onnx_path), end_node_names=END_NODES)

# Step 3: Load model script (normalization + HailoRT NMS)
# The conv layer names are generated by DFC and can change for other model sizes/families.
model_script = (
    "normalization1 = normalization([0.0, 0.0, 0.0], [255.0, 255.0, 255.0])\n"
    "change_output_activation(conv54, sigmoid)\n"
    "change_output_activation(conv65, sigmoid)\n"
    "change_output_activation(conv80, sigmoid)\n"
    f'nms_postprocess("{NMS_CONFIG}", meta_arch=yolov8, engine=cpu)\n'
    "allocator_param(width_splitter_defuse=disabled)"
)
runner.load_model_script(model_script)

# Step 4: Build calibration dataset (auto-downloads COCO128)
check_det_dataset("coco128.yaml")
calib_dir = DATASETS_DIR / "coco128" / "images" / "train2017"
image_files = list(calib_dir.glob("*.jpg")) + list(calib_dir.glob("*.png"))
if not image_files:
    raise FileNotFoundError(f"No calibration images found in {calib_dir}")

calibset = np.zeros((CALIB_IMAGES, IMGSZ, IMGSZ, 3), dtype=np.float32)
for i in range(CALIB_IMAGES):
    img = Image.open(random.choice(image_files)).convert("RGB").resize((IMGSZ, IMGSZ))
    calibset[i] = np.array(img, dtype=np.float32)

# Step 5: Optimize and quantize
runner.optimize(calibset)
runner.save_har(str(OUT_DIR / f"{MODEL}.o.har"))  # optional intermediate HAR

# Step 6: Compile to HEF
hef = runner.compile()
hef_path = OUT_DIR / f"{MODEL}.hef"
with open(hef_path, "wb") as f:
    f.write(hef)

# Note: the Hailo SDK writes *.log files (acceleras.log, allocator.log, hailo_sdk.client.log,
# hailo_sdk.core.log) to the working directory. They are diagnostic scratch, safe to ignore or delete.
print(f"Compiled HEF saved to: {hef_path}")

Script xuất khẩu tổ chức các cấu phần và tệp nhật ký như sau:

  • Thư mục triển khai: Các cấu phần được lưu vào yolo11n_hailo_model/, phản ánh bố cục <model>_<format>_model/ tiêu chuẩn được sử dụng bởi các lệnh xuất khẩu khác của Ultralytics.
  • Các tệp bắt buộc: Hai tệp cần thiết để triển khai là yolo11n.hef đã biên dịch và tệp đi kèm metadata.yaml.
  • Siêu dữ liệu (Metadata): Tệp metadata.yaml chứa các trường thiết yếu (names, imgsz, task, stride, v.v.) được trích xuất từ siêu dữ liệu ONNX. Các script suy luận (inference) tải tên lớp từ tệp này vì định dạng HEF không lưu trữ chúng.
  • Các tệp trung gian: Thư mục xuất khẩu cũng chứa các tệp kiểm tra (checkpoint) trung gian yolo11n.onnxyolo11n.o.har.
  • Tệp nhật ký: Hailo SDK tạo ra một số nhật ký chẩn đoán (ví dụ: acceleras.log, allocator.log, hailo_sdk.client.log, và hailo_sdk.core.log) trong thư mục làm việc; những tệp này có thể được bỏ qua hoặc xóa một cách an toàn.
  • Raspberry Pi AI Kit: Đối với phần cứng cụ thể này, hãy đảm bảo bạn đặt HW_ARCH = "hailo8l" trước khi chạy bước biên dịch.

Link to this sectionChi tiết từng bước#

Script đầy đủ ở trên chạy từ đầu đến cuối. Phần này giải thích chức năng của từng giai đoạn và các chi tiết cụ thể của mô hình cần lưu ý khi điều chỉnh script cho mô hình của riêng bạn.

Link to this sectionBước 1: Xuất sang ONNX và lưu Siêu dữ liệu#

Ultralytics xuất mô hình đã đào tạo của bạn sang định dạng ONNX, định dạng mà Hailo DFC tiếp nhận dưới dạng đầu vào. opset=11 cung cấp khả năng tương thích DFC rộng rãi và tệp ONNX được di chuyển vào thư mục triển khai yolo11n_hailo_model/ (phản ánh bố cục <model>_<format>_model/ của các lệnh xuất khẩu khác từ Ultralytics) để giữ cho thư mục làm việc gọn gàng.

HEF không lưu trữ tên lớp, vì vậy siêu dữ liệu mà Ultralytics nhúng vào ONNX được sao chép vào tệp đi kèm metadata.yaml tiêu chuẩn bên cạnh nó. Đây chính là tệp metadata.yaml mà các định dạng xuất khẩu khác tạo ra (names, imgsz, task, stride, và nhiều thông số khác), và suy luận sẽ đọc tên lớp từ tệp này, do đó quy trình làm việc hoạt động với các mô hình tùy chỉnh mà không cần mã hóa cứng bất kỳ nhãn nào.

Link to this sectionBước 2: Phân tích model ONNX#

runner.translate_onnx_model(...) chuyển đổi đồ thị ONNX sang biểu diễn HAR trung gian của Hailo. Danh sách end_node_names cho DFC biết nơi cắt đồ thị trước NMS để Hailo có thể gắn các thành phần xử lý hậu kỳ phần cứng của riêng nó.

Tìm các nút kết thúc

DFC sẽ in ra một gợi ý sau khi phân tích cú pháp:

[info] In order to use HailoRT post-processing capabilities, these end node names should be used: ...

Sao chép các tên nút đó nếu bạn không chắc chắn nên sử dụng nút nào, hoặc nếu bạn đang làm việc với một kiến trúc tùy chỉnh hoặc ít phổ biến hơn.

Link to this sectionBước 3: Tải Script Model#

Script model (.alls) cấu hình chuẩn hóa đầu vào, kích hoạt đầu ra và xử lý hậu kỳ NMS. Thiết lập meta_arch=yolov8 áp dụng cho cả YOLOv8 và YOLO11 vì chúng chia sẻ cùng một bố cục detection head.

MODEL = "yolo11n"
NMS_CONFIG = "yolo11n_nms_config.json"
model_script = (
    "normalization1 = normalization([0.0, 0.0, 0.0], [255.0, 255.0, 255.0])\n"
    "change_output_activation(conv54, sigmoid)\n"
    "change_output_activation(conv65, sigmoid)\n"
    "change_output_activation(conv80, sigmoid)\n"
    f'nms_postprocess("{NMS_CONFIG}", meta_arch=yolov8, engine=cpu)\n'
    "allocator_param(width_splitter_defuse=disabled)"
)
runner.load_model_script(model_script)
Lưu ý

Tên lớp change_output_activation (conv54, conv65, conv80) được DFC gán trong quá trình phân tích và là đặc thù cho từng model. Nếu bạn đang biên dịch một kiến trúc hoặc kích thước model khác, hãy kiểm tra đầu ra của DFC để lấy tên chính xác hoặc tạo các chỉ thị .alls từ đồ thị đã xuất.

File NMS_CONFIG cũng đặc thù cho từng model. Hãy sử dụng cấu hình khớp chính xác với model đã xuất của bạn.

engine=cpu chạy NMS thông qua HailoRT trên CPU máy chủ. Chỉ sử dụng engine=nn_core cho các tổ hợp model/script mà Hailo tài liệu là được hỗ trợ bởi phần cứng và phiên bản SDK mục tiêu.

Xóa dòng nms_postprocess nếu bạn thích chạy NMS hoàn toàn trong mã ứng dụng của mình. Nếu làm điều này, hãy cập nhật trình phân tích suy luận vì HEF sẽ xuất ra các tensor detection-head thô thay vì các phát hiện NMS đã nhóm.

Link to this sectionBước 4: Xây dựng tập dữ liệu hiệu chỉnh#

Việc lượng tử hóa INT8 yêu cầu một tập hợp hình ảnh đại diện được xếp chồng thành một mảng float32 (N, imgsz, imgsz, 3). Script sử dụng COCO128, được Ultralytics tự động tải xuống qua check_det_dataset.

Mẹo

Sử dụng ít nhất 64 hình ảnh để hiệu chỉnh. Nhiều hình ảnh hơn thường cải thiện chất lượng lượng tử hóa. Để có kết quả tốt nhất, hãy sử dụng các hình ảnh từ miền triển khai của bạn thay vì COCO128.

Link to this sectionBước 5: Tối ưu hóa và Lượng tử hóa#

runner.optimize(calibset) áp dụng tinh chỉnh nhận biết lượng tử hóa và phân tích nhiễu lớp, sau đó runner.save_har(...) ghi một tệp kiểm tra trung gian tùy chọn. Khuyến nghị sử dụng GPU; nếu không có, bước này có thể mất vài giờ.

Link to this sectionBước 6: Biên dịch sang HEF#

runner.compile() tạo ra tệp HEF cuối cùng, được ghi vào yolo11n_hailo_model/yolo11n.hef. Nó hiện nằm cùng với tệp metadata.yaml, sẵn sàng để sao chép sang thiết bị phục vụ suy luận.

Link to this sectionCác Model và Nút kết thúc được hỗ trợ#

Đối với các model phát hiện, end_node_names xác định các đầu ra detection-head ONNX mà Hailo nên biên dịch trước khi đính kèm xử lý hậu kỳ NMS của nó. Các tên này thay đổi theo kiến trúc và có thể thay đổi khi biểu đồ xuất thay đổi.

Các ví dụ về nút kết thúc dưới đây áp dụng cho các model phát hiện YOLOv8 và YOLO11 sử dụng hậu xử lý NMS theo kiểu YOLOv8 của Hailo. YOLO26 không dùng NMS và không sử dụng cấu hình NMS của YOLO11 này.

Link to this sectionYOLO11 và YOLOv8#

YOLO11 và YOLOv8 chia sẻ cùng một decoupled detection head. Chỉ số lớp khác nhau một đơn vị giữa hai họ này:

Họ ModelLớp Detection HeadMẫu nút kết thúc
YOLO11 (tất cả)model.23/model.23/cv2.0/cv2.0.2/Conv (6 nút)
YOLOv8 (tất cả)model.22/model.22/cv2.0/cv2.0.2/Conv (6 nút)

Các nút kết thúc YOLO11 (tất cả kích thước: n, s, m, l, x):

END_NODES = [
    "/model.23/cv2.0/cv2.0.2/Conv",
    "/model.23/cv3.0/cv3.0.2/Conv",
    "/model.23/cv2.1/cv2.1.2/Conv",
    "/model.23/cv3.1/cv3.1.2/Conv",
    "/model.23/cv2.2/cv2.2.2/Conv",
    "/model.23/cv3.2/cv3.2.2/Conv",
]

Các nút kết thúc YOLOv8 (tất cả kích thước: n, s, m, l, x):

END_NODES = [
    "/model.22/cv2.0/cv2.0.2/Conv",
    "/model.22/cv3.0/cv3.0.2/Conv",
    "/model.22/cv2.1/cv2.1.2/Conv",
    "/model.22/cv3.1/cv3.1.2/Conv",
    "/model.22/cv2.2/cv2.2.2/Conv",
    "/model.22/cv3.2/cv3.2.2/Conv",
]

Link to this sectionCác kiến trúc khác#

Đối với các kiến trúc phát hiện khác, hãy chạy bước phân tích cú pháp mà không có end_node_names trước, đọc các nút được gợi ý từ đầu ra nhật ký DFC, sau đó chạy lại với các nút đó:

# First pass: let the DFC suggest end nodes
runner = ClientRunner(hw_arch=HW_ARCH)
runner.translate_onnx_model(f"{MODEL}.onnx")
# Check the printed log for: "[info] In order to use HailoRT post-processing..."

Để có sự hỗ trợ trực tiếp từ Ultralytics, các chỉ thị .alls và cài đặt hậu xử lý này nên được tự động tạo hoặc chọn bởi bộ xuất thay vì yêu cầu người dùng phải tự lắp ráp thủ công.

Link to this sectionCác kiến trúc phần cứng được hỗ trợ#

Kiến trúcThiết bịTính toán đỉnh (Theo thông số nhà cung cấp)Trường hợp sử dụng phổ biến
hailo8Hailo-826 TOPSCard tăng tốc Hailo
hailo8lHailo-8L13 TOPSRaspberry Pi AI Kit
hailo15hHailo-15H20 TOPSCác thiết bị đích Hailo-15

Đặt HW_ARCH trong script khớp với thiết bị đích của bạn trước khi biên dịch.

Link to this sectionChạy suy luận (inference) trên phần cứng Hailo#

Once compilation finishes, copy the whole yolo11n_hailo_model/ folder (the .hef plus its metadata.yaml) to your Hailo-powered device and run inference using either the HailoRT Python API (hailo_platform package) or, on Raspberry Pi, the picamera2 Hailo helper (a HailoRT wrapper). Both are shown in the tabs below. Keeping the two files together lets the scripts below read the class names from metadata.yaml next to the HEF. Unlike the DFC export steps, inference runs directly on the edge device.

Lưu ý

Mã suy luận bên dưới chạy trên thiết bị sử dụng Hailo (ví dụ: Raspberry Pi + AI Kit), không phải trên máy x86 dùng để biên dịch.

Link to this sectionBước 1: Cài đặt HailoRT trên thiết bị#

Trên thiết bị đích, hãy cài đặt HailoRT và các Python binding. Đối với người dùng Raspberry Pi AI Kit và AI HAT+, hướng dẫn phần mềm AI chính thức của Raspberry Pi sẽ cài đặt HailoRT, trình điều khiển thiết bị và Python binding bằng cách:

sudo apt install dkms
sudo apt install hailo-all
sudo reboot

Đối với các thiết bị Hailo không phải Raspberry Pi, hãy cài đặt gói HailoRT khớp với thiết bị, trình điều khiển và phiên bản SDK của bạn từ Hailo Developer Zone.

Các thiết bị AI HAT+ 2 sử dụng gói Raspberry Pi khác (hailo-h10-all) và quy trình làm việc của Hailo-10H. Hãy làm theo hướng dẫn phần mềm AI của Raspberry Pi cho thế hệ phần cứng đó.

Link to this sectionBước 2: Kiểm tra nhanh#

Trước khi chạy suy luận Python, hãy xác nhận thiết bị Hailo đã được nhận diện:

hailortcli fw-control identify

Bạn sẽ thấy loại thiết bị, phiên bản firmware và số serial được in ra.

Executing on device: 0001:01:00.0
Identifying board
Control Protocol Version: 2
Firmware Version: 4.23.0 (release,app,extended context switch buffer)
Logger Version: 0
Board Name: Hailo-8
Device Architecture: HAILO8

Link to this sectionBước 3: Chạy suy luận#

The scripts below run object detection with the compiled HEF file. Both tabs accept the same --source inputs (an image, a video, a USB webcam index, or csi for the Raspberry Pi Camera Module) and differ only in the inference API: the Hailo SDK tab uses the low-level hailo_platform API (portable, minimal dependencies), while the picamera2 tab uses the Raspberry Pi picamera2 Hailo helper. Images and videos are written to an annotated file; webcam and CSI streams display in a live window.

Đường dẫn HailoRT gốc của nhà cung cấp chạy trên mọi nền tảng có thiết bị Hailo và không cần các phụ thuộc bổ sung. Chuyển --source là đường dẫn hình ảnh, đường dẫn video, chỉ mục webcam (ví dụ: 0) để quay USB/V4L2 trực tiếp, hoặc csi cho Raspberry Pi Camera Module. Tùy chọn CSI yêu cầu cài đặt picamera2, vì hệ điều hành Raspberry Pi hiện đại định tuyến camera thông qua libcamera thay vì thiết bị V4L2 thông thường.

import argparse
from pathlib import Path

import cv2
import numpy as np
import yaml
from hailo_platform import (
    HEF,
    ConfigureParams,
    FormatType,
    HailoStreamInterface,
    InferVStreams,
    InputVStreamParams,
    OutputVStreamParams,
    VDevice,
)
from tqdm import tqdm

IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".bmp", ".webp", ".tif", ".tiff"}

def parse_and_draw(per_class, frame, conf, names):
    """Draw HailoRT NMS detections (grouped by class, normalized [0, 1] coords) onto a BGR frame."""
    h, w = frame.shape[:2]
    for cls_idx, cls_dets in enumerate(per_class):
        for det in cls_dets:
            score = float(det[4])
            if score < conf:
                continue
            # HailoRT NMS returns normalized [0, 1] coords as (y1, x1, y2, x2)
            y1, x1, y2, x2 = det[:4]
            x1, y1, x2, y2 = int(x1 * w), int(y1 * h), int(x2 * w), int(y2 * h)
            label = f"{names[cls_idx]} {score:.2f}"
            cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
            cv2.putText(frame, label, (x1 + 2, y1 + 15), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 255, 0), 1, cv2.LINE_AA)

def preprocess(frame, imgsz):
    """BGR frame -> (1, imgsz, imgsz, 3) float32 in 0-255 (HEF normalizes internally)."""
    rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
    resized = cv2.resize(rgb, (imgsz, imgsz))
    return np.expand_dims(resized.astype(np.float32), axis=0)

def csi_frames(width=1280, height=720):
    """Yield BGR frames from the Pi CSI Camera Module via picamera2."""
    from picamera2 import Picamera2

    picam2 = Picamera2()
    # picamera2 "RGB888" is BGR-ordered in memory, so it drops straight into OpenCV
    picam2.configure(picam2.create_preview_configuration(main={"size": (width, height), "format": "RGB888"}))
    picam2.start()
    try:
        while True:
            yield picam2.capture_array("main")  # BGR
    finally:
        picam2.stop()
        picam2.close()

def cv2_frames(src):
    """Yield BGR frames from a video file or USB/V4L2 webcam via OpenCV."""
    cap = cv2.VideoCapture(src)
    if not cap.isOpened():
        raise RuntimeError(f"Could not open source {src}")
    total = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))  # 0 for live webcams
    pbar = tqdm(total=total, desc="Processing video", unit="frame") if total > 0 else None
    try:
        while True:
            ok, frame = cap.read()  # BGR
            if not ok:
                break
            yield frame
            if pbar is not None:
                pbar.update(1)
    finally:
        if pbar is not None:
            pbar.close()
        cap.release()

def open_source(source):
    """Yield (frame, kind) pairs where kind is 'image', 'video', or 'stream'."""
    if source == "csi":
        yield from ((f, "stream") for f in csi_frames())
    elif source.isdigit():
        yield from ((f, "stream") for f in cv2_frames(int(source)))
    elif Path(source).suffix.lower() in IMAGE_EXTS:
        frame = cv2.imread(source)
        if frame is None:
            raise FileNotFoundError(f"Could not read image {source}")
        yield frame, "image"
    else:
        yield from ((f, "video") for f in cv2_frames(source))

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Hailo YOLO inference (image, video, webcam, or CSI camera)")
    parser.add_argument("-m", "--model", default="yolo11n_hailo_model/yolo11n.hef", help="Path to the HEF model.")
    parser.add_argument("--source", default="0", help="Image/video path, webcam index (e.g. 0), or 'csi'.")
    parser.add_argument("--imgsz", type=int, default=640)
    parser.add_argument("--conf", type=float, default=0.25)
    args = parser.parse_args()

    # Load class names from metadata.yaml saved next to the HEF during compilation (keyed by class index)
    with open(Path(args.model).parent / "metadata.yaml") as f:
        names = yaml.safe_load(f)["names"]

    # Configure the device and network group ONCE
    hef = HEF(args.model)
    target = VDevice(VDevice.create_params())
    configure_params = ConfigureParams.create_from_hef(hef, interface=HailoStreamInterface.PCIe)
    network_group = target.configure(hef, configure_params)[0]
    network_group_params = network_group.create_params()
    input_vstreams_params = InputVStreamParams.make(network_group, quantized=False, format_type=FormatType.FLOAT32)
    output_vstreams_params = OutputVStreamParams.make(network_group, quantized=False, format_type=FormatType.FLOAT32)
    input_name = hef.get_input_vstream_infos()[0].name

    writer = None  # lazily created for video output

    # Keep the pipeline and activation OPEN across frames (re-opening per frame is slow)
    with InferVStreams(network_group, input_vstreams_params, output_vstreams_params) as pipeline:
        with network_group.activate(network_group_params):
            try:
                for frame, kind in open_source(args.source):
                    raw = pipeline.infer({input_name: preprocess(frame, args.imgsz)})
                    parse_and_draw(raw[next(iter(raw.keys()))][0], frame, args.conf, names)

                    if kind == "image":
                        cv2.imwrite("output.jpg", frame)
                        print("Saved output.jpg")
                    elif kind == "video":
                        if writer is None:
                            h, w = frame.shape[:2]
                            writer = cv2.VideoWriter("output.mp4", cv2.VideoWriter_fourcc(*"mp4v"), 30, (w, h))
                        writer.write(frame)
                    else:  # live stream
                        cv2.imshow("Hailo YOLO", frame)
                        if cv2.waitKey(1) & 0xFF == ord("q"):
                            break
            finally:
                if writer is not None:
                    writer.release()
                    print("Saved output.mp4")
                cv2.destroyAllWindows()

Chạy script với bất kỳ nguồn nào (hình ảnh lưu thành output.jpg, video lưu thành output.mp4, luồng trực tiếp hiển thị trong một cửa sổ, nhấn q để thoát):

python hailo_infer.py --source bus.jpg  # single image
python hailo_infer.py --source clip.mp4 # video file
python hailo_infer.py --source 0        # USB webcam, live
python hailo_infer.py --source csi      # Raspberry Pi Camera Module
Mẹo

Định dạng đầu ra nhận diện giả định rằng tệp HEF đã được biên dịch với nms_postprocess trong script .alls. Nếu bạn đã biên dịch mà không có NMS, các đầu ra thô sẽ là 6 tensor của đầu nhận diện và bạn phải chạy NMS riêng trong ứng dụng của mình.

Link to this sectionSuy luận video với TAPPAS#

Đối với các pipeline video có lưu lượng cao, TAPPAS cung cấp các phần tử GStreamer truyền phát video qua chip Hailo theo thời gian thực:

MODEL=yolo11n
gst-launch-1.0 filesrc location=video.mp4 ! decodebin ! \
  hailonet hef-path=${MODEL}.hef ! \
  hailofilter function-name=yolov8 ! \
  hailooverlay ! autovideosink

Xem tài liệu TAPPAS để biết các tùy chọn cấu hình pipeline đầy đủ.

Link to this sectionTóm tắt#

Hướng dẫn này đã bao gồm quy trình đầy đủ để xuất các mô hình nhận diện Ultralytics YOLO sang định dạng Hailo HEF:

  1. Xuất sang ONNX bằng Ultralytics (model.export(format="onnx")).
  2. Phân tích cú pháp mô hình ONNX với Hailo DFC và chỉ định các end node của đầu nhận diện.
  3. Cấu hình chuẩn hóa và NMS thông qua script mô hình.
  4. Lượng tử hóa với tập dữ liệu hiệu chuẩn (COCO128 thông qua Ultralytics).
  5. Biên dịch thành tệp .hef sẵn sàng cho Hailo-8, Hailo-8L hoặc Hailo-15.

Để biết thêm chi tiết, hãy xem Hailo Developer Zonetài liệu của Hailo. Đối với các mục tiêu xuất khác của Ultralytics, hãy xem các hướng dẫn liên quan về ONNX, OpenVINO, TensorRT, NCNN, TFLite Edge TPU, RKNN, Sony IMX500Qualcomm QNN. Để so sánh tốc độ và độ chính xác của model đã xuất giữa các định dạng, hãy sử dụng chế độ Benchmark. Để có danh sách đầy đủ các định dạng và tùy chọn, hãy truy cập tài liệu chế độ Exporttrang hướng dẫn tích hợp.

Link to this sectionCâu hỏi thường gặp#

Link to this sectionNhững thiết bị Hailo nào được hỗ trợ?#

Hailo DFC hỗ trợ Hailo-8 (hailo8), Hailo-8L (hailo8l) và Hailo-15H (hailo15h). Xem bảng Kiến trúc phần cứng được hỗ trợ để biết giá trị HW_ARCH khớp.

Link to this sectionNhững mô hình Ultralytics nào có thể được xuất?#

Hướng dẫn này tập trung vào các mô hình nhận diện. Xem Các tác vụ được hỗ trợ để biết phạm vi cấp tác vụ, Lưu ý về tính tương thích để biết các giới hạn tương thích của mô hình và Các mô hình và End node được hỗ trợ để biết ví dụ về end node cho YOLO11 và YOLOv8.

Link to this sectionTại sao script mô hình sử dụng meta_arch=yolov8 cho YOLO11?#

YOLO11 sử dụng cùng kiến trúc đầu nhận diện tách rời (decoupled detection head) như YOLOv8. Hailo DFC sử dụng meta_arch=yolov8 cho cấu hình NMS cho cả hai dòng mô hình.

Link to this sectionTôi có cần GPU cho bước tối ưu hóa không?#

GPU được khuyến nghị mạnh mẽ cho quá trình tinh chỉnh nhận biết lượng tử hóa (quantization-aware fine-tuning) trong runner.optimize(). Nếu không có, quá trình vẫn hoạt động nhưng chậm hơn đáng kể (vài giờ so với khoảng 10-20 phút nếu có GPU).

Link to this sectionLàm thế nào để tìm các end node chính xác cho mô hình của tôi?#

Chạy runner.translate_onnx_model(...) mà không chỉ định end_node_names, sau đó sử dụng các node đầu nhận diện được DFC đề xuất. Xem Các kiến trúc khác để biết lệnh ví dụ.

Link to this sectionTôi có thể lấy SDK Hailo DFC ở đâu?#

Python wheel cho SDK Hailo DFC có sẵn từ Hailo Developer Zone. Đối với bộ xuất trực tiếp Ultralytics sang Hailo, script model và cấu hình hậu xử lý nên được tạo hoặc chọn bên trong quy trình xuất.

Bình luận