Примеры
Все примеры из папки UNI/examples/ библиотеки. Открывайте их через Файл → Примеры → UNI в Arduino IDE.
Основы
Start — минимальная программа
Создаёт робота и инициализирует периферию: моторы, энкодеры, экран, светодиод и батарею. begin() можно не вызывать — первая команда сделает это сама.
#include <UNI.h>
UniBase robot;
UniDev module;
void setup() {
robot.begin("UNI");
// Ваш код, выполняется один раз. Например:
// robot.moveDist(50, 300);
// robot.rotate(50, 90);
}
void loop() {
// Ваш код, выполняется по кругу
}
BasicMovement — базовые движения
Робот проезжает квадрат со стороной 300 мм: четыре прямых участка и четыре поворота на 90°. Все команды блокирующие.
#include <UNI.h>
UniBase robot("UNI");
void setup() {
robot.begin();
for (int i = 0; i < 4; i++) {
robot.moveDist(50, 300); // вперед 300 мм на мощности 50
robot.rotate(50, 90); // поворот направо на 90 градусов
}
// Дополнительно: движение назад и по времени
robot.moveDist(-50, 200); // назад 200 мм
robot.moveTime(40, 1000); // вперед 1 секунду
robot.moveTime(-40, 1000); // назад 1 секунду
}
void loop() {
}
Движение
PrecisionTurns — точные повороты
rotate() поворачивает на угол относительно текущего — ошибки накапливаются. rotateTo() поворачивает к абсолютному курсу одометрии — ошибка предыдущих манёвров съедается.
#include <UNI.h>
UniBase robot("UNI");
void setup() {
robot.begin();
// Квадрат на относительных поворотах
robot.displayPrint("rotate");
delay(1000);
for (int i = 0; i < 4; i++) {
robot.moveDist(50, 300);
robot.rotate(50, 90);
}
robot.displayPrint("Angle", robot.getAngle()); // накопленный курс
delay(3000);
// Квадрат на абсолютных поворотах
robot.displayPrint("rotateTo");
delay(1000);
robot.setPosition(0, 0, 0); // обнуляем одометрию перед заездом
for (int i = 0; i < 4; i++) {
robot.moveDist(50, 300);
robot.rotateTo(50, (i + 1) * 90); // курсы 90, 180, 270, 360
}
robot.displayPrint("Angle", robot.getAngle());
}
void loop() {
}
DriveToPoint — езда по точкам одометрии
setPosition() привязывает одометрию к полю, а moveTo() сам доворачивает на курс к цели и едет к ней по прямой.
#include <UNI.h>
UniBase robot("UNI");
// Маршрут: массив точек {x, y} в миллиметрах
const float route[][2] = {
{400, 0},
{400, 400},
{0, 400},
{0, 0}, // возврат на старт
};
const int routeLen = sizeof(route) / sizeof(route[0]);
void setup() {
robot.begin();
// Робот стоит в начале координат и смотрит вдоль оси X
robot.setPosition(0, 0, 0);
for (int i = 0; i < routeLen; i++) {
robot.displayPrint("Point", i + 1);
robot.moveTo(50, route[i][0], route[i][1]);
delay(300);
}
robot.rotateTo(50, 0); // в конце вернуть исходный курс
robot.displayPrint("Route", "DONE");
}
void loop() {
robot.printOdometry(); // следим за позицией в Serial Monitor
delay(1000);
}
Arcs — дуги
moveArcDist() — дуга через соотношение скоростей колёс. moveArcRadius() — дуга с настоящей геометрией: радиус и угол сектора.
#include <UNI.h>
UniBase robot("UNI");
void setup() {
robot.begin();
// Квадрат со скруглёнными углами:
// прямые участки + четверти круга радиусом 100 мм
robot.displayPrint("Round sq");
delay(1000);
for (int i = 0; i < 4; i++) {
robot.moveDist(50, 250);
robot.moveArcRadius(50, 100, 90); // четверть круга R = 100 мм
}
delay(1000);
// Змейка через соотношение скоростей колёс
robot.displayPrint("Slalom");
delay(1000);
for (int i = 0; i < 3; i++) {
robot.moveArcDist(50, 30, 250); // дуга вправо
robot.moveArcDist(50, -30, 250); // дуга влево
}
robot.displayPrint("Arcs", "DONE");
}
void loop() {
}
AsyncMovement — асинхронные команды
Команды с суффиксом Async запускают движение и сразу возвращают управление. isMoving() — проверить, едет ли робот. waitMove(timeout) — ждать с защитой от застревания.
#include <UNI.h>
UniBase robot("UNI");
void setup() {
robot.begin();
robot.blinkLED(0);
// 1. Полезная работа, пока робот едет
robot.moveDistAsync(50, 800);
while (robot.isMoving()) {
robot.displayPrint("Dist", robot.getDistance()); // живой прогресс
delay(100);
}
robot.displayClear();
delay(500);
// 2. Запустить и просто дождаться
robot.rotateAsync(50, 180);
robot.blinkLED(100); // мигаем во время поворота
robot.waitMove();
robot.blinkLED(0);
delay(500);
// 3. Ожидание с таймаутом — страховка от застревания
robot.moveDistAsync(50, 800);
if (!robot.waitMove(5000)) { // не доехал за 5 секунд?
robot.stop(HARD);
robot.displayPrint("STUCK!");
return;
}
delay(500);
// 4. Прервать движение по собственному условию
robot.moveDistAsync(30, 2000); // далёкая цель...
delay(1500); // ...но через 1.5 секунды
robot.stop(HARD); // передумали
robot.displayPrint("Async", "DONE");
}
void loop() {
}
ManualControl — прямое управление моторами
motorsSync() удерживает соотношение скоростей по энкодерам. holdPosition() активно сопротивляется сдвигу. Все команды работают до вызова stop().
#include <UNI.h>
UniBase robot("UNI");
void setup() {
robot.begin();
// Прямо без стабилизации (может уводить в сторону)
robot.displayPrint("motors");
robot.motors(40, 40);
delay(1500);
robot.stop(HARD);
delay(500);
// Прямо со стабилизацией по энкодерам
robot.displayPrint("motorsSync");
robot.motorsSync(40, 40);
delay(1500);
// Дуга: правое колесо вдвое медленнее левого
robot.motorsSync(50, 25);
delay(1500);
// Задний ход со стабилизацией
robot.motorsSync(-40, -40);
delay(1500);
robot.stop(HARD);
delay(500);
// Дуга через мощность и угол
robot.displayPrint("motorsArc");
robot.motorsArc(40, 20);
delay(2000);
robot.stop(SOFT); // мягкая остановка без блокировки колёс
// Активное удержание позиции (10 секунд)
robot.displayPrint("HOLD");
robot.holdPosition();
delay(10000);
robot.stop(SOFT);
robot.displayPrint("Done");
}
void loop() {
}
Датчики
ObstacleStop — реакция на препятствие
Робот едет вперёд и следит за ультразвуковым датчиком. Препятствие ближе 150 мм — остановка; убрали — продолжает движение к цели. Подключение: датчик на P6 (trig) и P7 (echo).
#include <UNI.h>
UniBase robot("UNI");
UniDev module;
const int OBSTACLE_MM = 150; // дистанция остановки
const int TARGET_MM = 1500; // сколько всего проехать
void setup() {
robot.begin();
robot.resetDistance();
}
void loop() {
// Цель достигнута — стоим
if (robot.getDistance() >= TARGET_MM) {
robot.displayPrint("DONE");
return;
}
int dist = module.ultraSonic(P6, P7);
bool blocked = (dist > 0 && dist < OBSTACLE_MM);
if (blocked) {
if (robot.isMoving()) {
robot.stop(HARD);
robot.displayPrint("Obstacle", dist);
}
} else {
if (!robot.isMoving()) {
// Едем остаток пути асинхронно, продолжая опрашивать датчик
int remaining = TARGET_MM - (int)robot.getDistance();
robot.moveDistAsync(50, remaining);
robot.displayClear();
}
}
delay(50);
}
DistanceSensors — ультразвуковые датчики
Чтение расстояния в мм с нескольких датчиков. Подключение: передний — P6/P7, боковой — P3/P4. Откройте Serial Monitor (115200 бод).
#include <UNI.h>
UniBase robot;
UniDev module;
void setup() {
robot.begin("Dist");
}
void loop() {
int front = module.ultraSonic(P6, P7);
int side = module.ultraSonic(P3, P4);
Serial.print("Front: ");
Serial.print(front);
Serial.print(" mm, Side: ");
Serial.print(side);
Serial.println(" mm");
robot.displayPrint("Front", front);
delay(100);
}
LineSensor — датчик линии
Аналоговое чтение 0–4095. Подберите порог между светлым полом и чёрной линией. Подключение: датчик на P2.
#include <UNI.h>
UniBase robot;
UniDev module;
const int THRESHOLD = 2000; // порог — подберите под свой пол
void setup() {
robot.begin("Line");
}
void loop() {
int value = module.lineSensor(P2);
bool onLine = (value > THRESHOLD);
Serial.print("Line sensor: ");
Serial.print(value);
Serial.println(onLine ? " [LINE]" : "");
robot.displayPrint(onLine ? "LINE" : "floor", value);
delay(100);
}
ServoControl — сервопривод: углы, подключение, отключение
Поочерёдно устанавливает углы 0°, 90° и 180°, затем отключает привод — качалка не держит нагрузку и не ломается. Следующий вызов servo() включает его обратно автоматически. Подключение: сервопривод на P3.
#include <UNI.h>
UniBase robot;
UniDev module;
void setup() {
robot.begin("Servo");
}
void loop() {
// Рабочий цикл: серво включено и держит углы
robot.displayPrint("Servo", "ON");
module.servo(P3, 0);
delay(1000);
module.servo(P3, 90);
delay(1000);
module.servo(P3, 180);
delay(1000);
// Отключаем: привод обмякает, механизм мягко опускается.
// Попробуйте повернуть качалку рукой — сопротивления нет
robot.displayPrint("Servo", "OFF");
module.servoDetach(P3);
delay(3000);
// Следующий servo() в начале цикла снова включит привод
}
Настройка
Tuning — настройка точности движения
Все параметры ПИД-регуляторов собраны в TuningConfig. Геометрия платформы задаётся в UniConfig и передаётся в конструктор.
#include <UNI.h>
UniConfig cfg; // значения по умолчанию
// cfg.trackLengthMM = 106.0f; // при необходимости меняется до создания робота
UniBase robot("UNI", cfg);
void setup() {
robot.begin();
// Берём текущие настройки, меняем нужное и применяем
TuningConfig tuning = robot.getTuning();
tuning.rotateTolDeg = 0.5f; // точнее повороты
tuning.moveAccel = 1500.0f; // мягче торможение на прямой
robot.setTuning(tuning);
// Тестовый заезд с новыми настройками
robot.moveDist(70, 500);
robot.rotate(70, 180);
robot.moveDist(70, 500);
robot.rotate(70, 180);
// По одометрии видно качество возврата в исходную точку
robot.displayPrint("X err", robot.getAbsX());
}
void loop() {
}
UniBaseControl — управление по UART
Примеры из папки UniBaseControl/examples/. Прошивка ESP32 — UniBaseControl_Start; программа для Arduino Nano — Start или BasicMovement.
UniBaseControl_Start — сторона ESP32
Превращает платформу в исполнителя UART-команд. Загружается в ESP32. Подключение: UART2 (RX 16, TX 17, 38400 бод) к TX/RX платы Nano.
#include <UNI.h>
UniBase robot;
void setup() {
robot.begin("UNI");
robot.UniBaseControl(); // включаем прослушивание команд
}
void loop() {
// Приём команд, движение и экран работают на втором ядре.
// loop() свободен — сюда можно добавить свою логику.
}
Start — сторона Arduino Nano
Минимальная программа для управляющей платы. Загружается в Arduino Nano с библиотекой UniBaseControl.
#include <UniBaseControl.h>
UniBaseControl robot;
void setup() {
robot.begin();
}
void loop() {
}
BasicMovement — движение с Nano
Робот проезжает квадрат, команды отправляются с Arduino Nano по UART.
#include <UniBaseControl.h>
UniBaseControl robot;
void setup() {
robot.begin();
}
void loop() {
for (int i = 0; i < 4; i++) {
robot.moveDist(50, 100);
delay(2000);
robot.rotate(50, 90);
delay(1000);
}
}