Движение с поворотом на перекрестке

В этом уроке:

  • Виды перекрёстков и их распознавание
  • Повороты на перекрёстке
  • Управление поворотниками 
  • Изменение скорости езды при проезде перекрёстка

Видео версия урока

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

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

Виды перекрёстков

Для начала давайте поговорим о возможных типах перекрёстков. Они бывают трех видов:

  1. Т-образный с поворотом налево;
  2. Т-образный с поворотом направо;
  3. Х-образный с поворотом налево или направо.

*Также помните про вариант Т-образного перекрёстка с поворотом налево или направо. Это тот же перекрёсток, что и на первых двух изображениях, просто въезжаем мы на него с прилегающей дороги.

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

Принцип фиксации перекрёстка бампером

Давайте посмотрим на момент въезда на перекрёсток подробнее, а именно на то, что фиксируют датчики линии

Датчики линии, расположенные со стороны прилегающей дороги, начинают фиксировать её. Это довольно явный признак, по которому мы можем судить о том, что перед нами перекрёсток

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

Но каким же образом программно описать эту фиксацию?

Мы можем воспользоваться уже знакомой нам функцией getLineDigital() и определять значения, поступающие с каждого датчика, и на основании этого принимать решение о повороте.

Но можно воспользоваться специальной функцией, которая позволит нам сделать это проще.

Функция поиска перекрёстка

Функция getCross() позволяет обнаружить перекрестки и крутые повороты. Она возвращает true, если перекрёсток был обнаружен. Функция не указывает сторону возможного поворота, т.е. не определяет тип перекрёстка.

Синтаксис: getCross( ШИРИНА, [ВРЕМЯ] );

  • ШИРИНА — количество датчиков, расположенных над линией вне перекрёстка;
  • ВРЕМЯ — количество времени в мс, требуемое для преодоления перекрёстка (необязательный параметр).

Т.е. функция будет возвращать true указанное время после того, как перекрёсток был обнаружен (а если время не указано — то в момент проезда перекрёстка). Добавим условие и получим блок кода, который будет выполняться при обнаружении перекрёстка.

if (bum.getCross(3, 1000)){  //  Линия до перекрёстка фиксируется тремя датчиками. Время проезда перекрёстка — 1000 мс
  //  Код, выполняемый при движении на перекрёстке
}

Скетч для распознавания перекрёстка и вывода изображения на дисплей

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

Алгоритм такой: едем по П-регулятору. В случае, если зафиксировали перекрёсток (37 строка), выводим его условное изображение на дисплей (38 строка) на полсекунды, после чего стираем (40 строка)

О том, как создавать изображение, мы подробно говорили в уроке о работе с дисплеем.

if (bum.getCross(3, 1000)){          // Если зафиксирован перекрёсток, (в течение 1000мс возвращается true)
    disp.drawImage(myImage_1);       // Выводим на дисплей изображение массива myImage_1 
}
else disp.clrScr();                  // Иначе очищаем матрицу

Объединим скетч движения по П-регулятору и наш новый код.

#include <Wire.h>                         // Подключаем библиотеку для работы с аппаратной шиной I2C
#include <iarduino_I2C_Motor.h>           // Подключаем библиотеку для работы с мотором  I2C-flash
#include <iarduino_I2C_Bumper.h>          // Подключаем библиотеку для работы с бампером I2C-flash
#include <iarduino_I2C_Matrix_8x8.h>      // Подключаем библиотеку для работы с LED матрицей 8x8
                                          
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 для работы с LED матрицей 8x8, указывая её адрес на шине I2C
                                          
float speed = 30.0;                       // Скорость машины в %
       
byte myImage_1[8] = { 0b00011000,         // Создаём массив для отображения на матрице
                      0b00011000,                             
                      0b00011000,                         
                      0b11111111,                        
                      0b11111111,                       
                      0b00011000,                             
                      0b00011000,                             
                      0b00011000 };    
                      
