Руководство · Средний · 35 мин

Кинематика колёс Mecanum: четыре колеса, три оси

Выведите обратную кинематику четырёх колёс Mecanum из ролика под 45°, обратите её для одометрии и используйте лишнее уравнение для обнаружения скольжения.

Один ролик решает всё

Снимите одно колесо Mecanum с робота и подержите его. Оно вращается на своей оси, как любое колесо. По ободу расположены восемь или девять бочкообразных роликов, каждый на собственной маленькой оси, каждый свободно вращается и установлен под углом 45° к оси колеса.

Теперь спросите, какую силу это колесо может передать полу.

Оно не может толкать вдоль направления качения ролика — ролик просто вращается, и ничего не передаётся. Оно может толкать только вдоль оси ролика, то есть в единственном направлении, в котором ролик не катится. Поэтому сила колеса на полу привязана к одной линии под 45° к шасси, и никакой момент двигателя не меняет эту линию.

Вот и весь механизм. Всё остальное ниже — бухгалтерия.

Одно колесо Mecanum, изображённое сбоку над линией пола. Шина заштрихована полосами роликов, установленными под 45 градусами, с подписями «ролики свободно вращаются» и «оси под 45 градусами». От контактного пятна на полу красная стрелка направлена вниз и вправо под 45 градусов с подписью «сила реакции пола F на корень из двух», ограниченная величиной μN. Голубая стрелка направлена горизонтально вдоль пола с подписью «полезная F», нарисована как горизонтальная составляющая красной стрелки; пунктирная линия замыкает прямоугольный треугольник, а дуга отмечает угол 45 градусов. Подпись поясняет, что трение ограничивает весь вектор под 45 градусов, но робот перемещает только его горизонтальную часть, поэтому полезная сила на колесо равна μN, делённой на корень из двух, около 71% обычного колеса.
Весь штраф за сцепление в одном треугольнике. Ролик передаёт силу только вдоль своей оси, поэтому сила колеса на полу зафиксирована под 45°: трение ограничивает весь вектор, а робот перемещает только горизонтальную составляющую. Скачать SVG

Ограничение для каждого колеса

Задайте обычную систему координат робота: x вперёд, y влево от робота, ω против часовой стрелки. Это соглашение ROS, и использование любой другой системы заставит вас бороться с каждой библиотекой, к которой вы когда-либо обратитесь.

Поместите колесо i в позицию (xᵢ, yᵢ) в этой системе и задайте ему направление роликов sᵢ = ±1, указывающее, вдоль какой из двух линий под 45° расположены его ролики. Тогда скорость обода vᵢ (в метрах в секунду на шине, а не в радианах) связана с вектором twist шасси соотношением:

vᵢ = vx + sᵢ·vy + (sᵢ·xᵢ − yᵢ)·ω

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

  • vx — каждое колесо полностью участвует в движении вперёд независимо от направления роликов. Поэтому движение вперёд на базе Mecanum ощущается совершенно обычным.
  • sᵢ·vy — движение вбок вращает половину колёс в одну сторону, а половину — в другую. Направление роликов решает, какая половина куда движется.
  • (sᵢ·xᵢ − yᵢ)·ω — плечо момента. Обратите внимание, что это не просто ширина колёсной базы; направление роликов входит внутрь, поэтому колесо, установленное не в том углу шасси, портит не только боковое движение, но и вращение.

Чтобы перейти от скорости обода к команде, которую действительно получает двигатель, разделите на радиус колеса: ωwheel = vᵢ / r, в rad/s.

Четыре колеса: матрица

Сложите четыре строки, и получится матрица 4 × 3 A, где вектор twist шасси ξ = (vx, vy, ω):

v = A · ξ

Для стандартной компоновки — переднее левое и заднее правое с одним направлением роликов, переднее правое и заднее левое с другим — запишите k = lx + ly, где lx — половина колёсной базы (от передней оси к задней), а ly — половина ширины колёс (от левой к правой). Тогда:

