Перейти к содержанию
Материалы раздела

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.RVC1Режим RVC
bno085.SHTP2Режим SHTP

Интерфейсы

КонстантаЗначениеОписание
bno085.UART1UART интерфейс
bno085.I2C2I2C интерфейс
bno085.SPI3SPI интерфейс

I2C адреса

КонстантаЗначениеОписание
bno085.I2C_ADDR0x4AI2C адрес по умолчанию (SA0 = 0)
bno085.I2C_ADDR_ALT0x4BАльтернативный 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