Заставляем ракеты парить с помощью ПИД-регуляторов

Это не ракетостроение, это теория управления

Я провожу слишком много времени на YouTube… наверное, гораздо больше, чем я хотел бы признаться. Несмотря на все свои недостатки, на YouTube есть много очень интересного контента от преданных и увлеченных создателей. Время от времени я натыкаюсь на видео, в котором возникает идея чего-то, что я хочу попробовать. Одно из таких видео, с которым я недавно столкнулся, - это испытание двигателем SpaceX их космического корабля Dragon 2.

Что особенно выделяется в этом видео, так это то, что космический корабль использует свои ускорители, чтобы идеально парить на высоте нескольких футов над землей. На первый взгляд, это может показаться неважным. Однако, чтобы достичь этого, корабль должен активировать двигатели достаточно сильно, чтобы ускориться к цели, медленно уменьшая силу тяги до тех пор, пока она точно не противодействует гравитации и ракета не зависнет в воздухе [1].

Ракетное уравнение известно довольно давно, поэтому можно рассчитать точный график тяги, необходимый для того, чтобы ракета зависла. Однако гораздо проще (и веселее!) Позволить компьютеру делать за нас всю тяжелую работу. Вместо того, чтобы делать вычисления вручную, мы воспользуемся Теорией управления, чтобы ракета зависла.

Чтобы разобраться в происходящем, мы создадим нашу собственную базовую симуляцию ракеты и реализуем пару контроллеров. В отличие от моего последнего поста, для этого требуется намного больше кода, поэтому я дам лишь краткий набросок наиболее важных частей реализации [2]. Если вам нужно что-то, что действительно работает, или вам интересно, как выглядит конечный продукт, вы можете найти все на моем GitHub. Как всегда, пул-реквесты приветствуются :).

Игра в бога - моделирование физики

Прежде чем мы сможем управлять ракетой, нам нужно сначала построить ракету. Для этого мы реализуем очень физическое моделирование. Мы будем рассматривать нашу ракету как частицу, на которую действуют только силы тяжести и тяги. Поскольку боковых сил не будет, расчет угловой динамики можно легко пропустить. Мы также предположим, что у ракеты бесконечное количество топлива, поэтому нам не нужно иметь дело с такими вещами, как изменение массы или выброс топлива.

Чтобы вычислить мгновенную динамику ракеты, мы сначала дискретизируем время на интервалы размером dt и учитываем любые силы, действующие на ракету в конце каждого интервала. После определения сил мы можем обновить ускорение, скорость и положение ракеты.

Здесь следует отметить два момента:

  1. Наша ракета живет в плоском мире, поэтому мы будем использовать двумерные массивы в качестве двумерных векторов для нашего моделирования.
  2. К сожалению, системы координат экрана устанавливают начало координат (0, 0) в левый верхний угол экрана, поэтому увеличение y перемещает объекты вниз не вверх. Это означает, что в нашем мире сила тяжести составляет 9,8 м / с², а не -9,8 м / с².

Теперь, когда у нас есть ракета, единственное разумное решение - установить тягу на 100% и позволить этому ребенку порваться.

И вот так мы отправляемся на Луну. Интересно отметить, что даже несмотря на то, что это очень простое моделирование с примитивной графикой программиста, взлет ракеты на самом деле выглядит отчасти реалистичным. Хорошо, может быть, это не это реалистично.

В сторону: интеграция

Если вас не волнует, что наша простая физическая система немного ошибочна или вы просто ненавидели Calc I в колледже, не стесняйтесь пропустить этот раздел. Однако, если, как и я, Числовая квадратура действительно делает это за вас, то читайте дальше.

Если подумать, то, что мы действительно делаем в Rocket.update, - это интеграция ускорения и скорости с течением времени. Фактически мы используем метод Эйлера для аппроксимации интегралов прямоугольниками шириной dt. Поскольку ускорение постоянно, наш расчет скорости будет точным. Однако из-за этого постоянного ускорения скорость линейно увеличивается со временем, поэтому приближение положения будет неверным. Если мы подумаем о кривых, начерченных в декартовой плоскости, кривая ускорения образует прямоугольник с осью x, а кривая скорости образует треугольник. Интуитивно мы можем точно вычислить площадь прямоугольника по меньшим прямоугольникам, но как бы мы ни старались, мы не можем точно вычислить площадь треугольника и в конечном итоге будем переоценивать ее.

