Расширяем функционал беспилотника

В этом уроке:

  • Корректируем поведение автомобиля с некоторыми знаками
  • Добавляем новые знаки
  • Меняем схему дорог

Это заключительная, и, пожалуй, самая нестандартная статья в данной главе. Нам предстоит немного улучшить наш скетч и поэкспериментировать со сменой знаков. Поехали!

Вернёмся к серпантину

Самый первый знак, с которым мы работали — опасные повороты. Не кажется ли вам, что машинка слишком медленно проезжает их? Также видим, что она иногда теряет линию трассы. Да, в результате возвращается к ней, но мы ведь можем менять коэффициент П-регулятора и влиять на "резкость" поворотов. 

Давайте изменим коэффициент П-регулятора:

if( flg_112 ){bum_Error = bum.getErrPID()*3; flg_112 = false;}                 

Как Вы обратили внимание, мы ввели флаг знака 1.12 (flg_112) для того, чтобы менять коэффициент регулятора по нему. Соответственно, не забываем добавить его в блок работы со знаком (163 строка):

if(!strcmp(ir.sign_str, "1.12.1")){  // Если номер знака 1.12.1 - «Опасные повороты»          
  speed = low_Speed;                 // Ограничиваем скорость до ближайшего перекрёстка или нового знака
  flg_112 = true;                    // Меняем флаг для изменения коэффициента регулятора
  disp.drawImage(Image_1_12_1);      // Выводим изображение знака
}

В строке 162 мы также установили скорость 40км/ч (вместо 20).

Ну и не забываем ввести флаг в начале скетча:

bool flg_112 = false;   // Флаг знака 1.12 для изменения коэффициента П-регулятора 

Загрузите получившийся скетч и проверьте работу машинки со знаком "Опасные повороты".

Движение без остановки запрещено

Сейчас наш автомобиль останавливается, когда встречает знак "Движение без остановки запрещено". В реальной жизни этот знак необходим для того, чтобы водитель убедился в безопасности проезда. Давайте вместо обычной остановки сделаем так, чтобы наша машинка немного "повиляла" из стороны в сторону, как бы осмотревшись по сторонам.

delay(1000);
  while(bum.getErrPID() > -4) {                           // Поворот вправо
    mot_R.setSpeed(-convertSpeed(min_Speed), MOT_PWM);
    mot_L.setSpeed(convertSpeed(min_Speed), MOT_PWM);
  }
  while(bum.getErrPID() < 4) {                            // Поворот влево
    mot_R.setSpeed(convertSpeed(min_Speed), MOT_PWM);
    mot_L.setSpeed(-convertSpeed(min_Speed), MOT_PWM);
  }
  while(bum.getErrPID() > 0) {                            // Поворот вправо, возвращаемся к центру
    mot_R.setSpeed(-convertSpeed(min_Speed), MOT_PWM);
    mot_L.setSpeed(convertSpeed(min_Speed), MOT_PWM);
  }

После обычной остановки с delay() мы добавили циклы с прямой установкой скорости для моторов. Все используемые функции Вы отлично знаете, и, думаем, без проблем разберётесь в коде.

Загрузите скетч и протестируйте машинку. 

Меняем схему движения

В этой главе мы рассматривали знаки последовательно, один за одним. Но нашему автомобилю не важна последовательность знаков на дороге, он справится с любой схемой движения! Создайте новый маршрут движения автомобиля, поменяйте знаки местами и убедитесь, что он прекрасно справляется с задачей. 

А возможно, Вы захотите создать свою трассу. Используя черную изоленту и однотонный фон, Вы легко можете организовать движение на собственной дороге.

Готовый скетч первой главы

#include <Wire.h>                         // Подключаем библиотеку для работы с аппаратной шиной I2C
#include <iarduino_I2C_Motor.h>           // Подключаем библиотеку для работы с мотором  I2C-flash
#include <iarduino_I2C_Bumper.h>          // Подключаем библиотеку для работы с бампером I2C-flash
#include <iarduino_I2C_IR.h>              // Подключаем библиотеку для работы с Trema модулями ИК-приёмник/передатчик I2C-flash
#include <iarduino_I2C_Matrix_8x8.h>      // Подключаем библиотеку для работы с LED матрицей 8x8 I2C-flash
#include <iarduino_HC_SR04_tmr.h>         // Подключаем библиотеку для работы с датчиком расстояния HC-SR04
                                         
