BNO085
Модуль управляет девятиосевым инерциальным датчиком BNO085.
Обзор
BNO085 поддерживает несколько интерфейсов и режимов работы:
Интерфейсы
- UART — режим RVC или SHTP;
- I2C — только режим SHTP;
- SPI — только режим SHTP.
Режимы
- RVC (Robot Vacuum Cleaner) — режим UART со скоростью 115200 бод и частотой около 100 Гц. Возвращает углы Эйлера и ускорение;
- SHTP (Sensor Hub Transport Protocol) — высокоскоростной режим для UART, I2C и SPI. Поддерживает все перечисленные ниже типы данных.
При повреждении или потере байтов в потоке SHTP модуль восстанавливает приём со следующего корректного пакета. Числовые значения возвращаются уже пересчитанными в единицы, указанные в таблице.
Поддерживаемые данные SHTP
| Имя | Описание | Единицы |
|---|---|---|
| accel | Акселерометр | м/с² |
| gyro | Гироскоп | рад/с |
| mag | Магнитометр | мкТл |
| linear_accel | Линейное ускорение (без гравитации) | м/с² |
| gravity | Вектор гравитации | м/с² |
| rotation | Вектор вращения (кватернион + точность) | - |
| game_rotation | Игровой вектор вращения (без магнитометра) | - |
| geomag_rotation | Геомагнитный вектор вращения | - |
| step_counter | Счётчик шагов | шаги |
| stability | Классификация стабильности (0-5) | - |
| activity | Распознавание активности | - |
bno085.init_rvc(uart_id, tx_pin, rx_pin, [baudrate])
Инициализация драйвера в режиме RVC (Robot Vacuum Cleaner).
Аргументы:
- uart_id: номер UART (0, 1 или 2)
- tx_pin: номер GPIO для TX
- rx_pin: номер GPIO для RX
- baudrate (опционально): скорость UART, по умолчанию 115200
Возвращает: true в случае успеха.
-- Инициализация RVC на UART2
bno085.init_rvc(2, 17, 16)
-- С указанием скорости
bno085.init_rvc(2, 17, 16, 115200)
bno085.init_shtp(uart_id, tx_pin, rx_pin, [baudrate])
Инициализация драйвера в режиме SHTP (высокоскоростной).
Аргументы:
- uart_id: номер UART (0, 1 или 2)
- tx_pin: номер GPIO для TX
- rx_pin: номер GPIO для RX
- baudrate (опционально): скорость UART, по умолчанию 3000000 (3 МБод)
Возвращает: true в случае успеха.
Драйвер проверяет начальную синхронизацию SHTP. Ошибка транспорта или тайм-аут
синхронизации вызывают исключение вместо возврата true.
-- Инициализация SHTP на UART2
bno085.init_shtp(2, 17, 16)
-- С указанием скорости 1 МБод
bno085.init_shtp(2, 17, 16, 1000000)
bno085.init_i2c(i2c_device, [address])
Инициализация драйвера в режиме SHTP через I2C.
Аргументы:
- i2c_device: объект I2C, возвращённый
i2c.attach() - address (опционально): I2C адрес сенсора, по умолчанию 0x4A (bno085.I2C_ADDR)
Возвращает: true в случае успеха.
Объект I2C удерживается драйвером до вызова bno085.close() или повторной
инициализации. Ошибка обмена вызывает исключение.
-- Подключение I2C устройства
i2c_dev = i2c.attach(i2c.I2C0, i2c.MASTER, 400000)
-- Инициализация BNO085 через I2C
bno085.init_i2c(i2c_dev)
-- С указанием альтернативного адреса (SA0 = 1)
bno085.init_i2c(i2c_dev, bno085.I2C_ADDR_ALT) -- 0x4B
bno085.init_spi(spi_device)
Инициализация драйвера в режиме SHTP через SPI.
Аргументы:
- spi_device: объект SPI, возвращённый
spi.attach()
Возвращает: true в случае успеха.
Объект SPI удерживается драйвером до вызова bno085.close() или повторной
инициализации. Ошибка обмена вызывает исключение.
-- Подключение SPI устройства
spi_dev = spi.attach(spi.SPI3, spi.MASTER, cs_pin, 3000000, 8, 0)
-- Инициализация BNO085 через SPI
bno085.init_spi(spi_dev)
bno085.enable(sensors)
Включение сенсоров с заданной частотой обновления (только режим SHTP).
Аргументы (новый API):
- sensors: таблица {sensor_name = hz, …}, где hz - частота в Герцах (0 для отключения)
Аргументы (старый API для обратной совместимости):
- accel_hz: частота акселерометра в Гц
- gyro_hz (опционально): частота гироскопа в Гц
Возвращает: true в случае успеха.
Для activity драйвер запрашивает все девять классов активности. Если хотя бы
одна команда настройки не передана датчику, функция вызывает исключение.
-- Новый универсальный API
bno085.enable({accel=100, gyro=200, rotation=50, mag=10})
-- Включить только акселерометр и гироскоп
bno085.enable({accel=100, gyro=100})
-- Все доступные сенсоры
bno085.enable({
accel = 100, -- Акселерометр 100 Гц
gyro = 200, -- Гироскоп 200 Гц
mag = 10, -- Магнитометр 10 Гц
linear_accel = 100, -- Линейное ускорение
gravity = 50, -- Гравитация
rotation = 100, -- Вектор вращения
game_rotation = 100, -- Игровой вектор вращения
geomag_rotation = 50, -- Геомагнитный вектор
step_counter = 1, -- Счётчик шагов
stability = 10, -- Стабильность
activity = 1 -- Активность
})
-- Старый API (для обратной совместимости)
bno085.enable(100, 200) -- accel=100Hz, gyro=200Hz
bno085.read()
Чтение одного пакета данных.
Аргументы: нет
Возвращает: таблицу с данными или nil, если данных нет. Ошибка транспорта вызывает исключение.
Режим RVC
Возвращаемая таблица содержит:
- yaw: угол рыскания (градусы)
- pitch: угол тангажа (градусы)
- roll: угол крена (градусы)
- ax, ay, az: ускорение по осям (м/с²)
- index: индекс пакета
Пакет с неверной контрольной суммой отбрасывается и не возвращается как измерение.
bno085.init_rvc(2, 17, 16)
local data = bno085.read()
if data then
print(string.format("Yaw: %.2f, Pitch: %.2f, Roll: %.2f",
data.yaw, data.pitch, data.roll))
print(string.format("Accel: %.3f, %.3f, %.3f",
data.ax, data.ay, data.az))
end
Режим SHTP
Возвращаемая таблица может содержать (в зависимости от включённых сенсоров):
Акселерометр:
- ax, ay, az: ускорение по осям (м/с²)
- accel_status: статус калибровки (0-3)
Гироскоп:
- gx, gy, gz: угловая скорость по осям (рад/с)
- gyro_status: статус калибровки (0-3)
Магнитометр:
- mx, my, mz: магнитное поле по осям (мкТл)
- mag_status: статус калибровки (0-3)
Линейное ускорение:
- lax, lay, laz: линейное ускорение без гравитации (м/с²)
- linear_status: статус калибровки (0-3)
Гравитация:
- grx, gry, grz: вектор гравитации (м/с²)
- gravity_status: статус калибровки (0-3)
Вектор вращения (кватернион):
- qi, qj, qk, qr: компоненты кватерниона
- rotation_accuracy: точность в радианах
- rotation_status: статус калибровки (0-3)
Игровой вектор вращения:
- gqi, gqj, gqk, gqr: компоненты кватерниона
- game_rotation_status: статус калибровки (0-3)
Геомагнитный вектор вращения:
- mqi, mqj, mqk, mqr: компоненты кватерниона
- geomag_accuracy: точность в радианах
- geomag_status: статус калибровки (0-3)
Дополнительные сенсоры:
- steps: количество шагов
- stability: классификация стабильности (0-5)
- activity: наиболее вероятная активность
- activity_confidence: таблица с уверенностью для каждого типа активности
Временные метки:
- ts: временная метка системы (секунды)
- sensor_ts: временная метка сенсора
bno085.init_shtp(2, 17, 16)
bno085.enable({accel=100, gyro=100, rotation=50})
local data = bno085.read()
if data then
-- Акселерометр
if data.ax then
print(string.format("Accel: %.3f, %.3f, %.3f m/s²",
data.ax, data.ay, data.az))
end
-- Гироскоп
if data.gx then
print(string.format("Gyro: %.4f, %.4f, %.4f rad/s",
data.gx, data.gy, data.gz))
end
-- Кватернион вращения
if data.qi then
print(string.format("Rotation: i=%.4f j=%.4f k=%.4f r=%.4f",
data.qi, data.qj, data.qk, data.qr))
end
end
bno085.read_all([max])
Чтение всех доступных пакетов данных.
Аргументы:
- max (опционально): максимальное количество пакетов для чтения (по умолчанию 50, максимум 200)
Возвращает: массив таблиц с данными (может быть пустым).
-- Чтение всех доступных пакетов
local packets = bno085.read_all()
print("Получено пакетов: " .. #packets)
for i, data in ipairs(packets) do
if data.ax then
print(string.format("[%d] Accel: %.3f, %.3f, %.3f",
i, data.ax, data.ay, data.az))
end
end
-- Чтение максимум 10 пакетов
local packets = bno085.read_all(10)
bno085.stats()
Получение статистики пакетов и ошибок (только режим SHTP).
Аргументы: нет
Возвращает: таблицу со статистикой:
- received: всего получено байт
- valid: валидных пакетов
- invalid: невалидных/повреждённых пакетов
- sync_lost: потерь синхронизации
- escape_errors: ошибок escape-последовательностей
- length_errors: ошибок длины пакета
- channel_errors: ошибок канала
- sequence_gaps: пропущенных пакетов (по sequence number)
- buffer_overflows: переполнений буфера
- error_rate: процент ошибок
local stats = bno085.stats()
print("Статистика BNO085:")
print(" Получено: " .. stats.received)
print(" Валидных: " .. stats.valid)
print(" Ошибочных: " .. stats.invalid)
print(" Пропущено: " .. stats.sequence_gaps)
print(" Escape ошибок: " .. stats.escape_errors)
if stats.error_rate then
print(string.format(" Уровень ошибок: %.2f%%", stats.error_rate))
end
bno085.reset()
Программный сброс сенсора (только режим SHTP).
Аргументы: нет
Возвращает: true в случае успеха.
-- Сброс сенсора
bno085.reset()
-- После сброса нужно заново включить сенсоры
bno085.enable({accel=100, gyro=100})
bno085.flush()
Очистка входного буфера UART.
Аргументы: нет
Возвращает: true в случае успеха.
-- Очистка буфера перед началом чтения
bno085.flush()
bno085.close()
Закрытие драйвера и освобождение ресурсов.
Аргументы: нет
Возвращает: true в случае успеха.
-- Закрытие драйвера
bno085.close()
bno085.mode()
Получение текущего режима работы.
Аргументы: нет
Возвращает: строку с режимом (“rvc”, “shtp”) или nil, если драйвер не инициализирован.
local mode = bno085.mode()
if mode then
print("Текущий режим: " .. mode)
else
print("Драйвер не инициализирован")
end
bno085.interface()
Получение текущего интерфейса.
Аргументы: нет
Возвращает: строку с интерфейсом (“uart”, “i2c”, “spi”) или nil, если драйвер не инициализирован.
local iface = bno085.interface()
if iface then
print("Текущий интерфейс: " .. iface)
else
print("Драйвер не инициализирован")
end
Константы
Режимы работы
| Константа | Значение | Описание |
|---|---|---|
| bno085.RVC | 1 | Режим RVC |
| bno085.SHTP | 2 | Режим SHTP |
Интерфейсы
| Константа | Значение | Описание |
|---|---|---|
| bno085.UART | 1 | UART интерфейс |
| bno085.I2C | 2 | I2C интерфейс |
| bno085.SPI | 3 | SPI интерфейс |
I2C адреса
| Константа | Значение | Описание |
|---|---|---|
| bno085.I2C_ADDR | 0x4A | I2C адрес по умолчанию (SA0 = 0) |
| bno085.I2C_ADDR_ALT | 0x4B | Альтернативный I2C адрес (SA0 = 1) |
Примеры использования
Простое чтение в режиме RVC
-- Инициализация
bno085.init_rvc(2, 17, 16)
-- Цикл чтения
while true do
local data = bno085.read()
if data then
print(string.format("Yaw=%.1f Pitch=%.1f Roll=%.1f",
data.yaw, data.pitch, data.roll))
end
tmr.delayms(10)
end
Высокоскоростное чтение в режиме SHTP
-- Инициализация SHTP
bno085.init_shtp(2, 17, 16, 3000000)
-- Включение сенсоров
bno085.enable({
accel = 200,
gyro = 200,
rotation = 100
})
-- Чтение с мониторингом статистики
local last_stats = 0
while true do
local packets = bno085.read_all(50)
for _, data in ipairs(packets) do
if data.qi then
-- Обработка кватерниона вращения
local yaw = math.atan2(2*(data.qr*data.qk + data.qi*data.qj),
1 - 2*(data.qj*data.qj + data.qk*data.qk))
print(string.format("Yaw: %.2f rad", yaw))
end
end
-- Периодический вывод статистики
local now = os.time()
if now - last_stats >= 5 then
local stats = bno085.stats()
print(string.format("Stats: valid=%d, errors=%d (%.2f%%)",
stats.valid, stats.invalid, stats.error_rate or 0))
last_stats = now
end
tmr.delayms(5)
end
Распознавание активности
bno085.init_shtp(2, 17, 16)
bno085.enable({activity=1, step_counter=1})
local activity_names = {
[0] = "unknown",
[1] = "in_vehicle",
[2] = "on_bicycle",
[3] = "on_foot",
[4] = "still",
[5] = "tilting",
[6] = "walking",
[7] = "running",
[8] = "on_stairs"
}
while true do
local data = bno085.read()
if data then
if data.activity then
print("Активность: " .. (activity_names[data.activity] or "?"))
end
if data.steps then
print("Шаги: " .. data.steps)
end
end
tmr.delayms(100)
end
Чтение через I2C
-- Подключение I2C (шина 0, 400 кГц)
local i2c_dev = i2c.attach(i2c.I2C0, i2c.MASTER, 400000)
-- Инициализация BNO085 через I2C
bno085.init_i2c(i2c_dev)
-- Включение сенсоров
bno085.enable({accel=100, gyro=100, rotation=50})
-- Проверка интерфейса
print("Интерфейс: " .. bno085.interface()) -- "i2c"
-- Цикл чтения
while true do
local data = bno085.read()
if data then
if data.ax then
print(string.format("Accel: %.3f, %.3f, %.3f",
data.ax, data.ay, data.az))
end
end
tmr.delayms(10)
end
Чтение через SPI
-- Подключение SPI (шина 3, CS на GPIO 5, режим 0, 3 МГц)
local spi_dev = spi.attach(spi.SPI3, spi.MASTER, pio.GPIO5, 3000000, 8, 0)
-- Инициализация BNO085 через SPI
bno085.init_spi(spi_dev)
-- Включение сенсоров
bno085.enable({accel=200, gyro=200})
-- Проверка интерфейса
print("Интерфейс: " .. bno085.interface()) -- "spi"
-- Цикл чтения
while true do
local packets = bno085.read_all(20)
for _, data in ipairs(packets) do
if data.gx then
print(string.format("Gyro: %.4f, %.4f, %.4f rad/s",
data.gx, data.gy, data.gz))
end
end
tmr.delayms(5)
end