Skip to content

Commit 947a377

Browse files
committed
added calibration
1 parent 8977269 commit 947a377

File tree

2 files changed

+84
-62
lines changed

2 files changed

+84
-62
lines changed

adafruit_bno08x/__init__.py

+58-47
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@
6868
_SAVE_DCD = const(0x6)
6969
_ME_CALIBRATE = const(0x7)
7070
_ME_CAL_CONFIG = const(0x00)
71+
_ME_GET_CAL = const(0x01)
7172

7273
# Calibrated Acceleration (m/s2)
7374
BNO_REPORT_ACCELEROMETER = const(0x01)
@@ -179,7 +180,12 @@
179180
["channel_number", "sequence_number", "data_length", "packet_byte_count",],
180181
)
181182

182-
REPORT_STATUS = ["Unreliable", "Accuracy low", "Accuracy medium", "Accuracy high"]
183+
REPORT_ACCURACY_STATUS = [
184+
"Accuracy Unreliable",
185+
"Low Accuracy",
186+
"Medium Accuracy",
187+
"High Accuracy",
188+
]
183189

184190

185191
class PacketError(Exception):
@@ -218,14 +224,17 @@ def _parse_sensor_report_data(report_bytes):
218224
else:
219225
format_str = "<h"
220226
results = []
227+
accuracy = unpack_from("<B", report_bytes, offset=2)[0]
228+
accuracy &= 0b11
221229

222230
for _offset_idx in range(count):
223231
total_offset = data_offset + (_offset_idx * 2)
224232
raw_data = unpack_from(format_str, report_bytes, offset=total_offset)[0]
225233
scaled_data = raw_data * scalar
226234
results.append(scaled_data)
235+
results_tuple = tuple(results)
227236

228-
return tuple(results)
237+
return (results_tuple, accuracy)
229238

230239

