// Эталонный скетч: робот-человек берёт кубик с полки (площадка «Полка с кубиком»).
//
// Ноги держат стойку. Правая рука: согнуть локоть (кисть к груди), поднять
// плечо, опустить кисть на кубик сверху, сжать пальцы — кубик схвачен, —
// и поднять руку. Кубик весит 200 г, полка — под правой рукой.
//
// Привод: PCA9685 0x40 — ноги (0–9) и руки (12–15: плечо и локоть левой,
// плечо и локоть правой); PCA9685 0x41 — пальцы (канал 1 — правая кисть).
// «Прямо»: плечо write(60), локоть write(0), пальцы write(0) — ладонь.
//
// Хват в модели: пальцы сжаты сильнее 60° и кубик у ладони — он держится в
// кисти; разжать (меньше 30°) — отпустить.
#include <Wire.h>
#include <math.h>
const uint8_t PCA = 0x40, PCA2 = 0x41;
const float L1 = 0.42, L2 = 0.42, H = 0.87, ANKLE = 0.08;
const float R = 180.0 / M_PI;
void reg(uint8_t dev, uint8_t r, uint8_t v) { Wire.beginTransmission(dev); Wire.write(r); Wire.write(v); Wire.endTransmission(); }
// Угол от «прямо» → write() → импульс → канал.
void pulse(uint8_t dev, uint8_t ch, float zero, float deg) {
float w = zero + deg;
if (w < 0) w = 0;
if (w > 180) w = 180;
uint16_t us = 500 + (uint16_t)(w * (2000.0 / 180.0));
uint16_t off = (uint32_t)us * 4096UL / 19988UL;
Wire.beginTransmission(dev);
Wire.write(0x06 + 4 * ch);
Wire.write(0); Wire.write(0); Wire.write(off & 0xff); Wire.write(off >> 8);
Wire.endTransmission();
}
void joint(uint8_t ch, float zero, float deg) { pulse(PCA, ch, zero, deg); }
// Нога прямо под тазом на высоте H: бедро и колено по обратной кинематике.
void leg(uint8_t c0) {
float d = H - ANKLE;
float k = M_PI - acos((L1 * L1 + L2 * L2 - d * d) / (2 * L1 * L2));
float th = acos((L1 * L1 + d * d - L2 * L2) / (2 * L1 * d));
joint(c0 + 0, 90, 0);
joint(c0 + 1, 40, th * R);
joint(c0 + 2, 0, k * R);
joint(c0 + 3, 90, (k - th) * R);
joint(c0 + 4, 90, 0);
}
// Ключевые позы правой руки: время, плечо, локоть, пальцы (градусы от «прямо»).
const float KEYS[][4] = {
{0, 0, 0, 0}, {1, 0, 0, 0},
{2, 0, 70, 0}, // локоть вверх — кисть к груди
{3, 10, 70, 0}, // кисть над кубиком
{4.5, 20, 30, 0}, // опустить на кубик
{5.3, 20, 30, 90}, // сжать пальцы
{7, 30, 70, 90}, // поднять
};
const uint8_t NKEYS = sizeof(KEYS) / sizeof(KEYS[0]);
float smooth(float x) { x = x < 0 ? 0 : (x > 1 ? 1 : x); return x * x * (3 - 2 * x); }
void setup() {
Serial.begin(9600);
Wire.begin();
const uint8_t devs[2] = {PCA, PCA2};
for (uint8_t i = 0; i < 2; i++) {
reg(devs[i], 0x00, 0x10); reg(devs[i], 0xFE, 121); reg(devs[i], 0x00, 0x20);
}
delay(5);
reg(PCA, 0x00, 0xA0); reg(PCA2, 0x00, 0xA0);
}
void loop() {
float t = millis() / 1000.0;
leg(0); leg(5);
float p[3] = {KEYS[NKEYS - 1][1], KEYS[NKEYS - 1][2], KEYS[NKEYS - 1][3]};
for (uint8_t i = 0; i + 1 < NKEYS; i++) {
if (t < KEYS[i + 1][0]) {
float u = smooth((t - KEYS[i][0]) / (KEYS[i + 1][0] - KEYS[i][0]));
for (uint8_t j = 0; j < 3; j++) p[j] = KEYS[i][j + 1] + (KEYS[i + 1][j + 1] - KEYS[i][j + 1]) * u;
break;
}
}
joint(14, 60, p[0]); // правое плечо
joint(15, 0, p[1]); // правый локоть
pulse(PCA2, 1, 0, p[2]); // правая кисть
joint(12, 60, 10); joint(13, 0, 10); // левая рука чуть вперёд
delay(20);
}
Робот Port берёт кубик с полки
Правая рука Port сгибает локоть, опускает кисть на кубик, сжимает пальцы и поднимает его. Ноги держат стойку.
Что на схеме
Плата Arduino Uno, к ней подключены:
- Робот-человек Port —
r1, площадка «Полка с кубиком»
Задействованы выводы платы: GND, A4, A5. Всего соединений на схеме: 3.
Как смотреть
Нажмите ▶ Запустить — после сборки откроется окно робота (потом его открывает кнопка 🤖 над схемой). Мышью камеру можно повернуть, колесом — приблизить; щелчок по суставу показывает номер привода, канал PCA9685 и строку кода для него. Кнопка ⏺ над схемой записывает сцену в GIF — с теми поворотами камеры, что вы делаете.
Робот — не анимация, а модель с массами, шарнирами и трением: скетч только шлёт импульсы приводам и читает датчики, а встанет робот, пойдёт или упадёт — решает физика. Как она устроена — на странице «Физика сцен с роботами».
Как это работает
Движение руки — ключевые позы: время, угол плеча, локтя и пальцев; между позами углы меняются плавно. Плечо и локоть — каналы 14 и 15 первого PCA9685, пальцы правой кисти — канал 1 второго (адрес 0x41).
Хват в модели упрощён: пальцы сжаты сильнее 60° и кубик у ладони — он держится в кисти; разжать меньше 30° — отпустить.
Библиотеки
Дополнительных библиотек не нужно: всё делается ядром Arduino AVR, которое уже стоит в сборочном окружении.
Как это устроено в коде
Основное, чем пользуется скетч:
Serial.begin()— открывает монитор порта на заданной скоростиWire.begin()— поднимает шину I²C, по ней говорят датчики и дисплеиdelay()— пауза, на время которой ядро занято ожиданиемmillis()— миллисекунды с запуска: на них строят работу без остановки
В скетче 90 строк; свои функции: pulse(), leg(); подключено заголовков: 2. Сам скетч открыт на соседней вкладке — его можно менять и запускать заново.
Что попробовать изменить
- Поменяйте период в константе и посмотрите на анализаторе, как меняется картина на выводе.
- Перенесите деталь на другой вывод — на схеме и в коде — и убедитесь, что пример работает так же.
- Нажмите «Форкнуть»: копия станет вашим проектом, и в ней можно менять что угодно.
Смотрите также
- Робот Port идёт и держит равновесие — Человекоподобный робот Port (1,75 м, 50 кг) идёт по ровному полу: походка без остановок, равновесие и курс — по датчику…
- Робот Port поднимается по лестнице — Port поднимается на четыре ступени приставным шагом: места стоп — по плану лестницы, корпус — по расчёту ZMP с предуправ…
- Робот Port проходит лабиринт — Port идёт по коридорам и смотрит дальномером в голове: у стены поворачивает направо, если там занято — налево, и держитс…
- Робот Port доходит до стены и останавливается — Ходьба с дальномером: Port идёт к стене, ближе 60 см переходит на приставной шаг и останавливается.
- Робот Port обходит столб — Port видит столб дальномером, осматривается головой влево и вправо, обходит его с той стороны, где свободнее, и возвраща…
- Робот Port машет рукой — Port стоит и машет правой рукой, левая кисть сжимается в кулак и разжимается. Первый пример с руками и пальцами.
Сборка ещё не запускалась.
Печатайте в Serial числа — они станут графиком.
Serial.println(t); — один ряд,
Serial.printf("t:%.1f h:%.1f\n", t, h); — два с подписями.
Формат тот же, что понимает плоттер Arduino IDE.