Колесо vx vy ω
Переднее левое 1 −1 −k
Переднее правое 1 +1 +k
Заднее левое 1 +1 −k
Заднее правое 1 −1 +k

Вот четыре строки, которые пишут все:

float fl = vx - vy - K * omega;
float fr = vx + vy + K * omega;
float rl = vx + vy - K * omega;
float rr = vx - vy + K * omega;

Проверьте результат по четырём вещам, которые робот на Mecanum уже умеет:

Команда Скорости колёс (FL, FR, RL, RR) Что видно
Вперёд, vx = 1 1, 1, 1, 1 Все четыре одинаково. Боковые составляющие компенсируются.
Влево, vy = 1 −1, 1, 1, −1 Диагональные пары противоположны. Это фокус.
Поворот против часовой стрелки (CCW), ω = 1 −k, k, −k, k Левая сторона назад, правая вперёд — обычный танковый поворот.
Диагонально, vx = vy = 1 0, 2, 2, 0 Два колеса полностью останавливаются. Запомните это.
Четыре панели с видом сверху на один и тот же робот Mecanum с носом вверх, на каждой показано направление вращения четырёх колёс для одной команды и перечислены скорости переднего левого, переднего правого, заднего левого и заднего правого колёс, отмасштабированные по самому быстрому колесу. Вперёд: плюс один на всех четырёх, все стрелки в одну сторону, боковые части компенсируются. Влево: минус один, плюс один, плюс один, минус один, диагональные пары противоположны, янтарная стрелка указывает влево. Поворот против часовой стрелки: минус один, плюс один, минус один, плюс один, левая сторона назад, правая вперёд, янтарная дуга. Диагонально: ноль, плюс один, плюс один, ноль, янтарная стрелка направлена вперёд-влево, примечание говорит, что всё делают два колеса, а два ничего не делают.
Четыре команды прямо из матрицы. Обратите внимание на последнюю: диагональная команда требует от двух колёс всего, а от двух ничего, и отсюда предел 71% для диагонального движения. Скачать SVG

Разобранный пример

У базы с lx = 75 mm, ly = 85 mm и колёсами 97 mm (r = 48.5 mm) задана команда vx = 0.30 m/s, vy = 0.20 m/s, ω = 0.50 rad/s. Поэтому k = 0.160 m.

Колесо Арифметика Скорость обода Скорость двигателя
Переднее левое 0.30 − 0.20 − 0.160 × 0.50 0.020 m/s 0.41 rad/s (3.9 rpm)
Переднее правое 0.30 + 0.20 + 0.160 × 0.50 0.580 m/s 11.96 rad/s (114 rpm)
Заднее левое 0.30 + 0.20 − 0.160 × 0.50 0.420 m/s 8.66 rad/s (83 rpm)
Заднее правое 0.30 − 0.20 + 0.160 × 0.50 0.180 m/s 3.71 rad/s (35 rpm)

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

Насыщение: масштабировать, а не обрезать

Запросите больше, чем могут двигатели, и придётся отказаться от чего-то. Есть ровно два варианта, и они неэквивалентны.

float peak = max(max(fabs(fl), fabs(fr)), max(fabs(rl), fabs(rr)));
if (peak > V_MAX) {            // SCALE: keep the direction, lose the speed
  float s = V_MAX / peak;
  fl *= s; fr *= s; rl *= s; rr *= s;
}

В отличие от того, что произойдёт, если просто написать constrain(fl, -255, 255) для каждого двигателя: это обрезание. Обрезание сохраняет скорость и выбрасывает направление. Запросим движение вперёд 0.60 m/s, вбок — 0.15 и вращение 0.8 rad/s на роботе, максимальная скорость колёс которого равна 0.50 m/s:

Направление Вперёд Вращение Вердикт
Команда 14.0° 0.60 m/s 0.80 rad/s —
Масштаб 14.0° 0.34 m/s 0.46 rad/s Медленнее по всем осям, точно в указанном направлении
Обрезание 5.6° 0.46 m/s 0.28 rad/s Быстрее вперёд, отклонение 8.4° и на 40% меньше вращения

