Holonomic four-wheel drive, a depth-camera hoop detector, a PID alignment loop and a three-preset flywheel shooter — split across a ROS 2 host and four Arduino Mega boards, all talking over one deliberately simple serial line.
Every command — human or autonomous — takes the same path. That single choice is what lets the same firmware serve both manual driving and vision-guided alignment.
This is not an illustration. It runs the committed PIDController, the same error
terms the detector publishes, and the pid() mixer from the drive firmware — so it
reproduces what the robot actually does, including what it gets wrong.
Click the court to drop the robot somewhere else. The cyan cone is the D455's field of view; the green arc is the setpoint it is chasing.
Leave it on As committed and the robot never settles at the right range. Switch to
Corrected mapping and the same gains converge — the difference is two lines in
pid(). What's wrong and why →
Four wheels on a cross, each with a PWM pin and a direction pin. The firmware resolves a joystick vector into one of ten discrete cases — four quadrants, four axis-aligned moves and two rotations — rather than a continuous mixing matrix.
arduino_codes/Drive_1_for_new_controllers.ino
A YOLO model (last.pt) runs on the D455 colour stream at ~30 FPS. The box centre is
sampled against the aligned depth frame over an 11×11 window, outliers trimmed, and the mean
taken as range to the hoop.
basketball_robot/realsense_basket_detector.py
Two independent PID controllers — one on horizontal pixel offset, one on range error — produce a pair of normalised values clamped to ±0.5, which the bridge injects into axes 2 and 3 of the outgoing packet.
smart_drive_bridge.py · Drive_with_auto.ino
Two counter-rotating flywheels at one of three PWM presets, a servo that flicks the ball in, and a separate motor that tilts the launch angle. Autonomous shots run a timed three-stage state machine gated on an explicit permission button.
arduino_codes/_shoot_with_auto/_shoot_with_auto.ino
Every value here is read straight out of the source — not a spec sheet. Where the two robots differ, both are listed.
Bot 1 fires at PWM 100 / 120 / 150 from a 90° servo rest position; bot 2 at 50 / 80 / 120 from 100°. The pin maps differ too — see the pin tables.
Reading all four firmware variants and six ROS nodes side by side surfaces real inconsistencies — a distance target that resolves to the wrong number, a PID that advances twice per frame, and two override buttons that latch on different indices.