Skip to Content

Python SDK

Overview

BonicBot Bridge is a Python SDK designed for educational robotics programming. It provides a high-level, intuitive API that abstracts the complexity of ROS 2 robotics into simple commands suitable for STEM education and beginners.

System Requirements:

  • Python 3.8 or higher
  • BonicBot robot running ROS 2 Humble
  • Both the robot and control computer must be on the same network
  • rosbridge_server running on port 9090 on the robot

Installation: You can easily install the SDK via pip:

pip install bonicbot-bridge

Best Practice: The Context Manager
When connecting to the robot, it’s highly recommended to use the with statement (a context manager). This ensures that the robot safely and automatically disconnects when your program finishes, even if an error occurs.

from bonicbot_bridge import BonicBot with BonicBot(host='192.168.1.100') as bot: bot.move_forward(speed=0.3, duration=2.0)

Connection & Setup

The BonicBot class handles the connection to your robot.

BonicBot(host='localhost', port=9090, timeout=10)

Parameters:

  • host (str): Robot IP address or hostname. Use 'localhost' if running directly on the robot, or the robot’s IP/hostname for remote access over the same network.
  • port (int): rosbridge_server port (default: 9090).
  • timeout (int): Connection timeout in seconds.

Examples:

# Local connection (running directly on the robot) bot = BonicBot() # Remote connection (running on a computer on the same network) bot = BonicBot(host='192.168.1.100')
  • bot.connect(timeout): Explicitly connect to the robot (this is called automatically when creating a BonicBot object).
  • bot.disconnect(): Shuts down controllers and closes the connection. Automatically called if using the with block.
  • bot.is_connected(): Returns True if the robot is currently connected.
from bonicbot_bridge import BonicBot with BonicBot(host='192.168.1.100') as bot: if bot.is_connected(): print("Connected successfully!")

Basic Movement

You can control the robot’s movement easily with built-in commands. When providing a duration to these methods, they are ⏳ Blocking, meaning your program will wait until the movement finishes before going to the next line. If no duration is provided, the robot will move continuously until stop() is called.

  • bot.move(linear_x, linear_y, angular_z): Direct continuous velocity control (m/s and rad/s).
  • bot.move_forward(speed, duration): Move forward at a set speed (m/s) for a set duration (seconds).
  • bot.move_backward(speed, duration): Move backward.
  • bot.turn_left(speed, duration): Turn left (counter-clockwise) at a given rotational speed (rad/s).
  • bot.turn_right(speed, duration): Turn right (clockwise).
  • bot.stop(): Stop all movement immediately.
  • bot.is_moving(): Returns True if the robot is currently moving towards a navigation goal.

Note: For turn functions, speed is measured in radians per second (rad/s), not degrees!

from bonicbot_bridge import BonicBot with BonicBot(host='192.168.1.100') as bot: bot.move_forward(speed=0.3, duration=2.0) bot.turn_left(speed=0.5, duration=1.0) bot.move_backward(speed=0.2, duration=1.5) bot.stop()

Precise Motion

Use precise motion when you need high-accuracy movement, utilizing closed-loop odometry or the nav2 engine.

Note: Distance commands have a safety guard (MAX_PRECISE_DISTANCE = 10.0 meters). Exceeding it raises a PreciseMotionError.

Engines:

  • bot.precise.set_default_engine(engine): Choose 'internal' (odometry feedback) or 'nav2' (action server).

Single & Compound Commands:

  • bot.drive_distance(dist, speed, engine, timeout): Drive an exact distance. ⏳ Blocking.
  • bot.precise.rotate_angle(angle, speed, engine, timeout): Rotate an exact angle. ⏳ Blocking.
  • bot.precise.drive_and_rotate(dist, angle, speed, turn_speed): Drive then rotate sequentially. ⏳ Blocking.
  • bot.precise.draw_square(side_m, speed, turn_speed): Drive in a square pattern.

Queue-Based Sequences: Queue a sequence of movements to run autonomously.

  • bot.precise.enqueue_move(cmd_list): Push motion commands. Example: [{'type': 'drive', 'value': 1.0}, {'type': 'rotate', 'value': 90.0}].
  • bot.precise.run_queue(block): Execute the queued commands. If block=True, it is ⏳ Blocking.
  • bot.precise.clear_queue(): Flush pending commands and abort the current sequence.
  • bot.precise.is_precise_moving(): Check if a precise motion command is currently running.
