Skip to content

Commit 844f03d

Browse files
committed
make controller replacable
1 parent 40a5d72 commit 844f03d

1 file changed

Lines changed: 111 additions & 63 deletions

File tree

src/balance_original.jl

Lines changed: 111 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ include(joinpath(@__DIR__, "DiscreteKalmanFilter.jl"))
44
export BalanceController, compute_pwm!, handle_motion_mode!, balance_car!
55
export MotionMode, STOP, START, FORWARD, BACKWARD, LEFT, RIGHT
66
export car_stop!, apply_motor_output!
7+
export ParallelPID
78

89
# =============================================================================
910
# Hardware Interface Stubs (override for actual hardware)
@@ -26,30 +27,76 @@ const PWMB_RIGHT = 0
2627
const STBY_PIN = 0
2728

2829
# =============================================================================
29-
# Constants (matching original BalanceCar.h parameters)
30+
# ParallelPID Controller Parameters
3031
# =============================================================================
3132

32-
# PID parameters
33-
const KP_BALANCE = 55.0f0
34-
const KD_BALANCE = 0.75f0
35-
const KP_SPEED = 10.0f0
36-
const KI_SPEED = 0.26f0
37-
const KP_TURN = 2.5f0
38-
const KD_TURN = 0.5f0
39-
40-
# Angle limits (degrees)
41-
const BALANCE_ANGLE_MIN = -22.0f0
42-
const BALANCE_ANGLE_MAX = 22.0f0
43-
44-
# PWM limits
45-
const PWM_MAX = 255.0f0
46-
const PWM_MIN = -255.0f0
33+
"""
34+
ParallelPID
35+
36+
Controller parameters for parallel PD/PI architecture (original Tumbller).
37+
Stores all tunable gains and limits for the balance car controller.
38+
Default values match the original Tumbller code.
39+
40+
# Fields
41+
- `kp_balance`, `kd_balance`: Balance control (PD on tilt angle)
42+
- `kp_speed`, `ki_speed`: Speed control (PI on wheel speed)
43+
- `kp_turn`, `kd_turn`: Turn control
44+
- `balance_angle_min`, `balance_angle_max`: Angle limits (degrees)
45+
- `pwm_min`, `pwm_max`: PWM output limits
46+
- `integral_limit`: Anti-windup limit for speed integrator
47+
- `speed_control_period`: Decimation period for speed control loop
48+
"""
49+
@kwdef mutable struct ParallelPID
50+
# Parameters
51+
# Balance control (PD)
52+
kp_balance::Float32 = 55.0f0
53+
kd_balance::Float32 = 0.75f0
54+
55+
# Speed control (PI)
56+
kp_speed::Float32 = 10.0f0
57+
ki_speed::Float32 = 0.26f0
58+
59+
# Turn control
60+
kp_turn::Float32 = 2.5f0
61+
kd_turn::Float32 = 0.5f0
62+
63+
# Angle limits (degrees)
64+
balance_angle_min::Float32 = -22.0f0
65+
balance_angle_max::Float32 = 22.0f0
66+
67+
# PWM limits
68+
pwm_min::Float32 = -255.0f0
69+
pwm_max::Float32 = 255.0f0
70+
71+
# Integral anti-windup limit
72+
integral_limit::Float32 = 3000.0f0
73+
74+
# Speed control decimation period
75+
speed_control_period::Int = 8
76+
77+
# State (mutable, initialized to zero)
78+
speed_filter::Float32 = 0.0f0
79+
speed_filter_old::Float32 = 0.0f0
80+
car_speed_integral::Float32 = 0.0f0
81+
speed_control_period_count::Int = 0
82+
speed_control_output::Float32 = 0.0f0
83+
rotation_control_output::Float32 = 0.0f0
84+
end
4785

48-
# Integral anti-windup limit
49-
const INTEGRAL_LIMIT = 3000.0f0
86+
"""
87+
reset_state!(c::ParallelPID)
5088
51-
# Speed control decimation (runs every N cycles)
52-
const SPEED_CONTROL_PERIOD = 8
89+
Reset controller state to initial values (zeros).
90+
"""
91+
function reset_state!(c::ParallelPID)
92+
c.speed_filter = 0.0f0
93+
c.speed_filter_old = 0.0f0
94+
c.car_speed_integral = 0.0f0
95+
c.speed_control_period_count = 0
96+
c.speed_control_output = 0.0f0
97+
c.rotation_control_output = 0.0f0
98+
nothing
99+
end
53100

54101
# =============================================================================
55102
# Motion Mode Enum
@@ -61,24 +108,24 @@ const SPEED_CONTROL_PERIOD = 8
61108
# Controller State Struct
62109
# =============================================================================
63110