Самое неприятное здесь в том, что обрезание выглядит нормально на командах, которые обычно проверяют. Чистая диагональная команда запрашивает 2 для двух колёс и 0 для двух других; нули уже находятся в пределах, поэтому обрезание и масштабирование дают одинаковые числа, и ошибка скрывается. Позже она проявляется при работе системы следования по пути: робот выходит из поворотов без видимой причины. Посмотрите на оба ограничителя в одном прогоне, и разница станет видимой кривой.

Движение назад: одометрия

Прямая кинематика — это та же матрица, прочитанная наоборот. Но A имеет размер 4 × 3 — четыре измерения и три неизвестных, поэтому обратной матрицы нет. Есть решение методом наименьших квадратов:

ξ = (Aᵀ A)⁻¹ Aᵀ v

Для стандартной компоновки AᵀA получается диагональной, и всё выражение сворачивается в три строки, которые можно поместить на Arduino:

vx    = (fl + fr + rl + rr) / 4.0;
vy    = (-fl + fr + rl - rr) / 4.0;
omega = (-fl + fr - rl + rr) / (4.0 * K);

Интегрируйте это через курс робота на каждом тике, и получите счисление координат:

float dt = (now - last) * 1e-6f;
theta += omega * dt;
x += (vx * cosf(theta) - vy * sinf(theta)) * dt;
y += (vx * sinf(theta) + vy * cosf(theta)) * dt;

Оно работает ровно так же, как предположение, на котором основано, — что колёса не проскальзывают. На базе Mecanum проскальзывание является проектной особенностью колеса, поэтому относитесь к боковой одометрии как к наименее надёжному числу на роботе.

Четвёртое уравнение, которым никто не пользуется

Четыре уравнения, три неизвестных. Лишнее уравнение — не отходы, а единственная бесплатная диагностика всего привода.

Если робот жёсткий и каждое колесо имеет сцепление, четыре скорости обода не могут быть произвольными. Сложите первые две строки матрицы и последние две строки, и обе суммы дадут 2·vx, поэтому:

FL + FR = RL + RR

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

float residual = (fl + fr) - (rl + rr);   // should be ~0 forever

Ненулевое значение означает, что четыре энкодера описывают движение, которое не может выполнить твёрдое тело. Причины, примерно по убыванию вероятности: колесо оторвалось от пола, колесо стоит на влажном участке, ролик заклинило, проскальзывает муфта, отказал канал энкодера — или колесо установлено не в том углу шасси, из-за чего невязка становится большой и постоянной, а не скачущей.

Будьте честны в том, чего он не видит: если все четыре колеса проскальзывают одинаково и одновременно, невязка остаётся нулевой, и робот бесшумно скользит вбок, сходя с курса. Эта слепота — причина, по которой одометрия Mecanum тихо выходит из строя; поэтому серьёзные конструкции добавляют IMU или неприводные колёса, не полагаясь на приводные энкодеры.

Два следствия угла 45°

Оба следуют из матрицы, и ни одно нельзя устранить покупкой чего-либо.

Диагональное движение ограничено значением 1/√2. Команда под 45° задаёт vx + vy двум колёсам и 0 двум другим, поэтому нагруженная пара достигает предела скорости, когда робот движется только со скоростью V_MAX / √2 ≈ 71% своей максимальной скорости по прямой. Более быстрые двигатели поднимают предел, но не устраняют его.

Сцепление также ограничено значением 1/√2, но по другой причине. Полезная сила привода двигателя F направлена вдоль направления качения, но фактическая сила на полу равна F·√2 вдоль оси ролика, и трение ограничивает именно её. Поэтому на каждое колесо полезная сила ограничена значением μN/√2, а предел ускорения робота равен:

a_max = μ·g / √2

При μ = 0.85 это 5.9 m/s², тогда как тот же робот на обычных колёсах имел бы 8.3. Масса робота сокращается в обоих выражениях, поэтому балласт не помогает — урок, прямо противоположный роботу сумо, где вес является всей стратегией.

