План уроку
- Повторення кутів Ейлера та принципу, як це працює
- Візуалізація обертань через кути Ейлера
- Зміна підходу до обертань. Кватерніони.
- Комплексні числа
- Гіперкомплексні числа
Матеріали
- Роборука
- Система керування роборукою
Результат уроку
Результатом даного уроку має бути:
- Розуміння проблематики кутів Ейлера
- Розуміння доцільності застосування кватерніонів в розрахунках
- Розуміння основних властивостей кватерніонів
- Розуміння механізмів роботи комплексних та гіперкомплексних чисел
Візуалізація обертань через кути Ейлера
#include <Arduino.h> // Підключаємо бібліотеку Arduino для основних функцій
#include <Adafruit_MPU6050.h> // Підключаємо бібліотеку для роботи з сенсором MPU6050
#include <Adafruit_Sensor.h> // Бібліотека для роботи з різними сенсорами Adafruit
// Ініціалізація об'єкта для роботи з MPU6050
Adafruit_MPU6050 mpu;
// Оголошення змінних для зберігання даних з акселерометра, гіроскопа та температури
sensors_event_t a, g, temp;
// Змінна для зберігання часу останньої відправки даних
uint32_t lastSendTime = 0;
double deltaX, deltaY, deltaZ;
double angleX, angleY, angleZ;
// Буфер для надсилання даних
char buffer[80];
// Буфер для отримання команд
String inputString = "";
void calcBiases();
void calcOrient();
void(* resetFunc) (void) = 0;
void serialEvent() {
if (Serial.read() == 'r') {
resetFunc();
}
}
void setup()
{
// Ініціалізація серійного з'єднання зі швидкістю 115200 біт/с
Serial.begin(115200);
inputString.reserve(200); // Резервуємо пам'ять для вхідного рядка
// Перевірка на успішне підключення сенсора MPU6050
if (!mpu.begin())
{
// Якщо сенсор не знайдено, виводимо повідомлення про помилку
Serial.println("Failed to find MPU6050 chip");
while (1) // Безкінечний цикл у разі помилки
{
// Невелика затримка для зменшення навантаження на систему
delay(10);
}
}
// Налаштування діапазону вимірювань акселерометра (до 16g)
mpu.setAccelerometerRange(MPU6050_RANGE_16_G);
// Налаштування діапазону вимірювань гіроскопа (до 2000 градусів/с)
mpu.setGyroRange(MPU6050_RANGE_2000_DEG);
// Налаштування смуги пропускання фільтра на 260 Гц
mpu.setFilterBandwidth(MPU6050_BAND_260_HZ);
// Невелика затримка для стабілізації роботи сенсора
delay(100);
calcBiases();
}
void loop()
{
// Перевірка часу, щоб оновлювати дані кожні 10 мілісекунд
if((millis() - lastSendTime) >= 10) {
// Отримання даних з акселерометра, гіроскопа і температури
mpu.getEvent(&a, &g, &temp);
// Оновлення орієнтації
calcOrient();
// Виведення значень по кожній осі акселерометра
sprintf(buffer, "%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f\r\n",
a.acceleration.x, a.acceleration.y, a.acceleration.z,
g.gyro.x, g.gyro.y, g.gyro.z,
angleX * 57.2958, angleY * 57.2958, angleZ * 57.2958);
Serial.println(buffer);
// Оновлюємо час останньої відправки даних
lastSendTime = millis();
}
}
// Функція розрахунку аддитивної похибки
void calcBiases()
{
// g.gyro.x - Кутова швидкість навкого осі X
// g.gyro.y - Кутова швидкість навкого осі Y
// g.gyro.z - Кутова швидкість навкого осі Z
// deltaX - Значення аддитивної похибки по осі X
// deltaY - Значення аддитивної похибки по осі Y
// deltaZ - Значення аддитивної похибки по осі Z
// Розрахуємо відхилення за допомогою знаходження середнього значення (500 ітерацій)
for (size_t i = 0; i < 500; i++)
{
// Читаємо дані з давача
mpu.getEvent(&a, &g, &temp);
deltaX = deltaX + g.gyro.x; //
deltaY = deltaY + g.gyro.y; // сумуємо для кожної з осей значення від давачів
deltaZ = deltaZ + g.gyro.z; //
}
deltaX = deltaX / 500; //
deltaY = deltaY / 500; // Ділимо кожне значення на кількість ітерацій
deltaZ = deltaZ / 500; // і знаходимо середнє
}
// Функція розрахунку кутів
void calcOrient()
{
// angleX - Розрахований кут X
// angleY - Розрахований кут Y
// angleZ - Розрахований кут Z
angleX = angleX + (g.gyro.x - deltaX) * 0.01;
angleY = angleY + (g.gyro.y - deltaY) * 0.01;
angleZ = angleZ + (g.gyro.z - deltaZ) * 0.01;
}
Зміна підходу до обертань. Кватерніони
5 Зміна підходу до розрахунку орієнтації в просторі (в процесі)