Урок 33. Обучаем Arduino робота ездить по линии

При создании любого робота, его нужно оснастить датчиками (показания которых будет считывать робот), управляющими модулями (для вывода результатов работы робота), и скетчем (по алгоритму которого должен работать робот). В нашем случае, мы оснастим машинку, тремя аналоговыми датчиками линий и одним ультразвуковым датчиком расстояния, а в роли управляющих модулей выступят два закрепленных к каркасе моторчика, с колёсами на валах.
Видео:

Алгоритм работы:
- Если центральный датчик находится на линии, а боковые вне линии, то машинка едет прямо.
- Если левый датчик находится на линии, а правый вне линии, то машинка поворачивает налево (независимо от показаний центрального датчика)
- Если правый датчик находится на линии, а левый вне линии, то машинка поворачивает направо (независимо от показаний центрального датчика)
- Если правый и левый датчики находятся на линии (вне зависимости от показаний центрального датчика), то такое состояние является неопределённым, машинка продолжает предыдущее движение (прямо или с поворотом) в течении 2 секунд (можно менять в скетче). Если в течении этого времени состояние не изменится, то она остановится.
- Если все три датчика находятся вне линии, то такое состояние является неопределённым (потеря линии). Если этому состоянию предшествовал поворот, то машинка продолжит поворот в течении 2 секунд (можно менять в скетче). Если в течении этого времени состояние не изменится, то она остановится. Если этому состоянию не предшествовал поворот (машинка ехала прямо и линия оборвалась), то машинка сразу остановится.
- Если перед машинкой появилось препятствие, на расстоянии менее 10 см (можно менять в скетче), то машинка остановится и продолжит движение, как только препятствие исчезнет.
Скорость движения задаётся в константе valSpeed, от 1 до 255. Крутизна поворотов задаётся в константе valTurning, от 1 до 255. Время продолжения движения, при неопределённом состоянии, задаётся в константе tmrDelay, в микросекундах. Направление движения моторов указывается логическими значениями элементов массива arrRoute (0 элемент — правый мотор, 1 элемент — левый мотор), по умолчанию все элементы равны «1». Если вы перепутали полярность при подключении мотора, то измените значение соответствующего элемента этого массива на «0».
Калибровка для светлых, слабоконтрастных или цветных линий:
Машинка настроена на движение по темной линии, но она может ездить по светлым, слабоконтрастным или цветным линиям. Для этого её нужно откалибровать, указав значения для констант valSensor1 (показание датчика находящегося на линии) и valSensor0 (показание датчика находящегося вне линии). Для чего, в коде setup скетча, предусмотрен вывод показаний центрального датчика в монитор последовательного порта.
- Поместите машинку так, чтобы центральный датчик находился над линией.
- Подключите Arduino Uno по USB кабелю.
- Откройте монитор последовательного порта. В мониторе высветится показание датчика на линии.
- Поместите машинку так, чтобы центральный датчик находился вне линии.
- Нажмите кнопку reset на Motor Shield. В мониторе высветится показание датчика вне линии.
- Укажите первое значение константе valSensor1, а второе значение константе valSensor0 и повторно загрузите скетч.
Код программы:
// БИБЛИОТЕКИ: #include // подключаем библиотеку для работы с датчиком // НОМЕРА ВЫВОДОВ: const uint8_t pinSensorL = A3; // Вывод к которому подключен датчик находящийся слева (по направлению движения) const uint8_t pinSensorC = A4; // Вывод к которому подключен датчик находящийся по центру (по направлению движения) const uint8_t pinSensorR = A5; // Вывод к которому подключен датчик находящийся справа (по направлению движения) const uint8_t pinSens_TRIG = 2; // Вывод к которому подключен датчик расстояния HC_SR04 (вывод обозначенный на датчике как TRIG) const uint8_t pinSens_ECHO = 3; // Вывод к которому подключен датчик расстояния HC_SR04 (вывод обозначенный на датчике как ECHO) const uint8_t pinShield_LH = 7; // Вывод направления к которому подключен левый мотор (по направлению движения) const uint8_t pinShield_LE = 6; // Вывод ШИМ к которому подключен левый мотор (по направлению движения) const uint8_t pinShield_RE = 5; // Вывод ШИМ к которому подключен левый мотор (по направлению движения) const uint8_t pinShield_RH = 4; // Вывод направления к которому подключен левый мотор (по направлению движения) // ОБЪЕКТЫ: iarduino_HC_SR04_int hcsr(pinSens_TRIG, pinSens_ECHO); // Объект hcsr для работы с библиотекой iarduino_HC_SR04 (вывод TRIG, вывод ECHO) // УСТАНАВЛИВАЕМЫЕ ЗНАЧЕНИЯ: const uint16_t valSensor1 = 930; // Показание датчика находящегося на линии (указывается для конкретной трассы) const uint16_t valSensor0 = 730; // Показание датчика находящегося вне линии (указывается для конкретной трассы) const uint8_t valSpeed = 255; // Максимальная скорость (число от 1 до 255) const uint32_t tmrDelay = 2000; // Время в течении которого требуется остановиться (если в течении этого времени состояние остаётся неопределённым (линия не обнаружена), то требуется остановиться) const uint8_t valTurning = 10; // Крутизна поворотов (скорость реакции) (число от 1 до 255) const uint8_t valDistance = 20; // Минимально допустимое расстояние до объекта в сантиметрах (если расстояние будет меньше, то требуется остановитьтся) const bool arrRoute[2] = ; // Направление движения для каждого мотора (зависит от полярности, нулевой элемент - правый мотор, первый элемент - левый мотор) // РАССЧИТЫВАЕМЫЕ ЗНАЧЕНИЯ: uint8_t arrSpeed[2]; // Рассчитываемая скорость для каждого мотора (число от 1 до valSpeed, нулевой элемент - правый мотор, первый элемент - левый мотор) uint16_t valSensorM; // Рассчитываемое среднее значение датчика (значение между цветом линии и цветом вне линии) uint8_t valSensor; // Биты рассчитываемых логических уровней всех датчиков (0bxxxxxLCR) bool flgLine; // Флаг указывающий на то, что используется светлая линия (0 - тёмная линия, 1 - светлая линия) int8_t flgTurning; // Флаг наличия и направления поворота (0 - не поворачиваем, -1 - поворачиваем налево, +1 - поворачиваем направо) bool flgPWM; // Флаг указывающий на то, что требуется изменить ШИМ моторов (0 - тёмная линия, 1 - светлая линия) bool flgStop; // Флаг указывающий на необходимость остановиться (0 - без остановки, 1 - требуется остановиться) bool flgDistance; // Флаг обнаружения препятствия (0 - не обнаружено, 1 - обнаружено) uint32_t tmrMillis; // Время совершения последней операции (в миллисекундах) void setup()< // Узнаём цвет линии используемой на трассе, если он светлый, то устанавливаем флаг lineColor тёмный flgLine = (valSensor0>valSensor1); // Если условие (valSensor0>valSensor1) выполняется значит линия светлая и флаг flgLine установится в 1, иначе он сбросится в 0 // Вычисляем среднее значение между показаниями датчиков на линии и все линии if(flgLine) // Если на трассе используется светлая линия else // Если на трассе используется тёмная линия // Устанавливаем значение скорости обоих моторов arrSpeed[1]=valSpeed; // Максимальная скорость на левом моторе arrSpeed[0]=valSpeed; // Максимальная скорость на правом моторе // Устанавливаем флаг ШИМ, сбрасываем флаг наличия поворота, флаг остановки и флаг обнаружения припятствий flgPWM=1; flgTurning=0; flgStop=0; flgDistance=0; // Устанавливаем режим работы выводов и направление обоих моторов pinMode (pinSensorL, INPUT ); // Конфигурируем вывод pinSensorL как вход (для получения данных от левого датчика линии) pinMode (pinSensorC, INPUT ); // Конфигурируем вывод pinSensorC как вход (для получения данных от центрального датчика линии) pinMode (pinSensorR, INPUT ); // Конфигурируем вывод pinSensorR как вход (для получения данных от правого датчика линии) pinMode (pinShield_LH, OUTPUT ); // Конфигурируем вывод pinShield_LH как выход (для управления направлением движения левого мотора) pinMode (pinShield_LE, OUTPUT ); // Конфигурируем вывод pinShield_LE как выход (для управления скоростью вращения левого мотора, при помощи ШИМ) pinMode (pinShield_RE, OUTPUT ); // Конфигурируем вывод pinShield_RE как выход (для управления скоростью вращения правого мотора, при помощи ШИМ) pinMode (pinShield_RH, OUTPUT ); // Конфигурируем вывод pinShield_RH как выход (для управления направлением движения правого мотора) digitalWrite(pinShield_LH, arrRoute[1]); // Устанавливаем на выходе pinShield_LH уровень arrRoute[1] (направление движения левого мотора) digitalWrite(pinShield_RH, arrRoute[0]); // Устанавливаем на выходе pinShield_RH уровень arrRoute[0] (направление движения правого мотора) // Выводим показания центрального датчика линии Serial.begin(9600); while(!Serial)<> // Инициируем передачу данных по последовательному порту (на скорости 9600 бит/сек) Serial.println(analogRead(pinSensorC)); // Выводим показания центрального датчика линии (для указания значений константам valSensor0 и valSensor1) // Устанавливаем задержку и обновляем время совершения последней операции delay(2000); tmrMillis = millis(); > void loop()< // Читаем показания датчиков и преобразуем их в логические уровни // (1 - датчик на линии, 0 - датчик вне линии) valSensor = 0; // сбрасываем все биты переменной valSensor valSensor |= ((analogRead(pinSensorL)>valSensorM)^flgLine)valSensorM)^flgLine)valSensorM)^flgLine)" вернет "1" если значение analogRead(номер_вывода) больше чем значение valSensorM, а значит датчик находится над объектом, который темнее чем значение valSensorM, // Результат возвращённый оператором сравнения ">" нам подходит если используется тёмная линия, но если используется светлая линия, то результат нужно инвертировать . // Оператор побитового XOR "^" выполнит эту инверсию, только если установлен флаг flgLine, указывающий о том, что используется светлая линия // Оператор побитового сдвига влево " if(tmrMillis>millis()) < tmrMillis=0;>// Избавляемся от переполнения millis(); if(tmrMillis+tmrDelay// Останавливаемся если линия потеряна на более чем tmrDelay мс if(hcsr.distance() // Останавливаемся если обнаружено препятствие else if(flgDistance) < tmrMillis=millis(); flgPWM=1; flgDistance=0; >// Продолжаем движение если препятствие исчезло // Устанавливаем ШИМ для моторов if(flgPWM)0) arrSpeed[0]=valSpeed; break; // Уменьшаем скорость левого мотора (поворачиваем налево) case 0: arrSpeed[1]=valSpeed; arrSpeed[0]=valSpeed; break; // Устанавливаем одинаковую скорость (едим прямо) case 1: arrSpeed[1]=valSpeed; if(arrSpeed[0]>0) break; // Уменьшаем скорость правого мотора (поворачиваем направо) > if(flgStop) < arrSpeed[1]=0; arrSpeed[0]=0;>// Останавливаемся если установлен флаг flgStop if(flgDistance) < arrSpeed[1]=0; arrSpeed[0]=0;>// Останавливаемся если установлен флаг flgDistance // Выводим ШИМ analogWrite(pinShield_LE, arrSpeed[1]); analogWrite(pinShield_RE, arrSpeed[0]); > >
Ссылки:
- Код программы.
- Библиотека iarduino_HC_SR04_int.
- Wiki — Установка библиотек в Arduino IDE .
- Wiki — Датчик линии, аналоговый.
- Wiki — Ультразвуковой датчик расстояния HC-SR04 .
- Wiki — Trema Shield.
Робот, ездящий по линии под управлением Arduino
В данной статье будет описан процесс создания робота, ездящего по линии. Эта задача является классической, идейно простая, она может решаться много раз, и каждый раз вы будете открывать для себя что-то новое. Решение этой задачи и реализация полученного решения позволяют приобрести необходимые начальные навыки для дальнейшего совершенствования в робототехнике.
Существует множество подходов для решения задачи следования по линии. Выбор одного из них зависит от конкретной конструкции робота, от количества сенсоров, их расположения относительно колёс и друг друга.
В нашем примере будет собран робот на лёгкой платформе с двумя колёсами и двумя датчиками линии, расположенными на днище робота перед колёсами.
В результате выглядеть он будет так:

