Jettix & Coder
Jettix Jettix
Здоро́в, видел видео с прототипом электрического реактивного ранца, который делает полный круг! Хочешь помочь с кодом управления и протестировать?
Coder Coder
Конечно, без проблем. Давай сначала разберемся с текущей логикой цикла – что запускает взлёт, управление вектором тяги и торможение. Можно добавить ПИД-регулятор для вертикальной тяги, чтобы стабилизировать наивысшую точку, а потом подкорректируем профиль угловой скорости, чтобы пакет не перекручивался. У тебя есть текущий фрагмент кода или схема расположения датчиков? Это поможет мне точно определить, какие переменные нужно подкрутить.
Jettix Jettix
Получил основной код? Выложи кусочек сюда, и посмотрим, где этот флаг запуска зарыт. Я проверю проводку гироскопа и барометра, а потом быстро настроим PID для вертикального тяги. Как только зафиксируем эту точку, подкрутим кривую управления разворотом, чтобы реактивный ранец крутился плавно, а не заваливался. Давай, кидай!
Coder Coder
Вот тебе перевод: "Вот сокращенная версия основного цикла полета, который ты, вероятно, запускаешь на МК. Я оставил важные части в открытом виде, чтобы ты видел, где хранится флаг взлета и как обновляется вектор тяги. Можешь смело копировать его прямо в редактор. /* Глобальное состояние */ 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); }
Jettix Jettix
Отличный кусочек, выглядит надёжно. Сначала попробуй быстрее запускать – может, увеличь тягу с 60% до 70%, если у тебя более мощный модуль двигателей. Насчёт петли – вместо таймера в 5 миллисекунд лучше используй конечный автомат, основанный на высоте и угле крена; так, если двигатель заглохнет, он сможет среагировать. Поиграй с коэффициентом kp в ПИД-регуляторе крена, примерно до 1.0, добавь немного ki, чтобы он удерживал этот поворот на 360 градусов без дрейфа. И да, замени этот заглушка-микшер на твой реальный микшер для квадрокоптера, когда подключишь регуляторы скорости. Держи газ в нажатом положении на первом этапе, длительностью 0.8 секунд, и снижай его до 0.6 непосредственно перед посадкой, чтобы снижение было плавным. Готов запускать?