void setup() {
  mot_R.begin();                       // Инициируем работу с левым  мотором I2C-flash
  mot_L.begin();                       // Инициируем работу с правым мотором I2C-flash
  bum.begin();                         // Инициируем работу с бампером I2C-flash
  mot_R.setDirection(true);            // Указываем правому мотору, что его вращение должно быть прямым (по часовой стрелке при положительных скоростях)
  mot_L.setDirection(false);           // Указываем левому мотору, что его вращение должно быть обратным (против часовой стрелки при положительных скоростях)
  disp.begin();                        // Инициируем работу с LED матрицей 8x8
}

void loop() { 
  float kP = 3 + 0.125*(speed-20);     // Коэффициент П-регулятора
  float P = bum.getErrPID() * kP;      // Получаем значение от П-регулятора
  mot_R.setSpeed(speed - P, MOT_PWM);  // Устанавливаем скорость правого мотора 
  mot_L.setSpeed(speed + P, MOT_PWM);  // Устанавливаем скорость левого мотора 
  
  if (bum.getCross(3, 1000)){          // Если зафиксирован перекрёсток, (в течение 1000мс возвращается true)
    disp.drawImage(myImage_1);         // Выводим на дисплей изображение массива myImage_1 
  }
  else disp.clrScr();                  // Иначе очищаем матрицу
}

Алгоритм поворота на перекрёстке

Переменные и флаги состояния

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

int8_t  val_Turn      = -1;     // Выбранное направление движения на перекрёстке: 0 прямо, -1 влево, +1 вправо
bool    flg_CrossWait = true ;  // Флаг ожидания перекрёстка (машина "увидела" светофор или знак, сообщающий о наличии перекрёстка)
bool    flg_CrossFind = false;  // Флаг обнаружения перекрёстка (бампер заехал на перекрёсток)

На данном этапе направление поворота будем задавать вручную в переменной val_Turn (12 строка). К примеру, на перекрёстке всегда поворачивать направо (val_Turn = 1) или всегда налево (val_Turn = -1).

Также добавляем два флага: 

  • Флаг ожидания перекрёстка flg_CrossWait (13 строка) указывает, ожидает ли машинка появления перекрёстка. Пока мы пишем тестовый скетч, этот флаг равен true, но в дальнейшем он будет истинным только после появления светофора или соответствующего знака;
  • Флаг обнаружения перекрёстка flg_CrossFind (14 строка). Становится истинным при фиксации перекрёстка бампером.

Проверка наличия перекрёстков. Изменение флагов

//   ПРОВЕРКА НАЛИЧИЯ ПЕРЕКРЁСТКОВ:                                       
  if( bum.getCross(3,1000) ){             // Если под бампером обнаружен перекрёсток (указываем толщину линии трассы и время на преодоление перекрёстка в мс) ...
    if( flg_CrossWait ){                  // Если ожидается появление перекрёстка ...
      flg_CrossWait = false;              // Сбрасываем флаг ожидания перекрёстка
      flg_CrossFind = true;               // Устанавливаем флаг обнаружения перекрёстка
      disp.drawImage(myImage_1);          // Выводим на дисплей условное изображение перекрёстка
    }                                                               
  }
else{                                     // Если под бампером обычная линия ...          
     if( flg_CrossFind ){                 // Если ранее был обнаружен перекрёсток ...
       flg_CrossFind = false;             // Сбрасываем флаг обнаружения перекрёстка
       val_Turn = 0;                      // Выбираем движение прямо
       disp.clrScr();                     // Чистим экран светодиодной матрицы
     }                                                              
  }   

В момент, когда бампер обнаруживает перекрёсток (36 строка), мы меняем состояние флагов, после чего изменится алгоритм езды по линии (см. ниже).

Если перекрёсток пройден (это условие впервые сработает через 1 сек, 43 строка), меняем состояние флагов обратно и возвращаемся к обычному движению. Также меняем значение переменной val_Turn на 0, чтобы машинка ехала прямо. 

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

