Skip to main content

YOLO

Last Version: 12/09/2025

YOLO Model Overview

This example shows how to run YOLO-series object-detection inference on SpacemiT NPU, using either images or a live video stream as input, and then publish the results (object classes + bounding-box coordinates) via ROS 2.

Supported Model Versions

  • YOLOv5
  • YOLOv6
  • YOLOv8
  • YOLOv11 (and others)

ONNX models can be downloaded from ⭐ Bianbu AI Demo Zoo All models are trained on the COCO dataset and can recognize 80 common object classes (people, animals, fruits, vehicles, etc.).

Advantages

  • Fast inference
  • High accuracy & generalization
  • Easy deployment, low resource usage

Use-Cases

  • Autonomous driving – pedestrian & vehicle detection
  • Smart home – garbage classification, surveillance
  • Smart security – abnomaly detection, fall detection

Environment Setup

Platform Requirements

SpacemiT RISC-V:

  • Pre-flashed with the Bianbu ROS system image

Install Dependencies

Run the following command to install required libraries and tools:

sudo apt install python3-opencv ros-humble-cv-bridge ros-humble-camera-info-manager \
ros-humble-image-transport python3-spacemit-ort python3-yaml libyaml-dev python3-numpy

Import ROS 2 Environment

Set up your ROS 2 environment:

source /opt/bros/humble/setup.bash

Model Configuration Check

Run the following command to list the model configurations currently supported on your system:

ros2 launch br_perception infer_info.launch.py | grep 'detection'

Sample output:

  - config/detection/yolov8.yaml
- config/detection/yolov6.yaml
- config/detection/yolov11_640.yaml
- config/detection/yolov5.yaml
- config/detection/yolov11_320.yaml

During subsequent inference, set config_path to one of these .yaml files to select the desired YOLO model.

Running Inference on an Image

Prepare the Image

Copy a test image to your current folder:

cp /opt/bros/humble/share/jobot_infer_py/data/detection/test.jpg .

Save Inference Results Locally

ros2 launch br_perception infer_img.launch.py \
config_path:='config/detection/yolov6.yaml' \
img_path:='./test.jpg'

The processed output will be saved as det_result.jpg in your specified output directory.

Viewing Inference Results in a Web Browser

Launch Inference Server in Terminal 1:

ros2 launch br_perception infer_img.launch.py \
config_path:='config/detection/yolov6.yaml' \
img_path:='./test.jpg' \
publish_result_img:=true \
result_img_topic:='result_img' \
result_topic:='/inference_result'

Launch the web visualization tool in a new Terminal 2:

ros2 launch br_visualization websocket_cpp.launch.py image_topic:='/result_img'

The terminal will prompt the browser address:

...
Please visit in your browser: http://<IP>:8080
...

Open a browser and visit http://<IP>:8080 to see annotated results live.

Message Subscription and Monitoring

Check Topic Data

Inference results are published to the /inference_result topic. Use the following commands to monitor the output:

ros2 topic echo /inference_result

Example output:

header:
stamp:
sec: 1745571001
nanosec: 597668762
frame_id: camera_link
results:
- x_min: 109
y_min: 99
width: 149
height: 316
label: bottle
conf: 0.4000000059604645
- x_min: 270
y_min: 209
width: 150
height: 249
label: bottle
conf: 0.4000000059604645
- x_min: -1
y_min: 424
width: 326
height: 55
label: sink
conf: 0.30000001192092896
---

Check Message Format Definitions

ros2 interface show jobot_ai_msgs/msg/DetectionResultArray

Message Structure Example:

std_msgs/Header header
builtin_interfaces/Time stamp
int32 sec
uint32 nanosec
string frame_id

jobot_ai_msgs/DetectionResult[] results
int32 x_min
int32 y_min
int32 width
int32 height
string label
float32 conf

Python Subscription Example

from rclpy.node import Node
from std_msgs.msg import Header
from jobot_ai_msgs.msg import DetectionResultArray, DetectionResult
import rclpy

class DetectionSubscriber(Node):
def __init__(self):
super().__init__('detection_sub')
self.subscription = self.create_subscription(
DetectionResultArray,
'/detections',
self.listener_callback,
10)