Что понадобится
Для нашего примера понадобятся следующие детали:

Несколько соединительных проводов и болтов и гаек для крепления датчиков и Arduino Uno
Кабель питания от батарейки Крона и сама батарейка
Вообще говоря, лучше было бы использовать NiMH-аккумуляторы: они лучше отдают ток и значительно дольше держат напряжение, но для целей этого проекта одной батарейки на 9 В вполне хватило.
Собираем робота
Сначала соберём робота, установим всю механику и электронику.
Собираем платформу
Для начала прикрепим колёса к моторам.

Затем с помощью пластиковых П-образных креплений прикручиваем моторчики к платформе. Обратите внимание на взаимное расположение крепления и моторчики: в креплении есть небольшие углубления, так что если всё соединить правильно, то моторчики будут крепко держаться и никуда не выскочат.

Теперь крепим балансировочный шар.

Отлично! Платформа собрана. Если вам кажется, что колёсам отведено слишком мало места и они трутся о платформу, то скорее всего вам нужно посильнее надавить на колёса, чтобы они плотнее сели на вал мотора.
Крепим сенсоры
Закрепим их, как показано на фото:

Можно было бы выбрать и другое место. Это могло бы сделать контроль проще или сложнее, а самого робота более или менее эффективным. Оптимальное расположение — вопрос серии экспериментов. Для этого проекта просто был выбран такой способ крепления.
Крепим Arduino
Arduino закрепим с противоположной стороны двумя винтиками и гайками.

