Skip to content

Commit 63e72da

Browse files
committed
pbio/control: Implement smart coast.
Added `Stop.COAST_SMART` as `then` option for motors. This still coasts the motor, but it keeps track of the previously used position target. When a new relative angle command is given (e.g. rotate 90 degrees), it uses that position as the starting point. This avoids accumulation of errors when using relative angles in succession
1 parent f713423 commit 63e72da

File tree

5 files changed

+66
-13
lines changed

5 files changed

+66
-13
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@
77
### Added
88
- Added `Stop.NONE` as `then` option for motors. This allows subsequent
99
motor and drive base commands to transition without stopping.
10+
- Added `Stop.COAST_SMART` as `then` option for motors. This still coasts the
11+
motor, but it keeps track of the previously used position target. When a new
12+
relative angle command is given (e.g. rotate 90 degrees), it uses that
13+
position as the starting point. This avoids accumulation of errors when using
14+
relative angles in succession.
1015
- Made motor deceleration configurable separately from acceleration.
1116
- Enabled `ujson` module.
1217
- Added ability to use more than one `DriveBase` in the same script.

lib/pbio/include/pbio/control.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ typedef enum {
5151
PBIO_CONTROL_ON_COMPLETION_HOLD,
5252
/** On completion, keep moving at target speed */
5353
PBIO_CONTROL_ON_COMPLETION_CONTINUE,
54+
/** On completion, coast the motor, but use endpoint of this maneuver
55+
* as starting point for the next relative angle maneuver. */
56+
PBIO_CONTROL_ON_COMPLETION_COAST_SMART,
5457
} pbio_control_on_completion_t;
5558

5659
// State of a system being controlled.