from bonicbot_bridge import BonicBot with BonicBot(host='192.168.1.100') as bot: bot.precise.set_default_engine('internal') # Queue up a shape bot.precise.enqueue_move([ {'type': 'drive', 'value': 1.0, 'speed': 0.3}, {'type': 'rotate', 'value': 90.0, 'speed': 45.0}, {'type': 'drive', 'value': 1.0, 'speed': 0.3} ]) print("Executing queued sequence...") bot.precise.run_queue(block=True)

Reading Sensors

Access the robot’s position, battery level, and odometry data.

  • bot.get_position(): Returns a dictionary with the robot’s current position (x, y, and theta in degrees).
  • bot.get_x(): Returns the X position in meters.
  • bot.get_y(): Returns the Y position in meters.
  • bot.get_heading(): Returns the heading orientation in degrees (0-360).
  • bot.sensors.get_distance_traveled(start_pos): Calculates distance traveled from a provided starting position dictionary ({'x': float, 'y': float}).
  • bot.get_battery(): Returns the battery percentage.
    Note: Currently returns a placeholder of 85.0 as hardware integration is pending.
  • bot.sensors.wait_for_data(timeout): Wait until sensor data becomes available. This is ⏳ Blocking until data is received or the timeout expires.
  • bot.sensors.get_sensor_info(): Get a summary dictionary of all available sensor data states.

Live Subscriptions (Advanced): You can run functions automatically in the background whenever new data arrives.

  • bot.sensors.subscribe_to_position(callback): Pass a function callback(x, y, theta) that will be called on every position update.
from bonicbot_bridge import BonicBot with BonicBot(host='192.168.1.100') as bot: if bot.sensors.wait_for_data(timeout=5.0): # Save start position start = bot.get_position() bot.move_forward(0.3, duration=2.0) # Calculate distance dist = bot.sensors.get_distance_traveled(start) print(f"Traveled {dist:.2f} meters!")

Servo & Arm Control

Control the physical arms, grippers, and head of BonicBot. By default, arm movement functions are ⏳ Blocking and will wait for the physical move to finish.

Hardware Joint Limits (in degrees):

  • Shoulder pitch: -45° to 180°
  • Elbow: 0° to 50°
  • Gripper: -28.6° to 60° (or -45° to 60° for single finger)
  • Neck yaw: -90° to 90°

Basic Arm & Head Control:

  • bot.move_left_arm(shoulder, elbow, wait): Set left arm angles.
  • bot.move_right_arm(shoulder, elbow, wait): Set right arm angles.
  • bot.set_neck(yaw): Set neck rotation (-90° is right, 90° is left).
  • bot.look_left() / bot.look_right() / bot.look_center(): Easy neck shortcuts.

Grippers:

  • bot.open_grippers() / bot.close_grippers(): Quick toggles for both grippers.
  • bot.set_grippers(left, right): Set exact angles for both grippers.
  • bot.servo.set_left_gripper(angle) / bot.servo.set_right_gripper(angle): Control a single gripper individually.

Advanced Joint Operations:

  • bot.servo.set_single_servo(joint_name, angle): Set any specific servo’s angle by its name.
  • bot.servo.get_single_servo(joint_name): Get a specific servo’s current angle.
  • bot.servo.get_servo_angles(): Retrieve a dictionary of all current joint angles.
  • bot.servo.get_servo_limits(): Retrieve a dictionary of (min, max) tuples for every joint.
  • bot.servo.reset_all_servos(): Reset all servos back to 0°.
  • bot.servo.close_grippers(): Closes both left and right grippers and returns a boolean indicating success.
  • bot.servo.look_right(): Turns the neck to look fully to the right and returns a boolean indicating success.
  • bot.servo.look_center(): Centers the robot’s neck and returns a boolean indicating success.
  • bot.servo.set_servo_angles(angles): Legacy method to set multiple servo angles using a dictionary of joint names and values.
from bonicbot_bridge import BonicBot with BonicBot(host='192.168.1.100') as bot: bot.look_left() bot.open_grippers() # Advanced: Get current limits to make sure we are safe limits = bot.servo.get_servo_limits() print(f"Left shoulder limits: {limits.get('left_shoulder_pitch_joint')}") bot.servo.reset_all_servos()

