Skip to content

Commit 5a24099

Browse files
committed
pybricks.hubs: Add update_heading_correction method to imu.
1 parent c2b3b63 commit 5a24099

File tree

7 files changed

+139
-5
lines changed

7 files changed

+139
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
between reboots.
1212
- Added `heading_correction` to `hub.imu.settings` to allow for automatic
1313
correction of the `hub.imu.heading()` value ([support#1678]).
14+
- Added `update_heading_correction` to interactively set the heading
15+
correction value ([support#1678]).
1416

1517
### Changed
1618

bricks/_common/modules/_hub_extra.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from pybricks.parameters import Axis
12
from pybricks.tools import wait
23

34

@@ -7,3 +8,108 @@ def light_matrix_text_async(display, text, on, off):
78
yield from wait(on)
89
display.off()
910
yield from wait(off)
11+
12+
13+
def imu_update_heading_correction(hub):
14+
15+
# Number of turns to confirm the result.
16+
NUMBER_CONFIRM_TURNS = 5
17+
18+
# Maximum speed values before we consider the result invalid.
19+
MAX_XY_SPEED = 50
20+
MAX_Z_SPEED = 800
21+
22+
# Routine to wait on a button, with some extra time to avoid vibration directly after.
23+
def wait_for_click():
24+
while hub.buttons.pressed():
25+
wait(1)
26+
while not hub.buttons.pressed():
27+
wait(1)
28+
print("Processing...")
29+
while hub.buttons.pressed():
30+
wait(1)
31+
wait(1500)
32+
33+
# Disable stop buttons
34+
hub.system.set_stop_button(None)
35+
print(
36+
"Put the hub on a flat table. Align against a fixed reference like a wall or heavy book. Press hub button when ready."
37+
)
38+
39+
# Wait for fixed reference and store the initial angle value.
40+
wait_for_click()
41+
while not hub.imu.ready() or not hub.imu.stationary():
42+
wait(1)
43+
start_z = hub.imu.rotation(-Axis.Z)
44+
45+
# Wait for a full rotation and get the result.
46+
print(
47+
"Keep the hub flat and slide it to make a full turn clockwise. Put it against the same reference. Press hub button when ready."
48+
)
49+
wait_for_click()
50+
one_turn = hub.imu.rotation(-Axis.Z) - start_z
51+
52+
# Require clockwise...
53+
if one_turn < 0:
54+
raise ValueError("You turned it the wrong way. Please try again.")
55+
56+
# Sanity check. Should be close to 360.
57+
if not (350 < one_turn < 370):
58+
print(one_turn)
59+
raise ValueError(
60+
"The error was more than 10 degrees, which is unexpected. Please try again."
61+
)
62+
63+
# Instruct to make more turns.
64+
print("So far so good! Now make", NUMBER_CONFIRM_TURNS, "full clockwise turns.")
65+
66+
for i in range(NUMBER_CONFIRM_TURNS):
67+
68+
# The rotation target is just under a rotation so we can show the next
69+
# message to keep going in time, avoiding doubts about what to do.
70+
target = one_turn * (i + 2) - 10
71+
72+
# Wait until the hub reaches the target.
73+
while hub.imu.rotation(-Axis.Z) - start_z < target:
74+
wait(1)
75+
76+
if hub.buttons.pressed():
77+
raise RuntimeError("Don't press the button until all turns are complete!")
78+
79+
if abs(hub.imu.angular_velocity(Axis.Z)) > MAX_Z_SPEED:
80+
raise RuntimeError("Not so fast! Try again.")
81+
82+
if (
83+
abs(hub.imu.angular_velocity(Axis.X)) + abs(hub.imu.angular_velocity(Axis.Y))
84+
> MAX_XY_SPEED
85+
):
86+
87+
raise RuntimeError("Please keep the hub flat! Try again.")
88+
89+
# Inform user of status.
90+
print("Completed", i + 1, "out of", NUMBER_CONFIRM_TURNS, "turns. ", end="")
91+
if i < NUMBER_CONFIRM_TURNS - 1:
92+
print("Keep going!")
93+
else:
94+
print("Put it against the same reference. Press hub button when ready.")
95+
96+
# Wait for final confirmation.
97+
wait_for_click()
98+
99+
# Verify the result.
100+
expected = one_turn * (NUMBER_CONFIRM_TURNS + 1)
101+
result = hub.imu.rotation(-Axis.Z) - start_z
102+
103+
if abs(expected - result) > NUMBER_CONFIRM_TURNS / 2:
104+
print(
105+
"The",
106+
NUMBER_CONFIRM_TURNS,
107+
"extra turns where different from the first turn. Try again.",
108+
)
109+
print("Expected", expected, "but got", result)
110+
111+
# Get the final result to save.
112+
average_turn = result / (NUMBER_CONFIRM_TURNS + 1)
113+
print("For every 360-degree turn your gyro Z-axis reports:", average_turn)
114+
hub.imu.settings(heading_correction=average_turn)
115+
print("Saved! Now the heading() method will report the adjusted value.")

pybricks/common.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ extern const mp_obj_type_t pb_type_Speaker;
134134

135135
#if PYBRICKS_PY_COMMON_IMU
136136

137-
mp_obj_t pb_type_IMU_obj_new(mp_obj_t top_side_axis, mp_obj_t front_side_axis);
137+
mp_obj_t pb_type_IMU_obj_new(mp_obj_t hub_in, mp_obj_t top_side_axis, mp_obj_t front_side_axis);
138138

139139
#endif // PYBRICKS_PY_COMMON_IMU
140140

pybricks/common/pb_type_imu.c

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include <pbio/imu.h>
1515

1616
#include <pbsys/storage_settings.h>
17+
#include <pbsys/program_stop.h>
1718

1819
#include "py/obj.h"
1920

@@ -25,6 +26,7 @@
2526

2627
typedef struct _pb_type_imu_obj_t {
2728
mp_obj_base_t base;
29+
mp_obj_t hub;
2830
} pb_type_imu_obj_t;
2931

3032
// pybricks._common.IMU.up
@@ -218,6 +220,28 @@ STATIC mp_obj_t pb_type_imu_reset_heading(size_t n_args, const mp_obj_t *pos_arg
218220
}
219221
STATIC MP_DEFINE_CONST_FUN_OBJ_KW(pb_type_imu_reset_heading_obj, 1, pb_type_imu_reset_heading);
220222

223+
// pybricks._common.IMU.update_heading_correction
224+
STATIC mp_obj_t pb_type_imu_update_heading_correction(mp_obj_t self_in) {
225+
pb_type_imu_obj_t *self = MP_OBJ_TO_PTR(self_in);
226+
pb_module_tools_assert_blocking();
227+
228+
// Disable stop button and cache original setting to restore later.
229+
pbio_button_flags_t stop_button = pbsys_program_stop_get_buttons();
230+
231+
nlr_buf_t nlr;
232+
if (nlr_push(&nlr) == 0) {
233+
mp_obj_t func = pb_frozen_function_import(MP_QSTR__hub_extra, MP_QSTR_imu_update_heading_correction);
234+
mp_call_function_1(func, self->hub);
235+
pbsys_program_stop_set_buttons(stop_button);
236+
nlr_pop();
237+
} else {
238+
pbsys_program_stop_set_buttons(stop_button);
239+
nlr_jump(nlr.ret_val);
240+
}
241+
return mp_const_none;
242+
}
243+
MP_DEFINE_CONST_FUN_OBJ_1(pb_type_imu_update_heading_correction_obj, pb_type_imu_update_heading_correction);
244+
221245
// dir(pybricks.common.IMU)
222246
STATIC const mp_rom_map_elem_t pb_type_imu_locals_dict_table[] = {
223247
{ MP_ROM_QSTR(MP_QSTR_acceleration), MP_ROM_PTR(&pb_type_imu_acceleration_obj) },
@@ -230,6 +254,7 @@ STATIC const mp_rom_map_elem_t pb_type_imu_locals_dict_table[] = {
230254
{ MP_ROM_QSTR(MP_QSTR_stationary), MP_ROM_PTR(&pb_type_imu_stationary_obj) },
231255
{ MP_ROM_QSTR(MP_QSTR_tilt), MP_ROM_PTR(&pb_type_imu_tilt_obj) },
232256
{ MP_ROM_QSTR(MP_QSTR_up), MP_ROM_PTR(&pb_type_imu_up_obj) },
257+
{ MP_ROM_QSTR(MP_QSTR_update_heading_correction), MP_ROM_PTR(&pb_type_imu_update_heading_correction_obj)},
233258
};
234259
STATIC MP_DEFINE_CONST_DICT(pb_type_imu_locals_dict, pb_type_imu_locals_dict_table);
235260

@@ -244,7 +269,7 @@ STATIC pb_type_imu_obj_t singleton_imu_obj = {
244269
};
245270

246271
// pybricks._common.IMU.__init__
247-
mp_obj_t pb_type_IMU_obj_new(mp_obj_t top_side_axis_in, mp_obj_t front_side_axis_in) {
272+
mp_obj_t pb_type_IMU_obj_new(mp_obj_t hub_in, mp_obj_t top_side_axis_in, mp_obj_t front_side_axis_in) {
248273

249274
// Set user base orientation.
250275
pbio_geometry_xyz_t front_side_axis;
@@ -256,6 +281,7 @@ mp_obj_t pb_type_IMU_obj_new(mp_obj_t top_side_axis_in, mp_obj_t front_side_axis
256281
pbio_imu_set_base_orientation(&front_side_axis, &top_side_axis);
257282

258283
// Return singleton instance.
284+
singleton_imu_obj.hub = hub_in;
259285
return MP_OBJ_FROM_PTR(&singleton_imu_obj);
260286
}
261287

pybricks/hubs/pb_type_essentialhub.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ STATIC mp_obj_t hubs_EssentialHub_make_new(const mp_obj_type_t *type, size_t n_a
5454
#endif
5555
self->buttons = pb_type_Keypad_obj_new(pb_type_button_pressed_hub_single_button);
5656
self->charger = pb_type_Charger_obj_new();
57-
self->imu = pb_type_IMU_obj_new(top_side_in, front_side_in);
57+
self->imu = pb_type_IMU_obj_new(MP_OBJ_FROM_PTR(self), top_side_in, front_side_in);
5858
self->light = common_ColorLight_internal_obj_new(pbsys_status_light);
5959
self->system = MP_OBJ_FROM_PTR(&pb_type_System);
6060
return MP_OBJ_FROM_PTR(self);

pybricks/hubs/pb_type_primehub.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ STATIC mp_obj_t hubs_PrimeHub_make_new(const mp_obj_type_t *type, size_t n_args,
7979
self->buttons = pb_type_Keypad_obj_new(pb_type_primehub_button_pressed);
8080
self->charger = pb_type_Charger_obj_new();
8181
self->display = pb_type_LightMatrix_obj_new(pbsys_hub_light_matrix);
82-
self->imu = pb_type_IMU_obj_new(top_side_in, front_side_in);
82+
self->imu = pb_type_IMU_obj_new(MP_OBJ_FROM_PTR(self), top_side_in, front_side_in);
8383
self->light = common_ColorLight_internal_obj_new(pbsys_status_light);
8484
self->speaker = mp_call_function_0(MP_OBJ_FROM_PTR(&pb_type_Speaker));
8585
self->system = MP_OBJ_FROM_PTR(&pb_type_System);

pybricks/hubs/pb_type_technichub.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ STATIC mp_obj_t hubs_TechnicHub_make_new(const mp_obj_type_t *type, size_t n_arg
4343
self->ble = pb_type_BLE_new(broadcast_channel_in, observe_channels_in);
4444
#endif
4545
self->button = pb_type_Keypad_obj_new(pb_type_button_pressed_hub_single_button);
46-
self->imu = pb_type_IMU_obj_new(top_side_in, front_side_in);
46+
self->imu = pb_type_IMU_obj_new(MP_OBJ_FROM_PTR(self), top_side_in, front_side_in);
4747
self->light = common_ColorLight_internal_obj_new(pbsys_status_light);
4848
self->system = MP_OBJ_FROM_PTR(&pb_type_System);
4949
return MP_OBJ_FROM_PTR(self);

0 commit comments

Comments
 (0)