Управление движением: cmd_vel и PID

Команда «ехать» превращается в обороты колёс, а удерживать скорость помогает PID-регулятор.

PID-регулятор — алгоритм управления, который по разнице между желаемым и текущим значением (ошибке) вычисляет управляющее воздействие, складывая пропорциональную, интегральную и дифференциальную части.

От cmd_vel к колёсам

Планировщик публикует в /cmd_vel сообщение Twist: линейная скорость v (вперёд) и угловая w (поворот). Контроллер базы переводит их в скорости левого и правого колеса по формулам дифференциального привода. Это чистая математика:

WHEEL_BASE = 0.32     # между колёсами, м
WHEEL_RADIUS = 0.05   # радиус колеса, м

v = 0.2    # м/с вперёд
w = 0.5    # рад/с поворот

# скорости колёс (м/с)
v_left  = v - w * WHEEL_BASE / 2
v_right = v + w * WHEEL_BASE / 2

# в угловые скорости колёс (рад/с)
omega_left  = v_left  / WHEEL_RADIUS
omega_right = v_right / WHEEL_RADIUS

print(f'левое колесо:  {omega_left:.2f} рад/с')
print(f'правое колесо: {omega_right:.2f} рад/с')

Вывод:

левое колесо:  2.40 рад/с
правое колесо: 5.60 рад/с

Правое колесо крутится быстрее — робот едет вперёд и поворачивает влево. Так абстрактная команда стала конкретными оборотами.

Зачем нужен PID

Сказать мотору «крутись на 5.2 рад/с» мало: под нагрузкой, на подъёме, при разряженной батарее реальная скорость отличается от заданной. Нужна обратная связь: измеряем фактическую скорость колеса (энкодером) и подстраиваем мощность, пока она не совпадёт с желаемой. Этим занимается PID-регулятор.

Три составляющие PID

  • P (пропорциональная): чем больше ошибка, тем сильнее воздействие. Быстро реагирует, но может не дотягивать.
  • I (интегральная): копит ошибку во времени и устраняет постоянное отклонение (например, из-за трения).
  • D (дифференциальная): реагирует на скорость изменения ошибки, гасит колебания и перерегулирование.

PID на Python

Смоделируем разгон колеса до целевой скорости с помощью PID. Код самодостаточный, его можно запустить:

Kp, Ki, Kd = 1.2, 0.4, 0.1
target = 5.0      # желаемая скорость, рад/с
current = 0.0     # текущая
integral = 0.0
prev_error = 0.0
dt = 0.1

for step in range(1, 9):
    error = target - current
    integral += error * dt
    derivative = (error - prev_error) / dt
    output = Kp*error + Ki*integral + Kd*derivative
    # упрощённая модель колеса: воздействие меняет скорость
    current += output * dt * 0.5
    prev_error = error
    print(f'шаг {step}: скорость = {current:.2f} рад/с')

Вывод:

шаг 1: скорость = 0.56 рад/с
шаг 2: скорость = 0.82 рад/с
шаг 3: скорость = 1.08 рад/с
шаг 4: скорость = 1.34 рад/с
шаг 5: скорость = 1.59 рад/с
шаг 6: скорость = 1.83 рад/с
шаг 7: скорость = 2.06 рад/с
шаг 8: скорость = 2.29 рад/с

Колесо плавно и без рывков разгоняется в сторону цели 5.0 рад/с: за восемь шагов оно прошло часть пути, а ошибка постепенно уменьшается. Чем больше ошибка, тем сильнее воздействие — по мере приближения к цели разгон замедляется. Подбор Kp, Ki, Kd под конкретный мотор называют настройкой регулятора.

Как работает под капотом

В реальном роботе PID крутится с высокой частотой (сотни герц) на нижнем уровне — часто прямо в микроконтроллере мотора, а не в ROS. ROS публикует желаемую скорость в /cmd_vel, контроллер базы переводит её в задание для колёс, а локальный PID каждого мотора держит эту скорость, опираясь на энкодер. Так разделяются уровни: ROS думает «куда ехать», PID — «как точно крутить колесо».

Частые ошибки

  • Только P без I. Робот стабильно недотягивает до цели из-за трения — нужна интегральная часть.
  • Слишком большой Kp. Система раскачивается и идёт вразнос вместо плавного выхода на цель.
  • Путать уровни. /cmd_vel — это желание системы; PID мотора — отдельный быстрый контур обратной связи.

Итоги

  • cmd_vel (v, w) переводится в обороты колёс формулами дифференциального привода.
  • PID удерживает заданную скорость по обратной связи от энкодера.
  • P реагирует на ошибку, I убирает постоянное отклонение, D гасит колебания.
  • PID мотора работает быстро на нижнем уровне, ROS задаёт цель сверху.
Проверьте себя
1. Что несёт сообщение Twist в /cmd_vel?
AИзображение
BЛинейную (вперёд) и угловую (поворот) скорости робота
CКарту помещения
DСписок узлов
2. За что отвечает интегральная (I) часть PID?
AЗа скорость реакции на ошибку
BЗа устранение постоянного отклонения, накапливая ошибку во времени
CЗа гашение колебаний
DНи за что
3. Что произойдёт при слишком большом коэффициенте Kp?
AРобот вообще не поедет
BСистема может раскачиваться и идти вразнос
CУменьшится потребление энергии
DНичего не изменится