231240
def _parse_step_couter_report(report_bytes):
@@ -336,7 +345,7 @@ def _insert_command_request_report(
336345
return
337346

338347
for idx, param in enumerate(command_params):
339-
buffer[4 + 3 + idx] = param
348+
buffer[3 + idx] = param
340349

341350

342351
def _report_length(report_id):
@@ -477,7 +486,7 @@ def is_error(cls, header):
477486
return False
478487

479488

480-
class BNO08X:
489+
class BNO08X: # pylint: disable=too-many-instance-attributes, too-many-public-methods
481490
"""Library for the BNO08x IMUs from Hillcrest Laboratories
482491
483492
:param ~busio.I2C i2c_bus: The I2C bus the BNO08x is connected to.
@@ -500,8 +509,8 @@ def __init__(self, reset=None, debug=False):
500509
}
501510
self._dcd_saved_at = -1
502511
self._me_calibration_started_at = -1
503-
# self._sequence_number = {"in": [0, 0, 0, 0, 0, 0], "out": [0, 0, 0, 0, 0, 0]}
504-
# sef
512+
self._calibration_complete = False
513+
self._magnetometer_accuracy = 0
505514
self._wait_for_initialize = True
506515
self._init_complete = False
507516
self._id_read = False
@@ -696,6 +705,7 @@ def raw_magnetic(self):
696705
raise RuntimeError("No raw magnetic report found, is it enabled?") from None
697706

698707
def begin_calibration(self):
708+
"""Begin the sensor's self-calibration routine"""
699709
# start calibration for accel, gyro, and mag
700710
self._send_me_command(
701711
[
@@ -710,6 +720,25 @@ def begin_calibration(self):
710720
0, # reserved
711721
]
712722
)
723+
self._calibration_complete = False
724+
725+
@property
726+
def calibration_status(self):
727+
"""Get the status of the self-calibration"""
728+
self._send_me_command(
729+
[
730+
0, # calibrate accel
731+
0, # calibrate gyro
732+
0, # calibrate mag
733+
_ME_GET_CAL,
734+
0, # calibrate planar acceleration
735+
0, # 'on_table' calibration
736+
0, # reserved
737+
0, # reserved
738+
0, # reserved
739+
]
740+
)
741+
return self._magnetometer_accuracy
713742

714743
def _send_me_command(self, subcommand_params):
715744

@@ -718,7 +747,7 @@ def _send_me_command(self, subcommand_params):
718747
_insert_command_request_report(
719748
_ME_CALIBRATE,
720749
self._command_buffer, # should use self._data_buffer :\ but send_packet don't
721-
self.get_report_seq_id(_COMMAND_REQUEST),
750+
self._get_report_seq_id(_COMMAND_REQUEST),
722751
subcommand_params,
723752
)
724753
self._send_packet(_BNO_CHANNEL_CONTROL, local_buffer)
@@ -727,53 +756,32 @@ def _send_me_command(self, subcommand_params):
727756
self._process_available_packets()
728757
if self._me_calibration_started_at > start_time:
729758
break
730-
print("ME Started?")
731759

732760
def save_calibration_data(self):
761+
"""Save the self-calibration data"""
733762
# send a DCD save command
734-
# _COMMAND_REQUEST = const(0xF2)
735-
# _COMMAND_RESPONSE = const(0xF1)
736763
start_time = time.monotonic()
737764
local_buffer = bytearray(12)
738765
_insert_command_request_report(
739766
_SAVE_DCD,
740767
local_buffer, # should use self._data_buffer :\ but send_packet don't
741-
self.get_report_seq_id(_COMMAND_REQUEST),
768+
self._get_report_seq_id(_COMMAND_REQUEST),
742769
)
743770
self._send_packet(_BNO_CHANNEL_CONTROL, local_buffer)
744771
self._increment_report_seq(_COMMAND_REQUEST)
745772
while _elapsed(start_time) < _DEFAULT_TIMEOUT:
746773
self._process_available_packets()
747774
if self._dcd_saved_at > start_time:
748-
break
749-
print("DCD SAVED?")
750-
# Byte Description
751-
# 0 Report ID = 0xF2
752-
# 1 Sequence number
753-
# 2 Command
754-
# P0-9: a set of command-specific parameters. The interpretation of these
755-
# parameters is defined for each command.
756-
# 3 P0
757-
# 4 P1
758-
# 5 P2
759-
# 6 P3
760-
# 7 P4
761-
# 8 P5
762-
# 9 P6
763-
# 10 P7
764-
# 11 P8
765-
766-
# poll on DCD calibration status
767-
768-
# TODO: Make this a Packet creation
769-
770-
self._increment_report_seq(_COMMAND_REQUEST)
775+
return
776+
raise RuntimeError("Could not save calibration data")
771777

772778
############### private/helper methods ###############
773779
# # decorator?
774-
def _process_available_packets(self):
780+
def _process_available_packets(self, max_packets=None):
775781
processed_count = 0
776782
while self._data_ready:
783+
if max_packets and processed_count > max_packets:
784+
return
777785
# print("reading a packet")
778786
try:
779787
new_packet = self._read_packet()
@@ -866,15 +874,18 @@ def _handle_control_report(self, report_id, report_bytes):
866874

867875
def _handle_command_response(self, report_bytes):
868876
(report_body, response_values) = _parse_command_response(report_bytes)
869-
print(
870-
"report id: %x sequence number: %x command: %x command sequence number: %x response sequence number: %x"
871-
% report_body
872-
)
873-
report_id, sequence_number, command, command_sequence_number = report_body
874-
if command == _ME_CALIBRATE:
877+
878+
# report_id, seq_number, command, command_seq_number, response_seq_number) = report_body
879+
_report_id, _sequence_number, command = report_body
880+
881+
# status, accel_en, gyro_en, mag_en, planar_en, table_en, *_reserved) = response_values
882+
command_status, *_rest = response_values
883+
884+
if command == _ME_CALIBRATE and command_status == 0:
875885
self._me_calibration_started_at = time.monotonic()
886+
876887
if command == _SAVE_DCD:
877-
if response_values[0] == 0:
888+
if command_status == 0:
878889
self._dcd_saved_at = time.monotonic()
879890
else:
880891
raise RuntimeError("Unable to save calibration data")
@@ -917,9 +928,9 @@ def _process_report(self, report_id, report_bytes):
917928
activity_classification = _parse_activity_classifier_report(report_bytes)
918929
self._readings[BNO_REPORT_ACTIVITY_CLASSIFIER] = activity_classification
919930
return
920-
921-
sensor_data = _parse_sensor_report_data(report_bytes)
922-
931+
sensor_data, accuracy = _parse_sensor_report_data(report_bytes)
932+
if report_id == BNO_REPORT_MAGNETOMETER:
933+
self._magnetometer_accuracy = accuracy
923934
# TODO: FIXME; Sensor reports are batched in a LIFO which means that multiple reports
924935
# for the same type will end with the oldest/last being kept and the other
925936
# newer reports thrown away
@@ -965,7 +976,7 @@ def enable_feature(self, feature_id):
965976
start_time = time.monotonic() # 1
966977

967978
while _elapsed(start_time) < _FEATURE_ENABLE_TIMEOUT:
968-
self._process_available_packets()
979+
self._process_available_packets(max_packets=10)
969980
if feature_id in self._readings:
970981
return
971982
raise RuntimeError("Was not able to enable feature", feature_id)
@@ -1070,4 +1081,4 @@ def _increment_report_seq(self, report_id):
10701081
self._two_ended_sequence_numbers[report_id] = (current + 1) % 256
10711082

10721083
def _get_report_seq_id(self, report_id):
1073-
return self._two_ended_sequence_numbers[report_id]
1084+
return self._two_ended_sequence_numbers.get(report_id, 0)

examples/bno08x_calibration.py

+26-15
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,23 @@
88
import adafruit_bno08x
99
from adafruit_bno08x.i2c import BNO08X_I2C
1010

11-
i2c = busio.I2C(board.SCL, board.SDA, frequency=800000)
11+
i2c = busio.I2C(board.SCL, board.SDA)
1212
reset_pin = DigitalInOut(board.D5)
1313
bno = BNO08X_I2C(i2c, reset=reset_pin, debug=False)
1414

15+
bno.begin_calibration()
1516
# TODO: UPDATE UART/SPI
16-
bno.enable_feature(adafruit_bno08x.BNO_REPORT_GYROSCOPE)
17-
# bno.enable_feature(adafruit_bno08x.BNO_REPORT_MAGNETOMETER)
17+
bno.enable_feature(adafruit_bno08x.BNO_REPORT_MAGNETOMETER)
1818
bno.enable_feature(adafruit_bno08x.BNO_REPORT_GAME_ROTATION_VECTOR)
19-
bno.begin_calibration()
19+
start_time = time.monotonic()
20+
calibration_good_at = None
2021
while True:
2122
time.sleep(0.1)
22-
print("Gyro:")
23-
gyro_x, gyro_y, gyro_z = bno.gyro # pylint:disable=no-member
24-
print("X: %0.6f Y: %0.6f Z: %0.6f rads/s" % (gyro_x, gyro_y, gyro_z))
25-
print("")
2623

27-
# print("Magnetometer:")
28-
# mag_x, mag_y, mag_z = bno.magnetic # pylint:disable=no-member
29-
# print("X: %0.6f Y: %0.6f Z: %0.6f uT" % (mag_x, mag_y, mag_z))
30-
# print("")
24+
print("Magnetometer:")
25+
mag_x, mag_y, mag_z = bno.magnetic # pylint:disable=no-member
26+
print("X: %0.6f Y: %0.6f Z: %0.6f uT" % (mag_x, mag_y, mag_z))
27+
print("")
3128

3229
print("Game Rotation Vector Quaternion:")
3330
(
@@ -40,6 +37,20 @@
4037
"I: %0.6f J: %0.6f K: %0.6f Real: %0.6f"
4138
% (game_quat_i, game_quat_j, game_quat_k, game_quat_real)
4239
)
43-
print("")
44-
bno.save_calibration_data()
45-
print("calibration done")
40+
calibration_status = bno.calibration_status
41+
print(
42+
"Magnetometer Calibration quality:",
43+
adafruit_bno08x.REPORT_ACCURACY_STATUS[calibration_status],
44+
" (%d)" % calibration_status,
45+
)
46+
if not calibration_good_at and calibration_status >= 2:
47+
calibration_good_at = time.monotonic()
48+
if calibration_good_at and (time.monotonic() - calibration_good_at > 5.0):
49+
input_str = input("\n\nEnter S to save or anything else to continue: ")
50+
if input_str.strip().lower() == "s":
51+
bno.save_calibration_data()
52+
break
53+
calibration_good_at = None
54+
print("**************************************************************")
55+
56+
print("calibration done")

0 commit comments

Comments
 (0)