New Control Modes, New Radio, Bugfixes, Lots of Work#133
Open
nickwitten wants to merge 177 commits into
Open
Conversation
…mmunication-update
…rmware into dev/joe/nora-w36-2
…rrent limits, add new PID mode for curvel control
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Merge Report: dev/nick/pose-control -> main
Overview
This merge rewrites the robot's motion control architecture from a single-mode
velocity controller into a multi-mode position/velocity/acceleration control
system. Along the way it replaces the motor control interface with closed-loop
current (torque) control, adds support for a new radio module, and reorganizes
shared libraries.
130 files changed, +15,426 / -3,133 lines.
1. Motion Control Rewrite
The body controller (
robot_controller.rs) was replaced. The oldBodyVelocityControlleraccepted a single body velocity setpoint, ran itthrough a hand-tuned constant-gain Kalman filter (CGKF) estimating 3 body
velocity states from 4 encoder + 1 gyro measurements, applied a PID, clamped
velocities and accelerations, and output wheel velocity commands. It ran at
100 Hz. The control law, state estimator, and robot kinematics were all
implemented inline in the firmware.
The new
BodyControllerdelegates state estimation and kinematics to theateam-controlslibrary (added as a submodule undercontrols/). Thecontrols library provides a
RobotModelthat contains a 6-state Kalmanfilter tracking both position and velocity in the global frame, using 8
measurements (3 vision pose + 4 encoder + 1 gyro). The firmware creates a
RobotModelat initialization and callskf_updateandkf_predicteachtick. The control loop now runs at 1 kHz (derived from
DEFAULT_CONTROL_DT= 0.001s in the controls library).
The controller accepts a
BasicControlpacket from the radio and matcheson its
SkillCommandvariant to select a control policy:GlobalPosition: bang-bang trajectory planning to target pose, with PIDfeedback. Requires active vision (200ms timeout). Position error uses
calculate_with_derivative(a new PID method that takes an externalderivative signal -- here, the twist error between trajectory and
estimate -- instead of numerically differentiating position error).
Theta wrapping uses
remainderfto stay in [-pi, pi].GlobalVelocity/LocalVelocity: bang-bang trajectory planning totarget twist, with PID feedback on the twist error between the tracked
trajectory state and the estimated twist. Local commands are rotated
into the global frame via
z_rotation_mat(theta).GlobalAcceleration/LocalAcceleration: direct passthrough -- appliesthe target acceleration through the dynamics model
A*x + B*uto getthe next state.
All policies output a
(body_twist, body_accel)pair. The controller thenapplies Coulomb friction compensation: when the commanded acceleration is
above a deadzone threshold, it uses the target twist direction to compute
the friction force (to overcome static friction during motion); when at rest,
it uses the deadzoned estimated twist (for a stable equilibrium). Friction
force is computed by the robot model and subtracted via the inverse inertia
matrix. The final body-level commands are converted to wheel velocities and
wheel torques through the robot model's kinematic transforms (which are
theta-dependent).
Both velocity and position control modes use trajectory planning to shape
the acceleration profile. The trajectory is recomputed when the target
changes or when the actual state strays too far from the tracked trajectory
state (thresholds configurable via
TRAJ_RECOMPUTE_ERROR). Trajectoryparameters (max vel/accel) can be sent from the software stack via the
command packet, falling back to defaults when all zeros.
The old parameter files (
body_vel_filter_params.rswith its hardcoded 5x3Kalman gain matrix,
body_vel_pid_params.rs,robot_physical_params.rs)are deleted. A new
controller_params.rsdefines PID gains for pose andtwist feedback, pose control gains (ff/fb weighting), trajectory recompute
thresholds, and the friction compensation deadzone. The
ParameterInterfaceimplementation now exposes the controls library's
KalmanFilterParams,RobotPhysicalParams, and individual PID/control parameters for livetuning via the parameter command protocol.
The PID controller itself gains a
reset()method and acalculate_with_derivative()method. The CGKF gains areset()methodbut is otherwise unused by the new controller (it remains in the codebase).
2. Motor Current Control
The motors now run
wheel-torque.bininstead ofwheel.bin. On thecontrol board, the
WheelMotordriver is replaced byCurrentControlledMotor(
motor.rs), which communicates with the motor controller using a newCcmCommand/CcmResponsepacket protocol (replacing the oldMotionCommand/MotorTelemetryprotocol). The motor typeCcmMotionControlTypehas modes for velocity, current, velocity+current,and motor-off. The control task converts the body controller's wheel torque
output to wheel currents via
robot_model.torques_to_currents(), convertsto milliamps, applies a 1500 mA safety clamp, and sends current setpoints
to each motor.
On the STM32F031 motor controllers, 6-step commutation (
6step.c) wasrewritten as
6step_current.c. The old code did open-loop voltage/duty-cyclecommutation. The new code adds a closed-loop current PI controller running in
the commutation ISR. The PI controller operates entirely in fixed-point
arithmetic (new
fixedarith.clibrary) because the F031 has no FPU. The PIuses S7.10 / S5.13 / S12.0 fixed-point formats with explicit bit-width
tracking through each arithmetic operation. An anti-jitter threshold
suppresses output oscillation near zero error.
Current sensing (
current_sensing.c/.h) was overhauled. The old code hadthree ADC modes (polling, DMA, timer-DMA); the new code uses two
(PWM-triggered DMA and software). The ADC result struct was simplified to
three channels: motor current, bus voltage, and STSPIN temperature. Proper
op-amp gain calculations are documented inline (gain = 5.5, sense resistor
= 0.05 ohm, settling time = 0.247us). A zero-current calibration routine
samples the ADC at startup and stores the bias. Calibration data can be
persisted to the last page of flash (address 0x08007C00) via the STM32
bootloader interface, which gained
read_device_memoryandwrite_device_memoryimplementations (the oldread_device_memorywasa
panic!).New motor controller binaries:
wheel-torque(production current-controlledimage),
wheel-torque-test(test harness). New control board binaries:profile-wheel-currandprofile-wheel-velcapture step response dataover USB for offline tuning, and
hwtest-torquefor interactive motortesting.
3. Nora W36x Radio
A driver stack was added for the u-blox Nora W36x WiFi module, which
replaces the Odin W26x module on newer board revisions. The existing Odin
driver files were reorganized under
lib-stm32/src/drivers/radio/w26x/.The Nora driver operates in pure AT command mode (no EDM framing, unlike the
Odin driver).
nora_w36x.rsimplements the UART transport, commandsend/response, and high-level operations: WiFi configuration (SSID, WPA
passphrase), UART baud rate negotiation (115200 startup -> 921600 runtime),
socket creation (TCP/UDP, with multicast support via
AT+USOCM), datatransfer (buffered mode and direct binary mode via
+UESODBevents), andconnection lifecycle management.
at_protocol.rsparses AT responses and unsolicited result codes (URCs)from raw UART buffers. It handles
OK,ERROR,+prefixed events, andinline socket data events with binary payloads.
radio_robot_nora.rswraps the Nora driver in the robot's packet-levelprotocol: hardware reset sequencing, UART connect with retry, WiFi
association, multicast group join, the
HelloRequest/HelloResponsehandshake with the software bridge, and runtime packet send/receive
(encoding
RadioPacketstructs to/from raw bytes usingRadioHeaderandRadioDataunions).radio_nora_task.rsis the Embassy async task thatruns the radio lifecycle with error recovery and publishes/subscribes
on the inter-task channels.
The radio stress test infrastructure includes
coms_reliability.py(aPython script that floods the radio with packets and measures loss/latency)
and
hwtest-radio-w36(a firmware binary for isolated radio testing).4. Control Task Changes
Beyond switching to the new
BodyControllerandCurrentControlledMotor,the control task has significant structural changes:
Loop timing is derived from
DEFAULT_CONTROL_DT(1ms), with allfrequencies (basic telemetry at 100 Hz, extended telemetry at 100 Hz,
trace logging at 10 Hz, packet timeout at 200ms) expressed as tick
counts relative to the control frequency.
Telemetry is rate-limited rather than sent every loop iteration. Basic
telemetry is sent every 10 ticks, extended telemetry every 10 ticks or
immediately on a vision update. The
BasicTelemetrypacket now includesinline
BodyControlTelemetry(body mode, wheel velocities, bodyvelocity estimate). The
ExtendedTelemetrypacket now carries aBodyControlExtendedTelemetrystruct with full state information:IMU readings, vision pose, trajectory state, KF prediction/estimate,
body commands, and per-skill telemetry.
Timing instrumentation tracks motor packet processing, command
processing, control update, and telemetry publishing durations. Warnings
and error telemetry are sent when the loop exceeds 400us.
controls_erris a new atomic flag onSharedRobotState. If thecontrols library returns an error (e.g. from
kf_updateor trajectoryplanning), the flag is set, motors are locked out, and an error telemetry
packet is sent.
reset_controllerin the command packet clears thecontroller state and re-enables motion.
BCM_OFFbody control mode now correctly results in zero motor commands,and the wheel motion type is set to
CCM_MCT_MOTOR_OFFwhen neithervelocity nor torque control is enabled.
Vision pose measurements and the vision update flag are extracted from
BasicControland passed intoBodyController::control_update(),which manages the Kalman filter's vision measurement gating internally.
5. lib-crossarch
A new
lib-crossarchcrate was created forno_stdcode that is notSTM32-specific. The queue, filter, math, and power modules were moved from
lib-stm32intolib-crossarch, andlib-stm32re-exports them.The queue (
queue.rs) is a fixed-size SPSC ring buffer with async wakersupport. The move included several changes to eviction and cancellation
semantics:
EnqueueRefnow tracks whether an eviction occurred, andcancel()properly restores the write index and size when an eviction isrolled back (important for DMA operations where a cancel means the DMA
engine has already overwritten the buffer contents). A comprehensive test
suite (459 lines) was added covering basic operations, overflow, eviction,
cancel-after-eviction, and concurrent access patterns.
The filter module provides an
IirFilter(single-pole exponential) and aWindowAveragingFilter(generic over window size, with optional softinitialization that fills the window with the first sample). The math
module provides
lerp,linear_map, andrangeutilities with aNumbertrait alias for generic numeric operations. The power moduleprovides battery percentage estimation from LiPo voltage curves.
6. Python Tooling
A
pyproject.tomlwithuv.lockwas added at the repo root, and Pythondependencies are integrated into the nix flake so they are available in
nix develop.torque_data_writer.pydecodes C struct telemetry from USB serial byparsing the C header files in
software-communication/and unpackingraw bytes into named fields. It can print to console or save to JSON.
packet_decoder.pyis a general-purpose decoder for the radio packetformat (
RadioHeader+ payload), printing decodedBasicTelemetry,ExtendedTelemetry, andErrorTelemetrypackets.plot_telemetry.pyplots decoded telemetry data (state estimates,commands, errors) for offline analysis.
coms_reliability.pyis a radio stress test tool that connects to therobot's multicast group, floods it with command packets at configurable
rates, and measures packet loss, round-trip latency, and throughput.
7. Build and Clock Configuration
PLL3 was reconfigured: PLL3Q changed from 124 MHz to 48 MHz (required
for USB), PLL3P from 186 MHz to 192 MHz. HSI48 now syncs from USB SOF.
This fixes USB connectivity on the control board.
The Makefile adds CMakeLists.txt as a dependency for motor controller
targets. The VS Code nix extension recommendation was updated from the
abandoned extension to the community-maintained one. Packet buffer sizes
were increased from 60 to 80 bytes to accommodate the larger telemetry
structs. CLAUDE.md was added documenting repository structure and build
commands.
The inline float safety validation functions (
is_float_safe,is_command_packet_safe) were removed fromlib.rs-- packet validationis now handled at the controls library and packet decoding layers.