lib/pbio/src/control.c

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,32 @@ void pbio_control_update(pbio_control_t *ctl, int32_t time_now, pbio_control_sta
154154
// If on target, decide what to do next using the on-completion type.
155155
switch (ctl->on_completion) {
156156
case PBIO_CONTROL_ON_COMPLETION_COAST:
157+
// Coast the motor and stop the control loop.
157158
*actuation = PBIO_DCMOTOR_ACTUATION_COAST;
158159
*control = 0;
159160
pbio_control_stop(ctl);
160161
break;
161162
case PBIO_CONTROL_ON_COMPLETION_BRAKE:
163+
// Passively brake and stop the control loop.
162164
*actuation = PBIO_DCMOTOR_ACTUATION_BRAKE;
163165
*control = 0;
164166
pbio_control_stop(ctl);
165167
break;
168+
case PBIO_CONTROL_ON_COMPLETION_COAST_SMART:
169+
// For smart coast, keep actuating (holding) briefly to enforce
170+
// standstill. It also gives some time for two subsequent
171+
// blocks to smoothly transition without going through coast.
172+
if (time_ref - ctl->trajectory.t3 < 100 * US_PER_MS) {
173+
*actuation = PBIO_DCMOTOR_ACTUATION_TORQUE;
174+
*control = torque;
175+
}
176+
// After that, coast the motor and stop the control loop.
177+
else {
178+
*actuation = PBIO_DCMOTOR_ACTUATION_COAST;
179+
*control = 0;
180+
pbio_control_stop(ctl);
181+
}
182+
break;
166183
case PBIO_CONTROL_ON_COMPLETION_CONTINUE:
167184
// Fall through, same as hold.
168185
case PBIO_CONTROL_ON_COMPLETION_HOLD:
@@ -298,19 +315,34 @@ pbio_error_t pbio_control_start_angle_control(pbio_control_t *ctl, int32_t time_
298315

299316
pbio_error_t pbio_control_start_relative_angle_control(pbio_control_t *ctl, int32_t time_now, pbio_control_state_t *state, int32_t relative_target_count, int32_t target_rate, pbio_control_on_completion_t on_completion) {
300317

301-
// Get the count from which the relative count is to be counted
318+
// First, we need to decide where the relative motion starts from.
302319
int32_t count_start;
303320

304-
// If no control is active, count from the physical count
305-
if (!pbio_control_is_active(ctl)) {
306-
count_start = state->count;
307-
}
308-
// Otherwise count from the current reference
309-
else {
321+
if (pbio_control_is_active(ctl)) {
322+
// If control is already active, restart from current reference.
310323
int32_t time_ref = pbio_control_get_ref_time(ctl, time_now);
311324
pbio_trajectory_reference_t ref;
312325
pbio_trajectory_get_reference(&ctl->trajectory, time_ref, &ref);
313326
count_start = ref.count;
327+
} else {
328+
// Control is inactive. We still have two options.
329+
// If the previous command used smart coast and we're still close to
330+
// its target, we want to start from there. This avoids accumulating
331+
// errors in programs that use mostly relative motions like run_angle.
332+
if (ctl->on_completion == PBIO_CONTROL_ON_COMPLETION_COAST_SMART &&
333+
abs(ctl->trajectory.th3 - state->count) < ctl->settings.count_tolerance * 2) {
334+
// We're close enough, so make the new target relative to the
335+
// endpoint of the last one.
336+
count_start = ctl->trajectory.th3;
337+
338+
// FIXME: We need to clear the on-completion state between program
339+
// to make each program run independent.
340+
341+
} else {
342+
// No special cases apply, so the best we can do is just start from
343+
// the current state.
344+
count_start = state->count;
345+
}
314346
}
315347

316348
// The target count is the start count plus the count to be traveled. If speed is negative, traveled count also flips.
@@ -360,6 +392,11 @@ pbio_error_t pbio_control_start_timed_control(pbio_control_t *ctl, int32_t time_
360392

361393
pbio_error_t err;
362394

395+
if (on_completion == PBIO_CONTROL_ON_COMPLETION_COAST_SMART) {
396+
// For timed maneuvers, the end point has no meaning, so just coast.
397+
on_completion = PBIO_CONTROL_ON_COMPLETION_COAST;
398+
}
399+
363400
// Set new maneuver action and stop type, and state
364401
ctl->on_completion = on_completion;
365402
ctl->on_target = false;

lib/pbio/src/servo.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,9 @@ pbio_error_t pbio_servo_stop(pbio_servo_t *srv, pbio_control_on_completion_t on_
333333
return err;
334334
}
335335

336-
switch (on_completion)
337-
{
336+
switch (on_completion) {
337+
case PBIO_CONTROL_ON_COMPLETION_COAST_SMART:
338+
// Same as normal coast, so fall through.
338339
case PBIO_CONTROL_ON_COMPLETION_COAST:
339340
return pbio_servo_actuate(srv, PBIO_DCMOTOR_ACTUATION_COAST, 0);
340341
case PBIO_CONTROL_ON_COMPLETION_BRAKE:

pybricks/parameters/pb_type_stop.c

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ const pb_obj_enum_member_t pb_Stop_COAST_obj = {
1717
.value = PBIO_CONTROL_ON_COMPLETION_COAST
1818
};
1919

20+
const pb_obj_enum_member_t pb_Stop_COAST_SMART_obj = {
21+
{&pb_enum_type_Stop},
22+
.name = MP_QSTR_COAST_SMART,
23+
.value = PBIO_CONTROL_ON_COMPLETION_COAST_SMART,
24+
};
25+
2026
const pb_obj_enum_member_t pb_Stop_BRAKE_obj = {
2127
{&pb_enum_type_Stop},
2228
.name = MP_QSTR_BRAKE,
@@ -36,10 +42,11 @@ const pb_obj_enum_member_t pb_Stop_NONE_obj = {
3642
};
3743

3844
STATIC const mp_rom_map_elem_t pb_enum_Stop_table[] = {
39-
{ MP_ROM_QSTR(MP_QSTR_COAST), MP_ROM_PTR(&pb_Stop_COAST_obj) },
40-
{ MP_ROM_QSTR(MP_QSTR_BRAKE), MP_ROM_PTR(&pb_Stop_BRAKE_obj) },
41-
{ MP_ROM_QSTR(MP_QSTR_HOLD), MP_ROM_PTR(&pb_Stop_HOLD_obj) },
42-
{ MP_ROM_QSTR(MP_QSTR_NONE), MP_ROM_PTR(&pb_Stop_NONE_obj)},
45+
{ MP_ROM_QSTR(MP_QSTR_COAST), MP_ROM_PTR(&pb_Stop_COAST_obj) },
46+
{ MP_ROM_QSTR(MP_QSTR_COAST_SMART), MP_ROM_PTR(&pb_Stop_COAST_SMART_obj) },
47+
{ MP_ROM_QSTR(MP_QSTR_BRAKE), MP_ROM_PTR(&pb_Stop_BRAKE_obj) },
48+
{ MP_ROM_QSTR(MP_QSTR_HOLD), MP_ROM_PTR(&pb_Stop_HOLD_obj) },
49+
{ MP_ROM_QSTR(MP_QSTR_NONE), MP_ROM_PTR(&pb_Stop_NONE_obj)},
4350
};
4451
STATIC MP_DEFINE_CONST_DICT(pb_enum_type_Stop_locals_dict, pb_enum_Stop_table);
4552

0 commit comments

Comments
 (0)