Mapping & Navigation

BonicBot can map its environment using SLAM and navigate autonomously.

Mapping:

  • bot.start_mapping(): Begin building a new map using SLAM.
  • bot.save_map(): Save the generated map.
  • bot.stop_mapping(): Turn off mapping.

Navigation & Goals:

  • bot.start_navigation(force): Start the navigation system.
    Note: This returns False if mapping is active unless force=True is provided. If you want to explore and map simultaneously, use setup_for_exploration() instead.
  • bot.stop_navigation(): Turn off navigation.
  • bot.go_to(x, y, theta): Navigate to an x, y coordinate with theta orientation.
  • bot.wait_for_goal(timeout): Wait until the robot reaches its destination. This is ⏳ Blocking.
  • bot.cancel_goal(): Stop navigating to the current goal.
  • bot.set_initial_pose(x, y, theta): Required when using a saved map to tell the robot where it starts.

System Status & Locations:

  • bot.get_nav_status(): Returns current status (e.g., 'idle', 'navigating', 'goal_reached').
  • bot.get_distance_to_goal(): Returns remaining distance to the goal in meters.
  • bot.get_system_status(): Get a complete status dictionary of the robot’s systems.
  • bot.system.get_robot_state(): Get the current robot state string.
  • bot.system.save_location(name): Save the robot’s current pose as a named location.
  • bot.system.goto_location(name): Command the robot to navigate to a saved location name.
  • bot.system.delete_location(name): Remove a saved location.

Map Data Access:

  • bot.system.has_saved_map(): Check if a map file is saved on the robot’s disk.
  • bot.system.get_map_info(): Get metadata about the map (resolution, width, height).
  • bot.system.get_map_data(): Get the full cached OccupancyGrid data array.
  • bot.motion.subscribe_to_nav_status(callback): Subscribes a callback function to receive real-time updates on the navigation status.
  • bot.motion.subscribe_to_distance_to_goal(callback): Subscribes a callback function to receive real-time updates on the remaining distance to the goal.
  • bot.system.is_mapping(): Checks if the robot is currently mapping the environment and returns a boolean.
  • bot.system.is_navigating(): Checks if the navigation system is active and returns a boolean.
  • bot.system.setup_for_mapping(): Sets up the robot for mapping operations and returns a boolean indicating success.
  • bot.system.setup_for_navigation(): Sets up the robot for autonomous navigation and returns a boolean indicating success.
  • bot.system.delete_all_locations(): Removes all saved named locations from the system.
  • bot.system.subscribe_to_map(callback, throttle_rate): Subscribes a callback function to receive live map updates at a specified throttle rate.
  • bot.system.subscribe_to_odom(callback, throttle_rate): Subscribes a callback function to receive live odometry data at a specified throttle rate.
  • bot.system.subscribe_to_robot_state(callback): Subscribes a callback function to receive changes in the robot’s state string.
  • bot.system.subscribe_to_mapping_active(callback): Subscribes a callback function to receive boolean updates when mapping starts or stops.
  • bot.system.subscribe_to_navigation_active(callback): Subscribes a callback function to receive boolean updates when navigation starts or stops.
  • bot.system.subscribe_to_current_goal(callback, throttle_rate): Subscribes a callback function to receive updates on the robot’s current navigation goal coordinates.
  • bot.system.subscribe_to_locations_list(callback, throttle_rate): Subscribes a callback function to receive the list of saved named locations whenever it changes.
  • bot.system.subscribe_to_map_available(callback): Subscribes a callback function to receive boolean updates when a saved map becomes available.

Live Subscriptions (Advanced Dashboarding): The bot.system controller includes many background callback subscribers: subscribe_to_map(), subscribe_to_odom(), subscribe_to_robot_state(), subscribe_to_navigation_active(), subscribe_to_current_goal(), and subscribe_to_locations_list().

from bonicbot_bridge import BonicBot with BonicBot(host='192.168.1.100') as bot: bot.start_mapping() bot.start_navigation(force=True) bot.go_to(x=1.5, y=2.0, theta=90) # Monitor the goal progress while bot.get_nav_status() == 'navigating': dist = bot.get_distance_to_goal() print(f"Distance remaining: {dist:.2f}m") bot.system.save_location("kitchen") bot.save_map()

