У моей собаки Pin двенадцать сервоприводов, десять килограммов веса и ни грамма железа: она живёт в браузере. Тем не менее она трусит рысью со скоростью 35 см/с, держит курс по гироскопу и однажды села на хвост, когда я попробовал научить её прыгать.
Это рассказ о том, как в учебной платформе PinPort появился четвероногий робот, что у него внутри и почему ходить у него получилось раньше, чем бегать.

Почему вообще собака
PinPort: эмулятор Arduino и ESP32 в браузере: схема, скетч, кнопка «Запустить», и светодиод мигает. Я делал его, чтобы человек мог учиться электронике, ничего не покупая и ничего не сжигая. Роботы появились потому, что у школьников сейчас тренд на робототехнику, и я не хотел отставать.
Первым я сделал человека. Его зовут Port: 1,75 м, 50 кг, двадцать приводов. И он оказался капризным: две ноги, высокий центр масс, и каждая лишняя сотня граммов в руке меняла походку. Я потратил недели на то, чтобы он вообще пошёл и не упал на втором шаге.
Когда Port наконец зашагал, мне захотелось создать робота, который стоит на четырех ногах. Так появился Pin: четыре опоры вместо двух, низкий центр масс, и никакого трагизма при каждом чихе гироскопа.
Знакомьтесь, Pin
Pin: собака, и она, повторю, не существует. Но паспорт у неё есть, причём подробный:
Параметр |
Значение |
|---|---|
Масса |
10 кг |
Приводов |
12: по три на лапу (отведение/качение в сторону, бедро, колено) |
Бедро и голень |
по 21 см |
Стопа |
резиновый шар 2 см с датчиком касания |
Высота оси бедра |
0,44 м в сборочной позе, 0,28 м при ходьбе |
Скорость рыси |
около 35 см/с |
Схема лап взята у настоящих четвероногих роботов вроде Unitree Go1: три сустава на лапу, колено сгибается назад.
Имя появилось из пары. В PinPort уже был робот Port, робот-человек. Ему нужен был напарник. Port и Pin.
Что у неё внутри
Важное условие, с которым я запускал весь проект: мозги: настоящие. То есть на собаке без всяких хитростей крутится не «программа на JavaScript, похожая на скетч», а обычный скетч на C++, собранный в прошивку для Arduino Uno и исполняемый эмулятором AVR. Тот же файл вы можете прошить в живой плате.
Внутри три части:
Arduino Uno: мозг. Никаких подпорок: 2 КБ оперативки и 16 МГц.
PCA9685: шестнадцатиканальный шим-контроллер по I2C (адрес 0x40). Двенадцать каналов, двенадцать приводов: 0–2 передняя левая, 3–5 передняя правая, 6–8 задняя левая, 9–11 задняя правая.
MPU6050 (0x68): гироскоп. От него зависит, уйдёт ли собака вкось (об этом ниже).
Физику считает движок Rapier 3D, собранный в WebAssembly, с шагом 2 мс.
Самое интересное место скетча: обратная кинематика. Мы не задаём углы суставов, мы говорим, где должна быть стопа: на сколько ниже оси бедра и на сколько вперёд. Дальше тригонометрия двух звеньев по 21 см:
void paw(uint8_t c0, float fh, float fz) { float d = sqrt(fh * fh + fz * fz); if (d > L1 + L2 - 0.0005) d = L1 + L2 - 0.0005; float k = M_PI - acos((L1 * L1 + L2 * L2 - d * d) / (2 * L1 * L2)); float th = atan2(fz, fh) - acos((L1 * L1 + d * d - L2 * L2) / (2 * L1 * d)); setJoint(c0 + 0, 0, 0); setJoint(c0 + 1, 1, th * R); setJoint(c0 + 2, 2, k * R); }
Здесь fh: сколько метров от оси бедра вниз до стопы, fz: вперёд. Длина «линии от бедра до стопы» d обрезается, если лапа просит дотянуться дальше, чем она всё равно может. Потом теорема косинусов, и на выходе угол колена k и угол бедра th. Отведение/качение в сторону для рыси не нужно, оно всегда ноль: лапы ходят в плоскости тела.
Есть одна мелочь, которая легко сбивает с толку: колено у настоящей собаки гнётся «назад», то есть голень уходит вперёд, и прямой ноге соответствует write(90) на бедре, но write(0) на колене. Поэтому в скетче массив ZERO = {90, 90, 0}: «ноль» у трёх суставов одной лапы разный.
Рысь
Для первого шага я выбрал рысь. Если лапы двигать по диагоналям, передняя левая вместе с задней правой, потом передняя правая с задней левой, то в каждый момент на земле стоит минимум одна пара, а центр масс оказывается над прямой между ними. Баланс почти бесплатный. Не придуманный мной трюк, а то, как ходят собаки, лошади и большинство четвероногих роботов.
В скетче это умещается в одну строку: массив «кто в какой паре»:
const uint8_t pair[4] = {0, 1, 1, 0}; // ПЛ, ПП, ЗЛ, ЗП
Каждая пара сначала переносится вперёд по дуге, потом стоит и толкает корпус, а пока она толкает, другая переносится. Время дуги: T_STEP, 0,25 с, шаг STEP: 10 см, подъём лапы LIFT: 5 см. Работает так: при включении собака стоит на почти прямых лапах, за секунду приседает на рабочую высоту 0,28 м, ещё полторы секунды стоит и начинает шагать, плавно нарастивая длину шага. Итог на Uno: около 35 см/с, наклон корпуса до 4°.
Но самое ценное в рыси: гироскоп. Без него собака «идёт прямо», пока левые и правые лапы не стали чуть по-разному проскальзывать по полу, и она потихоньку заворачивает. Скетч читает угловую скорость по оси Z у MPU6050, интегрирует её в курс и корректирует разницу шага левых и правых лап:
float diff = TURN != 0 ? TURN : constrain(-K_HEAD * heading, -0.03, 0.03);
Увело влево, шагаем почти вправо. Тот же параметр TURN даёт поворот «как у танка»: с STEP = 0 и TURN = 0,05 собака разворачивается на месте на угол около 180° за 10 секунд. В итоге курс уходит всего на 1° за 10 секунд.
Ещё одно наблюдение, которое меня порадовало: собака намного устойчивее человека. Port опрокидывается, если его толкнуть по-настоящему: ему нужна обратная связь по крену. У Pin опор четыре и центр масс низко, и она держит боковой толчок 8–10 Н·с вообще без какой-либо стабилизации. Как настоящая собака на прогулке: толкнули, постояла на четырёх, пошла дальше.
Бег и галоп, которого не вышло
Рысь на 35 см/с: это прогулочный шаг, а хотелось бежать. Тот же скетч, только T_STEP = 0,15 с и STEP = 0,22 м, и Pin пробегает около 1,25 м/с. Наклон корпуса доходит до 10°, а курс уходит до 4°: вбок сносит до метра на десять метров, она бежит чуть «крабом».
Теперь честная часть. Если смотреть на запись, фазы полёта почти нет: все четыре лапы в воздухе около 2 % времени. Строго говоря, это быстрая рысь, а не бег в том смысле, в каком бегает настоящая собака.