iarduino_I2C_Motor mot_R (0x0A);          // Объявляем объект mot_R для правого мотора, указав адрес модуля на шине I2C
iarduino_I2C_Motor mot_L (0x0B);          // Объявляем объект mot_L для правого мотора, указав адрес модуля на шине I2C
iarduino_I2C_Bumper bum (0x0C);           // Объявляем объект bum  для работы с бампером I2C-flash, указав адрес модуля на шине I2C
iarduino_I2C_Matrix_8x8 disp (0x0D);      // Объявляем объект disp для работы с светодиодной матрицей I2C-flash, указав адрес модуля на шине I2C
iarduino_I2C_IR ir(0x09);                 // Объявляем объект ir для работы с функциями и методами библиотеки iarduino_I2C_IR, указывая адрес модуля на шине I2C
iarduino_HC_SR04_tmr len (12,11);         // Объявляем объект len  для работы с датчиком расстояния, указав номера выводов arduino подключённых к выводам TRIG (12) и ECHO (11) датчика
                                        
const float min_Speed = 20;               // Минимальная  скорость движения в км/ч. Используется при движении вблизи школ, а также на опасных участках дороги
const float low_Speed = 40;               // Низкая       скорость движения в км/ч. Используется при движении по опасным участкам дороги, или при соответствующем знаке
const float mid_Speed = 60;               // Средняя      скорость движения в км/ч. Используется при движении по дорогам
const float max_Speed = 90;               // Максимальная скорость движения в км/ч. Используется при движении по скоростным дорогам
const uint16_t min_Distance  = 15;        // Минимальное расстояние до препятствия в см, при котором требуется остановиться
const uint32_t tim_Wait = 1000;           // Определяем время ожидания в мс до начала движения после остановки
 
float speed = mid_Speed;                  // Указываем, что изначально скорость равна средней
int8_t  val_Turn      = 0;                // Выбранное направление движения на перекрёстке: 0 прямо, -1 влево, +1 вправо
bool    flg_TLIGHT = false;               // Флаг получения данных от светофора
bool    flg_CrossWait = false ;           // Флаг ожидания    перекрёстка (машина "увидела" светофор или знак, сообщающий о наличии перекрёстка)
bool    flg_CrossFind = false;            // Флаг обнаружения перекрёстка (бампер заехал на перекрёсток)
bool    flg_Turn = false;                 // Флаг необходимости разворота          
bool    flg_25 = false;                   // Флаг повторной фиксации знака 2.5 "Движение из остановки запрещено"
bool    flg_112 = false;                  // Флаг знака 1.12 для изменения коэффициента П-регулятора 
uint32_t timeSign;                        // Время для отсчёта времени перед повторной фиксацией знака
byte Image_TLIGHT[8];                     // Генерируется функцией fnc_CreateImage()
 
//   1.12.1 «Опасные повороты»            //  Изображение знака "Опасные повороты"
byte Image_1_12_1[8] = { 0b00000000,      //
                         0b00000010,      //       #
                         0b00100010,      //   #   #
                         0b01010010,      //  # #  #
                         0b01001010,      //  #  # #
                         0b01000100,      //  #   #
                         0b01000000,      //  #
                         0b00000000 };    //
 
//   3.31 «Конец зоны ограничений»        //
byte Image_3_31[8] =   { 0b00111100,      //   ####
                         0b01000010,      //  #    #
                         0b10000101,      // #    # #
                         0b10001001,      // #   #  #
                         0b10010001,      // #  #   #
                         0b10100001,      // # #    #
                         0b01000010,      //  #    #
                         0b00111100 };    //   ####
 
//   3.1 «Въезд запрещен»                 
byte Image_3_1[8] =    { 0b00111100,      //   ####
                         0b01000010,      //  #    #
                         0b10000001,      // #      #
                         0b10111101,      // # #### #
                         0b10111101,      // # #### #
                         0b10000001,      // #      #
                         0b01000010,      //  #    #
                         0b00111100 };    //   ####         
 