Autonomous Exploration

Let BonicBot map the room all by itself using the explore_lite frontier exploration package.

Note: Exploration bypasses bot.start_navigation() because it requires SLAM and Nav2 to boot concurrently.

Exploration Setup & Control:

  • bot.setup_for_exploration(progress_callback): Boots SLAM, Nav2, and explore_lite safely. ⏳ Blocking (~90s on first boot).
  • bot.explore.start_explore(): Manually publish a resume signal to explore_lite.
  • bot.explore.stop_explore(): Pauses exploration and kills the node.
  • bot.explore.is_exploring(): Returns True if exploration is actively hunting frontiers.
  • bot.explore.wait_for_map_complete(min_area, timeout): Wait until the target area is mapped. ⏳ Blocking.

Monitoring & Intervention:

  • bot.explore.suspend_for_manual_control(): Pauses exploration so you can take over with joystick or movement commands.
  • bot.explore.resume_from_manual_control(): Restarts exploration without re-running the full 90s setup process.
  • bot.explore.diagnostics(): Returns a dictionary of the exploration stack’s internal state for debugging.
  • bot.explore.set_lifecycle_callback(callback): Attaches a callback function to listen to exploration lifecycle events.
from bonicbot_bridge import BonicBot with BonicBot(host='192.168.29.52') as bot: bot.setup_for_exploration() bot.explore.wait_for_map_complete(timeout=300.0)

Camera

Stream video and take pictures using the robot’s camera.

Note: Images fetched via get_latest_image() will be in BGR color format if you have OpenCV installed, and RGB format if you are using Pillow.

  • bot.start_camera(): Starts the camera hardware and the data stream.
  • bot.camera.get_latest_image(): Grabs the most recent image frame.
  • bot.camera.save_image(filepath): Saves the current frame to a local file.
  • bot.camera.wait_for_image(timeout): Wait for the first image to arrive. This is ⏳ Blocking.
  • bot.camera.get_camera_info(): Get camera metadata dictionary (width, height, distortion_model).
  • bot.camera.is_streaming(): Returns True if the camera stream is currently active.
  • bot.system.stop_camera(): Stops the camera system hardware and returns a boolean indicating success.
  • bot.system.is_camera_active(): Checks if the camera system hardware is currently active and returns a boolean.
  • bot.camera.start_streaming(callback, throttle_ms): Starts camera streaming with an optional callback function for each frame.
  • bot.camera.stop_streaming(): Stops the camera stream from receiving frames and returns a boolean indicating success.
from bonicbot_bridge import BonicBot with BonicBot(host='192.168.1.100') as bot: bot.start_camera() if bot.camera.wait_for_image(timeout=5.0): info = bot.camera.get_camera_info() print(f"Camera Info: {info}") bot.camera.save_image("my_robot_photo.jpg") bot.system.stop_camera()

Vision & Detection

[!NOTE] Architecture: All vision inference runs on the robot’s onboard vision_pipeline.py ROS 2 node. The bridge SDK simply sends configuration commands and receives detection results — no local models or GPU required on your machine.

ℹ️ Setup: To use vision features, the physical camera hardware must be turned on first before enabling a detection mode. If you also want to see the live video on your computer, you must start the camera stream.

bot.system.start_camera() # 1. Turn on the physical camera hardware bot.start_camera() # 2. (Optional) Start receiving image frames to your computer bot.enable_detection('yolo') # 3. Start the vision pipeline

BonicBot has powerful AI computer vision capabilities. The vision pipeline uses string literals to define all available pipeline modes.

ModeDetects
'yolo'YOLO object detection (80 COCO classes: person, bottle, chair, etc.)
'face'Human faces with 5 facial landmarks
'pose'33 MediaPipe body pose landmarks
'gesture'Hand gestures (Thumb_Up, Open_Palm, Victory, etc.)
'aruco'ArUco fiducial markers with full pose

Enabling Pipelines:

  • bot.enable_detection(mode, model='yolov8n'): Turn on a vision mode using one of the string names above.
  • bot.disable_detection(): Turn the vision pipeline off.
  • bot.get_active_mode(): Return the currently active vision mode string.

