IMU-сенсор 10 DOF v1 для Raspberry Pi Pico: инструкция, примеры использования и документация
Используйте IMU-сенсор 10 DOF v1 для определения положения вашего девайса в пространстве. Модуль выполнен в родном форм-факторе для платформы Raspberry Pi Pico.
IMU-модуль 10 DOF включает в себя:
- Акселерометр для определения величины ускорения свободного падения по осям X, Y, Z.
- Гироскоп для определения угловой скорости вокруг собственных осей X, Y, Z.
- Магнитометр для определения углов между собственными осями сенсора X, Y, Z и силовыми линиями магнитного поля Земли.
- Барометр для определения атмосферного давления, высоты над уровнем моря и температуры.
Подключение и настройка
На роль контроллера для работы с IMU-модулем рассмотрим платформу Raspberry Pi Pico.
Что понадобится
Инструкция по сборке
- Установите Raspberry Pi Pico сверху на контактные колодки платы с IMU-сенсора. Для правильности коммуникации воспользуйтесь меткой USB-порта на плате дисплея.
- Подключите полученный сет дисплеем вверх к компьютеру по USB. Для коммуникации используйте кабель micro-USB.
Примеры работы
Отобразим демонстрацию работы IMU-сенсора. Для работы примеров скачайте и установите библиотеку amperka_pico_imu_10dof.
Вывод показаний барометра
Для начала выведем в консоль показания барометра.
- barometer_demo.py
# Импортируем необходимые модули import time from amperka_pico_imu_10dof import lps22hb # Печатаем тестовую строку в консоль print("Pressure Sensor Test Program") # Создаём объект для работы с барометром barometer = lps22hb.LPS22HB() # Выводим атмосферное давление, температуру и высоту над уровнем моря while True: pressure = barometer.read_pressure_millimeters_hg() temperature = barometer.read_temperature_c() altitude = barometer.read_altitude() print(f"Pressure {round(pressure,2)} mmHg") print(f"Temperature {round(temperature,2)} °C") print(f"Altitude {round(altitude,2)} m\n") time.sleep(0.1)
Вывод показаний акселерометра
Далее, выведем в консоль показания акселерометра: величины ускорения свободного падения по осям X, Y и Z.
- accelerometer_demo.py
# Импортируем необходимые модули from amperka_pico_imu_10dof import icm20948 import time import math # Печатаем тестовую строку в консоль print("Accelerometer Test Program") # Создаём объект для работы с IMU-сенсором imu=icm20948.ICM20948() # Выводим ускорения свободного падения относительно осей X, Y и Z в «сырых значениях» # Выводим ускорения свободного падения относительно осей X, Y и Z в G # Выводим ускорения свободного падения относительно осей X, Y и Z в м/с² while True: accelerometer = imu.read_accelerometer() accelerometer_g = imu.read_accelerometer_g() accelerometer_a = imu.read_accelerometer_a() print(f"Accelerometer (Raw):\t{accelerometer}") print(f"Accelerometer (G):\t{accelerometer_g}") print(f"Accelerometer (A):\t{accelerometer_a}") print() time.sleep(0.1)
Вывод показаний гироскопа
В продолжении, выведем в консоль показания гироскопа: угловую скорость вокруг собственных осей X, Y, Z.
- gyroscope_demo.py
# Импортируем необходимые модули from amperka_pico_imu_10dof import icm20948 import time import math # Печатаем тестовую строку в консоль print("IMU-sensor Test Program") # Создаём объект для работы с IMU-сенсором imu=icm20948.ICM20948() # Выводим угловую скорость относительно осей X, Y и Z в «сырых значениях» # Выводим угловую скорость относительно осей X, Y и Z в градусах в секунду # Выводим угловую скорость относительно осей X, Y и Z в радианах while True: gyroscope = imu.read_gyroscope() gyroscope_deg = imu.read_gyroscope_deg() gyroscope_rad = imu.read_gyroscope_rad() print(f"Gyroscope (Raw):\t{gyroscope}") print(f"Gyroscope (Deg):\t{gyroscope_deg}") print(f"Gyroscope (Rad):\t{gyroscope_rad}") print() time.sleep(0.1)
Вывод показаний магнитометра
Следующим этапом, выведем в консоль показания магнитометра: значения напряженности магнитного поля по осям X, Y, Z.
- magnetometer_demo.py
# Импортируем необходимые модули from amperka_pico_imu_10dof import icm20948 import time import math # Печатаем тестовую строку в консоль print("IMU-sensor Test Program") # Создаём объект для работы с IMU-сенсором imu=icm20948.ICM20948() # Выводим значения напряженности магнитного поля по осям X, Y, Z в «сырых значениях» # Выводим значения напряженности магнитного поля по осям X, Y, Z в «мкТс» while True: magnetometer = imu.read_magnetometer() magnetometer_uT = imu.read_magnetometer_uT() print(f"Magnetometer (RAW):\t{magnetometer}") print(f"Magnetometer (uT):\t{magnetometer_uT}") print() time.sleep(0.1)
Вывод показаний инерциального датчика
Выведем показания сразу трёх сенсоров:
- акселерометра: ускорения свободного падения по осям X, Y и Z.
- гироскопа: угловую скорость вокруг собственных осей X, Y, Z.
- магнитометра: напряженность магнитного поля по осям X, Y, Z.
- imu_demo.py
# Импортируем необходимые модули from amperka_pico_imu_10dof import icm20948 import time import math # Печатаем тестовую строку в консоль print("IMU-sensor Test Program") # Создаём объект для работы с IMU-сенсором imu=icm20948.ICM20948() # Выводим ускорения свободного падения относительно осей X, Y и Z в м/с² # Выводим угловую скорость относительно осей X, Y и Z в градусах в секунду # Выводим значения напряженности магнитного поля по осям X, Y, Z в «мкТс» while True: accelerometer = imu.read_accelerometer_a() gyroscope = imu.read_gyroscope_deg() magnetometer = imu.read_magnetometer_uT() print(f"Accelerometer:\t{accelerometer}") print(f"Gyroscope:\t{gyroscope}") print(f"Magnetometer:\t{magnetometer}") print() time.sleep(0.1)
Ориентация объекта в пространстве
Определим ориентацию объекта в пространстве с учётом направления на север. Для этого показания акселерометра, гироскопа и компаса используем как входные данные для фильтра Маджвика. На выходе получим кватернионы, которые для наглядности пересчитаем в самолётные углы Эйлера.
- imu_9DOF.py
# Импортируем необходимые модули from amperka_pico_imu_10dof import icm20948 import time import math # Печатаем тестовую строку в консоль print("IMU-sensor Test Program") # Создаём объект для работы с IMU-сенсором imu=icm20948.ICM20948() while True: # Считываем данных с акселерометра accelerometer = imu.read_accelerometer_g() # Считываем данных с гироскопа gyroscope = imu.read_gyroscope_rad() # Считываем данных с магнитометра magnetometer = imu.read_magnetometer_uT() ax = accelerometer[0] ay = accelerometer[1] az = accelerometer[2] gx = gyroscope[0] gy = gyroscope[1] gz = gyroscope[2] mx = magnetometer[0] my = magnetometer[1] mz = magnetometer[2] # Обновляем входные данные в фильтр Маджвика imu.AHRS_update(gx, gy, gz, ax, ay, az, mx, my, mz) # Получаем из фильтра углы Эйлера: yaw, pitch и roll yaw = imu.get_yaw(); pitch = imu.get_pitch(); roll = imu.get_roll(); print(f"Yaw:\t{yaw}") print(f"Pitch:\t{pitch }") print(f"Roll:\t{roll}") print() time.sleep(0.1)
Визуальное отображение объекта в пространстве
Для наглядности приведём пример отображение IMU-сенсора в пространстве в виде самолёта.
Порядок действий
- Скачайте библиотеки для Processing и распакуйте их в директорию хранения модулей для Processing:
Код для Pico
- imu_visualization.py
# Импортируем необходимые модули from amperka_pico_imu_10dof import icm20948 import time import math # Печатаем тестовую строку в консоль print("IMU-sensor Test Program") # Создаём объект для работы с IMU-сенсором imu=icm20948.ICM20948() while True: # Считываем данных с акселерометра accelerometer = imu.read_accelerometer_g() # Считываем данных с гироскопа gyroscope = imu.read_gyroscope_rad() # Считываем данных с магнитометра magnetometer = imu.read_magnetometer_() ax = accelerometer[0] ay = accelerometer[1] az = accelerometer[2] gx = gyroscope[0] gy = gyroscope[1] gz = gyroscope[2] mx = magnetometer[0] my = magnetometer[1] mz = magnetometer[2] # Обновляем входные данные в фильтр Маджвика imu.AHRS_update(gx, gy, gz, ax, ay, az, mx, my, mz) # Считываем кватернион quaternion = imu.read_quaternion() # Выводим кватернион в консоль для передачи в Processing print(f"{quaternion[0]},{quaternion[1]},{quaternion[2]},{quaternion[3]}") time.sleep(0.1)
Код для Processing
- imu_visualization_draw.pde
import processing.serial.*; import toxi.geom.*; import toxi.processing.*; // NOTE: requires ToxicLibs to be installed in order to run properly. // 1. Download from http://toxiclibs.org/downloads // 2. Extract into [userdir]/Processing/libraries // (location may be different on Mac/Linux) // 3. Run and bask in awesomeness // The serial port Serial port; String message; float[] q = new float[4]; Quaternion quat = new Quaternion(1, 0, 0, 0); // New line character in ASCII final char newLine = '\n'; String [] massQ = new String [4]; float[] ypr = new float[3]; void setup() { // Size form 400x400 size(400, 400, P3D); // Open serial port // Replace "COM7" with the COM port on which your arduino is connected port = new Serial(this, "COM7", 9600); } void draw() { // Read and parse incoming serial message serialEvent(); // Set background to black background(0); printQuaternions(); printYawPitchRoll(); // Set position to centre translate(width / 2, height / 2); // Begin object pushMatrix(); float[] axis = quat.toAxisAngle(); rotate(axis[0], axis[2], axis[3], axis[1]); // Draw main body in red drawBody(); // Draw front-facing tip in blue drawCylinder(); // Draw Triangles drawTriangles(); // Draw Quads drawQuads(); // End of object popMatrix(); // Send character 's' to Arduino //port.write('s'); } void serialEvent() { // Read from port until new line (ASCII code 13) message = port.readStringUntil(newLine); if (message != null) { // Split message by commas and store in String array massQ = split(message, ","); q[0] = float(massQ[0]); q[1] = float(massQ[1]); q[2] = float(massQ[2]); q[3] = float(massQ[3]); } // Print values to console print(q[0]); print("\t"); print(q[1]); print("\t"); print(q[2]); print("\t"); print(q[3]); println("\t"); // Set our toxilibs quaternion to new data quat.set(q[0], q[1], q[2], q[3]); } void drawCylinder() { float topRadius = 0; float bottomRadius = 20; float tall = 20; int sides = 8; // Begin object pushMatrix(); translate(0, 0, -120); rotateX(PI/2); fill(0, 0, 255, 200); float angle = 0; float angleIncrement = TWO_PI / sides; beginShape(QUAD_STRIP); for (int i = 0; i < sides + 1; ++i) { vertex(topRadius * cos(angle), 0, topRadius * sin(angle)); vertex(bottomRadius * cos(angle), tall, bottomRadius * sin(angle)); angle += angleIncrement; } endShape(); // if it is not a cone, draw the circular top cap if (topRadius != 0) { angle = 0; beginShape(TRIANGLE_FAN); // Center point vertex(0, 0, 0); for (int i = 0; i < sides + 1; i++) { vertex(topRadius * cos(angle), 0, topRadius * sin(angle)); angle += angleIncrement; } endShape(); } // If it is not a cone, draw the circular bottom cap if (bottomRadius != 0) { angle = 0; beginShape(TRIANGLE_FAN); // Center point vertex(0, tall, 0); for (int i = 0; i < sides + 1; i++) { vertex(bottomRadius * cos(angle), tall, bottomRadius * sin(angle)); angle += angleIncrement; } endShape(); } popMatrix(); } void drawBody() { fill(255, 0, 0, 200); box(10, 10, 200); } void drawTriangles() { // Draw wings and tail fin in green fill(0, 255, 0, 200); beginShape(TRIANGLES); // Wing top layer vertex(-100, 2, 30); vertex(0, 2, -80); vertex(100, 2, 30); // Wing bottom layer vertex(-100, -2, 30); vertex(0, -2, -80); vertex(100, -2, 30); // Tail left layer vertex(-2, 0, 98); vertex(-2, -30, 98); vertex(-2, 0, 70); // Tail right layer vertex( 2, 0, 98); vertex( 2, -30, 98); vertex( 2, 0, 70); endShape(); } void drawQuads() { beginShape(QUADS); vertex(-100, 2, 30); vertex(-100, -2, 30); vertex( 0, -2, -80); vertex( 0, 2, -80); vertex( 100, 2, 30); vertex( 100, -2, 30); vertex( 0, -2, -80); vertex( 0, 2, -80); vertex(-100, 2, 30); vertex(-100, -2, 30); vertex(100, -2, 30); vertex(100, 2, 30); vertex(-2, 0, 98); vertex(2, 0, 98); vertex(2, -30, 98); vertex(-2, -30, 98); vertex(-2, 0, 98); vertex(2, 0, 98); vertex(2, 0, 70); vertex(-2, 0, 70); vertex(-2, -30, 98); vertex(2, -30, 98); vertex(2, 0, 70); vertex(-2, 0, 70); endShape(); } void printQuaternions() { // Set text mode to shape textMode(SHAPE); textSize(13); fill(255, 255, 255); text("Quaternions:", 20, 20, 10); text(q[0], 20, 40, 10); text(q[1], 20, 60, 10); text(q[2], 20, 80, 10); text(q[3], 20, 100, 10); } void printYawPitchRoll() { // Calculate yaw/pitch/roll angles ypr[0] = atan2(2*q[1]*q[2] - 2*q[0]*q[3], 2*q[0]*q[0] + 2*q[1]*q[1] - 1) * 57.2; ypr[1] = atan2(2 * q[2] * q[3] - 2 * q[0] * q[1], 2 * q[0] * q[0] + 2 * q[3] * q[3] - 1) * 57.2; ypr[2] = -atan2(2 * (q[0] * q[2] - q[1] * q[3]), 1 - 2 * (q[2] * q[2] + q[1] *q[1])) * 57.2; text("Yaw:", 150, 20, 10); text(ypr[0], 150, 40, 10); text("Pitch:", 220, 20, 10); text(ypr[1], 220, 40, 10); text("Roll:", 290, 20, 10); text(ypr[2], 290, 40, 10); }
При запуске визуализации на «processinge» откроется окно с графическим отображением IMU-сенсора в виде самолёта. Самолёт на экране будет повторять перемещения IMU-сенсора в пространстве.
Элементы платы
Инерциальный чип ICM-20948
За 9 степеней свободы отвечает инерциальный чип ICM-20948, который включает в себя акселерометр, гироскоп и магнитометр.
- Акселерометр для определения величины ускорения свободного падения по осям X, Y, Z.
- Гироскоп для определения угловой скорости вокруг собственных осей X, Y, Z.
- Магнитометр для определения углов между собственными осями сенсора X, Y, Z и силовыми линиями магнитного поля Земли.
За определения десятой степени свободы отвечает отдельный чип барометр LPS22HB, который подскажет высоту над уровнем моря.
Барометр LPS22HB
Датчик атмосферного давления LPS22HB служит альтиметром для носимого гаджета, а также барометром для метеостанции.
Светодиодная индикация
Имя светодиода | Назначение |
---|---|
PWR | Индикатор питания. Горит — на плату поступает напряжение, не горит — на плату не поступает напряжение. |
Преобразователь питания RT9193-33
Понижающий DC-DC преобразователь RT9193-33 обеспечивает питание барометра и другой логики на плате. Входное напряжение поступает от пина VSYS управляющей платы. Выходное напряжение 3,3 В с максимальным выходным током 300 мА.
Преобразователь питания RT9193-18
Понижающий DC-DC преобразователь RT9193-18 обеспечивает питание инерциального датчика и светодиодной индикации. Входное напряжение поступает от преобразователя RT9193-33. Выходное напряжение 1,8 В с максимальным выходным током 300 мА.
Контактные колодки
Плата IMU-сенсор подключается к контроллерам форм-фактора Raspberry Pi Pico через две параллельных контактных колодки на 20 пинов. Все подробности коммуникации и задействованные контакты читайте в разделе распиновка.
Распиновка
Принципиальная и монтажная схема
Габаритный чертёж
Характеристики
- Модель: Waveshare Pico-10DOF-IMU (SKU 19358)
- Совместимость: контроллеры Raspberry Pi Pico
- Чипы: IMU-сенсор ICM20948, барометр LPS22HB
- Напряжение питания: 5 В
- Размеры: 52×25×13 мм
IMU-сенсор ICM20948
- Интерфейс: I²C (адрес 0x68)
- Диапазон измерения ускорения акселерометра: ±2/±4/±8/±16g
- Диапазон измерения поворота гироскопа: ±125/±250/±500/±1000/±2000 град./с
- Диапазон измерения магнитной индукции: ±4900 мкТл
Барометр LPS22HB
- Интерфейс: I²C (адрес 0x5C)
- Диапазон измерения давления: 260–1260 мбар
- Погрешность давления: ±0,025 мбар
Ресурсы
- IMU-сенсор 10 DOF v1 для Raspberry Pi Pico в магазине.