//   4.1.2 «Движение налево»           
byte Image_4_1_3[8] =  { 0b00111100,      //   ####
                         0b01000010,      //  #    #
                         0b10001001,      // #   #  #
                         0b11111101,      // ###### #
                         0b11111101,      // ###### #
                         0b10001001,      // #   #  #
                         0b01000010,      //  #    #
                         0b00111100 };    //   ####                                         
                         
void setup() {
  mot_R.begin();                          // Инициируем работу с левым  мотором I2C-flash
  mot_L.begin();                          // Инициируем работу с правым мотором I2C-flash
  bum.begin();                            // Инициируем работу с бампером I2C-flash
  disp.begin();                           // Инициируем работу с светодиодной матрицей I2C-flash
  disp.codingDetect("п");                 // Выполняем автоопределение кодировки скетча
  mot_R.setDirection(true);               // Указываем правому мотору, что его вращение должно быть прямым (по часовой стрелке при положительных скоростях)
  mot_L.setDirection(false);              // Указываем левому мотору, что его вращение должно быть обратным (против часовой стрелки при положительных скоростях)
  ir.begin();                             // Инициируем работу с ИК-приёмником/передатчиком I2C-flash
  ir.setProtocol(IR_IARDUINO);            // Указываем протокол для приёма/передачи данных по ИК-каналу
  randomSeed(analogRead(A2));             // Выбираем начальную позицию для генерации случайных чисел
  len.begin();                            // Инициируем работу с датчиком расстояния HC-SR04
}
 