64-
mutable struct BalanceController
111+
"""
112+
BalanceController{C}
113+
114+
Balance controller state with parametric controller type `C`.
115+
The controller type determines the control algorithm (e.g., `ParallelPID`).
116+
Controller-specific state is stored in the controller itself.
117+
"""
118+
mutable struct BalanceController{C}
119+
# Controller (contains parameters and algorithm-specific state)
120+
controller::C
121+
65122
# Kalman filter for angle estimation
66123
kf::IMUKalmanFilter
67124

68125
# Encoder pulse accumulators (signed based on PWM direction)
69126
encoder_left_pulse::Int
70127
encoder_right_pulse::Int
71128

72-
# Speed control state (PI integrator)
73-
speed_filter::Float32
74-
speed_filter_old::Float32
75-
car_speed_integral::Float32
76-
speed_control_period_count::Int
77-
78-
# Control outputs
79-
speed_control_output::Float32
80-
rotation_control_output::Float32
81-
82129
# Setpoints
83130
setting_car_speed::Int
84131
setting_turn_speed::Int
@@ -96,21 +143,20 @@ mutable struct BalanceController
96143
end
97144

98145
"""
99-
BalanceController(; angle_zero=0.0f0, angular_velocity_zero=0.0f0)
146+
BalanceController(; controller=ParallelPID(), angle_zero=0.0f0, angular_velocity_zero=0.0f0)
100147
101-
Create a balance controller with default parameters matching the original Tumbller code.
148+
Create a balance controller with the specified controller type (defaults to ParallelPID).
102149
"""
103-
function BalanceController(; angle_zero::Float32=0.0f0, angular_velocity_zero::Float32=0.0f0)
104-
BalanceController(
150+
function BalanceController(;
151+
controller::C = ParallelPID(),
152+
angle_zero::Float32 = 0.0f0,
153+
angular_velocity_zero::Float32 = 0.0f0
154+
) where C
155+
BalanceController{C}(
156+
controller,
105157
IMUKalmanFilter(), # kf
106158
0, # encoder_left_pulse
107159
0, # encoder_right_pulse
108-
0.0f0, # speed_filter
109-
0.0f0, # speed_filter_old
110-
0.0f0, # car_speed_integral
111-
0, # speed_control_period_count
112-
0.0f0, # speed_control_output
113-
0.0f0, # rotation_control_output
114160
0, # setting_car_speed
115161
0, # setting_turn_speed
116162
0.0f0, # pwm_left
@@ -133,17 +179,18 @@ Updates `ctrl.pwm_left` and `ctrl.pwm_right` in place.
133179
134180
Returns the Kalman-filtered angle for use in motion mode handling.
135181
136-
# Control Architecture (parallel, not cascade):
182+
# Control Architecture for ParallelPID (parallel, not cascade):
137183
- Balance control: PD on tilt angle
138-
- Speed control: PI on wheel speed (runs every 8 cycles)
184+
- Speed control: PI on wheel speed (runs every N cycles)
139185
- Turn control: P + D on turn rate
140186
141187
Final: `pwm = balance - speed ± rotation`
142188
"""
143-
function compute_pwm!(ctrl::BalanceController,
189+
function compute_pwm!(ctrl::BalanceController{ParallelPID},
144190
encoder_count_left::Integer, encoder_count_right::Integer,
145191
ax::Integer, ay::Integer, az::Integer,
146192
gx::Integer, gy::Integer, gz::Integer)
193+
c = ctrl.controller
147194

148195
# Accumulate encoder pulses with sign based on current PWM direction
149196
ctrl.encoder_left_pulse += ctrl.pwm_left < 0 ? -encoder_count_left : encoder_count_left
@@ -157,43 +204,43 @@ function compute_pwm!(ctrl::BalanceController,
157204
kalman_angle = angle(ctrl.kf)
158205

159206
# Balance control (PD on tilt angle)
160-
balance_control_output = KP_BALANCE * (kalman_angle - ctrl.angle_zero) +
161-
KD_BALANCE * (gyro_x - ctrl.angular_velocity_zero)
207+
balance_control_output = c.kp_balance * (kalman_angle - ctrl.angle_zero) +
208+
c.kd_balance * (gyro_x - ctrl.angular_velocity_zero)
162209

163-
# Speed control (PI, runs every 8 cycles = 40ms at 5ms sample time)
164-
ctrl.speed_control_period_count += 1
165-
if ctrl.speed_control_period_count >= SPEED_CONTROL_PERIOD
166-
ctrl.speed_control_period_count = 0
210+
# Speed control (PI, runs every N cycles)
211+
c.speed_control_period_count += 1
212+
if c.speed_control_period_count >= c.speed_control_period
213+
c.speed_control_period_count = 0
167214

168215
# Average wheel speed from encoder pulses
169216
car_speed = (ctrl.encoder_left_pulse + ctrl.encoder_right_pulse) * 0.5f0
170217
ctrl.encoder_left_pulse = 0
171218
ctrl.encoder_right_pulse = 0
172219

173220
# Low-pass filter
174-
ctrl.speed_filter = ctrl.speed_filter_old * 0.7f0 + car_speed * 0.3f0
175-
ctrl.speed_filter_old = ctrl.speed_filter
221+
c.speed_filter = c.speed_filter_old * 0.7f0 + car_speed * 0.3f0
222+
c.speed_filter_old = c.speed_filter
176223

177224
# PI integrator with setpoint
178-
ctrl.car_speed_integral += ctrl.speed_filter - ctrl.setting_car_speed
225+
c.car_speed_integral += c.speed_filter - ctrl.setting_car_speed
179226

180227
# Anti-windup
181-
ctrl.car_speed_integral = clamp(ctrl.car_speed_integral, -INTEGRAL_LIMIT, INTEGRAL_LIMIT)
228+
c.car_speed_integral = clamp(c.car_speed_integral, -c.integral_limit, c.integral_limit)
182229

183230
# Speed control output (negative feedback)
184-
ctrl.speed_control_output = -KP_SPEED * ctrl.speed_filter - KI_SPEED * ctrl.car_speed_integral
231+
c.speed_control_output = -c.kp_speed * c.speed_filter - c.ki_speed * c.car_speed_integral
185232

186233
# Turn control output (computed at same rate as speed control)
187-
ctrl.rotation_control_output = ctrl.setting_turn_speed + KD_TURN * gyro_z
234+
c.rotation_control_output = ctrl.setting_turn_speed + c.kd_turn * gyro_z
188235
end
189236

190237
# Combine control outputs
191-
ctrl.pwm_left = balance_control_output - ctrl.speed_control_output - ctrl.rotation_control_output
192-
ctrl.pwm_right = balance_control_output - ctrl.speed_control_output + ctrl.rotation_control_output
238+
ctrl.pwm_left = balance_control_output - c.speed_control_output - c.rotation_control_output
239+
ctrl.pwm_right = balance_control_output - c.speed_control_output + c.rotation_control_output
193240

194241
# Clamp to PWM limits
195-
ctrl.pwm_left = clamp(ctrl.pwm_left, PWM_MIN, PWM_MAX)
196-
ctrl.pwm_right = clamp(ctrl.pwm_right, PWM_MIN, PWM_MAX)
242+
ctrl.pwm_left = clamp(ctrl.pwm_left, c.pwm_min, c.pwm_max)
243+
ctrl.pwm_right = clamp(ctrl.pwm_right, c.pwm_min, c.pwm_max)
197244

198245
return kalman_angle
199246
end
@@ -211,11 +258,12 @@ Modifies `ctrl.pwm_left`, `ctrl.pwm_right`, `ctrl.motion_mode`, and integrator s
211258
Returns `true` if motors should be stopped (car_stop! should be called).
212259
"""
213260
function handle_motion_mode!(ctrl::BalanceController, kalman_angle::Float32; key_flag::Char='0')
261+
c = ctrl.controller
214262
should_stop_motors = false
215263

216264
# Check angle limits - force STOP if exceeded (except during START or STOP modes)
217265
if ctrl.motion_mode != START && ctrl.motion_mode != STOP
218-
if kalman_angle < BALANCE_ANGLE_MIN || kalman_angle > BALANCE_ANGLE_MAX
266+
if kalman_angle < c.balance_angle_min || kalman_angle > c.balance_angle_max
219267
ctrl.motion_mode = STOP
220268
should_stop_motors = true
221269
end
@@ -225,14 +273,14 @@ function handle_motion_mode!(ctrl::BalanceController, kalman_angle::Float32; key
225273
if ctrl.motion_mode == STOP
226274
if key_flag != '4'
227275
# Full stop - zero everything
228-
ctrl.car_speed_integral = 0.0f0
276+
reset_state!(c)
229277
ctrl.setting_car_speed = 0
230278
ctrl.pwm_left = 0.0f0
231279
ctrl.pwm_right = 0.0f0
232280
should_stop_motors = true
233281
else
234282
# key_flag == '4': Reset state but don't stop motors (allows restart)
235-
ctrl.car_speed_integral = 0.0f0
283+
reset_state!(c)
236284
ctrl.setting_car_speed = 0
237285
ctrl.pwm_left = 0.0f0
238286
ctrl.pwm_right = 0.0f0

0 commit comments

Comments
 (0)