Чтобы сделать это явным, давайте посмотрим, какова ошибка между нашим приблизительным и фактическим положением, которую мы можем вычислить с помощью кинематических уравнений. Предположим, что у ракеты есть постоянное ускорение 5 м / с², и мы используем dt = 0,5. Согласно нашему приближению, через 5 секунд ракета будет смещена на 68,75 м, в то время как кинематические уравнения дают 62,5 м, поэтому наша ошибка аппроксимации составляет + 6,25 м.

Можно получить более точные интегральные приближения, используя более сложные методы, такие как Рунге-Кутта. При этом dt = .5 неоправданно велико. Если вместо этого мы используем что-то вроде dt = 1/60 ( то, что я использую), мы получим ошибку только ~ .63m. Итак, хотя наша симуляция неверна, это не так. Конечно, при dt → 0 наше приближение будет становиться все ближе и ближе к фактическому значению.

Контроль

Увести ракету в космос - это круто и все такое, но разве не было смысла заставлять ее парить? Для этого нам понадобится какой-то способ управлять ракетой, и что может быть лучше, чем с помощью теории управления. Теория управления - глубокая и интересная область, для объяснения которой у меня нет ни времени, ни опыта. А пока мы будем использовать ровно столько, чтобы делать с ним что-нибудь интересное.

Включение-выключение управления

Если мы сели и попытались найти быстрое решение, сразу же приходит в голову мысль просто нажать на газ, пока мы не достигнем своей цели, а затем щелкнуть тормозами, как только мы окажемся там. На самом деле это один из самых простых контроллеров, который называется контроллер включения-выключения (или контроллер Bang-Bang, который, на мой взгляд, звучит в 100 раз круче). Реализовать это предельно просто.

В терминах теории управления мы пытаемся приблизить процесс к желаемой уставке. Мы можем сделать это, минимизируя ошибку (то есть разницу) между уставкой и измеренным состоянием процесса, известной как переменная процесса. Говоря нашими собственными терминами, уставка - это высота, на которой мы хотим, чтобы ракета зависала, а переменная процесса - это просто вертикальное положение ракеты.

Поскольку двухпозиционный контроллер является двоичным, он возвращает либо ∞, если ошибка положительная, либо -∞, если она отрицательная. Мы можем использовать этот сигнал, чтобы установить тягу ракеты, но сначала мы должны раздавить ее через сигмовид (💁‍♂️🦋 это ML?) И преобразовать в процент, который может использовать ракета. Наконец-то у нас есть все компоненты, необходимые для непрерывной работы нашего моделирования.

Теперь, когда мы наделили нашу ракету некоторыми способностями к принятию решений, давайте посмотрим, насколько хорошо она может зависать.

К сожалению, не очень хорошо. Это потому, что мы не учитываем инерцию ракеты. Когда мы отключим тягу в целевой точке, ракета все еще будет иметь высокую восходящую скорость, что заставит ее некоторое время продолжать движение вверх. Точно так же, когда ракета падает, ее скорость из-за силы тяжести будет слишком высокой, чтобы сила тяги могла немедленно противодействовать.

Вместо этого нам нужно медленно уменьшать ускорение до 0 по мере приближения к цели. Для этого мы должны толкать с силой, достаточной для того, чтобы уравновесить гравитацию. Поскольку контроллер включения-выключения является двоичным, мы не можем этого сделать [3], и вместо этого мы должны использовать что-то более мощное. Войдите в ПИД-регулятор.

ПИД-регулирование