Опять же, можно выбрать и другое место. Например над колёсами, если приподнять Arduino на латунных стойках. Это изменило бы положение центра масс и повлияло бы на эффективность робота в лучшую или худшую сторону.
Крепим Motor Shield и соединительные провода
Установим Motor Shield на Arduino и подсоединим соединительные провода. Обратите внимание, чтобы соотвествовать программному коду из примера ниже, моторчики соединены с Motor Shield так: правый — к клеммам M1 с прямой полярностью (плюс к плюсу), а левый — к M2 с обратной (плюс к минусу).

В этом проекте, для экономии времени концы соединительных проводов просто скручены с контактами моторов. При работе «начисто» стоит жёстко припаять провода к моторам.
Крепим Troyka Shield
Присоединяем сверху Troyka Shield и подключаем датчики к 8 и 9 цифровым контактам. В итоге получаем следующую конструкцию:

Программирование
Теперь напишем программу, которая заставит собранную конструкцию двигаться по нарисованной линии. В проекте мы будем использовать чёрную линию, напечатанную на белых листах бумаги.
Основная идея алгоритма
Пусть у нас усть белое поле, и на нём чёрным нарисован трек для нашего робота. Используемые датчики линии выдают логический ноль, когда «видят» чёрное и единицу, когда «видят» белое.
На прямой робот должен пропускать трек между сенсоров, то есть оба сенсора должны показывать единички.
При повороте траектории направо, правый сенсор наезжает на трек и начинает показывать логический ноль. При повороте налево, ноль показывает левый сенсор.
Таким образом получаем простую систему с тремя состояниями:
STATE_FORWARD — нужно ехать вперёд
STATE_RIGHT — нужно поворачиваться направо
STATE_LEFT — нужно поворачиваться налево
На вход системы поступает информация с сенсоров. Получаем следующую логику переходов:
| Левый | Правый | Целевое состояние |
|---|---|---|
| 0 | 0 | STATE_FORWARD |
| 0 | 1 | STATE_RIGHT |
| 1 | 0 | STATE_LEFT |
| 1 | 1 | STATE_FORWARD |
Реализация на Arduino
// Моторы подключаются к клеммам M1+,M1-,M2+,M2- // Motor shield использует четыре контакта 6,5,7,4 для управления моторами #define SPEED_LEFT 6 #define SPEED_RIGHT 5 #define DIR_LEFT 7 #define DIR_RIGHT 4 #define LEFT_SENSOR_PIN 8 #define RIGHT_SENSOR_PIN 9 // Скорость, с которой мы движемся вперёд (0-255) #define SPEED 35 // Коэффициент, задающий во сколько раз нужно затормозить // одно из колёс для поворота #define BRAKE_K 4 #define STATE_FORWARD 0 #define STATE_RIGHT 1 #define STATE_LEFT 2 int state = STATE_FORWARD; void runForward() { state = STATE_FORWARD; // Для регулировки скорости `SPEED` может принимать значения от 0 до 255, // чем болше, тем быстрее. analogWrite(SPEED_LEFT, SPEED); analogWrite(SPEED_RIGHT, SPEED); // Если в DIR_LEFT или DIR_RIGHT пишем HIGH, мотор будет двигать соответствующее колесо // вперёд, если LOW - назад. digitalWrite(DIR_LEFT, HIGH); digitalWrite(DIR_RIGHT, HIGH); } void steerRight() { state = STATE_RIGHT; // Замедляем правое колесо относительно левого, // чтобы начать поворот analogWrite(SPEED_RIGHT, SPEED / BRAKE_K); analogWrite(SPEED_LEFT, SPEED); digitalWrite(DIR_LEFT, HIGH); digitalWrite(DIR_RIGHT, HIGH); } void steerLeft() { state = STATE_LEFT; analogWrite(SPEED_LEFT, SPEED / BRAKE_K); analogWrite(SPEED_RIGHT, SPEED); digitalWrite(DIR_LEFT, HIGH); digitalWrite(DIR_RIGHT, HIGH); } void setup() { // Настраивает выводы платы 4,5,6,7 на вывод сигналов for(int i = 4; i 7; i++) pinMode(i, OUTPUT); // Сразу едем вперёд runForward(); } void loop() { // Наш робот ездит по белому полю с чёрным треком. В обратном случае не нужно // инвертировать значения с датчиков boolean left = !digitalRead(LEFT_SENSOR_PIN); boolean right = !digitalRead(RIGHT_SENSOR_PIN); // В какое состояние нужно перейти? int targetState; if (left == right) { // под сенсорами всё белое или всё чёрное // едем вперёд targetState = STATE_FORWARD; } else if (left) { // левый сенсор упёрся в трек // поворачиваем налево targetState = STATE_LEFT; } else { targetState = STATE_RIGHT; } if (state == targetState) { // мы уже делаём всё что нужно, // делаем измерения заново return; } switch (targetState) { case STATE_FORWARD: runForward(); break; case STATE_RIGHT: steerRight(); break; case STATE_LEFT: steerLeft(); break; } // не позволяем сильно вилять на прямой delay(50); }
Проблема инертности и её решение
Однако если выставить скорость моторов побольше, мы столкнёмся со следующей проблемой: наш робот будет вылетать с трека, не успевая отреагировать на поворот. Это связано с тем, что наши моторчики не умеют тормозить мгновенно.
В этом легко убедиться поставив следующий эксперимент: с заданной скоростью робот будет двигаться по поверхности, и в некоторый момент будет установлена нулевая скорость и измерен тормозной путь робота. Пусть робот разгоняется по монотонной поверхности и тормозится при фиксировании импровизированной стоп-линии.
Эксперимент проведём для разных скоростей. Код программы для эксперимента таков:
#define LEFT_SENSOR_PIN 8 #define RIGHT_SENSOR_PIN 9 #define SPEED_LEFT 6 #define SPEED_RIGHT 5 #define DIR_LEFT 7 #define DIR_RIGHT 4 // Для того чтобы убедиться, что именно тормозной путь долог, а не команда остановиться // приходит слишком поздно, будем включать светодиод, когда отдаётся команда. #define LED_PIN 13 int currSpeed = 40; void setup() { for(int i = 4; i 7; ++i) pinMode(i, OUTPUT); analogWrite(SPEED_RIGHT, currSpeed); digitalWrite(DIR_RIGHT, HIGH); analogWrite(SPEED_LEFT, currSpeed); digitalWrite(DIR_LEFT, HIGH); pinMode(LED_PIN, OUTPUT); } void loop() { if (currSpeed > 120) return; boolean white[] = { !digitalRead(LEFT_SENSOR_PIN), !digitalRead(RIGHT_SENSOR_PIN) }; if (white[0] && white[1]) { // едем пока не упрёмся return; } // зажигаем светодиод, останавливаем моторы // и наблюдаем digitalWrite(LED_PIN, HIGH); analogWrite(SPEED_RIGHT, 0); analogWrite(SPEED_LEFT, 0); delay(5000); // повторяем эксперимент, увеличивая скорость // на 10 пунктов currSpeed += 10; if (currSpeed > 120) return; digitalWrite(LED_PIN, LOW); analogWrite(SPEED_RIGHT, currSpeed); analogWrite(SPEED_LEFT, currSpeed); }
На той поверхности, на которой проводился эксперимент, были получены следующие результаты:

