Lesson 1: Teaching Your Robot to See
Learning Objective
Connect to your BonicBot, turn on its camera, and get the robot to detect and report one object it currently sees using the built-in YOLO detector.
Introduction
Every BonicBot program starts the same way: connect to the robot, wake up its camera, and ask it what it sees. In this lesson, you’ll write the shortest possible program that does exactly that — using the robot’s built-in YOLO object detector, a computer vision model that recognizes 80 everyday object categories like “person,” “chair,” and “bottle,” without you training anything yourself. Object detection is the foundation of almost every CV project in this course: robots that follow people, sort objects, or react to their surroundings all start with a call like the one you’re about to make. By the end of this lesson, your BonicBot will look at the world and hand you back one concrete piece of information about what’s in front of it.
Setup: Installing Packages
Before running the code, make sure your computer has the required Python packages installed. Open a terminal and run:
pip install bonicbot-bridge opencv-python numpyWhat each package does
| Package | Purpose |
|---|---|
bonicbot-bridge | The official BonicBot SDK. Provides the BonicBot class used to connect to the robot, control its camera, and read detection results. |
opencv-python | Imported as cv2. Used here to draw bounding boxes on frames, display the live video window, and listen for the q keypress to quit. |
numpy | Imported as np. Used for array/image handling that opencv-python and bonicbot-bridge rely on internally. |
If pip install fails, try pip3 install ... instead, or use a virtual environment:
python3 -m venv bonicbot-env
source bonicbot-env/bin/activate # On Windows: bonicbot-env\Scripts\activate
pip install bonicbot-bridge opencv-python numpyDon’t have a physical BonicBot? Try it in simulation (optional)
-
Launch the BonicBot simulation:
ros2 launch my_bot robot_system.launch.py use_sim_time:=true world:=obsworld.sdf use_real_camera:=Trueuse_real_camera:=True— the camera feed comes from your laptop’s webcam, while the robot’s body and motion are still simulated. This is the easiest way to test detection, since you can just hold whatever you want detected up to your laptop camera.use_real_camera:=False— the camera feed comes from Gazebo instead (i.e. whatever the simulated camera sees inside the simulated world).
-
Once the simulation is running, use
localhostas the host instead of a physical IP address:with BonicBot(host="localhost") as bot: -
Everything else in the code — enabling face detection, drawing boxes, reading
bot.get_faces()— works the same way, since the simulation exposes the same interface as a real robot. This path is mainly useful for exploring the lesson without hardware on hand; if you have a real BonicBot, connecting to its actual IP address is the recommended way to go through this lesson.
Code
Click to view the complete program
import time
import cv2
import numpy as np
from bonicbot_bridge import BonicBot
def draw_detections(frame, detections):
h_px, w_px = frame.shape[:2]
for det in detections:
bbox = det.get("bbox")
if not bbox or len(bbox) < 4: continue
# Pipeline publishes normalised [cx, cy, w, h] (0-1)
cx_n, cy_n, bw_n, bh_n = bbox
x1 = max(0, int((cx_n - bw_n / 2) * w_px))
y1 = max(0, int((cy_n - bh_n / 2) * h_px))
x2 = min(w_px - 1, int((cx_n + bw_n / 2) * w_px))
y2 = min(h_px - 1, int((cy_n + bh_n / 2) * h_px))
cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
label = f"{det.get('class', '?')} {det.get('confidence', 0.0):.0%}"
cv2.putText(frame, label, (x1 + 2, y1 - 4), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2)
return frame
with BonicBot(host='[IP_ADDRESS]') as bot:
print("📷 Starting camera and streaming...")
bot.system.start_camera()
bot.start_camera()
bot.camera.wait_for_image(timeout=5.0)
print("🚀 Starting YOLO detection mode...")
bot.enable_detection("yolo")
# Wait for RPi to load the model and confirm YOLO is active
while not bot.vision.yolo_enabled:
time.sleep(0.1)
print("✅ Live Stream Active. Press 'q' to quit.")
while True:
frame = bot.get_image()
if frame is not None:
detections = bot.vision.get_detections()
display = draw_detections(frame.copy(), detections)
cv2.imshow("BonicBot Vision", display)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
bot.disable_detection()
bot.stop_camera()
bot.system.stop_camera()Replace [IP_ADDRESS] with the IP address of your own BonicBot. Every BonicBot may have a different IP address depending on your network configuration.
📌 Example:
with BonicBot(host="172.20.10.2") as bot:Replace 172.20.10.2 with the IP address assigned to your BonicBot.
Code Walkthrough
Line-by-line explanation
- Imports —
timeis used to pause briefly while waiting for YOLO to load;cv2(OpenCV) handles drawing and displaying video;numpysupports the underlying image arrays;BonicBotis the SDK class that talks to the robot. draw_detections(frame, detections)— Takes a raw camera frame and the list of detections, then draws a green rectangle and a text label (class name + confidence percentage) for each detected object. Coordinates arrive normalized (0–1), so the function converts them to actual pixel positions using the frame’s width and height before drawing.with BonicBot(host='[IP_ADDRESS]') as bot:— Opens a connection to your robot. Usingwithensures the connection is cleanly closed even if the program crashes or is interrupted.bot.system.start_camera()/bot.start_camera()/bot.camera.wait_for_image(timeout=5.0)— Purpose of this block: Initializes the robot connection context, powers on the camera hardware, starts streaming live video frames over the bridge, and waits up to 5 seconds for the first valid image frame before moving forward.bot.enable_detection("yolo")— Tells the robot to load and activate its YOLO object-detection model.while not bot.vision.yolo_enabled: time.sleep(0.1)— Loops in short 0.1-second waits until the robot confirms the YOLO model has finished loading and is active.- Main
while Trueloop — Continuously grabs the latest frame, fetches the current list of detections, draws boxes/labels on a copy of the frame, and shows it in a window named “BonicBot Vision”. cv2.waitKey(1) & 0xFF == ord("q")— Checks every loop iteration whether theqkey was pressed; if so, the loop breaks and the program moves to cleanup.- Cleanup (
cv2.destroyAllWindows(),bot.disable_detection(),bot.stop_camera(),bot.system.stop_camera()) — Closes the video window, turns off YOLO detection, and shuts down the camera stream so the robot isn’t left running unnecessarily.
Expected Output
Click to see expected output
Visual Output:
Terminal Output:
{'class': 'person', 'confidence': 0.91, 'bbox': [0.52, 0.44, 0.18, 0.36]}Your exact numbers will differ based on what’s in front of the camera. bbox is normalized [cx, cy, w, h] (0–1 range), so [0.52, 0.44, 0.18, 0.36] means roughly centered, slightly right and up. If nothing is in frame, you’ll see Nothing detected yet instead.
🔧 Under the Hood
How does object detection actually work?
YOLO (“You Only Look Once”) is a family of models that scan an entire image in a single pass through a convolutional neural network, rather than sliding a window across the image and checking each spot separately. The network divides the image into a grid, and for each grid cell it predicts candidate bounding boxes, a confidence score, and class probabilities all at once. Overlapping boxes for the same object are then cleaned up with a step called non-max suppression, which keeps only the highest-confidence box and discards duplicates. YOLO models are typically trained on the COCO dataset, which is where the 80 object classes come from.
This explains the general, publicly documented YOLO architecture — it does not describe bonicbot-bridge’s exact internal pipeline (thresholds, pre/post-processing choices, etc.), which isn’t publicly documented beyond the model='yolov8n' default parameter you can pass to enable_detection().
Student Challenge
bot.get_detections() accepts an optional class_filter argument. Change the code to bot.get_detections(class_filter='person') so the robot only reports people, ignoring every other object class. Run it while pointing the camera at different objects — when does the printed message change?
Hint
class_filter takes a string matching one of the 80 COCO class names — try 'person', 'chair', or 'bottle'.
Reflection Question
Your program printed a confidence score along with the detected class. Why might a robot need a confidence number instead of a simple yes/no answer for “is this a person”? What could go wrong if the robot acted on every detection, even ones with very low confidence?