Используйте IMU-сенсор 10 DOF v1 для определения положения вашего девайса в пространстве. Модуль выполнен в родном форм-факторе для платформы Raspberry Pi Pico.
IMU-модуль 10 DOF включает в себя:
На роль контроллера для работы с IMU-модулем рассмотрим платформу Raspberry Pi Pico.
Отобразим демонстрацию работы IMU-сенсора. Для работы примеров скачайте и установите библиотеку amperka_pico_imu_10dof.
Для начала выведем в консоль показания барометра.
# Импортируем необходимые модули 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.
# Импортируем необходимые модули 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.
# Импортируем необходимые модули 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.
# Импортируем необходимые модули 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)
Выведем показания сразу трёх сенсоров:
# Импортируем необходимые модули 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)
Определим ориентацию объекта в пространстве с учётом направления на север. Для этого показания акселерометра, гироскопа и компаса используем как входные данные для фильтра Маджвика. На выходе получим кватернионы, которые для наглядности пересчитаем в самолётные углы Эйлера.
# Импортируем необходимые модули 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-сенсора в пространстве в виде самолёта.
# Импортируем необходимые модули 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)
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-сенсора в пространстве.
За 9 степеней свободы отвечает инерциальный чип ICM-20948, который включает в себя акселерометр, гироскоп и магнитометр.
За определения десятой степени свободы отвечает отдельный чип барометр LPS22HB, который подскажет высоту над уровнем моря.
Датчик атмосферного давления LPS22HB служит альтиметром для носимого гаджета, а также барометром для метеостанции.
Имя светодиода | Назначение |
---|---|
PWR | Индикатор питания. Горит — на плату поступает напряжение, не горит — на плату не поступает напряжение. |
Понижающий DC-DC преобразователь RT9193-33 обеспечивает питание барометра и другой логики на плате. Входное напряжение поступает от пина VSYS управляющей платы. Выходное напряжение 3,3 В с максимальным выходным током 300 мА.
Понижающий DC-DC преобразователь RT9193-18 обеспечивает питание инерциального датчика и светодиодной индикации. Входное напряжение поступает от преобразователя RT9193-33. Выходное напряжение 1,8 В с максимальным выходным током 300 мА.
Плата IMU-сенсор подключается к контроллерам форм-фактора Raspberry Pi Pico через две параллельных контактных колодки на 20 пинов. Все подробности коммуникации и задействованные контакты читайте в разделе распиновка.