Таким образом, начиная с некоторого момента у нашего робота нет никакой возможности успеть среагировать и остаться на треке.
Что можно сделать?! После того, как сенсоры улавливают поворот, можно остановиться и вернуться назад на некоторое расстояние, зависящее от скорости перед остановкой. Однако мы можем отдать команду роботу ехать с какой-то скоростью, но не можем приказать ему проехать какое-то расстояние.
Для того, чтобы понять зависимость расстояния при заднем ходе от времени, был проведён ещё один замер:
#define SPEED_LEFT 6 #define SPEED_RIGHT 5 #define DIR_LEFT 7 #define DIR_RIGHT 4 void go(int speed, bool reverseLeft, bool reverseRight, int duration) { analogWrite(SPEED_LEFT, speed); analogWrite(SPEED_RIGHT, speed); digitalWrite(DIR_LEFT, reverseLeft ? LOW : HIGH); digitalWrite(DIR_RIGHT, reverseRight ? LOW : HIGH); delay(duration); } void setup() { for(int i = 4; i 7; ++i) pinMode(i, OUTPUT); } void loop() { // Задержка 5 секунд после включения питания delay(5000); for (int i = 200; i 1000; i += 100) { // Несколько сотен мс вперёд go(50, false, false, 200); go(0, false, false, 0); // Задержка 5 секунд delay(5000); } // Остановка до ресета или выключения питания go(0, false, false, 0); // Приехали while (true) ; }
На скорости 50, например, робот проделывал путь, зависящий от времени следующим образом:

Полученные две зависимости были линейно аппроксимированы, затем была выведена формула зависимости времени, которое надо двигаться назад, от скорости перед остановкой.
Обратим внимание на то, что у вас значения могут оказаться другими: из-за особенностей сборки либо из-за поверхности, поэтому в общем случае лучше провести все измерения самостоятельно.
Адаптивное поведение
Перед финальным экспериментом произведём ещё несколько поправок.
Во-первых, нам необязательно давать команду ехать назад перед каждым поворотом, как мы помним, на маленькой скорости робот прекрасно справляется и без этого. К тому же лучше ему двигаться не прямо назад, а немного поворачивая, всё-таки робот находится перед поворотом.
Во-вторых, нам стоит различать состояния робота: когда он движется по прямой, и ничто ему не мешает ускоряться; и когда робот входит в поворот. В первом случае действительно будем увеличивать скорость робота для более динамичного прохождения трека, во втором случае будем сбрасывать скорость до значения, достаточного для успешного прохождения поворота, и будем держать эту скорость ещё какое-то время.
В итоге наш код будет выглядит следующим образом:
// Моторы подключаются к клеммам M1+,M1-,M2+,M2- // Motor shield использует четыре контакта 6,5,7,4 для управления моторами #define SPEED_LEFT 6 #define SPEED_RIGHT 5 #define DIR_LEFT 7 #define DIR_RIGHT 4 #define LEFT_SENSOR_PIN 8 #define RIGHT_SENSOR_PIN 9 // Скорость, с которой мы движемся вперёд (0-255) #define SPEED 100 // Скорость прохождения сложных участков #define SLOW_SPEED 35 #define BACK_SLOW_SPEED 30 #define BACK_FAST_SPEED 50 // Коэффициент, задающий во сколько раз нужно затормозить // одно из колёс для поворота #define BRAKE_K 4 #define STATE_FORWARD 0 #define STATE_RIGHT 1 #define STATE_LEFT 2 #define SPEED_STEP 2 #define FAST_TIME_THRESHOLD 500 int state = STATE_FORWARD; int currentSpeed = SPEED; int fastTime = 0; void runForward() { state = STATE_FORWARD; fastTime += 1; if (fastTime FAST_TIME_THRESHOLD) { currentSpeed = SLOW_SPEED; } else { currentSpeed = min(currentSpeed + SPEED_STEP, SPEED); } analogWrite(SPEED_LEFT, currentSpeed); analogWrite(SPEED_RIGHT, currentSpeed); digitalWrite(DIR_LEFT, HIGH); digitalWrite(DIR_RIGHT, HIGH); } void steerRight() { state = STATE_RIGHT; fastTime = 0; // Замедляем правое колесо относительно левого, // чтобы начать поворот analogWrite(SPEED_RIGHT, 0); analogWrite(SPEED_LEFT, SPEED); digitalWrite(DIR_LEFT, HIGH); digitalWrite(DIR_RIGHT, HIGH); } void steerLeft() { state = STATE_LEFT; fastTime = 0; analogWrite(SPEED_LEFT, 0); analogWrite(SPEED_RIGHT, SPEED); digitalWrite(DIR_LEFT, HIGH); digitalWrite(DIR_RIGHT, HIGH); } void stepBack(int duration, int state) { if (!duration) return; // В зависимости от направления поворота при движении назад будем // делать небольшой разворот int leftSpeed = (state == STATE_RIGHT) ? BACK_SLOW_SPEED : BACK_FAST_SPEED; int rightSpeed = (state == STATE_LEFT) ? BACK_SLOW_SPEED : BACK_FAST_SPEED; analogWrite(SPEED_LEFT, leftSpeed); analogWrite(SPEED_RIGHT, rightSpeed); // реверс колёс digitalWrite(DIR_RIGHT, LOW); digitalWrite(DIR_LEFT, LOW); delay(duration); } void setup() { // Настраивает выводы платы 4,5,6,7 на вывод сигналов for(int i = 4; i 7; i++) pinMode(i, OUTPUT); // Сразу едем вперёд runForward(); } void loop() { // Наш робот ездит по белому полю с чёрным треком. В обратном случае не нужно // инвертировать значения с датчиков boolean left = !digitalRead(LEFT_SENSOR_PIN); boolean right = !digitalRead(RIGHT_SENSOR_PIN); // В какое состояние нужно перейти? int targetState; if (left == right) { // под сенсорами всё белое или всё чёрное // едем вперёд targetState = STATE_FORWARD; } else if (left) { // левый сенсор упёрся в трек // поворачиваем налево targetState = STATE_LEFT; } else { targetState = STATE_RIGHT; } if (state == STATE_FORWARD && targetState != STATE_FORWARD) { int brakeTime = (currentSpeed > SLOW_SPEED) ? currentSpeed : 0; stepBack(brakeTime, targetState); } switch (targetState) { case STATE_FORWARD: runForward(); break; case STATE_RIGHT: steerRight(); break; case STATE_LEFT: steerLeft(); break; } }
Результат
Что дальше?
Представленный алгоритм оставляет множество возможностей для улучшения и оптимизации. Скорость поворота можно так же менять адаптивно. Можно добавить контроль заноса. Можно поиграть с расположением сенсоров и центром масс. В конце концов можно получить непобедимого на треке робота.
Нет ничего лучше, чем обставить оппонента на секунду-другую.
Если не указано иное, содержимое этой вики предоставляется на условиях следующей лицензии: CC Attribution-Noncommercial-Share Alike 4.0 International
Производные работы должны содержать ссылку на http://wiki.amperka.ru, как на первоисточник, непосредственно перед содержимым работы.
Вики работает на суперском движке DokuWiki.
робототехника/робот-с-датчиками-линии-на-arduino.txt · Последние изменения: 2014/10/20 13:46 — acos
Инструменты страницы
- Показать исходный текст
- История страницы
- Ссылки сюда
- Наверх
Робот «Умный бот», ездит по линии, световые эффекты, цвет синий