ПИД-регулятор является более сложным, поскольку он отслеживает состояние процесса во времени и пытается учесть обсуждаемые проблемы инерции. Это достигается путем регулировки отклика с помощью пропорционального, интегрального и производного управления. Давайте посмотрим, как каждый компонент играет роль в нашем случае:

  1. Пропорциональное управление масштабирует тягу ракеты в зависимости от величины ошибки. Это интуитивно понятно: если ошибка велика и отрицательна (т.е. ракета находится намного ниже цели), мы хотим поразить тягу как можно сильнее. С другой стороны, если мы приближаемся к цели, мы хотим приложить достаточно тяги, чтобы добраться до цели, но не промахнуться мимо нее.
  2. Интегральный контроль, вероятно, наименее интуитивно понятный компонент, о котором стоит думать. Он регулирует тягу ракеты на основе накопленной ошибки с течением времени, что приводит к приближению ошибки установившегося состояния к нулю. Например, если ошибка продолжает увеличиваться с течением времени, для противодействия этому будет применено большее усилие. В моих экспериментах интегральный компонент имел наименьшее значение для управления парением ракеты, поэтому, если вы точно не понимаете, что это такое, не волнуйтесь, я тоже.
  3. Наконец, производный контроль учитывает скорость изменения ошибки с течением времени. Это действительно тот термин, который заставляет всю систему работать, поскольку он отвечает за отжиг ускорения до нуля по мере того, как мы приближаемся к цели. Подобно пропорциональному управлению, это также имеет очень интуитивную интерпретацию: если ошибка меняется быстро, тяга также должна быстро меняться.

Чтобы полностью осмыслить 3 различных компонента ПИД-регулятора, нужно немного подумать. К счастью, реализация довольно проста и легко переводится в код.

Параметры kp, ki и kd представляют собой весовые константы, применяемые к каждому из трех компонентов. Поскольку пропорциональные, интегральные и производные члены взаимодействуют неинтуитивно, их правильная установка сильно зависит от задачи и требует небольшой ручной настройки. Есть даже методы, которые делают это за вас автоматически [4].

Когда я впервые все реализовал, я пробовал кучу разных настроек весов, но ничего не работало. Ракета либо бешено колебалась вокруг цели, как контроллер включения-выключения, либо просто улетала в космос, никогда больше не возвращаясь. Через некоторое время я обнаружил, что интегральная константа смехотворно высока. Хорошее практическое правило, по крайней мере для нашей ситуации с ракетой, - установить ki примерно на 3–4 порядка меньше, чем kp и kd . Фактически, начало с ki = 0 и kp = kd = 1 почти наверняка даст вам хорошие результаты. Затем вы можете поиграть с параметрами, пока траектория ракеты не станет хорошей. После некоторой настройки я обнаружил, что kp = 1.0, ki = .0001, kd = 2.3 работают очень хорошо.

Это довольно гладкая парящая ракета.

Заключение

Впервые я услышал о ПИД-регулировании в контексте некоторых экспериментов по обучению с подкреплением, которые я проводил на работе. Учитывая недавний ажиотаж вокруг машинного обучения и глубокого обучения, эти «классические» методы легко упустить из виду, но, как мы видели, мы можем получить очень хорошие результаты, приложив совсем немного усилий. Кажется, что тысячи часов, которые люди потратили на исследование и совершенствование этих методов, не прошли даром.

При этом отличным следующим шагом было бы посмотреть, сможет ли алгоритм обучения с подкреплением научиться управлять ракетой, чтобы она зависала в цели. Хотя я уверен, что это выполнимо, мне интересно, сколько времени вам нужно будет тренироваться и насколько хорошими будут конечные результаты.

Спасибо за прочтение! Посетите GitHub для получения полного кода.

Евгений Хотай
7 мая 2019 г.

[1] Как бы то ни было, космический корабль, кажется, подвешен на тросах в точном целевом положении, поэтому все, что ему нужно сделать, - это приложить достаточно силы, чтобы противодействовать гравитации - гораздо более простая задача.

[2] Самый большой фрагмент кода, который я пропускаю, - это код рендеринга, поскольку он довольно скучный. Забавная история, пока я писал весь код для этого сообщения в блоге, я столкнулся с проблемами с PyGame на Mac. Потратив примерно 3–4 часа на опробование различных фреймворков (tkinter, turtle, kivy и т. Д.), Я остановился на graphics.py. Это предельно просто, но оказалось, что есть все необходимое.

[3] Можно добиться еще лучших результатов с двухпозиционным контроллером. Например, мы можем создать зону нечувствительности вокруг цели, чтобы двигатели отключались, когда ракета приближается к цели, или быстро включались / выключались для имитации приложения определенной силы тяги. Однако, если бы мы реализовали все это, мы бы обнаружили, что то, что мы создали, в любом случае является грубым приближением к ПИД-регулятору.

[4] Должна быть предусмотрена возможность настройки обучения и этих констант с использованием обычной регрессии наименьших квадратов. Было бы интересно посмотреть, насколько это проще или сложнее по сравнению с традиционными методами и чем будут отличаться полученные коэффициенты.