Примеры

Все примеры из папки 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);
  }
}