void loop() {
//   ОСТАНОВКА ПЕРЕД ПРЕПЯТСТВИЕМ:                                                                 
     fnc_StopObstacle(min_Distance);      // Остановка при наличии препятствия ближе min_Distance см.  
//   АНИМАЦИЯ ДВИЖЕНИЯ НА СВЕТОФОРЕ: 
  if( flg_TLIGHT ){                                                 // Если от светофора получены данные
    fnc_CreateImage(ir.track_L, ir.track_F, ir.track_R, val_Turn);  // Генерируем изображение перекрёстка светофора
    disp.drawImage(Image_TLIGHT);                                   // Выводим изображение перекрёстка светофора (функция в конце скетча)
  }  
 
//    РАЗВОРОТ:
if( flg_Turn ){                            // 1 ФАЗА. Если под бампером обнаружен перекрёсток (указываем толщину линии трассы и время на преодоление перекрёстка в мс) ...
  flg_Turn = false;                        //         Сбрасываем флаг разворота
  mot_R.setSpeed( convertSpeed(low_Speed), MOT_PWM );    // Устанавливаем скорость правого мотора в %
  mot_L.setSpeed(-convertSpeed(low_Speed), MOT_PWM );    // Устанавливаем скорость левого мотора в %
                                           //         Ждём завершение манёвра:
  while( bum.getLineSum() > 0 ) {;}        // 2 ФАЗА. Ждём выхода линии за пределы бампера. Цикл выполняется пока под бампером есть линия
  while( bum.getLineSum() == 0 ){;}        // 3 ФАЗА. Ждём появление линии под бампером.    Цикл выполняется пока под бампером нет  линии
                                           //         Останавливаемся:
  mot_R.setStop();                         //         Останавливаем правое колесо
  mot_L.setStop();                         //         Останавливаем левое колесо
  val_Turn  = 0;                           // 4 ФАЗА. Выбираем движение прямо
  speed = mid_Speed;                       //         Снимаем наложенные ранее ограничения скорости
  disp.clrScr();                           //         Чистим экран светодиодной матрицы
  bum.setTurnSignal(BUM_TURN_OFF);         //         Отключаем поворотники
}
 
//   ПРОВЕРКА НАЛИЧИЯ ПЕРЕКРЁСТКОВ:                                       
  if( bum.getCross(3,1000) ){              // Если под бампером обнаружен перекрёсток (указываем толщину линии трассы и время на преодоление перекрёстка в мс) ...                            
    flg_TLIGHT = false;                    // Сбрасываем флаг наличия данных от светофора
    disp.clrScr();                         // Чистим экран светодиодной матрицы
    if( flg_CrossWait ){                   // Если ожидается появление перекрёстка ...
      flg_CrossWait = false;               // Сбрасываем    флаг ожидания    перекрёстка
      flg_CrossFind = true;                // Устанавливаем флаг обнаружения перекрёстка
    }                                                               
  }
else{                                      // Если под бампером обычная линия ...          
     if (flg_CrossFind) {                  // Если ранее был обнаружен перекрёсток 
       flg_CrossFind = false;              // Сбрасываем флаг обнаружения перекрёстка
       val_Turn = 0;                       // Выбираем движение прямо
       speed = mid_Speed;                  // Снимаем наложенные ранее ограничения скорости
       bum.setTurnSignal(BUM_TURN_OFF);    // Отключаем поворотники
     }                                                               
  }
 
//  Включаем поворотники:  
  if( val_Turn==-1 ){ bum.setTurnSignal(BUM_TURN_LEFT ); }                            
  if( val_Turn== 0 ){ bum.setTurnSignal(BUM_TURN_OFF  ); }
  if( val_Turn== 1 ){ bum.setTurnSignal(BUM_TURN_RIGHT); }
 
//   ПРОВЕРКА НАЛИЧИЯ ПРИНЯТЫХ ИК ДАННЫХ:                                  
  if( ir.check(true) ){                    // Если принят пакет данных от знака
    switch (ir.device) {
      case MODUL_TLIGHT:                   // ПРИНЯТЫ ДАННЫЕ ОТ МОДУЛЯ СВЕТОФОР:       
        if( flg_TLIGHT == false ){         // Если данные от светофора еще не получались
          flg_TLIGHT =  true;              // Фиксируем факт получения данных от светофора
      
          //  Выбираем направление поворота:                      
          if( !ir.track_L &&  ir.track_F && !ir.track_R ){ val_Turn=0;                 } // Перекрёсток светофора имеет дорогу только прямо (F)         , выбираем движение прямо 0
          if( !ir.track_L &&  ir.track_F &&  ir.track_R ){ val_Turn=random( 0,2);      } // Перекрёсток светофора имеет дорогу прямо (F) и направо (R)  , выбираем движение прямо 0 или направо 1
          if(  ir.track_L &&  ir.track_F && !ir.track_R ){ val_Turn=random(-1,1);      } // Перекрёсток светофора имеет дорогу прямо (F) и налево  (L)  , выбираем движение налево -1 или прямо 0
          if(  ir.track_L &&  ir.track_F &&  ir.track_R ){ val_Turn=random(-1,2);      } // Перекрёсток светофора имеет дорогу в любом направлении (LFR), выбираем движение налево -1, прямо 0, или направо 1
          if(  ir.track_L && !ir.track_F &&  ir.track_R ){ val_Turn=random( 0,2)? -1:1;} // Перекрёсток светофора имеет дорогу налево и направо    (LR) , выбираем движение налево -1 или направо 1
          flg_CrossWait = val_Turn ? true : false;                                       // Ждём начало перекрёстка если требуется поворачивать (если val_Turn != 0)
        }                                                        
      
        //   Снижаем скорость или останавливаемся на светофоре:       
        if( flg_CrossWait ){ speed = low_Speed; }             // Если ожидается перекрёсток, то снижаем скорость перед ним
        else               { speed = mid_Speed; }             // Иначе выбираем обычную скорость
        if( val_Turn== 0 && ir.forvard==0 ){ speed = 0; }     // Если выбрано движение прямо  и оно запрещено светофором, то останавливаемся (снижаем сорость до 0%)
        if( val_Turn==-1 && ir.left   ==0 ){ speed = 0; }     // Если выбрано движение влево  и оно запрещено светофором, то останавливаемся (снижаем сорость до 0%)
        if( val_Turn== 1 && ir.right  ==0 ){ speed = 0; }     // Если выбрано движение вправо и оно запрещено светофором, то останавливаемся (снижаем сорость до 0%)
      break;     
        
      case MODUL_SIGN:                                         // Данные отправлены дорожным знаком
        if(!strcmp(ir.sign_str, "1.12.1")){                    // Если номер знака 1.12.1 - «Опасные повороты»          
          speed = low_Speed;                                   // Ограничиваем скорость до ближайшего перекрёстка или нового знака
          flg_112 = true;                                      // Меняем флаг для изменения коэффициента регулятора
          disp.drawImage(Image_1_12_1);                        // Выводим изображение знака.
        }
        if( !strncmp( ir.sign_str, "3.24", 4) ){               // Обнаружен знак ПДД 3.24 «Ограничение максимальной скорости»:
          disp.print(ir.sign[2]);                              // Выводим цифру скорости
          if( ir.sign[2]< 3 ){ speed = min_Speed; }else        // ir.sign[2] < 3 значит на знаке написано меньше «30»
          if( ir.sign[2]< 5 ){ speed = low_Speed; }else        // ir.sign[2] < 5 значит на знаке написано меньше «50»
          if( ir.sign[2]< 7 ){ speed = mid_Speed; }else        // ir.sign[2] < 7 значит на знаке написано маньше «70»
          if( ir.sign[2]!=0 ){ speed = max_Speed; }            // ir.sign[2] != 0 значит на знаке написано "90"
        } 
        if( !strcmp( ir.sign_str, "1.23" ) ){                  // Eсли номер знака 1.23 - «Дети»
          disp.scrollPos(0);                                   // Переходим к началу бегущей строки
          disp.print("Дети");                                  // Загружаем текст для бегущей строки
          disp.autoScroll(245);                                // Выводим загруженный текст однократно со скоростью 245
          speed = min_Speed;                                   // Ограничиваем скорость до ближайшего перекрёстка или нового знака
        }
        if( !strcmp( ir.sign_str, "3.31" ) ){                  // Если обнаружен знак ПДД 3.31 «Конец зоны ограничений»
          disp.drawImage(Image_3_31);                          // Выводим изображение знака
          speed = mid_Speed;                                   // Снимаем наложенные ранее ограничения скорости
        }
        if( !strcmp( ir.sign_str, "2.5" )){                    // Если номер знака 2.5 - «Движение без остановки запрещено»
          if (!flg_25) {                                       // Если не установлен флаг знака
            timeSign = millis();                               // Сбрасываем значение переменной времени обнаружения знака
            flg_25 = true;                                     // Устанавливаем флаг знака
          }
        }
        if( !strcmp( ir.sign_str, "3.1" )){                    // Обнаружен знак ПДД 3.1 «Въезд запрещен»: 
          disp.drawImage(Image_3_1);
          flg_Turn = true;                                     // Устанавливаем флаг разворота
          bum.setTurnSignal(BUM_TURN_LEFT );                   // Включаем левый поворотник
        }
        if( !strcmp( ir.sign_str, "4.1.3" ) ){                 // Обнаружен знак ПДД 4.1.3 «Движение налево»:
          disp.drawImage(Image_4_1_3);                         // Выводим изображение знака
          flg_CrossWait = true;                                // Ждём начало перекрёстка
          val_Turn = -1;                                       // Выбираем движение влево
          speed = min_Speed;                                   // Ограничиваем скорость до завершения перекрёстка
        }
      break;
         
      case MODUL_CAR:                                          // Данные отправлены другим автомобилем
                                                               // Пока тут пусто, с другими машинками не взаимодействуем
      break;
    }     
  }
  // Срабатываение по времени знака "Движение без остановки запрещено"
  if(flg_25 && timeSign + 3000 < millis()){                    // Если флаг установлен и с момента последней фиксации прошло больше 3 сек
    flg_25 = false;                                            // Сбрасываем флаг
    disp.setTimeIdleFirst(100);                                // Указываем бегущей строке задерживаться на первом    символе в течении 100 мс (допускаются значения от 0 до 2550 мс)
    disp.setTimeIdleLast(100);                                 // Указываем бегущей строке задерживаться на последнем символе в течении 100 мс (допускаются значения от 0 до 2550 мс)
    disp.scrollPos(0);                                         // Переходим к началу бегущей строки    
    disp.print("STOP");                                        // Загружаем текст для бегущей строки
    disp.autoScroll(245);                                      // Выводим загруженный текст однократно со скоростью 245
    mot_R.setSpeed( 0, MOT_PWM );                              // Выключаем правый мотор
    mot_L.setSpeed( 0, MOT_PWM );                              // Выключаем левый мотор
    
    delay(1000);
    while(bum.getErrPID() > -4) {                              // Поворот вправо
      mot_R.setSpeed(-convertSpeed(min_Speed), MOT_PWM);
      mot_L.setSpeed(convertSpeed(min_Speed), MOT_PWM);
    }
    while(bum.getErrPID() < 4) {                               // Поворот влево
      mot_R.setSpeed(convertSpeed(min_Speed), MOT_PWM);
      mot_L.setSpeed(-convertSpeed(min_Speed), MOT_PWM);
    }
    while(bum.getErrPID() > 0) {                               // Поворот вправо, возвращаемся к центру
      mot_R.setSpeed(-convertSpeed(min_Speed), MOT_PWM);
      mot_L.setSpeed(convertSpeed(min_Speed), MOT_PWM);
    }
  }
 
//   ОПРЕДЕЛЯЕМ ОШИБКУ ЦЕНТРИРОВАНИЯ ЛИНИИ ПОД БАМПЕРОМ:                   
//   Если ожидается перекрёсток: Машина должна ехать по центру линии немного сместившись в сторону поворота
     float bum_Error;                                                      // Объявляем переменную: ошибка для П-регулятора
     if( flg_CrossWait ){ bum_Error = bum.getErrPID()+val_Turn;   }else    // Получаем ошибку центрирования линии смещённую на 1 датчик в сторону ожидаемого поворота «val_Turn»
//   Если бампер заехал на перекрёсток:                                    // Машина должна ехать по краю линии выполняя поворот
     if( flg_CrossFind ){ bum_Error = bum.getSidePID(val_Turn)*3; }else    // Получаем ошибку нахождения на краю линии, со стороны поворота «val_Turn». Умножаем ошибку, это увеличит резкость поворота, актуально для тонких линий трассы
//   Если не ждём перекрёсток и не находимся на нём:                       // Машина должна ехать по центру линии
                        { bum_Error = bum.getErrPID();            }        // Получаем ошибку центрирования линии
     if( flg_112 ){bum_Error = bum.getErrPID()*3; flg_112 = false;}                 
                        
  float kP = 3 + 0.125*(convertSpeed(speed)-20);        // Коэффициент П-регулятора
  float P = bum_Error * kP;                             // Получаем значение от П-регулятора
  mot_R.setSpeed(convertSpeed(speed) - P, MOT_PWM);     // Устанавливаем скорость правого мотора 
  mot_L.setSpeed(convertSpeed(speed) + P, MOT_PWM);     // Устанавливаем скорость левого мотора 
}
 