Accessing Data:

  • bot.get_detections(class_filter=None): Get the latest list of detected objects.
  • bot.get_faces(): Get the latest face data.
  • bot.get_pose_keypoints(): Get raw MediaPipe 33 body pose landmarks.
  • bot.get_gesture(): Get the current detected hand gesture name.
  • bot.get_gesture_full(): Get full gesture data including hand landmarks.
  • bot.get_aruco_markers(): Get the latest list of detected ArUco marker IDs (integers).
  • bot.get_nearest_person(): Retrieves bounding box data for the person detected nearest to the camera.

Waiting for Objects:

  • bot.wait_for_detection(target_class, timeout): Wait for a specific object class to appear. ⏳ Blocking.
  • bot.wait_for_marker(marker_id, timeout): Wait for a specific ArUco tag ID to appear. ⏳ Blocking.
  • bot.wait_for_face(timeout): Wait for any face to appear. ⏳ Blocking.
  • bot.wait_for_pose(timeout): Wait for pose keypoints to appear. ⏳ Blocking.
  • bot.wait_for_gesture(gesture_name, timeout): Wait for a specific gesture. ⏳ Blocking.

Status Properties & Low-level Control (bot.vision.*): Check if specific detectors are currently active on the robot using properties on bot.vision:

  • bot.vision.vision_active: True if the vision pipeline is running.
  • bot.vision.yolo_enabled, bot.vision.pose_enabled, bot.vision.face_enabled, bot.vision.gesture_enabled, bot.vision.aruco_enabled
  • bot.vision.is_any_detection_active: True if at least one detector is currently active.
  • bot.vision.is_subscribed: True if data-topic subscriptions are open.

These lower-level methods let you control the vision pipeline lifecycle directly:

  • bot.vision.start_vision() / bot.vision.stop_vision(): Start or stop the entire robot-side vision pipeline.
  • bot.vision.enable_detector(detector) / bot.vision.disable_detector(detector): Enable or disable a single detector by name.
  • bot.vision.toggle_detector(detector, enable): Convenience wrapper to enable or disable a detector.
  • bot.vision.subscribe_to_vision_pipeline() / bot.vision.unsubscribe_from_vision_pipeline(): Manually open or close data-topic subscriptions.
  • bot.vision.get_active_detectors(): Return a list of all currently active detector names.
from bonicbot_bridge import BonicBot with BonicBot(host='192.168.1.100') as bot: # 1. Turn on the physical camera! bot.system.start_camera() # 2. Enable pose detection bot.enable_detection('pose') print("Strike a pose!") while True: keypoints = bot.get_pose_keypoints() # MediaPipe returns 33 landmarks, let's check for the nose (index 0) if keypoints and len(keypoints) > 0: print("I can see you!") break bot.disable_detection() bot.system.stop_camera()

Error Handling

When writing robust code, it’s good practice to catch errors (exceptions). All exceptions in the BonicBot Bridge SDK inherit from a base BonicBotError.

Here is the complete exception hierarchy:

ExceptionDescription
BonicBotErrorThe base error for everything.
ConnectionErrorCould not connect to the robot’s WebSocket.
NavigationErrorNav2 failed, an obstacle blocked the path, or a timeout occurred.
PreciseMotionErrorA precise motion guard limit was violated (like exceeding 2.0 meters) or timed out.
SystemControlErrorEssential ROS services like mapping or navigation failed to boot.
VisionErrorGeneral failure in the vision processing pipeline.
ServoErrorA servo or joint command failed to execute.
CameraErrorA camera operation failed, such as failing to start the stream.
SensorErrorA sensor read or subscription failed.
ExploreErrorGeneral failure in the autonomous exploration stack.
ExploreTimeoutErrorExploration setup or wait ran out of time before reaching its target.

Example:

from bonicbot_bridge import BonicBot from bonicbot_bridge.exceptions import BonicBotError, ConnectionError, NavigationError try: with BonicBot(host='192.168.1.100', timeout=5.0) as bot: bot.start_navigation() bot.go_to(x=5.0, y=5.0) bot.wait_for_goal(timeout=30.0) except ConnectionError: print("Could not connect. Is the robot turned on and connected to Wi-Fi?") except NavigationError as e: print(f"Failed to reach destination: {e}") except BonicBotError as e: print(f"A general robot error occurred: {e}")
Last updated on