Skip to content

Commit a276eaf

Browse files
committed
pybricks.common.Motor: Implement load().
This replaces Control.load(). Now also works under passive conditions. And it means it's back on Move Hub, which no longer has the Control class.
1 parent d956d1a commit a276eaf

File tree

6 files changed

+76
-4
lines changed

6 files changed

+76
-4
lines changed

CHANGELOG.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
## [Unreleased]
66

77
### Added
8-
- Added support for `PBIO_PYBRICKS_COMMAND_REBOOT_TO_UPDATE_MODE` Pybricks Profile BLE command.
8+
- Added support for `PBIO_PYBRICKS_COMMAND_REBOOT_TO_UPDATE_MODE` Pybricks
9+
Profile BLE command.
10+
- Implemented `Motor.load()` which now measures load both during active
11+
conditions (`run`) and passive conditions (`dc`).
912

1013
### Changed
1114
- The Pybricks Code stop button will force the program to exit even if the user

lib/pbio/include/pbio/observer.h

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ void pbio_observer_reset(pbio_observer_t *obs, pbio_control_settings_t *settings
9393
void pbio_observer_get_estimated_state(const pbio_observer_t *obs, int32_t *speed_num, pbio_angle_t *angle_est, int32_t *speed_est);
9494
void pbio_observer_update(pbio_observer_t *obs, uint32_t time, const pbio_angle_t *angle, pbio_dcmotor_actuation_t actuation, int32_t voltage);
9595
bool pbio_observer_is_stalled(const pbio_observer_t *obs, uint32_t time, uint32_t *stall_duration);
96+
int32_t pbio_observer_get_feedback_torque(pbio_observer_t *obs, const pbio_angle_t *angle);
9697

9798
// Model conversion functions:
9899

lib/pbio/include/pbio/servo.h

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ pbio_error_t pbio_servo_get_state_control(pbio_servo_t *srv, pbio_control_state_
9393
pbio_error_t pbio_servo_get_state_user(pbio_servo_t *srv, int32_t *angle, int32_t *speed);
9494
bool pbio_servo_update_loop_is_running(pbio_servo_t *srv);
9595
pbio_error_t pbio_servo_is_stalled(pbio_servo_t *srv, bool *stalled, uint32_t *stall_duration);
96+
pbio_error_t pbio_servo_get_load(pbio_servo_t *srv, int32_t *load);
9697

9798
// Servo end user commands:
9899

lib/pbio/src/observer.c

+15-3
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,18 @@ static void update_stall_state(pbio_observer_t *obs, uint32_t time, pbio_dcmotor
105105
}
106106
}
107107

108+
/**
109+
* Gets observer feedback torque to keep it close to measured value.
110+
*
111+
* @param [in] obs The observer instance.
112+
* @param [in] angle Measured angle used to correct the model.
113+
* @return Feedback torque in uNm.
114+
*/
115+
int32_t pbio_observer_get_feedback_torque(pbio_observer_t *obs, const pbio_angle_t *angle) {
116+
int32_t error = pbio_angle_diff_mdeg(angle, &obs->angle);
117+
return pbio_control_settings_mul_by_gain(error, obs->model->gain);
118+
}
119+
108120
/**
109121
* Predicts next system state and corrects the model using a measurement.
110122
*
@@ -125,10 +137,10 @@ void pbio_observer_update(pbio_observer_t *obs, uint32_t time, const pbio_angle_
125137
// Update numerical derivative as speed sanity check.
126138
obs->speed_numeric = pbio_differentiator_get_speed(&obs->differentiator, angle);
127139

128-
int32_t error = pbio_angle_diff_mdeg(angle, &obs->angle);
129-
130140
// Apply observer error feedback as voltage.
131-
int32_t feedback_voltage = pbio_observer_torque_to_voltage(m, pbio_control_settings_mul_by_gain(error, m->gain));
141+
int32_t feedback_voltage = pbio_observer_torque_to_voltage(m,
142+
pbio_observer_get_feedback_torque(obs, angle)
143+
);
132144

133145
// Check stall condition.
134146
update_stall_state(obs, time, actuation, voltage, feedback_voltage);

lib/pbio/src/servo.c

+45
Original file line numberDiff line numberDiff line change
@@ -682,4 +682,49 @@ pbio_error_t pbio_servo_is_stalled(pbio_servo_t *srv, bool *stalled, uint32_t *s
682682
return PBIO_SUCCESS;
683683
}
684684

685+
/**
686+
* Gets estimated external load experienced by the servo.
687+
*
688+
* @param [in] srv The servo instance.
689+
* @param [out] load Estimated load (mNm).
690+
* @return Error code.
691+
*/
692+
pbio_error_t pbio_servo_get_load(pbio_servo_t *srv, int32_t *load) {
693+
694+
// Don't allow access if update loop not registered.
695+
if (!pbio_servo_update_loop_is_running(srv)) {
696+
*load = 0;
697+
return PBIO_ERROR_INVALID_OP;
698+
}
699+
700+
// Get passive actuation type.
701+
pbio_dcmotor_actuation_t applied_actuation;
702+
int32_t voltage;
703+
pbio_dcmotor_get_state(srv->dcmotor, &applied_actuation, &voltage);
704+
705+
// Get best estimate based on control and physyical state.
706+
if (applied_actuation == PBIO_DCMOTOR_ACTUATION_COAST) {
707+
// Can't estimate load on coast.
708+
*load = 0;
709+
} else if (pbio_control_is_active(&srv->control)) {
710+
// The experienced load is the opposite sign of what the PID is
711+
// trying to overcome.
712+
*load = -srv->control.pid_average;
713+
} else {
714+
// Read the angle.
715+
pbio_angle_t angle;
716+
pbio_error_t err = pbio_tacho_get_angle(srv->tacho, &angle);
717+
if (err != PBIO_SUCCESS) {
718+
return err;
719+
}
720+
// Use observer error as a measure of torque.
721+
*load = pbio_observer_get_feedback_torque(&srv->observer, &angle);
722+
}
723+
724+
// Convert to user torque units (mNm).
725+
*load = pbio_control_settings_actuation_ctl_to_app(*load);
726+
727+
return PBIO_SUCCESS;
728+
}
729+
685730
#endif // PBDRV_CONFIG_NUM_MOTOR_CONTROLLER

pybricks/common/pb_type_motor.c

+10
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,15 @@ STATIC mp_obj_t common_Motor_done(mp_obj_t self_in) {
362362
}
363363
MP_DEFINE_CONST_FUN_OBJ_1(common_Motor_done_obj, common_Motor_done);
364364

365+
// pybricks._common.Motor.load
366+
STATIC mp_obj_t common_Motor_load(mp_obj_t self_in) {
367+
common_Motor_obj_t *self = MP_OBJ_TO_PTR(self_in);
368+
int32_t load;
369+
pb_assert(pbio_servo_get_load(self->srv, &load));
370+
return mp_obj_new_int(load);
371+
}
372+
MP_DEFINE_CONST_FUN_OBJ_1(common_Motor_load_obj, common_Motor_load);
373+
365374
// dir(pybricks.builtins.Motor)
366375
STATIC const mp_rom_map_elem_t common_Motor_locals_dict_table[] = {
367376
//
@@ -386,6 +395,7 @@ STATIC const mp_rom_map_elem_t common_Motor_locals_dict_table[] = {
386395
{ MP_ROM_QSTR(MP_QSTR_stalled), MP_ROM_PTR(&common_Motor_stalled_obj) },
387396
{ MP_ROM_QSTR(MP_QSTR_done), MP_ROM_PTR(&common_Motor_done_obj) },
388397
{ MP_ROM_QSTR(MP_QSTR_track_target), MP_ROM_PTR(&common_Motor_track_target_obj) },
398+
{ MP_ROM_QSTR(MP_QSTR_load), MP_ROM_PTR(&common_Motor_load_obj) },
389399
};
390400
MP_DEFINE_CONST_DICT(common_Motor_locals_dict, common_Motor_locals_dict_table);
391401

0 commit comments

Comments
 (0)