float convertSpeed(float speedLevel){                   // Функция преобразования скорости из км/ч в %ШИМ
  if (speedLevel == 0) return 0;                        // Если скорость равно 0 возвращаем 0
  else if (speedLevel < 20) return 20;                  // Если скорость меньше минимальной, устанавливаем минимальное заполнение ШИМ
  else if (speedLevel > 90) return 60;                  // Если скорость больше максимальной, устанавливаем максимальное заполнение ШИМ
  else return map(speedLevel, 20, 90, 20, 60);          // Преобразование диапазон остальных скоростей
}
 
//   Функция создания анимации движения на перекрёстке светофора:
void fnc_CreateImage(bool l, bool f, bool r, int8_t t){        // Параметры: l - наличие дороги влево, f - прямо, r - вправо, t - выбранное направление (L=-1 F=0 R=+1)
//   Рисуем дорогу к перекрёстку:
     Image_TLIGHT[0]=0x18; Image_TLIGHT[1]=0x18; Image_TLIGHT[2]=0x18; Image_TLIGHT[3]=0x18; Image_TLIGHT[4]=0x18; Image_TLIGHT[5]=0; Image_TLIGHT[6]=0; Image_TLIGHT[7]=0;
//   Добавляем дороги от перекрёстка:
     if( f  ){ Image_TLIGHT[5]|=0x18; Image_TLIGHT[6]|=0x18; Image_TLIGHT[7]|=0x18; }
     if( l  ){ Image_TLIGHT[3]|=0x1F; Image_TLIGHT[4]|=0x1F; } // Дорога влево
     if( r  ){ Image_TLIGHT[3]|=0xF8; Image_TLIGHT[4]|=0xF8; } // Дорога вправо
//   Добавляем точку движения:                                 
     static uint8_t i=0, j=0;                                  // Определяем переменные i - позиция точки, f - флаг задержки
     if( millis()%50 < 25 ){                                   
         if( j==0 ){ j=1; i++; if(i>8){i=0;} }                 // Увеличиваем позицию i
     }else{ j=0; }                                             // Сбрасываем задержку
     if(i<=3){ Image_TLIGHT[i]&=0xEF; }                        // Точка на дороге к перекрёстку
     else{ if(t==0){ if(i<8){Image_TLIGHT[i]&=0xEF;        } } // Точка на дороге от перекрёстка прямо
           if(t <0){ if(i<9){Image_TLIGHT[4]&=~(1<<(8-i)); } } // Точка на дороге от перекрёстка влево
           if(t >0){ if(i<7){Image_TLIGHT[3]&=~(1<<(i+1)); } } // Точка на дороге от перекрёстка вправо
     }                                                         
}  
 
