Skip to content

Comments

feat: pdb servo controller#75

Merged
ConnorNeed merged 1 commit intomainfrom
camera_control
Feb 21, 2026
Merged

feat: pdb servo controller#75
ConnorNeed merged 1 commit intomainfrom
camera_control

Conversation

@ConnorNeed
Copy link
Member

YOLO merging due to SAR (All nighter number 6 of 2026 go RARR). (I may have lost my mind)

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new ROS 2 servo controller backend that drives up to 4 servos over I2C via a “PDB” device, and wires it into the servo_pkg package so it can be run as a console script.

Changes:

  • Reformats distortion coefficient serialization in the mrcal→ROS/OpenCV YAML conversion script.
  • Registers a new pdb_controller console entry point in servo_pkg.
  • Introduces pdb_controller.py implementing an I2C-based servo controller (SMBus) and adds smbus2 to Python requirements.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments.

File Description
src/Nav/aruco_localizer/scripts/convert_mrcal_to_opencv_ros.py Small formatting/parenthesization change for YAML “data” field construction.
src/HW-Devices/servo_pkg/setup.py Adds pdb_controller executable entry point for ROS 2 ros2 run.
src/HW-Devices/servo_pkg/servo_pkg/pdb_controller.py New I2C/SMBus-based servo controller node subscribing to per-servo Float32 topics and writing PWM values to hardware registers.
requirements.txt Adds smbus2 dependency (and fixes formatting of odrive line).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 30 to 36
"console_scripts": [
"USB_Servo = servo_pkg.USB_Servo:main",
"servo_client = servo_pkg.servo_client:main",
"i2c_Servo = servo_pkg.i2c_Servo:main",
"pi_Servo = servo_pkg.pi_Servo:main",
"pdb_controller = servo_pkg.pdb_controller:main",
],
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new pdb_controller executable is being added, but there is no corresponding launch file under launch/ (unlike the existing usb_servo_launch, i2c_servo_launch, and pi_servo_launch). Adding a launch file (and any needed YAML config) will make this controller discoverable and runnable in the same way as the other servo backends.

Copilot uses AI. Check for mistakes.
Comment on lines +14 to +22
def convert_from_radians(angle, servo_info):
total_range = servo_info.max - servo_info.min
return int(servo_info.min + (total_range * angle / servo_info.rom))


def convert_to_radians(value, servo_info):
total_range = servo_info.max - servo_info.min
return servo_info.rom * (value - servo_info.min) / total_range

Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The radians<->PWM conversion helpers are duplicated from servo_pkg/USB_Servo.py (same formulas). Consider moving these conversions into a shared utility module (e.g., servo_pkg/conversions.py) and importing from both controllers to avoid future drift if the mapping ever changes.

Copilot uses AI. Check for mistakes.

if not (servo_info.min <= target_value_us <= servo_info.max):
self.get_logger().warning(
f"Servo {port} input out of range. " f"Last angle: {current_position}"
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The out-of-range warning logs Last angle: {current_position} rather than the requested angle (msg.data) that was actually out of range. This makes the warning misleading when debugging; include the requested angle (and optionally the valid range) in the message.

Suggested change
f"Servo {port} input out of range. " f"Last angle: {current_position}"
f"Servo {port} input out of range. "
f"Requested angle: {msg.data} rad, "
f"valid PWM range: [{servo_info.min}, {servo_info.max}] us, "
f"last angle: {current_position}"

Copilot uses AI. Check for mistakes.
Comment on lines +91 to +93
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

main() spins the node without a try/finally, so a KeyboardInterrupt or exception can skip destroy_node() and leave the I2C bus handle open. Wrap rclpy.spin(node) in try: ... finally: to always close the SMBus and call rclpy.shutdown().

Suggested change
rclpy.spin(node)
node.destroy_node()
rclpy.shutdown()
try:
rclpy.spin(node)
finally:
node.destroy_node()
rclpy.shutdown()

Copilot uses AI. Check for mistakes.
@ConnorNeed ConnorNeed merged commit 0d04345 into main Feb 21, 2026
5 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant