В этом уроке
- Генератор случайных чисел
- Что общего между машинкой и роботом-пылесосом
- Учим машинку избегать препятствий
Видео версия урока
Генератор случайных чисел
Теперь можно усовершенствовать машинку и сделать так, чтобы при возникновении препятствий она автоматически их объезжала.
Однако, если мы реализуем поворот всегда в одну сторону при возникновении препятствия, машинка будет двигаться по довольно скучным и предсказуемым траекториям. Но мы можем сделать так, чтобы она поворачивала каждый раз в произвольном направлении. Для этого в скетче используется генератор псевдослучайных чисел.
Функция random() — возвращает псевдослучайное число.
Синтаксис: random(min, max); , где
min — нижняя граница значений (включая само значение min);
max — верхняя граница значений (не включая само значение max).
Запись random(max) идентична верхней, но значение min в ней по умолчанию равно 0.
Функция randomSeed(seed) — выбирает начальную позицию в последовательности чисел для функции random(). Несмотря на то, что последовательность состоит из большого количества случайных чисел, она всегда одинакова. Точка последовательности, с которой начинается генерация чисел, зависит от параметра seed.
Если важно каждый раз получать последовательность различных случайных чисел, то для инициализации генератора используйте функцию randomSeed() со случайным параметром. Для случайного параметра можно использовать функцию analogRead(), считывающую значение с неподсоединенного вывода. Если вывод контроллера никуда не подсоединён, в нём возникают помехи от других электронных устройств, которые можно считать случайной величиной.
randomSeed() инициализируется один раз, в блоке void setup().
Пример:
int x; void setup() { Serial.begin(9600); randomSeed(analogRead(A2)); } void loop() { x = random(200); Serial.println(x); delay(500); }
Код из примера будет выводить в «Монитор порта» (8 строка) случайное число (7 строка) в диапазоне от 0 до 199. Точкой отсчёта для генератора чисел будет значение, взятое с вывода A2 (4 строка).
Объезд препятствий со случайным выбором направления
В скетче ниже мы реализовали автоматический выбор направления для объезда препятствия (разумеется, случайным образом). Давайте загрузим в контроллер следующий скетч и посмотрим, как движется машинка.
#include <Wire.h> // Подключаем библиотеку для работы с шиной I2C #include <iarduino_HC_SR04.h> // Подключаем библиотеку для ультразвукового датчика расстояния #include <iarduino_I2C_Motor.h> // Подключаем библиотеку для работы с мотором iarduino_I2C_Motor mot_R (0x0A); // Объявляем объект mot_R для правого мотора iarduino_I2C_Motor mot_L (0x0B); // Объявляем объект mot_L для левого мотора uint8_t pinSens_TRIG = 12; // Вывод, к которому подключен вывод TRIG датчика HC-SR04 uint8_t pinSens_ECHO = 11; // Вывод, к которому подключен вывод ECHO датчика HC-SR04 iarduino_HC_SR04 hcsr(pinSens_TRIG, pinSens_ECHO);// Создаём объект hcsr для работы с библиотекой iarduino_HC_SR04 uint8_t minDist = 20; // Минимальное расстояние, при котором начинается поворот, см uint32_t timeStartTurn; // Время начала разворота uint32_t timeTurning = 1000; // Длительность разворота, мс uint8_t speedMot = 100; // Скорость мотора, % ШИМ bool flagLR; // Переменная для определения направления поворота uint8_t wTutn = 0; // Переменная для принятия решения о необходимости отъезда назад void setup() { mot_R.begin(); // Инициируем работу с правым мотором mot_L.begin(); // Инициируем работу с левым мотором mot_R.setDirection(true); // Задаём направление вращения правого мотора mot_L.setDirection(false); // Задаём направление вращения левого мотора randomSeed(analogRead(A2)); // Выбираем случайную точку в последовательности чисел } void loop() { if (timeStartTurn + timeTurning < millis()){ // Если прошло время длительности поворота if (hcsr.distance() > minDist){ // Если расстояние до препятствия больше заданного mot_R.setSpeed(speedMot, MOT_PWM); // Запускаем правый мотор mot_L.setSpeed(speedMot, MOT_PWM); // Запускаем левый мотор wTutn = 0; // Сбрасываем значение переменной отъезда назад } else { // Если расстояние меньше заданного wTutn = wTutn + 1; // Увеличиваем значение переменной if (wTutn >= 2) { // Если оно больше или равно 2 (было несколько поворотов подряд, скорее всего, машинка застряла) timeStartTurn = millis(); // Записываем время начала движения mot_R.setSpeed(-speedMot, MOT_PWM); // Запускаем правый мотор в обратную сторону mot_L.setSpeed(-speedMot, MOT_PWM); // Запускаем левый мотор в обратную сторону return; // Выходим из цикла (чтобы не выполнять код, следующий далее. Поворот пока не нужен) } flagLR = random(2); // Случайным образом выбираем число: 0 или 1 if (flagLR == 0){ // Если это число 0 timeStartTurn = millis(); // Записываем время начала движения mot_R.setSpeed(speedMot, MOT_PWM); // Запускаем правый мотор mot_L.setSpeed(0, MOT_PWM); // Останавливаем левый мотор } else { // Иначе timeStartTurn = millis(); // Записываем время начала движения mot_R.setSpeed(0, MOT_PWM); // Останавливаем правый мотор mot_L.setSpeed(speedMot, MOT_PWM); // Запускаем левый мотор }}}}
Алгоритм работы машинки в режиме автоматического объезда препятствий выглядит следующим образом:
- Ультразвуковой датчик определяет расстояние до препятствия;
- Когда расстояние становится меньше заданного в условии, машинка случайным образом выбирает направление и поворачивает;
- Однако, если было несколько поворотов подряд, скорее всего, машинка застряла, в этом
случае отъезжаем назад.
По такому принципу работали первые роботы-пылесосы. Они не ориентировались в комнате, а случайным образом ездили по ней. Так, рано или поздно, в комнате не оставалось мест, где бы ни проехал робот.
Дополнительное задание.
Напишите такую программу для машинки, чтобы она держалась на одном расстоянии от
препятствия. Если оно приближается, машинка отъезжает назад, если отдаляется — едет вперёд. Так можно управлять машинкой рукой — она будет следовать за ней.
Обсуждение