Я пробовал сделать настоящий полёт: опору короче половины цикла и сильный толчок лапой. С полётом около 20 % времени собака начинает рыскать и в конце концов опрокидывается. Я пробовал добавить обратную связь по наклону через высоту лап, и она не помогла. Галоп и рысь с полётом остаются задачей, которая у меня не решена. Я оставил это в документации в таком же виде: «пробовал, не получилось».
И мне кажется, это правильное место, чтобы остановиться и сказать вслух: симулятор тут не виноват. Если бы собака бегала легко, я бы не поверил ни ей, ни себе.
Прыжок и собака, севшая на хвост
Прыжок: второй скетч, dog_jump.ino. Алгоритм прост и ясен: присесть (ось бедра на 0,20 м), отвести лапы назад, резко выпрямиться до 0,41 м, в воздухе поджать лапы до 0,30 м и приземлиться. Раз в три секунды повторить и написать в порт «прыжок N».

Результат скромный: полёт около 0,18 с, центр масс поднимается самое большое на 4 см, наклон корпуса до 13°. Всё упирается в приводы: 10 рад/с и 20 Н·м: именно таковы быстрые сервоприводы, которые можно купить. Я проверил для любопытства 30 рад/с: корпус взлетал вдвое выше, но собака тут же опрокидывалась. Такой прыжок нужно настраивать отдельно, а таких сервоприводов в наборе всё равно нет.
А теперь самое смешное, и заодно поучительное. В первом прыжке я поставил задние лапы прямо под бедро, как бы логично: пусть толкаются прямо вверх. У меня это превратилось в сцену мультфильма: задний толчок задирал нос вверх, и Pin садилась на хвост с наклоном 50–60°.
Почему так? Видно, если представить рычаг. Лапа толкает вверх только заднюю часть, центр масс есть посредине тела, и сила проходит не через него, а мимо. Решение оказалось простым: отвести передние лапы на 4 см назад от оси плеча, а задние на 8 см, тогда толчок проходит через центр масс. Ничего нового для человека, знающего механику, но я дошёл до этого не из учебника, а из того, что увидел собаку, сидящую на хвосте. Так мне кажется, для этого и нужен симулятор: ошибка здесь стоит не поломанный сервопривод, а перезапуск.
Что можно попробовать самому
Запустить собаку можно за пару кликов, ставить ничего не нужно:
Паспорт Pin: 3D-модель, таблицы каналов и размеров.
Все роботы в галерее: 13 примеров с Port и Pin.
Pin идёт рысью и Pin прыгает: открыть и нажать «▶ Запустить».
Если захотите повозиться, в скетче вверху есть три константы, которые стоит покрутить:
STEP: длина шага. Добавьте длины, увидите, как растёт наклон корпуса.T_STEP: время полушага. Чем меньше, тем быстрее; значение 0,15 уже делает из рыси быстрый бег.TURN: разница шага левых и правых лап. С нулём держит курс гироскоп, с ненулевым поворачивает.
Если спросите себя «а насколько быстро можно?», ваш тест, скорее всего, повторит мой: до 1,25 м/с собака ещё держится, а дальше нужны уже не параметры, а новые идеи.

Чего в Pin нет
Список небольшой, но я хочу, чтобы он был в статье, а не только в документации:
Нет настоящего бега и галопа. Есть быстрая рысь, фаза полёта около 2 % времени.
Нет высокого прыжка. Центр масс поднимается примерно на 4 см. Более быстрые приводы подбросят выше, но собака начнёт опрокидываться.
Нет ходьбы по неровностям. Пол ровный, лестницу осилил только Port, и то не во всех сочетаниях параметров.
Нет зрения и нет слуха. Датчики у Pin: гироскоп, четыре датчика касания на стопах и значения углов самих приводов. Всё.
Нет гарантии, что на железе будет так же. Физика всего лишь примерная: шаг 2 мс, упрощённый привод, трение шара о пол. Сервы из «набора» ведут себя не как идеальная модель, и я не буду уверять, что скетч сразу заработает на настоящей собаке.
b_vova Автор
И да, это виртуальныя собака, в виртуальном физическом 3D движке, в виртуальном симуляторе Arduino ))))