График боковой скорости от передней скорости в метрах в секунду, показывающий все скорости, доступные роботу Mecanum с колёсами 0.60 м/с. Достижимое множество — закрашенный голубой ромб с вершинами на плюс и минус 0.60 по каждой оси. Для сравнения нарисована пунктирная серая окружность радиусом 0.60 с подписью об отсутствии штрафа за направление. Янтарные маркеры находятся на 0.60 м/с вперёд и 0.60 м/с вбок в вершинах ромба, а красный маркер на диагональной грани подписан 0.424 м/с на диагонали.
Каждая скорость, доступная роботу, рассчитана по направлениям. Прямо вперёд и прямо вбок бесплатны; диагональная грань ромба — место, где команда обходится в 1/√2. Только привод без угла 45° получает окружность. Скачать SVG

В прошивке

Разместите геометрию в одном месте и никогда не вводите её дважды.

struct Mecanum {
  float lx, ly, r;                 // half wheelbase, half track, wheel radius (m)
  float k() const { return lx + ly; }

  // twist -> four wheel angular velocities, rad/s
  void inverse(float vx, float vy, float w, float out[4]) const {
    out[0] = (vx - vy - k() * w) / r;   // FL
    out[1] = (vx + vy + k() * w) / r;   // FR
    out[2] = (vx + vy - k() * w) / r;   // RL
    out[3] = (vx - vy + k() * w) / r;   // RR
  }

  // four measured wheel angular velocities -> twist, plus the residual
  float forward(const float in[4], float &vx, float &vy, float &w) const {
    float fl = in[0] * r, fr = in[1] * r, rl = in[2] * r, rr = in[3] * r;
    vx = (fl + fr + rl + rr) * 0.25f;
    vy = (-fl + fr + rl - rr) * 0.25f;
    w  = (-fl + fr - rl + rr) * 0.25f / k();
    return (fl + fr) - (rl + rr);       // the free diagnostic
  }
};

Измеряйте lx и ly от центра колеса до центра колеса, а затем делите пополам, а не по платформе шасси, которая обычно шире. Неправильное k не остановит движение робота; оно смешает вращение и поступательное движение, поэтому команда чистого вращения будет ползти, а чистое боковое движение — заворачиваться. Если робот ползёт при вращении на месте, сначала подозревайте k, а не код.

Диагностика

Симптом Вероятная причина Что проверить
Вперёд нормально, вбок заворачивает и вращается Колесо, установленное не в том углу шасси Проверку X из приведённой выше схемы; невязка будет большой и постоянной
Двигается вбок в сторону, противоположную команде Все четыре колеса зеркально установлены Поменяйте левую пару с правой
При вращении на месте также перемещается Неверное k или lx/ly измерены по шасси, а не по колёсам Измерьте от центра колеса до центра колеса
Диагональное движение намного медленнее прямого Предел 1/√2 — не отказ Исправлять нечего; проектируйте с учётом него
Робот выходит из углов при работе системы следования по пути Обрезание по каждому двигателю вместо масштабирования Масштабируйте всю команду по самому быстрому колесу
Боковая одометрия быстро смещается, прямая в порядке Проскальзывание роликов, как и задумано IMU для курса, неприводные колёса для позиции
Невязка скачет только на одной поверхности Участок скользкий или ролик заклинил Проверните каждый ролик вручную

Далее

Проверьте числа на роботе, который может проскальзывать: симулятор привода Mecanum прогоняет именно эти уравнения через модель трения, поэтому можно наблюдать, как одометрия расходится с полом. Затем добейтесь, чтобы колёса действительно удерживали рассчитанные скорости, — замкнутое управление скоростью колёс — и добейтесь, чтобы «вперёд» означало вперёд на полу, а не на роботе, с помощью привода с ориентацией по полю.

Исследуйте граф

Входит в эти сборки

Проекты и учебные пути, в которые входит это руководство.

Дополнительные материалы

Источники