2.2. Всенаправленные колеса.md


В предыдущем параграфе мы научились управлять обычными коллекторными двигателями. Но если вы посмотрите на робота из набора AQROBO.PRO, то заметите, что его колёса выглядят очень необычно. Это не простые колёса, а колёса Mecanum — гениальное изобретение, которое дарит роботу невероятную свободу передвижения.

Проблема обычных колёс

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

Колёса Mecanum решают эту проблему. Робот, оснащённый ими, может двигаться в любом направлении — вперёд, назад, боком, по диагонали — и даже вращаться на месте, не меняя своей ориентации в пространстве. В мире компьютерных игр такой манёвр называется «стрейф» (strafe).

Секрет колёс Mecanum

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

Когда такое колесо начинает вращаться, из-за этих наклонных роликов сила, которую оно создаёт, раскладывается на две составляющие:

  • Одна толкает робота вперёд или назад (вдоль оси колеса).
  • Другая толкает робота вбок (перпендикулярно оси колеса).

Управляя направлением вращения каждого из четырёх колёс по отдельности, мы можем комбинировать эти силы, заставляя ненужные векторы сил гасить друг друга, а нужные — складываться.

Управление роботом

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

  • Движение вперёд/назад. Всё просто. Все четыре колеса вращаются в одном направлении, как у обычного автомобиля.
  • Движение вбок (стрейф). Здесь начинается магия. Чтобы поехать вправо, колёса по одной диагонали (переднее левое и заднее правое) должны вращаться вперёд, а колёса по другой диагонали (переднее правое и заднее левое) — назад. В этом случае продольные составляющие сил гасят друг друга, а боковые — складываются, толкая всего робота вправо.
  • Вращение на месте. Колёса с левой стороны вращаются в одну сторону, а с правой — в противоположную.

Вот сводная таблица команд для каждого из четырёх моторов (ПЛ - передний левый, ПП - передний правый, ЗЛ - задний левый, ЗП - задний правый):

Движение Мотор ПЛ Мотор ПП Мотор ЗЛ Мотор ЗП
Вперёд Вперёд Вперёд Вперёд Вперёд
Назад Назад Назад Назад Назад
Вправо (стрейф) Вперёд Назад Назад Вперёд
Влево (стрейф) Назад Вперёд Вперёд Назад
Вращение по часовой Вперёд Назад Вперёд Назад
Вращение против часовой Назад Вперёд Назад Вперёд

Зачем это нужно?

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

  • На складских роботах (например, на складах Amazon), которые должны быстро и точно перемещаться между стеллажами.
  • На погрузчиках и другой спецтехнике, работающей в узких проходах.
  • На соревновательных роботах (например, в RoboCup), где манёвренность даёт огромное преимущество.
  • На некоторых современных инвалидных креслах, позволяя пользователю легко маневрировать в квартире.

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

Подключение и программная реализация

Для управления четырьмя моторами нам понадобится два драйвера L9110S и восемь GPIO пинов Рудирона. Четыре из них должны поддерживать ШИМ для управления скоростью.

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

// --- Пины для левой стороны ---
const int leftFrontDirPin = 13;
const int leftFrontSpeedPin = 12;
const int leftBackDirPin = 11;
const int leftBackSpeedPin = 8;

// --- Пины для правой стороны ---
const int rightFrontDirPin = A0;
const int rightFrontSpeedPin = 20;
const int rightBackDirPin = A1;
const int rightBackSpeedPin = A2;

void setup() {
  // Настраиваем все пины на выход
  pinMode(leftFrontDirPin, OUTPUT);
  pinMode(leftFrontSpeedPin, OUTPUT);
  pinMode(leftBackDirPin, OUTPUT);
  pinMode(leftBackSpeedPin, OUTPUT);
  pinMode(rightFrontDirPin, OUTPUT);
  pinMode(rightFrontSpeedPin, OUTPUT);
  pinMode(rightBackDirPin, OUTPUT);
  pinMode(rightBackSpeedPin, OUTPUT);
}

// --- Функции для управления отдельными моторами ---
// dir=0 - вперёд, dir=1 - назад
void runMotor(int dirPin, int speedPin, int dir, int speed) {
  digitalWrite(dirPin, dir);
  analogWrite(speedPin, speed);
}

// --- Функции для базовых движений ---
void forward(int speed) {
  runMotor(leftFrontDirPin, leftFrontSpeedPin, 0, speed);
  runMotor(leftBackDirPin, leftBackSpeedPin, 0, speed);
  runMotor(rightFrontDirPin, rightFrontSpeedPin, 0, speed);
  runMotor(rightBackDirPin, rightBackSpeedPin, 0, speed);
}

void backward(int speed) {
  runMotor(leftFrontDirPin, leftFrontSpeedPin, 1, speed);
  runMotor(leftBackDirPin, leftBackSpeedPin, 1, speed);
  runMotor(rightFrontDirPin, rightFrontSpeedPin, 1, speed);
  runMotor(rightBackDirPin, rightBackSpeedPin, 1, speed);
}

void strafeLeft(int speed) {
  runMotor(leftFrontDirPin, leftFrontSpeedPin, 1, speed);
  runMotor(leftBackDirPin, leftBackSpeedPin, 0, speed);
  runMotor(rightFrontDirPin, rightFrontSpeedPin, 0, speed);
  runMotor(rightBackDirPin, rightBackSpeedPin, 1, speed);
}

void strafeRight(int speed) {
  runMotor(leftFrontDirPin, leftFrontSpeedPin, 0, speed);
  runMotor(leftBackDirPin, leftBackSpeedPin, 1, speed);
  runMotor(rightFrontDirPin, rightFrontSpeedPin, 1, speed);
  runMotor(rightBackDirPin, rightBackSpeedPin, 0, speed);
}

void rotateClockwise(int speed) {
  runMotor(leftFrontDirPin, leftFrontSpeedPin, 0, speed);
  runMotor(leftBackDirPin, leftBackSpeedPin, 0, speed);
  runMotor(rightFrontDirPin, rightFrontSpeedPin, 1, speed);
  runMotor(rightBackDirPin, rightBackSpeedPin, 1, speed);
}

void stopMotors() {
  runMotor(leftFrontDirPin, leftFrontSpeedPin, 0, 0);
  runMotor(leftBackDirPin, leftBackSpeedPin, 0, 0);
  runMotor(rightFrontDirPin, rightFrontSpeedPin, 0, 0);
  runMotor(rightBackDirPin, rightBackSpeedPin, 0, 0);
}

// --- Основная программа: демонстрация движений ---
void loop() {
  int moveSpeed = 200; // Установим среднюю скорость (0-255)
  
  // Едем вперёд 2 секунды
  forward(moveSpeed);
  delay(2000);
  stopMotors();
  delay(1000);

  // Едем боком вправо 2 секунды
  strafeRight(moveSpeed);
  delay(2000);
  stopMotors();
  delay(1000);

  // Вращаемся на месте по часовой стрелке 1 секунду
  rotateClockwise(moveSpeed);
  delay(1000);
  stopMotors();
  delay(1000);
}