Skip to content

Commit 7aeadf0

Browse files
committed
pbio/control: distinguish actuation limits
Distinguish between the maximum PID control value and the maximum duty cycle value applied to the motors.
1 parent f923b3f commit 7aeadf0

File tree

7 files changed

+71
-55
lines changed

7 files changed

+71
-55
lines changed

lib/pbio/include/pbio/control.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ typedef struct _pbio_control_settings_t {
3636
int32_t pid_kp; /**< Proportional position control constant (and integral speed control constant) */
3737
int32_t pid_ki; /**< Integral position control constant */
3838
int32_t pid_kd; /**< Derivative position control constant (and proportional speed control constant) */
39-
int32_t max_control; /**< Upper limit on control output */
39+
int32_t max_torque; /**< Upper limit on control torque */
40+
int32_t max_duty; /**< Upper limit on duty cycle */
4041
int32_t integral_range; /**< Region around the target count in which integral errors are accumulated */
4142
int32_t integral_rate; /**< Maximum rate at which the integrator is allowed to increase */
4243
bool use_estimated_rate; /**< Whether to use the estimated speed (true) or the reported/measured speed (false) for feedback control */
@@ -88,8 +89,8 @@ typedef struct _pbio_control_t {
8889
int32_t pbio_control_counts_to_user(pbio_control_settings_t *s, int32_t counts);
8990
int32_t pbio_control_user_to_counts(pbio_control_settings_t *s, int32_t user);
9091

91-
void pbio_control_settings_get_limits(pbio_control_settings_t *s, int32_t *speed, int32_t *acceleration, int32_t *actuation);
92-
pbio_error_t pbio_control_settings_set_limits(pbio_control_settings_t *ctl, int32_t speed, int32_t acceleration, int32_t actuation);
92+
void pbio_control_settings_get_limits(pbio_control_settings_t *s, int32_t *speed, int32_t *acceleration, int32_t *duty, int32_t *torque);
93+
pbio_error_t pbio_control_settings_set_limits(pbio_control_settings_t *ctl, int32_t speed, int32_t acceleration, int32_t duty, int32_t torque);
9394

9495
void pbio_control_settings_get_pid(pbio_control_settings_t *s, int32_t *pid_kp, int32_t *pid_ki, int32_t *pid_kd, int32_t *integral_range, int32_t *integral_rate);
9596
pbio_error_t pbio_control_settings_set_pid(pbio_control_settings_t *s, int32_t pid_kp, int32_t pid_ki, int32_t pid_kd, int32_t integral_range, int32_t integral_rate);

lib/pbio/platform/motors/settings.c

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ static const pbio_control_settings_t settings_servo_ev3_m = {
3434
.pid_kd = 30,
3535
.integral_range = 45,
3636
.integral_rate = 10,
37-
.max_control = 150000,
37+
.max_duty = 10000,
38+
.max_torque = 150000,
3839
.use_estimated_rate = false,
3940
.use_estimated_count = false,
4041
};
@@ -63,7 +64,8 @@ static const pbio_control_settings_t settings_servo_ev3_l = {
6364
.pid_kd = 250,
6465
.integral_range = 45,
6566
.integral_rate = 10,
66-
.max_control = 430000,
67+
.max_duty = 10000,
68+
.max_torque = 430000,
6769
.use_estimated_rate = false,
6870
.use_estimated_count = false,
6971
};
@@ -96,7 +98,8 @@ static const pbio_control_settings_t settings_servo_technic_m_angular = {
9698
.pid_kd = 1200,
9799
.integral_range = 45,
98100
.integral_rate = 5,
99-
.max_control = 160000,
101+
.max_duty = 10000,
102+
.max_torque = 160000,
100103
.use_estimated_rate = true,
101104
.use_estimated_count = false,
102105
};
@@ -125,7 +128,8 @@ static const pbio_control_settings_t settings_servo_technic_l_angular = {
125128
.pid_kd = 4500,
126129
.integral_range = 45,
127130
.integral_rate = 5,
128-
.max_control = 330000,
131+
.max_duty = 10000,
132+
.max_torque = 330000,
129133
.use_estimated_rate = true,
130134
.use_estimated_count = false,
131135
};
@@ -154,7 +158,8 @@ static const pbio_control_settings_t settings_servo_interactive = {
154158
.pid_kd = 1000,
155159
.integral_range = 45,
156160
.integral_rate = 3,
157-
.max_control = 100000,
161+
.max_duty = 10000,
162+
.max_torque = 100000,
158163
.use_estimated_rate = true,
159164
.use_estimated_count = false,
160165
};
@@ -185,7 +190,8 @@ static const pbio_control_settings_t settings_servo_movehub = {
185190
.pid_kd = 500,
186191
.integral_range = 45,
187192
.integral_rate = 5,
188-
.max_control = 150000,
193+
.max_duty = 10000,
194+
.max_torque = 150000,
189195
.use_estimated_rate = true,
190196
.use_estimated_count = false,
191197
};
@@ -216,7 +222,8 @@ static const pbio_control_settings_t settings_servo_technic_l = {
216222
.pid_kd = 1000,
217223
.integral_range = 45,
218224
.integral_rate = 5,
219-
.max_control = 260000,
225+
.max_duty = 10000,
226+
.max_torque = 260000,
220227
.use_estimated_rate = true,
221228
.use_estimated_count = false,
222229
};
@@ -245,7 +252,8 @@ static const pbio_control_settings_t settings_servo_technic_xl = {
245252
.pid_kd = 2000,
246253
.integral_range = 45,
247254
.integral_rate = 5,
248-
.max_control = 260000,
255+
.max_duty = 10000,
256+
.max_torque = 260000,
249257
.use_estimated_rate = true,
250258
.use_estimated_count = false,
251259
};

lib/pbio/src/control.c

Lines changed: 29 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ void pbio_control_update(pbio_control_t *ctl, int32_t time_now, int32_t count_no
1515
int32_t time_ref;
1616
int32_t count_ref, count_ref_ext, count_err, count_feedback, count_err_integral, rate_err_integral;
1717
int32_t rate_err, rate_feedback;
18-
int32_t duty, duty_due_to_proportional, duty_due_to_integral, duty_due_to_derivative;
18+
int32_t torque, torque_due_to_proportional, torque_due_to_integral, torque_due_to_derivative;
1919

2020
// Get the time at which we want to evaluate the reference position/velocities.
2121
// This compensates for any time we may have spent pausing when the motor was stalled.
@@ -43,28 +43,28 @@ void pbio_control_update(pbio_control_t *ctl, int32_t time_now, int32_t count_no
4343
}
4444

4545
// Corresponding PID control signal
46-
duty_due_to_proportional = ctl->settings.pid_kp * count_err;
47-
duty_due_to_derivative = ctl->settings.pid_kd * rate_err;
48-
duty_due_to_integral = (ctl->settings.pid_ki * (count_err_integral / US_PER_MS)) / MS_PER_SECOND;
46+
torque_due_to_proportional = ctl->settings.pid_kp * count_err;
47+
torque_due_to_derivative = ctl->settings.pid_kd * rate_err;
48+
torque_due_to_integral = (ctl->settings.pid_ki * (count_err_integral / US_PER_MS)) / MS_PER_SECOND;
4949

50-
// Total duty signal, capped by the actuation limit
51-
duty = duty_due_to_proportional + duty_due_to_integral + duty_due_to_derivative;
52-
duty = max(-ctl->settings.max_control, min(duty, ctl->settings.max_control));
50+
// Total torque signal, capped by the actuation limit
51+
torque = torque_due_to_proportional + torque_due_to_integral + torque_due_to_derivative;
52+
torque = max(-ctl->settings.max_torque, min(torque, ctl->settings.max_torque));
5353

5454
// This completes the computation of the control signal.
5555
// The next steps take care of handling windup, or triggering a stop if we are on target.
5656

57-
// We want to stop building up further errors if we are at the proportional duty limit. So, we pause the trajectory
57+
// We want to stop building up further errors if we are at the proportional torque limit. So, we pause the trajectory
5858
// if we get at this limit. We wait a little longer though, to make sure it does not fall back to below the limit
5959
// within one sample, which we can predict using the current rate times the loop time, with a factor two tolerance.
60-
int32_t max_windup_duty = ctl->settings.max_control + (ctl->settings.pid_kp * abs(rate_now) * PBIO_CONTROL_LOOP_TIME_MS * 2) / MS_PER_SECOND;
60+
int32_t max_windup_torque = ctl->settings.max_torque + (ctl->settings.pid_kp * abs(rate_now) * PBIO_CONTROL_LOOP_TIME_MS * 2) / MS_PER_SECOND;
6161

62-
// Position anti-windup: pause trajectory or integration if falling behind despite using maximum duty
62+
// Position anti-windup: pause trajectory or integration if falling behind despite using maximum torque
6363

6464
// Position anti-windup in case of angle control (accumulated position error may not get too high)
6565
if (ctl->type == PBIO_CONTROL_ANGLE) {
66-
if (abs(duty_due_to_proportional) >= max_windup_duty && pbio_math_sign(duty_due_to_proportional) == pbio_math_sign(rate_err)) {
67-
// We are at the duty limit and we should prevent further position error integration.
66+
if (abs(torque_due_to_proportional) >= max_windup_torque && pbio_math_sign(torque_due_to_proportional) == pbio_math_sign(rate_err)) {
67+
// We are at the torque limit and we should prevent further position error integration.
6868
pbio_count_integrator_pause(&ctl->count_integrator, time_now, count_now, count_ref);
6969
} else {
7070
// Not at the limit so continue integrating errors
@@ -73,8 +73,8 @@ void pbio_control_update(pbio_control_t *ctl, int32_t time_now, int32_t count_no
7373
}
7474
// Position anti-windup in case of timed speed control (speed integral may not get too high)
7575
else {
76-
if (abs(duty_due_to_proportional) >= max_windup_duty && pbio_math_sign(duty_due_to_proportional) == pbio_math_sign(rate_err)) {
77-
// We are at the duty limit and we should prevent further speed error integration.
76+
if (abs(torque_due_to_proportional) >= max_windup_torque && pbio_math_sign(torque_due_to_proportional) == pbio_math_sign(rate_err)) {
77+
// We are at the torque limit and we should prevent further speed error integration.
7878
pbio_rate_integrator_pause(&ctl->rate_integrator, time_now, count_now, count_ref);
7979
} else {
8080
// Not at the limit so continue integrating errors
@@ -102,11 +102,11 @@ void pbio_control_update(pbio_control_t *ctl, int32_t time_now, int32_t count_no
102102

103103
// The new hold control does not take effect until the next iteration, so keep actuating for now.
104104
*actuation = PBIO_ACTUATION_DUTY;
105-
*control = duty;
105+
*control = torque;
106106
} else {
107-
// The end point not reached, or we have to keep holding, so return the calculated duty for actuation
107+
// The end point not reached, or we have to keep holding, so return the calculated torque for actuation
108108
*actuation = PBIO_ACTUATION_DUTY;
109-
*control = duty;
109+
*control = torque;
110110
}
111111

112112
// Log control data
@@ -120,9 +120,9 @@ void pbio_control_update(pbio_control_t *ctl, int32_t time_now, int32_t count_no
120120
*rate_ref,
121121
count_est,
122122
rate_est,
123-
duty_due_to_proportional,
124-
duty_due_to_integral,
125-
duty_due_to_derivative,
123+
torque_due_to_proportional,
124+
torque_due_to_integral,
125+
torque_due_to_derivative,
126126
};
127127
pbio_logger_update(&ctl->log, log_data);
128128
}
@@ -326,17 +326,21 @@ int32_t pbio_control_user_to_counts(pbio_control_settings_t *s, int32_t user) {
326326
return pbio_math_mul_i32_fix16(user, s->counts_per_unit);
327327
}
328328

329-
void pbio_control_settings_get_limits(pbio_control_settings_t *s, int32_t *speed, int32_t *acceleration, int32_t *actuation) {
329+
void pbio_control_settings_get_limits(pbio_control_settings_t *s, int32_t *speed, int32_t *acceleration, int32_t *duty, int32_t *torque) {
330330
*speed = pbio_control_counts_to_user(s, s->max_rate);
331331
*acceleration = pbio_control_counts_to_user(s, s->abs_acceleration);
332+
*duty = s->max_duty / 100;
333+
*torque = s->max_torque;
332334
}
333335

334-
pbio_error_t pbio_control_settings_set_limits(pbio_control_settings_t *s, int32_t speed, int32_t acceleration, int32_t actuation) {
335-
if (speed < 1 || acceleration < 1 || actuation < 1) {
336+
pbio_error_t pbio_control_settings_set_limits(pbio_control_settings_t *s, int32_t speed, int32_t acceleration, int32_t duty, int32_t torque) {
337+
if (speed < 1 || acceleration < 1 || duty < 1 || torque < 1 || duty > 100) {
336338
return PBIO_ERROR_INVALID_ARG;
337339
}
338340
s->max_rate = pbio_control_user_to_counts(s, speed);
339341
s->abs_acceleration = pbio_control_user_to_counts(s, acceleration);
342+
s->max_duty = duty * 100;
343+
s->max_torque = torque;
340344
return PBIO_SUCCESS;
341345
}
342346

@@ -396,8 +400,8 @@ int32_t pbio_control_settings_get_max_integrator(pbio_control_settings_t *s) {
396400
if (s->pid_ki <= 10) {
397401
return 1000000000;
398402
}
399-
// Get the maximum integrator value for which ki*integrator does not exceed max_control
400-
return ((s->max_control * US_PER_MS) / s->pid_ki) * MS_PER_SECOND;
403+
// Get the maximum integrator value for which ki*integrator does not exceed max_torque
404+
return ((s->max_torque * US_PER_MS) / s->pid_ki) * MS_PER_SECOND;
401405
}
402406

403407
int32_t pbio_control_get_ref_time(pbio_control_t *ctl, int32_t time_now) {

lib/pbio/src/drivebase.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ static pbio_error_t drivebase_adopt_settings(pbio_control_settings_t *s_distance
3232
s_distance->pid_kd = (s_left->pid_kd + s_right->pid_kd) / 2;
3333

3434
// Maxima are bound by the least capable motor
35-
s_distance->max_control = min(s_left->max_control, s_right->max_control);
35+
s_distance->max_torque = min(s_left->max_torque, s_right->max_torque);
3636
s_distance->stall_time = min(s_left->stall_time, s_right->stall_time);
3737

3838
// Copy rate estimator usage, required to be the same on both motors

pybricks/common/pb_type_control.c

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,21 @@ STATIC mp_obj_t common_Control_limits(size_t n_args, const mp_obj_t *pos_args, m
5656
common_Control_obj_t, self,
5757
PB_ARG_DEFAULT_NONE(speed),
5858
PB_ARG_DEFAULT_NONE(acceleration),
59-
PB_ARG_DEFAULT_NONE(actuation));
59+
PB_ARG_DEFAULT_NONE(duty),
60+
PB_ARG_DEFAULT_NONE(torque));
6061

6162
// Read current values
62-
int32_t speed, acceleration, actuation;
63-
pbio_control_settings_get_limits(&self->control->settings, &speed, &acceleration, &actuation);
63+
int32_t speed, acceleration, duty, torque;
64+
pbio_control_settings_get_limits(&self->control->settings, &speed, &acceleration, &duty, &torque);
6465

6566
// If all given values are none, return current values
66-
if (speed_in == mp_const_none && acceleration_in == mp_const_none && actuation_in == mp_const_none) {
67-
mp_obj_t ret[3];
67+
if (speed_in == mp_const_none && acceleration_in == mp_const_none && duty_in == mp_const_none && torque_in == mp_const_none) {
68+
mp_obj_t ret[4];
6869
ret[0] = mp_obj_new_int(speed);
6970
ret[1] = mp_obj_new_int(acceleration);
70-
ret[2] = mp_obj_new_int(actuation);
71-
return mp_obj_new_tuple(3, ret);
71+
ret[2] = mp_obj_new_int(duty);
72+
ret[3] = mp_obj_new_int(torque);
73+
return mp_obj_new_tuple(4, ret);
7274
}
7375

7476
// Assert control is not active
@@ -77,9 +79,10 @@ STATIC mp_obj_t common_Control_limits(size_t n_args, const mp_obj_t *pos_args, m
7779
// Set user settings
7880
speed = pb_obj_get_default_int(speed_in, speed);
7981
acceleration = pb_obj_get_default_int(acceleration_in, acceleration);
80-
actuation = pb_obj_get_default_int(actuation_in, actuation);
82+
duty = pb_obj_get_default_int(duty_in, duty);
83+
torque = pb_obj_get_default_int(torque_in, torque);
8184

82-
pb_assert(pbio_control_settings_set_limits(&self->control->settings, speed, acceleration, actuation));
85+
pb_assert(pbio_control_settings_set_limits(&self->control->settings, speed, acceleration, duty, torque));
8386

8487
return mp_const_none;
8588
}

pybricks/common/pb_type_motor.c

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,22 +206,22 @@ STATIC mp_obj_t common_Motor_run_until_stalled(size_t n_args, const mp_obj_t *po
206206
mp_int_t speed = pb_obj_get_int(speed_in);
207207
pbio_actuation_t then = pb_type_enum_get_value(then_in, &pb_enum_type_Stop);
208208

209-
// If duty_limit argument, given, limit actuation during this maneuver
209+
// If duty_limit argument, given, limit duty during this maneuver
210210
bool override_duty_limit = duty_limit_in != mp_const_none;
211211

212-
int32_t orig_speed, acceleration, actuation;
212+
int32_t orig_speed, acceleration, duty, torque;
213213

214214
if (override_duty_limit) {
215215
// Read original values so we can restore them when we're done
216-
pbio_control_settings_get_limits(&self->srv->control.settings, &orig_speed, &acceleration, &actuation);
216+
pbio_control_settings_get_limits(&self->srv->control.settings, &orig_speed, &acceleration, &duty, &torque);
217217

218218
// Get user given limit
219219
mp_int_t duty_limit = pb_obj_get_int(duty_limit_in);
220220
duty_limit = duty_limit < 0 ? -duty_limit : duty_limit;
221221
duty_limit = duty_limit > 100 ? 100 : duty_limit;
222222

223223
// Apply the user limit
224-
pb_assert(pbio_control_settings_set_limits(&self->srv->control.settings, orig_speed, acceleration, duty_limit));
224+
pb_assert(pbio_control_settings_set_limits(&self->srv->control.settings, orig_speed, acceleration, duty_limit, torque));
225225
}
226226

227227
mp_obj_t ex = MP_OBJ_NULL;
@@ -241,7 +241,7 @@ STATIC mp_obj_t common_Motor_run_until_stalled(size_t n_args, const mp_obj_t *po
241241

242242
// Restore original settings
243243
if (override_duty_limit) {
244-
pb_assert(pbio_control_settings_set_limits(&self->srv->control.settings, orig_speed, acceleration, actuation));
244+
pb_assert(pbio_control_settings_set_limits(&self->srv->control.settings, orig_speed, acceleration, duty, torque));
245245
}
246246

247247
if (ex != MP_OBJ_NULL) {

pybricks/robotics/pb_type_drivebase.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ STATIC mp_obj_t robotics_DriveBase_make_new(const mp_obj_type_t *type, size_t n_
6868

6969
// Get defaults for drivebase as 1/3 of maximum for the underlying motors
7070
int32_t straight_speed_limit, straight_acceleration_limit, turn_rate_limit, turn_acceleration_limit, _;
71-
pbio_control_settings_get_limits(&self->db->control_distance.settings, &straight_speed_limit, &straight_acceleration_limit, &_);
72-
pbio_control_settings_get_limits(&self->db->control_heading.settings, &turn_rate_limit, &turn_acceleration_limit, &_);
71+
pbio_control_settings_get_limits(&self->db->control_distance.settings, &straight_speed_limit, &straight_acceleration_limit, &_, &_);
72+
pbio_control_settings_get_limits(&self->db->control_heading.settings, &turn_rate_limit, &turn_acceleration_limit, &_, &_);
7373

7474
self->straight_speed = straight_speed_limit / 3;
7575
self->straight_acceleration = straight_acceleration_limit / 3;
@@ -220,8 +220,8 @@ STATIC mp_obj_t robotics_DriveBase_settings(size_t n_args, const mp_obj_t *pos_a
220220

221221
// If some values are given, set them, bound by the control limits
222222
int32_t straight_speed_limit, straight_acceleration_limit, turn_rate_limit, turn_acceleration_limit, _;
223-
pbio_control_settings_get_limits(&self->db->control_distance.settings, &straight_speed_limit, &straight_acceleration_limit, &_);
224-
pbio_control_settings_get_limits(&self->db->control_heading.settings, &turn_rate_limit, &turn_acceleration_limit, &_);
223+
pbio_control_settings_get_limits(&self->db->control_distance.settings, &straight_speed_limit, &straight_acceleration_limit, &_, &_);
224+
pbio_control_settings_get_limits(&self->db->control_heading.settings, &turn_rate_limit, &turn_acceleration_limit, &_, &_);
225225

226226
self->straight_speed = min(straight_speed_limit, abs(pb_obj_get_default_int(straight_speed_in, self->straight_speed)));
227227
self->straight_acceleration = min(straight_acceleration_limit, abs(pb_obj_get_default_int(straight_acceleration_in, self->straight_acceleration)));

0 commit comments

Comments
 (0)