def listener_callback(self, msg: DetectionResultArray):
self.get_logger().info(f"Frame: {msg.header.frame_id}")
for det in msg.results:
self.get_logger().info(
f"[{det.label}] ({det.x_min},{det.y_min}) "
f"{det.width}x{det.height} conf={det.conf:.2f}"
)

def main(args=None):
rclpy.init(args=args)
node = DetectionSubscriber()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()

main()

infer_img.launch.py Parameters

ParameterDescriptionDefault
config_pathPath to the model configuration file used for inferenceconfig/detection/yolov6.yaml
img_pathPath to the image file to run inference ondata/detection/test.jpg
publish_result_imgWhether to publish the detection result as an image messagefalse
result_img_topicTopic name for rendered image output (only valid if publish_result_img = true)/result_img
result_topicTopic name for the inference result message/inference_result

Video Stream Inference

Start the Camera (USB Example)

ros2 launch br_sensors usb_cam.launch.py video_device:="/dev/video20"

Option 1: Inference with Web Visualization

Terminal 1 — Run inference:

ros2 launch br_perception infer_video.launch.py \
config_path:='config/detection/yolov6.yaml' \
sub_image_topic:='/image_raw' \
publish_result_img:=true \
result_topic:='/inference_result'

Terminal 2 — Start WebSocket visualization:

ros2 launch br_visualization websocket_cpp.launch.py image_topic:='/result_img'

Terminal prompts access address:

...
Please visit in your browser http://<IP>:8080
...

Open a browser at http://<IP>:8080 to view the inference results in real time:

Option 2: Inference without Visualization (Data Only)

To obtain raw inference results without visualization, execute:

ros2 launch br_perception infer_video.launch.py \
config_path:='config/detection/yolov6.yaml' \
sub_image_topic:='/image_raw' \
publish_result_img:=false \
result_topic:='/inference_result'

Obtain data through the ros2 topic echo /inference_result topic.

Message Subscription and Viewing

Print /inference_result topic data

ros2 topic echo /inference_result
header:
stamp:
sec: 1745571001
nanosec: 597668762
frame_id: camera_link
results:
- x_min: 109
y_min: 99
width: 149
height: 316
label: bottle
conf: 0.4000000059604645
- x_min: 270
y_min: 209
width: 150
height: 249
label: bottle
conf: 0.4000000059604645
- x_min: -1
y_min: 424
width: 326
height: 55
label: sink
conf: 0.30000001192092896
---

To check message format:

ros2 interface show jobot_ai_msgs/msg/DetectionResultArray

Structure:

bianbu@bianbu:~$ ros2 interface show jobot_ai_msgs/msg/DetectionResultArray
std_msgs/Header header
builtin_interfaces/Time stamp
int32 sec
uint32 nanosec
string frame_id

jobot_ai_msgs/DetectionResult[] results
int32 x_min
int32 y_min
int32 width
int32 height
string label
float32 conf

Python Subscription Example

from rclpy.node import Node
from std_msgs.msg import Header
from jobot_ai_msgs.msg import DetectionResultArray, DetectionResult
import rclpy

class DetectionSubscriber(Node):
def __init__(self):
super().__init__('detection_sub')
self.subscription = self.create_subscription(
DetectionResultArray,
'/inference_result',
self.listener_callback,
10)

def listener_callback(self, msg: DetectionResultArray):
self.get_logger().info(f"Frame: {msg.header.frame_id}")
for det in msg.results:
self.get_logger().info(
f"[{det.label}] ({det.x_min},{det.y_min}) "
f"{det.width}x{det.height} conf={det.conf:.2f}"
)

def main(args=None):
rclpy.init(args=args)
node = DetectionSubscriber()
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()

main()

infer_video.launch.py Parameters

Parameter NameDescriptionDefault Value
config_pathPath to the model configuration file for inferenceconfig/detection/yolov6.yaml
sub_image_topicImage topic to subscribe to/image_raw
publish_result_imgWhether to publish the rendered inference imagefalse
result_img_topicImage topic to publish (only when publish_result_img=true)/result_img
result_topicInference-result topic to publish/inference_result