Реализация поворота

Теперь переходим непосредственно к повороту. Поскольку мы работаем с функциями ПИД-регулирования, будет корректнее выполнить поворот, также используя данные функции (а не задавать параметры скорости каждого из колёс через переменные, ведь машинка при движении на перекрёстке также  нуждается в адаптивном управлении — регулировании).

Очень удобно будет использовать функцию getSidePID(), которая позволяет считать ошибку центрирования края линии для ПИД-регулятора. Удобнее использовать за ориентир при повороте именно край линии, т.к. иначе машинка просто проедет вперёд. Если же мы работаем с краем линии, то её резкий переход в сторону (поворот) сразу же фиксируется бампером, и машинка начинает поворот в этом направлении.

Синтаксис: getSidePID( [КРАЙ] );

  • Параметры: int8_t КРАЙ — число, отличное от 0 со знаком края линии (- левый, + правый);
  • Возвращаемое значение: float — значение ошибки центрирования края от 0 до ±4,5 (отрицательные значения, если край линии находится левее центра бампера, и положительные — если правее).
Таким образом, если функция getErrPID() позволяет получить ошибку центрирования линии (именно её центра), то обращение к функции getSidePID() позволяет получить ошибку центрирования края этой линии. Особенно хорошо отличие заметно при широких линиях. Для понимания работы функции и алгоритма поворота можете представлять именно широкую линию. 
//   ОПРЕДЕЛЯЕМ ОШИБКУ ЦЕНТРИРОВАНИЯ ЛИНИИ ПОД БАМПЕРОМ:                   
//   Если ожидается перекрёсток:                                           // Машина должна ехать по центру линии, немного сместившись в сторону поворота
     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();            }        // Получаем ошибку центрирования линии

Если машинка находится в ожидании перекрёстка, то смещаемся в сторону поворота на один датчик (52 строка). Помним, что обычно мы едем по центральному датчику (не ожидаем появление перекрёстка), но только в этом уроке мы делаем допущение и всегда находимся в ожидании перекрёстка.

