Lesson 25 - Servo Feedback & Safety Limits
Learning Objective
- Query hardware-enforced min/max joint angle bounds using
bot.get_servo_limits(). - Retrieve real-time joint angle feedback reported by physical servos using
bot.get_servo_angles(). - Command single servos directly by joint name using
bot.set_single_servo()andbot.get_single_servo().
Introduction
Physical servos have strict mechanical travel bounds (e.g. neck yaw limits between -90° and +90°). Commanding angles outside these limits can cause gear strain or electrical stalls.
This lesson covers querying hardware safety limits, reading live joint angle feedback, and setting individual servos safely.
Code
import time
from bonicbot_bridge import BonicBot
with BonicBot(host="192.168.29.52") as bot:
print("1. Fetching hardware servo limits dictionary...")
limits = bot.get_servo_limits()
for joint_name, (min_deg, max_deg) in limits.items():
print(f" - {joint_name}: [{min_deg}°, {max_deg}°]")
print("\n2. Setting single servo 'neck_yaw' to 30.0°...")
bot.set_single_servo("neck_yaw", 30.0)
time.sleep(2)
print("\n3. Querying single servo angle feedback...")
current_neck = bot.get_single_servo("neck_yaw")
print(f"Current 'neck_yaw' angle: {current_neck}°")
print("\n4. Reading live dictionary of all active joint angles...")
all_angles = bot.get_servo_angles()
print("All joint angles:", all_angles)
print("\n5. Resetting all servos to neutral 0° positions...")
bot.reset_servos()Expected Output
Click to see expected output
Visual Output
Terminal Output
🤖 Connected to BonicBot at 192.168.29.52:9090
1. Fetching hardware servo limits dictionary...
- left_shoulder: [-45.0°, 180.0°]
- left_elbow: [0.0°, 50.0°]
- right_shoulder: [-45.0°, 180.0°]
- right_elbow: [0.0°, 50.0°]
- left_gripper: [-45.0°, 60.0°]
- right_gripper: [-45.0°, 60.0°]
- neck_yaw: [-90.0°, 90.0°]
2. Setting single servo 'neck_yaw' to 30.0°...
3. Querying single servo angle feedback...
Current 'neck_yaw' angle: 30.0°
4. Reading live dictionary of all active joint angles...
All joint angles: {'neck_yaw': 30.0, 'left_shoulder': 0.0, 'left_elbow': 0.0, ...}
5. Resetting all servos to neutral 0° positions...
🔌 Disconnected from BonicBot🔧 Under the Hood
How _validate_angle() protects servos in servo.py
_validate_angle() protects servos in servo.pyServoController checks requested target angles against SERVO_LIMITS before publishing:
def _validate_angle(self, joint_name, angle):
if joint_name not in SERVO_LIMITS:
raise ServoError(f"Unknown servo joint: {joint_name}")
min_angle, max_angle = SERVO_LIMITS[joint_name]
if angle < min_angle or angle > max_angle:
print(f"⚠️ Angle {angle}° outside limits [{min_angle}°, {max_angle}°], rejecting")
return None
return angleOut-of-bound target commands are safely rejected before reaching hardware.
Student Challenge
Challenge 1 — Python Only
Write a safety validation function validate_joint_command(joint_name, requested_angle, limits_dict) that returns True if valid or raises ValueError if out of bounds.
Click to see solution
def validate_joint_command(joint_name, requested_angle, limits_dict):
if joint_name not in limits_dict:
raise ValueError(f"Unknown joint '{joint_name}'")
min_deg, max_deg = limits_dict[joint_name]
if not (min_deg <= requested_angle <= max_deg):
raise ValueError(f"Angle {requested_angle}° out of bounds [{min_deg}, {max_deg}] for {joint_name}")
return True
limits = {'neck_yaw': (-90.0, 90.0)}
print("Validation result:", validate_joint_command('neck_yaw', 45.0, limits))Challenge 2 — Robot
Write a script that queries get_servo_angles(), verifies that every active joint angle is currently inside its hardware limit bounds, and prints a safety diagnostic summary.
Click to see solution
from bonicbot_bridge import BonicBot
with BonicBot(host="192.168.0.188") as bot:
limits = bot.get_servo_limits()
angles = bot.get_servo_angles()
print("--- Servo Joint Health Audit ---")
for joint, current_angle in angles.items():
if joint in limits:
min_lim, max_lim = limits[joint]
status = "✅ OK" if min_lim <= current_angle <= max_lim else "⚠️ OUT OF BOUNDS"
print(f"{joint}: {current_angle:.1f}° [{min_lim}°, {max_lim}°] -> {status}")Quick Reference
Feedback & Limits Methods
| Method | Return Type | Description |
|---|---|---|
bot.get_servo_limits() | dict | Dictionary mapping joint_name: (min_deg, max_deg) |
bot.get_servo_angles() | dict | Live dictionary mapping joint_name: current_deg |
bot.set_single_servo(joint, angle) | bool | Command single joint by name in degrees |
bot.get_single_servo(joint) | float | Retrieve current angle of single joint |
Reflection Questions
Why is write-path validation (rejecting out-of-bounds commands before publishing) crucial in physical robotics APIs?
Why might live feedback from get_servo_angles() slightly differ from the commanded target angle during active motion?