«Умный бот» — интерактивный робот, который умеет двигаться по нарисованной линии.
Как играть?
- Возьмите лист бумаги и чёрный маркер.
- Нарисуйте траекторию движения. Это должна быть непрерывная линия без острых углов. Робот спокойно двигается по спирали и преодолевает повороты.
- Теперь установите игрушку в начало пути, включите и наблюдайте за процессом.
- У робота светятся глаза во время движения.
Общие
- Артикул 4807925
- Сертификат ЕАС
- Страна производитель Китай
Упаковка и фасовка
- В боксе 72 шт.
- Фасовка по 1 шт.
- Индивидуальная упаковка Картонная коробка с PVC
- Размер упаковки (Длина × Ширина × Высота) 12,5 см х 17,5 см х 6,5 см
Габариты и вес
- Размер (Длина × Ширина × Высота) 12,6 см х 6,6 см х 17,5 см
- Вес брутто 134 г
Особенности
- Тип питания От батареек
- Возраст От 3 лет
- Вид упаковки Коробка картонная с PVC
- Дополнительные функции Звуковые эффекты , Светящаяся
- Типоразмер батареек LR44 (V13GA, AG13, LR1154, SR44)
- Тип механизма Самоходный
- Форма Классический робот
Товар находится в категориях
Робот «Умный бот», ездит по линии, световые эффекты, цвет жёлтый
Выбор конкретных цветов и моделей не предоставляется. На фотографиях могут быть представлены не все варианты.
- Описание
- Характеристики
«Умный бот» — интерактивный робот, который умеет двигаться по нарисованной линии.
Как играть?
- Возьмите лист бумаги и чёрный маркер.
- Нарисуйте траекторию движения. Это должна быть непрерывная линия без острых углов. Робот спокойно двигается по спирали и преодолевает повороты.
- Теперь установите игрушку в начало пути, включите и наблюдайте за процессом.
- У робота светятся глаза во время движения.
Общие
- Артикул 3795360
- Сертификат ЕАС
- Страна производитель Китай
- Состав Пластик
Упаковка и фасовка
- В боксе 72 шт.
- Фасовка по 1 шт.
- Индивидуальная упаковка Картонная коробка с PVC
- Размер упаковки (Длина × Ширина × Высота) 12,5 см х 6,5 см х 17,5 см
Габариты и вес
- Размер (Длина × Ширина × Высота) 6,5 см х 4,5 см х 8,5 см
- Вес брутто 134 г
Особенности
- Тип питания От батареек
- Возраст От 3 лет
- Количество батареек 2
- Вид упаковки Коробка
- Материал Пластик
- Звук Нет
- Дополнительные функции Ездит по линии , Подвижная , Светящаяся
- Типоразмер батареек LR44 (V13GA, AG13, LR1154, SR44)
- Тип механизма Самоходный
- Форма Классический робот
Товар находится в категориях