Если в данный момент машинка находится на перекрёстке, то получаем ошибку нахождения на краю линии со стороны выполняемого поворота (54 строка).  

    Если оба условия не выполняются, т.е. перекрёсток пройден, движемся по прямой — это обычная задача, которую мы уже решали. Для этого используем обычную функцию getErrPID(); (56 строка).

    Таким образом, получаем скетч ниже.

    Готовый скетч для поворота на перекрёстке с индикацией на дисплее

    #include <Wire.h>                         // Подключаем библиотеку для работы с аппаратной шиной I2C
    #include <iarduino_I2C_Motor.h>           // Подключаем библиотеку для работы с мотором  I2C-flash
    #include <iarduino_I2C_Bumper.h>          // Подключаем библиотеку для работы с бампером I2C-flash
    #include <iarduino_I2C_Matrix_8x8.h>      // Подключаем библиотеку для работы с LED матрицей 8x8
                                              
    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 для работы с LED матрицей 8x8, указывая её адрес на шине I2C   
                                              
    float   speed         = 30.0;             // Скорость машины в %
    int8_t  val_Turn      = -1;               // Выбранное направление движения на перекрёстке: 0 прямо, -1 влево, +1 вправо
    bool    flg_CrossWait = true ;            // Флаг ожидания перекрёстка (машина "увидела" светофор или знак, сообщающий о наличии перекрёстка)
    bool    flg_CrossFind = false;            // Флаг обнаружения перекрёстка (бампер заехал на перекрёсток)
    
    byte myImage_1[8] = { 0b00011000,         // Создаём массив для отображения на матрице
                          0b00011000,                             
                          0b00011000,                         
                          0b11111111,                        
                          0b11111111,                       
                          0b00011000,                             
                          0b00011000,                             
                          0b00011000 };    
                          
    void setup() {
      mot_R.begin();                          // Инициируем работу с левым  мотором I2C-flash
      mot_L.begin();                          // Инициируем работу с правым мотором I2C-flash
      bum.begin();                            // Инициируем работу с бампером I2C-flash
      mot_R.setDirection(true);               // Указываем правому мотору, что его вращение должно быть прямым (по часовой стрелке при положительных скоростях)
      mot_L.setDirection(false);              // Указываем левому мотору, что его вращение должно быть обратным (против часовой стрелки при положительных скоростях)
      disp.begin();                           // Инициируем работу с LED матрицей 8x8
    }
    
    void loop() { 
      //   ПРОВЕРКА НАЛИЧИЯ ПЕРЕКРЁСТКОВ:                                       
      if( bum.getCross(3,1000) ){             // Если под бампером обнаружен перекрёсток (указываем толщину линии трассы и время на преодоление перекрёстка в мс) ...                            
        if( flg_CrossWait ){                  // Если ожидается появление перекрёстка ...
          flg_CrossWait = false;              // Сбрасываем флаг ожидания перекрёстка
          flg_CrossFind = true;               // Устанавливаем флаг обнаружения перекрёстка
          disp.drawImage(myImage_1);          // Выводим на дисплей условное изображение перекрёстка
        }                                                               
      }
    else{                                     // Если под бампером обычная линия ...          
         if( flg_CrossFind ){                 // Если ранее был обнаружен перекрёсток ...
           flg_CrossFind = false;             // Сбрасываем флаг обнаружения перекрёстка
           val_Turn = 0;                      // Выбираем движение прямо
           disp.clrScr();                     // Чистим экран светодиодной матрицы
         }                                                              
      }
    //   ОПРЕДЕЛЯЕМ ОШИБКУ ЦЕНТРИРОВАНИЯ ЛИНИИ ПОД БАМПЕРОМ:                   
    //   Если ожидается перекрёсток: Машина должна ехать по центру линии немного сместившись в сторону поворота
         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();            }        // Получаем ошибку центрирования линии
                            
      float kP = 3 + 0.125*(speed-20);        // Коэффициент П-регулятора
      float P = bum_Error * kP;               // Получаем значение от П-регулятора
      mot_R.setSpeed(speed - P, MOT_PWM);     // Устанавливаем скорость правого мотора 
      mot_L.setSpeed(speed + P, MOT_PWM);     // Устанавливаем скорость левого мотора 
    }                                                                   

    Поворот на перекрёстке с изменением скорости (без дисплея)

    Добавим небольшие корректировки в наш скетч. Мы говорили о том, что скорость нужно снижать перед перекрестком. Но ведь после проезда можно её увеличить! Давайте так и сделаем.

    Также можно удалить строки кода для работы с дисплеем — наши перекрёстки уже чётко определяются, и теперь в индикации нет необходимости. Лишний код будет только отвлекать нас. 

    Итак, необходимо добавить константу скорости (10 строка) и присвоить переменной скорости минимальное значение (11 строка):

    const float min_Speed = 30;  // Минимальная  скорость движения в %. Используется при движении по дорогам
    const float mid_Speed = 60;  // Средняя скорость движения в %. Используется при движении по дорогам
    float speed = min_Speed;     // Указываем, что изначально скорость равна минимальной
    

    В блок перехода от перекрёстка в прямой трассе добавляем снятие ограничения скорости (36 строка).

    if (flg_CrossFind) {                 // Если ранее был обнаружен перекрёсток 
      flg_CrossFind = false;             // Сбрасываем флаг обнаружения перекрёстка
      val_Turn = 0;                      // Выбираем движение прямо
      speed = mid_Speed;                 // Снимаем наложенные ранее ограничения скорости
    }

    Управление поворотниками на перекрёстке

    Мы уже умеем управлять поворотниками. Давайте добавим подачу сигналов поворотами в наш скетч (40-42 строка).

    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); }   //  Если поворот будет осуществляться направо, включаем правый поворотник

    Теперь машинка будет включать поворотники до и во время проезда перекрёстка, а после, когда val_Turn становится равен 0, — выключать их.

    Готовый скетч поворота с изменением скорости (без дисплея)

    #include <Wire.h>                         // Подключаем библиотеку для работы с аппаратной шиной I2C
    #include <iarduino_I2C_Motor.h>           // Подключаем библиотеку для работы с мотором  I2C-flash
    #include <iarduino_I2C_Bumper.h>          // Подключаем библиотеку для работы с бампером I2C-flash
                                              
    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
                                              
    const float min_Speed = 30;               // Минимальная  скорость движения в %. Используется при движении по дорогам
    const float mid_Speed = 60;               // Средняя скорость движения в %. Используется при движении по дорогам
    float speed = min_Speed;                  // Указываем, что изначально скорость равна минимальной
    int8_t  val_Turn      = -1;               // Выбранное направление движения на перекрёстке: 0 прямо, -1 влево, +1 вправо
    bool    flg_CrossWait = true ;            // Флаг ожидания перекрёстка (машина "увидела" светофор или знак, сообщающий о наличии перекрёстка)
    bool    flg_CrossFind = false;            // Флаг обнаружения перекрёстка (бампер заехал на перекрёсток)
                          
    void setup() {
      mot_R.begin();                          // Инициируем работу с левым  мотором I2C-flash
      mot_L.begin();                          // Инициируем работу с правым мотором I2C-flash
      bum.begin();                            // Инициируем работу с бампером I2C-flash
      mot_R.setDirection(true);               // Указываем правому мотору, что его вращение должно быть прямым (по часовой стрелке при положительных скоростях)
      mot_L.setDirection(false);              // Указываем левому мотору, что его вращение должно быть обратным (против часовой стрелки при положительных скоростях)
    }
    
    void loop() { 
      //   ПРОВЕРКА НАЛИЧИЯ ПЕРЕКРЁСТКОВ:                                       
      if( bum.getCross(3,1000) ){             // Если под бампером обнаружен перекрёсток (указываем толщину линии трассы и время на преодоление перекрёстка в мс) ...                            
        if( flg_CrossWait ){                  // Если ожидается появление перекрёстка ...
          flg_CrossWait = false;              // Сбрасываем флаг ожидания перекрёстка
          flg_CrossFind = true;               // Устанавливаем флаг обнаружения перекрёстка
        }                                                               
      }
    else{                                     // Если под бампером обычная линия ...          
         if (flg_CrossFind) {                 // Если ранее был обнаружен перекрёсток 
           flg_CrossFind = false;             // Сбрасываем флаг обнаружения перекрёстка
           val_Turn = 0;                      // Выбираем движение прямо
           speed = mid_Speed;                 // Снимаем наложенные ранее ограничения скорости
         }                                                               
      }
    
    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); }  //  Если поворот будет осуществляться направо, включаем правый поворотник
    
    //   ОПРЕДЕЛЯЕМ ОШИБКУ ЦЕНТРИРОВАНИЯ ЛИНИИ ПОД БАМПЕРОМ:                   
    //   Если ожидается перекрёсток: машина должна ехать по центру линии, немного сместившись в сторону поворота
         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();            }        // Получаем ошибку центрирования линии
                            
      float kP = 3 + 0.125*(speed-20);        // Коэффициент П-регулятора
      float P = bum_Error * kP;               // Получаем значение от П-регулятора
      mot_R.setSpeed(speed - P, MOT_PWM);     // Устанавливаем скорость правого мотора 
      mot_L.setSpeed(speed + P, MOT_PWM);     // Устанавливаем скорость левого мотора 
    }

    Сохраните полученный скетч, он понадобится нам в следующей главе при работе со знаками.

    Поздравляю с изучением данного урока!
    Следующий урок:
    №24. Движение с разворотом.
    приступить к изучению

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

    «ROBORACE» — Образовательный набор на базе Arduino

    В магазин

    Обсуждение

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