Jettix & Coder
Здоро́в, видел видео с прототипом электрического реактивного ранца, который делает полный круг! Хочешь помочь с кодом управления и протестировать?
Конечно, без проблем. Давай сначала разберемся с текущей логикой цикла – что запускает взлёт, управление вектором тяги и торможение. Можно добавить ПИД-регулятор для вертикальной тяги, чтобы стабилизировать наивысшую точку, а потом подкорректируем профиль угловой скорости, чтобы пакет не перекручивался. У тебя есть текущий фрагмент кода или схема расположения датчиков? Это поможет мне точно определить, какие переменные нужно подкрутить.
Получил основной код? Выложи кусочек сюда, и посмотрим, где этот флаг запуска зарыт. Я проверю проводку гироскопа и барометра, а потом быстро настроим PID для вертикального тяги. Как только зафиксируем эту точку, подкрутим кривую управления разворотом, чтобы реактивный ранец крутился плавно, а не заваливался. Давай, кидай!
Вот тебе перевод:
"Вот сокращенная версия основного цикла полета, который ты, вероятно, запускаешь на МК. Я оставил важные части в открытом виде, чтобы ты видел, где хранится флаг взлета и как обновляется вектор тяги. Можешь смело копировать его прямо в редактор.
/* Глобальное состояние */
bool liftOffRequested = false; // Флаг установлен пользователем через кнопку или последовательность
bool inLoopMode = false; // Истина, когда мы выполняем полный цикл
float thrustCommand = 0.0; // 0.0 до 1.0, управляет драйвером мотора
float rollTarget = 0.0; // Желаемый угол крена в градусах
float pitchTarget = 0.0; // Желаемый угол тангажа в градусах
float yawTarget = 0.0; // Желаемый угол рыскания в градусах
/* Показания датчиков (обновляются в ISR или задаче высокого приоритета) */
float gyroX, gyroY, gyroZ; // Угловые скорости
float baroAlt; // Текущая высота
/* Простые структуры ПИД-регулятора (заполнители) */
struct PID { float kp, ki, kd; float integral, prevError; };
PID rollPID = { 0.8, 0.0, 0.1, 0.0, 0.0 };
PID pitchPID = { 0.8, 0.0, 0.1, 0.0, 0.0 };
PID yawPID = { 0.5, 0.0, 0.05, 0.0, 0.0 };
/* Основной управляющий цикл – вызывается на частоте 200 Гц */
void controlLoop() {
/* 1. Проверка флага взлета */
if (liftOffRequested && !inLoopMode) {
// Начало первоначального подъема
thrustCommand = 0.6; // Примерно 60% тяги для взлета
if (baroAlt > 2.0) { // 2 метра над землей
inLoopMode = true; // Переход к траектории цикла
liftOffRequested = false;
}
}
/* 2. Если в режиме цикла, выполняем траекторию цикла */
if (inLoopMode) {
// Простая фазовая логика, основанная на времени (заполнитель)
static float loopTimer = 0.0;
loopTimer += 0.005; // 5 мс на цикл
// Фаза 1: Подтягивание до угла тангажа 90°
if (loopTimer < 1.0) {
pitchTarget = 90.0;
thrustCommand = 0.9; // большая тяга, чтобы перевернуться
}
// Фаза 2: Выполнение горизонтального крена
else if (loopTimer < 1.5) {
rollTarget = 360.0; // Полный крен
pitchTarget = 0.0;
thrustCommand = 0.7;
}
// Фаза 3: Посадка
else {
rollTarget = 0.0;
pitchTarget = -30.0;
thrustCommand = 0.5;
if (baroAlt < 0.3) { // близко к земле
inLoopMode = false;
}
}
/* 3. Вычисление выходных сигналов ПИД-регуляторов по каждой оси */
float rollOut = pidCompute(&rollPID, rollTarget, gyroX);
float pitchOut = pidCompute(&pitchPID, pitchTarget, gyroY);
float yawOut = pidCompute(&yawPID, yawTarget, gyroZ);
/* 4. Смешивание выходных сигналов ПИД-регуляторов в выходные сигналы моторов (упрощенно) */
applyMotorMix(thrustCommand, rollOut, pitchOut, yawOut);
}
}
/* Помощник ПИД-регулятора */
float pidCompute(PID *p, float setpoint, float measurement) {
float error = setpoint - measurement;
p->integral += error * 0.005;
float derivative = (error - p->prevError) / 0.005;
p->prevError = error;
return p->kp * error + p->ki * p->integral + p->kd * derivative;
}
/* Смешивание моторов – заглушка */
void applyMotorMix(float thrust, float roll, float pitch, float yaw) {
// Комбинирование тяги и поправок по ориентации в 4 значения ШИМ для моторов
// Это очень упрощенно – замените своей фактической логикой миксера
int m1 = constrain((int)(thrust + roll + pitch + yaw), 0, 1000);
int m2 = constrain((int)(thrust - roll + pitch - yaw), 0, 1000);
int m3 = constrain((int)(thrust + roll - pitch - yaw), 0, 1000);
int m4 = constrain((int)(thrust - roll - pitch + yaw), 0, 1000);
setMotorPWM(1, m1);
setMotorPWM(2, m2);
setMotorPWM(3, m3);
setMotorPWM(4, m4);
}
Отличный кусочек, выглядит надёжно. Сначала попробуй быстрее запускать – может, увеличь тягу с 60% до 70%, если у тебя более мощный модуль двигателей. Насчёт петли – вместо таймера в 5 миллисекунд лучше используй конечный автомат, основанный на высоте и угле крена; так, если двигатель заглохнет, он сможет среагировать. Поиграй с коэффициентом kp в ПИД-регуляторе крена, примерно до 1.0, добавь немного ki, чтобы он удерживал этот поворот на 360 градусов без дрейфа. И да, замени этот заглушка-микшер на твой реальный микшер для квадрокоптера, когда подключишь регуляторы скорости. Держи газ в нажатом положении на первом этапе, длительностью 0.8 секунд, и снижай его до 0.6 непосредственно перед посадкой, чтобы снижение было плавным. Готов запускать?