Первый пультовый робот я собрал по автомобильной схеме: два ведущих колеса на задней оси и поворотная опора спереди. Схема прожила ровно один вечер — на паркете поворотная опора проскальзывала, на ковре закапывалась, и робот вместо поворота изображал стояние с жужжанием. Хватает ли вам этого описания, чтобы больше не связываться с рулём? Мне хватило: следующая платформа встала на два независимых мотора, и руль исчез как класс — направление теперь складывается из разности оборотов двух каналов, как у танка или гусеничного экскаватора.
Раздел уже собирал пультовую машину в уроке про робота и L298N: там расчёты ENA и ENB, стоп по millis-таймауту с последнего кадра и раздельное питание. Здесь я забираю из того урока готовый каркас и меняю раскладку на танковую: оба канала равноправны, каждый задаётся парой IN1/IN2 для направления и ШИМ для скорости. Сам драйвер по выводам и реверсу разобран в уроке про L298N и ШИМ, а если моста под рукой нет, для первого заезда хватит транзисторного ключа без реверса — робот будет ездить только вперёд, зато завтра, а не когда придёт посылка.
План урока: раскладка кнопок по двум каналам и таблица движений, ток моторов против потолка USB — грабля, которая валит больше роботов, чем кривой код, авто-повтор пульта и дёрганье, которое он вызывает, и симулятор одного канала с честным логом команд. Одно мнение вынесу вперёд: на два мотора я ставлю TB6612FNG, а L298N оставляю для стендов, и ниже объясню это цифрами, а не брендами.
Танковая раскладка: руль заменяет разность оборотов
Механика умещается в четыре строки. Вперёд — оба канала крутятся вперёд с одинаковой скоростью. Пятимся — оба назад. Поворот на месте — левый канал реверсирует, правый тянет вперёд, и робот вертится почти вокруг своей оси, на что рулевая схема не способна в принципе. Плавная дуга — оба вперёд, но левому ШИМ 120, правому 200. Всё разнообразие манёвров — это комбинация двух чисел на канал: направления и заполнения ШИМ. Именно поэтому танковая схема дружит с пультом: пять кнопок закрывают все движения без единого серво.
У L298N каждый канал — это пара входов направления и вход разрешения: IN1/IN2 задают полярность моста, ENA принимает ШИМ. IN1=HIGH и IN2=LOW — мотор крутится в одну сторону, инверсия пары — в другую, оба LOW — свободный выбег, и это лучший «нейтраль» для стопа. Числа по току: жёлтые TT-моторредукторы 3–6 В под нагрузкой едят 200–700 мА на канал, заклинивший мотор — за ампер. Теперь потолок USB: порт отдаёт 500 мА на всё, включая плату. Два мотора под нагрузкой плюс Arduino — и защита провода либо урезает ток, либо просадка валит питание: плата перезагружается ровно в момент разворота, когда ток пиковый. Робот кормится от батарейного отсека на 4–6 батареек AA, моторная часть сидит на отсеке, логика — через стабилизатор, земля общая.
Про выбор моста честно: L298N — биполярная классика, и на своих транзисторах он сжигает порядка двух вольт — с шести вольт отсека мотор получает четыре. TB6612FNG на полевых ключах роняет доли вольта, держит 1,2 А на канал непрерывно и стоит сравнимых денег; минус — он капризнее к подключению и не переживёт 15 вольт, которые L298N глотает от 35 В. Если у вас моторы до 12 В и хочется запаса по току на дёшево — L298N оправдан; для батарейного робота на TT-моторчиках мой выбор сделан, и переплачивать за просадку не хочется.
Питание я теперь развожу так, и оно пережило четыре платформы. Отсек на четыре батареи AA — шесть вольт, минус два вольта просадки L298N — моторам достаются честные четыре, на которые они и рассчитаны. Джампер 5V на модуле снимается и выбрасывается: он включает бортовой стабилизатор модуля, которому при шести вольтах входа не хватает на честную работу, и логика модуля начинает питаться от батареи напрямую. Плата кормится от того же отсека через её собственный стабилизатор или от повербанка отдельным хвостом — главное, что земля у моторной и логической половины общая, иначе ШИМ-помехи от моторов читаются приёмником как команды. Три провода земли — дешевле одной пропавшей вечерности.
// Танковый робот: TSOP на D2, L298N: ENA/IN1/IN2 и ENB/IN3/IN4#include <IRremote.hpp> const int IR_PIN = 2;const int ENA = 5, IN1 = 4, IN2 = 3; // левая гусеницаconst int ENB = 6, IN3 = 8, IN4 = 7; // правая гусеницаconst int SPEED = 170; // стартовая скорость 0-255 const int CMD_FWD = 69; // CH+ - вперёдconst int CMD_BACK = 70; // CH- - назадconst int CMD_LEFT = 21; // VOL+ - влево на местеconst int CMD_RIGHT = 7; // VOL- - вправо на местеconst int CMD_STOP = 67; // OK - стоп void drive(bool lf, bool lv, bool rf, bool rv) { digitalWrite(IN1, lf); digitalWrite(IN2, lv); // левый: направление digitalWrite(IN3, rf); digitalWrite(IN4, rv); // правый: направление analogWrite(ENA, SPEED); analogWrite(ENB, SPEED); // оба: ШИМ} void setup() { pinMode(ENA, OUTPUT); pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT); pinMode(ENB, OUTPUT); pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT); Serial.begin(9600); IrReceiver.begin(IR_PIN, ENABLE_LED_FEEDBACK);} void loop() { if (IrReceiver.decode()) { if (!(IrReceiver.decodedIRData.flags & IRDATA_FLAGS_IS_REPEAT)) { switch (IrReceiver.decodedIRData.command) { case CMD_FWD: drive(HIGH, LOW, HIGH, LOW); break; // оба вперёд case CMD_BACK: drive(LOW, HIGH, LOW, HIGH); break; // оба назад case CMD_LEFT: drive(LOW, HIGH, HIGH, LOW); break; // реверс левого case CMD_RIGHT: drive(HIGH, LOW, LOW, HIGH); break; // реверс правого case CMD_STOP: analogWrite(ENA, 0); analogWrite(ENB, 0); digitalWrite(IN1, LOW); digitalWrite(IN2, LOW); digitalWrite(IN3, LOW); digitalWrite(IN4, LOW); break; } } IrReceiver.resume(); }}| Кнопка пульта | Левый канал IN1/IN2/ENA | Правый канал IN3/IN4/ENB | Что делает робот |
|---|---|---|---|
| CH+ (вперёд) | HIGH / LOW / ШИМ 170 | HIGH / LOW / ШИМ 170 | едет вперёд по прямой |
| CH- (назад) | LOW / HIGH / ШИМ 170 | LOW / HIGH / ШИМ 170 | пятится, не разворачиваясь |
| VOL+ (влево) | LOW / HIGH / ШИМ 170 | HIGH / LOW / ШИМ 170 | вертится на месте против часовой |
| VOL- (вправо) | HIGH / LOW / ШИМ 170 | LOW / HIGH / ШИМ 170 | вертится на месте по часовой |
| OK (стоп) | LOW / LOW / 0 | LOW / LOW / 0 | свободный выбег, стоит |
Авто-повтор: почему робот дёргается
Пульт не умеет молчать: пока кнопку держат, он стреляет repeat-кадрами каждые 110 миллисекунд. Во втором поколении библиотеки это было волшебное слово 0xFFFFFFFF, в четвёртой — флаг IRDATA_FLAGS_IS_REPEAT, но суть та же: обработчик loop получает «вперёд» не один раз, а десять в секунду. Для команды «задать ШИМ и направление» повтор безвреден — команда идемпотентна, десять одинаковых приказов меняют ничего. Дёрганье начинается, когда внутри ветки живёт состояние: вспышка индикатора, счётчик кадров, toggle-логика или перезапуск скорости с паузой. Мой первый дёргающийся робот гасил и зажигал ШИМ на каждом кадре — выглядело как нервный тик, лечилось одной строкой.
Лечение: в самом верху ветки decode отсечь повторы условием по флагу — и кнопки-движения перестают зависеть от того, как долго вы держите палец. Удержание при желании превращается в фичу: пока летят повторы, повышайте скорость шагом за кадр — получится плавный разгон без единого таймера. Как устроен кадр повтора на уровне эфира, разобрано в уроке про repeat-кадры, а весь спектр фильтров — гашение, millis-окно, качель из удержания — в уроке про repeat-фильтр; здесь я беру из них минимальный флаг и еду дальше. Если же робот и с фильтром чешет куда-то не туда — вернитесь к чтению кодов кнопок и сверьте константы: чужие номера команд — вторая по частоте причина «самодельного интеллекта».
Симулятор честно показывает половину картины: один канал, одна кнопка хода, скорость как число от 0 до 255. Вторая половина живёт на настоящей плате: правый канал подключается зеркально и получает те же команды, разворот на месте рождается из инверсии одной пары IN-входов, а реверс требует полноценного моста — моторчик симулятора пятиться не умеет, но печатает в лог, какой приказ ушёл бы в правую гусеницу. Стартовую скорость я держу на 170 из 255: ниже TT-моторчики на ковре трогаются рывком, выше — робот успевает доехать до стены быстрее, чем вы находите кнопку стопа. Финальную калибровку делайте на полу, а не в воздухе: висящий в воздухе мотор ест меньше тока и прощает то, что под нагрузкой прощать перестаёт.
Про нижний порог скорости — число, которого нет в даташитах. Мои TT-моторчики на ковре перестают трогаться ниже 90–100 из 255: ШИМ подаётся, ток течёт, ротор дрожит, а колёса стоят — мотор работает обогревателем. Плавная дуга в танковой схеме живёт не от нуля, а от этого порога: левому каналу я задаю не 60, а порог плюс треть разницы — примерно 130 против 170 правого. Если хотите точнее, найдите порог своего покрытия один раз на калибровочном заезде: командуйте ШИМ ступенями по десять и слушайте, где дрожь превращается в ход.
И последнее, что стоит проверить до первого заезда: поведение при пропаже связи. Пульт сдох на середине комнаты — робот продолжает выполнять последний приказ, и танковая схема, в отличие от машинки с серво, честно поедет в стену. Лекарство из урока про робота на L298N переносится без изменений: millis-таймаут с последнего принятого кадра, после 300–500 мс тишины — стоп. Двадцать байт кода, которые однажды спасут бампер.
- 1. Почему робота не кормят от USB-порта, если моторы TT-типа?
- 2. За что отвечает пара IN1/IN2 у канала L298N?
- 3. Пульт при удержании шлёт repeat-кадры каждые 110 мс. Как их гасит скетч на IRremote 4.x?
- 4. Чем TB6612FNG честно лучше L298N для батарейного робота?
- 5. Как танковая схема делает поворот на месте?
- Танковая схема: пять кнопок закрывают все движения — руль заменяется разностью оборотов двух равноправных каналов.
- Канал = пара IN1/IN2 (направление) плюс ENA с ШИМ (скорость); оба IN в LOW — свободный выбег.
- TT-моторы: 200–700 мА на канал под нагрузкой, заклинивший — за ампер; USB с его 500 мА валит плату на развороте — кормим от отсека AA.
- Авто-повтор пульта — кадры каждые 110 мс: гасим флагом IRDATA_FLAGS_IS_REPEAT, иначе робот дёргается.
- TB6612 роняет доли вольта против ~2 В у L298N; плюс таймаут 300–500 мс тишины — робот не уедет с мёртвой батарейкой пульта.
Частые вопросы по теме урока
- Почему робота не кормят от USB-порта, если моторы TT-типа?
- TT-моторчик берёт 200-700 мА на канал, заклинивший — за ампер. Два канала плюс плата не влезают в 500 мА порта: на пиковом токе разворота питание проседает. Моторы кормим батарейным отсеком, землю делаем общей. два мотора под нагрузкой едят 400-1400 мА против потолка порта в 500 мА — питание проседает и плата перезагружается
- За что отвечает пара IN1/IN2 у канала L298N?
- Пара IN-входов задаёт полярность моста — направление. Заполнение ШИМ подаётся на ENA. Оба IN в LOW дают свободный выбег, инверсия пары — реверс. за направление вращения; скорость живёт на ENA с ШИМ
- Пульт при удержании шлёт repeat-кадры каждые 110 мс. Как их гасит скетч на IRremote 4.x?
- В четвёртой версии повтор — флаг в decodedIRData.flags, фильтр умещается в одну строку с побитовым И. Задержка в loop заморозит приёмник, а сравнение с 0xFFFFFFFF не ловит повторы расширенных NEC-кадров. проверкой флага IRDATA_FLAGS_IS_REPEAT в decodedIRData.flags
- Чем TB6612FNG честно лучше L298N для батарейного робота?
- Биполярный L298N сжигает на своих транзисторах порядка 2 В — с шести вольт отсека мотор получает четыре. TB6612 на MOSFET роняет доли вольта и держит 1,2 А на канал, но не переживает 15 В и капризнее к разводке. он на полевых ключах: роняет доли вольта против двух вольт просадки у биполярного L298N
- Как танковая схема делает поворот на месте?
- Разность направлений вместо разности скоростей: инверсия одной пары IN-входов разворачивает робот почти вокруг оси. Это главное преимущество перед рулевой схемой на скользком полу. каналы крутятся в противоположные стороны: левый реверс, правый вперёд