//   ФУНКЦИЯ ОСТАНОВКИ ПЕРЕД ПРЕПЯТСТВИЕМ:                                                                                                                                                     
void fnc_StopObstacle(uint16_t distance){         //   Функция получает дистанцию в см при которой требуется остановиться
    
//   Проверяем наличие препятствия:                           
     if( len.distance() > distance ){ return; }   //   Выходим из функции если расстояние до препятствия больше «distance» см.
 
//   Определяем время обнаружения препятствия:                    
     uint32_t currentTime = millis();             //   Получаем текущее время
 
//   Выполняем остановку:                                         
     mot_R.setStop();                             //   Останавливаем правый мотор
     mot_L.setStop();                             //   Останавливаем левый мотор
     disp.print("STOP");                          //   Загружаем текст "STOP" для бегущей строки
     disp.scrollPos(0);                           //   Переходим к началу бегущей строки
     disp.autoScroll(245, 300);                   //   Выводим загруженный текст со скоростью 245 и паузой между прокрутками строки в 300 мс
 
//   Ждём освобождения дороги от препятствия:                     
     while( currentTime > millis()-tim_Wait ){    //   Пока не пройдёт «tim_Wait» мс после времени «currentTime»
        if( len.distance() < distance+3 ){        //   Если до препятствия меньше «distance»+3 см. ...
            currentTime = millis();               //   Обновляем текущее время
        }                                                         
     }                                                            
 
//   Информируем о готовности продолжить движение:                
     disp.clrScr();                               //   Чистим экран светодиодной матрицы
     delay(500);                                                  
     disp.print("GO");                            //   Загружаем текст "GO" для бегущей строки
     disp.scrollPos(0);                           //   Переходим к началу бегущей строки
     disp.autoScroll(245);                        //   Выводим загруженный текст однократно со скоростью 245
     delay(1000);                                                 
     disp.clrScr();                               //   Чистим экран светодиодной матрицы
}

Добавляем новые знаки

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

Добавьте их в ваш скетч, чтобы при встрече их машинка знала, что ей нужно делать, а также выводила на дисплей правильную анимацию.

На этом данный раздел заканчивается, но мы не прощаемся! Увидимся в следующей главе.

Ну что же, поздравляем!
Вы освоили основы программирования, выполнили множество упражнений и проектов. Не стесняйтесь возвращаться к предыдущим урокам, если что-нибудь забыли. А мы предлагаем двигаться дальше и переходить к следующей главе.
перейти к выбору главы

Продукт в магазине

Комплект знаков для базового курса по "Роботраффику" для образования (продолжение ROBORACE)

В магазин

Обсуждение

Гарантии и возврат. Используя сайт, Вы соглашаетесь с условиями.