Runtime Configuration
This page documents the steering-servo constants in code/controller/current/rc_car_app/config.py and how code/controller/current/rc_car_app/hardware.py turns a logical steering command into a real PCA9685 Servo Controller pulse. Logical steering is 0 = left, 90 = center, 180 = right; the servo is on the PCA9685 Servo Controller at I2C 0x40, channel 0, running at 50 Hz.
config.py is also the central source for subsystem flags, GPIO assignments, LiDAR thresholds, dashboard transport, controller indices, model defaults, and CSV settings. Values below describe the checked-in branch; environment variables and local tuning files can override selected settings.
How It Works
The servo is driven through the Adafruit ServoKit on the PCA9685. config.py sets the electrical envelope and the center trim, and hardware.py's PCA9685SteeringServo applies them.
| Constant | Value | Meaning |
|---|---|---|
PCA9685_FREQUENCY_HZ |
50 |
Servo PWM frequency |
STEERING_SERVO_MIN_PULSE_US |
1000 |
Pulse width at the 0 (full-left) end |
STEERING_SERVO_MAX_PULSE_US |
2000 |
Pulse width at the 180 (full-right) end |
STEERING_SERVO_ACTUATION_RANGE_DEG |
180 |
Full logical/servo travel; center is 90 |
STEERING_SERVO_REFERENCE_LEFT_LIMIT_DEG |
48.812 |
Reference full-left angle before center trim |
STEERING_SERVO_REFERENCE_RIGHT_LIMIT_DEG |
131.188 |
Reference full-right angle before center trim |
STEERING_SERVO_CENTER_OFFSET |
0.188888... |
Checked-in +17-degree center trim; environment or saved tuning can override it |
STEERING_SERVO_CENTER_PRELOAD |
0.0 |
Near-center preload is disabled |
STEERING_SERVO_CENTER_PRELOAD_WINDOW |
0.0 |
Near-center preload window is disabled |
STEERING_CENTER_SNAP_DEG |
0.5 |
Logical commands less than 0.5 degrees from center snap to 90 |
The trim math lives in apply_steering_center_trim_degrees(). It clamps logical input to 0..180, maps the two halves into the characterized 48.812..131.188 reference range, adds center_offset * 90, and clamps the physical result to 0..180. The generic preload code remains available, but its value and window are both zero. With the checked-in +17-degree trim, logical center 90 writes physical angle 107; the two logical endpoints write approximately 65.812 and 148.188. The model and CSV labels remain in logical 0..180 units.
Why This Choice
The project has observed direction-dependent steering return and left-right asymmetry. Isolating the reference range and center trim lets those mappings be tuned without changing the model or its labels. RC_CAR_STEERING_SERVO_CENTER_OFFSET and an optional steering_tune.json entry named trim_delta_deg can override the checked-in trim; no saved tuning file is checked into this branch.
Two motor-scale constants sit next to the servo block for a related reason:
LEFT_MOTOR_PWM_SCALE = 1.0andRIGHT_MOTOR_PWM_SCALE = 1.0. Both are neutral today. Changing them is one possible motor-balance experiment, not proof that an observed pull originates in the motors.
Steering-trim constants and yaw-controller gains are hardware calibration values. Record the test condition and resulting number before changing them.
Failure Symptom
If the PCA9685 or its I2C/servo dependencies are missing, PCA9685SteeringServo.__init__ raises PCA9685 servo dependencies are unavailable (or an I2C error). hardware.py retries up to four times, then falls back to a DummyServo and prints Error initializing GPIO: ... Running in simulation mode. — the wheels then never move even though steering values still update on the dashboard. On a clean boot the expected line is Using PCA9685 steering servo at 0x40, channel 0.
GPIO and Hardware Addresses
| Device | Checked-in assignment |
|---|---|
| Right motor forward/backward | BCM 19 / 20 |
| Left motor forward/backward | BCM 25 / 13 |
| Hall-effect wheel sensor | BCM 24 |
| PCA9685 Servo Controller | I2C 0x40, channel 0 |
Motor outputs use 1 kHz PWM. Both motor scale factors are currently 1.0.
Camera and Safety Flags
The Raspberry Pi Camera Module is enabled at camera index 0 with a 180-degree mounting transform. The active LiDAR center corridor has a 0.254 m half-width. Governor boundaries are 1.65 m for full reference throttle, 1.25 m for minimum moving reference throttle, and 1.05 m for emergency stop. The minimum moving reference command is 60%, mapped onto the physical motor range beginning at 55% PWM. LiDAR does not steer.
Constants that remain in the file are not automatically active. In particular, old autonomous cruise/turn PWM and camera blend values are legacy unless a current runtime call site uses them.
Dashboard and Logging
Dashboard telemetry uses UDP over USB Ethernet to 192.168.10.2:8765 at a nominal 10 Hz. The fixed USB address is the live route; Wi-Fi/mDNS is not a fallback. Linked shutdown is enabled.
Runtime CSV files are timestamped per process and written at a nominal 0.1 s interval. Rows are flushed immediately. Steering labels/logs remain logical degrees, while throttle labels use absolute physical PWM. The exact header in config.py is authoritative because columns evolve with the runtime.