Алгоритм программы для ардуино

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

Это самые популярные платы серии Arduino

Движение робота ардуино по черной линии

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

схема мобильного робота ардуино для движения по черной линии

Для движения мобильного робота ардуино по черной линии возьмем за основу подвижную платформу мобильного робота Ардуино, схема которого разобрана на уроке мобильный робот Arduino.
Световые датчики или датчики черной линии подсоединяются к плате ардуино следующим образом: порт датчика черной линии GND подсоединяется к GND через макетной платы, порт датчика VCC подсоединяется к шине питания через макетную плату в 5V, А0 подсоединяется к аналоговым портам платы ардуино A0, A1. Чтобы считать данные со светового датчика, используется команда

Программирование Ардуино с нуля. Arduino для начинающих.


analogRead (номер пина);
Эта команда считывает значение датчика с номером пина.
Подробнее о работе светового датчика описано на уроке Считывание и калибровка датчиков Arduino.

Алгоритм движения по черной линии мобильного робота Ардуино

Если робот ардуино заезжает левым датчиком на черную линию, то он поворачивается определенное количество времени налево.
Если робот ардуино заезжает правым датчиком на черную линию, то он поворачивается определенное количество времени направо.
Если оба датчика белые, то тогда робот едет определенное количество времени вперед.
Мы разбираем пример без остановки на перекрестке. Если оба датчика черные, то тогда робот едет на определенное количество времени вперед.
Чтобы понять, что датчик заехал на черную линию, нужно считать данные с датчика и сравнить его с откалиброванным значением. Если значение считанное с датчика черной линии больше откалиброванного значения, то мы заехали на черную линию, если меньше, то на белую (Это зависит от работы конкретного датчика черной линии). Чтобы выполнялось одновремменоо два условия необходимо использовать оператор условия и логическую операции И Подробонее об условиях и логических операциях в Си.
Разберем алгоритм движения по черной линии робота адрдуино
Left_1 = analogRead (LeftSensor_1); //Считываем данные с левого датчика
Right_1 = analogRead (RightSensor_1);//Считываем данные с правого датчика
Таким образом, при заезде мобильного робота Ардуино левым датчиком на черную линию:
Left_1 = analogRead (LeftSensor_1); //Считываем данные с левого датчика
Right_1 = analogRead (RightSensor_1);//Считываем данные с правого датчика
//Если левый датчик видит черныйа правый белый, то поворачиваем налево
if ((Left_1 > sl1) (Right_1 < sp1))) analogWrite(SPEED_1, pov);
digitalWrite(DIR_1,LOW);
analogWrite(SPEED_2,povt);
digitalWrite(DIR_2, HIGH);
delay(1);
>
При заезде мобильного робота Arduino на черную линию правым датчиком:
Left_1 = analogRead (LeftSensor_1); //Считываем данные с левого датчика
Right_1 = analogRead (RightSensor_1);//Считываем данные с правого датчика
// Если правый датчик видит черный, а левый датчик белый, то поворачиваем направо
if ((Right_1 > sp1) (Left_1 < sl1)) analogWrite(SPEED_1, povt);
digitalWrite(DIR_1,HIGH);
analogWrite(SPEED_2,pov);
digitalWrite(DIR_2, LOW);
delay(1);
>
Когда оба датчика мобильного робота Ардуино видят белую линию:
Left_1 = analogRead (LeftSensor_1); //Считываем данные с левого датчика
Right_1 = analogRead (RightSensor_1);//Считываем данные с правого датчика
//Если оба датчика белые, то едем вперед
if ((Left_1 < sl1) (Right_1 < sp1)) analogWrite(SPEED_1,v);
digitalWrite(DIR_1,HIGH);
analogWrite(SPEED_2,v);
digitalWrite(DIR_2, HIGH);
delay(1);
>
Когда оба датчика мобильного робота Adruino видят черную линию:
Left_1 = analogRead (LeftSensor_1); //Считываем данные с левого датчика
Right_1 = analogRead (RightSensor_1);//Считываем данные с правого датчика
//Если оба датчика черные, то едем вперед
if ((Left_1 > sl1) (Right_1 > sp1)) analogWrite(SPEED_1,v);
digitalWrite(DIR_1,HIGH);
analogWrite(SPEED_2,v);
digitalWrite(DIR_2, HIGH);
delay(1);
>
Мы создадим процедуру движения по черной линии мобильного робота ардуина назовём ее lin
Чтобы робот ехал по черной линии бесконечно мы должны вызывать процедуру в теле основной программы
void loop() lin();
>
Полный текст программы для движения робота arduino по черной линии

#define SPEED_1 6 // скорость первого мотора
#define DIR_1 7 // направление первого мотора
#define DIR_2 4 // направление второго мотора
#define SPEED_2 5 // скорость второго мотора

int Left_1; // прописываем левый датчик
int Right_1; // прописываем правый датчик

// Подсоединение к пинам
#define RightSensor_1 A0
#define LeftSensor_1 A1

// Сюда записываем данные с калибровки датчиков
int sl1=193;// Средняя освещённость левого датчика
int sp1=206;// Средняя освещёность правого датчика

Читайте также:
Программа для французских автомобилей

// Переменные для регулировки скорости робота
int povt=30;
int pov=60;
int v=70;

void setup() Serial.begin(9600);
for(int i = 4; i < 8; i++) // определяем порты для моторов
pinMode(i, OUTPUT);
pinMode(A0, INPUT);// настраиваем порты для датчиков черной линии
pinMode(A1, INPUT);
delay(1000);
>
void lin() <
Left_1 = analogRead (LeftSensor_1);
Right_1 = analogRead (RightSensor_1);
//Если левый датчик видит черный, то поворачиваем налево
if ((Left_1 > sl1) (Right_1 < sp1)) analogWrite(SPEED_1, pov);
digitalWrite(DIR_1,LOW);
analogWrite(SPEED_2,povt);
digitalWrite(DIR_2, HIGH);
delay(1);
>
Left_1 = analogRead (LeftSensor_1);
Right_1 = analogRead (RightSensor_1);
// Если правый датчик видит черный, по поворачиваем направо
if ((Right_1 > sp1) (Left_1 < sl1)) analogWrite(SPEED_1, povt);
digitalWrite(DIR_1,HIGH);
analogWrite(SPEED_2,pov);
digitalWrite(DIR_2, LOW);
delay(1);
>
Left_1 = analogRead (LeftSensor_1);
Right_1 = analogRead (RightSensor_1);
//Если оба датчика черные, то едем вперед
if ((Left_1 > sl1) (Right_1 > sp1)) analogWrite(SPEED_1,v);
digitalWrite(DIR_1,HIGH);
analogWrite(SPEED_2,v);
digitalWrite(DIR_2, HIGH);
delay(1);
>
Left_1 = analogRead (LeftSensor_1);
Right_1 = analogRead (RightSensor_1);
//Если оба датчика белые, то едем вперед
if ((Left_1 < sl1) (Right_1 < sp1)) analogWrite(SPEED_1,v);
digitalWrite(DIR_1,HIGH);
analogWrite(SPEED_2,v);
digitalWrite(DIR_2, HIGH);
delay(1);
>
>

Источник: itrobo.ru

Arduino нейросеть для управления роботом

Расскажу о своем давнем проекте — нейронная сеть для управления роботом. Весьма специфичная и сложная задача в виду того, что реализована была на Arduino. Нейронная сеть работает с большими объемами данных. Требуется скорость в ее обработки для принятия решений. Но проведя пару экспериментов понял, что не все так уж и плохо.

Arduino нейросеть для управления роботом

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

Из конструкции робота видно, что даже без использования нейронной сети, его возможности ограничены. Максимум что можно «логически» запрограммировать, это обход препятствий с выбором наилучшего направления, включение/выключение робота по звуку или уровню освещенности и. т.д, одним словом в интернете масса подобных поделок.

Но вот какая штука – все это быстро надоедает,… Что если захотелось что- то поменять, например реакцию на свет или звук или получить некоторую непредсказуемость в поведении. Конечно, можно предусмотреть все это в программе контроллера, данные грузить с SD карточки, а непредсказуемость получить простым рандомом. Но мы не ищем легких путей…:). Делать будем так: пусть все выше перечисленные задачи решает нейронная сеть. Обучать будем методом обратного распространения ошибки.
Коэффициенты сети будут переноситься в контроллер посредством SD карты.

Как же это работает?

Был заложен следующий принцип работы:
На компьютере:
1) Создается обучающая выборка
2) Сеть обучается до тех пор, пока глобальная ошибка на всех выборках не станет равной нулю.(на самом деле будет немного не так)
3) После обучения создается отчет и файл для контроллера.
4) Файл записывается на карточку.

На Arduino:
1) После включения с SD карточки загружаются параметры сети.
2) После загрузки сеть готова к работе.
3) На основании показаний датчиков формируется «вектор данных».
4) Этот вектор подается на вход сети.
5) После просчета, выходной вектор сети подается на дешифратор.
6) Дешифратор, в зависимости от результата работы сети, определяет тип команды и выполняет ее.
7) Цикл повторяется с пункта 3,

Вектором данных я буду называть одномерный массив определенной размерности, элементы этого массива – числа. Скажем, что в нулевом элементе будет храниться расстояние до препятствия справа, в первом расстояние в прямой видимости и.т.д.
Почему же все-таки вектор? Когда я только начинал разбираться с нейронными сетями я сформировал для себя следующие понятие: «любой набор данных есть точка в N мерном пространстве». В нашем случае пространство будет размерности 5. Да, да такие бывают :).

Вот точное расположение данных в массиве:

input[0] – расстояние с права. input[1] – расстояние в прямой видимости. input[2] – расстояние слева. input[3] – значение фоторезистора. Input[4] – уровень микрофона (1 или 0)

Двухслойная нейросеть с пятью нейронами на слой для Arduino

Железо:

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

Читайте также:
Программа чтобы посмотреть кто заходил на мою страницу Вконтакте

Честно говоря, в электронике я не селен и подключил ее по первой загугленной схеме. В итоге оказалось, что для управления направлением вращения приходилось переводить ножку в состояние INPUT. Это было весьма не привычно, и я до сих пор не знаю чем это может аукнуться…
Насчет колес…в качестве редукторов были использованы серво — машинки, переделанные на постоянное вращение. Все остальные примочки как датчик расстояния и SD card модуля были подключены по стандартным схемам.

Arduino нейросеть для управления роботомArduino нейросеть для управления роботомArduino нейросеть для управления роботом

Да, качество сборки на 3 🙂

Часть программная:

Даже не знаю с чего начать. С одной стороны тут нет ни каких велосипедов все стандартно:

Начнем с программы для ПК:
Написана она, а точнее они на C#. Потому что это достаточно простой язык и я часто использую его в своей работе, но не об этом. В программах нет классов все сделано в «функциональном» стиле для более простого понимания начинающим. Как Вы успели понять программ две: Первая это простой интерфейс, код которого не заслуживает тут присутствовать ибо та все очень просто. Будет только скриншот:

Arduino нейросеть для управления роботом

Тут все предельно просто: движками крутим значение на входах, выбираем мысленно реакцию на эти данные из предложенных пяти и ставим галочку. Жмем «добавить».

Arduino нейросеть для управления роботом

Слева двоеточия что подаем на вход, справа то, что хотим получить.

После того как мы заполнили этот файл, нажимаем кнопку «Создать» и он автоматически передается другой программе. Собственно та другая программа выполняет две функции: учит сеть на предъявленных данных и формирует отчет. Начнем с конца:

Вот такой отчет хороший:

Arduino нейросеть для управления роботом

Хороший он потому, что строки Input Res и NET OUT совпадают. Это говорит о том, что при подачи на вход Input data мы хотели бы получить Input Res, а получаем NET OUT. Так что о качестве работы сети мы будем судить из этого отчета.

Теория гласит, что при обучении сети образцы стоит предъявлять в случайном порядке. К чему я это – в моей практике это, на первый взгляд, незначительное утверждение в действительности влияет на скорость сходимости алгоритма. Так же на скорость влияет и первоначально выбранные веса. Бывало что с первой попытки алгоритм не сходился, приходилось по несколько раз запускать обучение для того что бы добиться результата.

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

static int InputN = 5, //Входные нейронны OutN = 5; //Выходные нейроны static int InputW = 5, //Веса на входе OutW = 5; //Веса на выходе static Random run = new Random(); //Гениратор случайностей static float[,] INPUT_W = new float[InputN, InputW]; //Входной слой static float[,] OUTPUT_W = new float[OutN,OutW]; //Выходной слой static float[] INPUT_RESULT = new float[InputN]; //Выход первого слоя static float[] OUTPUT_RESULT = new float[OutN]; //Выход последнего слоя static float[] OUTPUT_ERROR = new float[OutN]; //Ошибка на выходе static float[] INPUT_ERROR = new float[InputN]; //Ошибка на входе static float Sig = 0.5f; //Значение порога static float etta = 0.001f; //Значение параметра скорости static List Input_patern = new List(); //Входные данные static List Out_patern = new List(); //Ответы /*Инициализация весов*/ static void init_net() < for (int i = 0; i < InputN; i++)< for (int j = 0; j < InputW; j++)< INPUT_W[i, j] = (float)run.NextDouble() — 0.5f; >> for (int i = 0; i < OutN; i++)< for (int j = 0; j < OutW; j++)< OUTPUT_W[i, j] = (float)run.NextDouble() — 0.5f; >> > /*Прямой проход*/ static void DirectMotion(float[] vector) < if (vector.Length >InputW) < return; >/*Первый слой*/ for (int i = 0; i < InputN; i++)< INPUT_RESULT[i] = 0.0f; for (int j = 0; j < InputW; j++)< INPUT_RESULT[i] += INPUT_W[i, j] * vector[j]; >INPUT_RESULT[i] = Sigma(INPUT_RESULT[i]); > /*Выходной слой*/ for (int i = 0; i < OutN; i++)< OUTPUT_RESULT[i] = 0.0f; for (int j = 0; j < OutW ; j++)< OUTPUT_RESULT[i] += OUTPUT_W[i, j] * INPUT_RESULT[j]; >OUTPUT_RESULT[i] = Sigma(OUTPUT_RESULT[i]); > > static float BackPropagation(float[] vector, float[] target) < /*———————————Прямой проход——————————————-*/ DirectMotion(vector); /*—————————————————————————————-*/ /*———————————Расчет ошибки——————————————-*/ /*Ошибка на выходе*/ for (int i = 0; i < OUTPUT_ERROR.Length; i++) < OUTPUT_ERROR[i] = 0.0f; OUTPUT_ERROR[i] = target[i] — OUTPUT_RESULT[i]; >/*Ошибка на входе*/ for (int neurons = 0; neurons < INPUT_ERROR.Length; neurons++)< INPUT_ERROR[neurons] = 0.0f; for (int out_n = 0; out_n < OutN; out_n++) < INPUT_ERROR[neurons] += OUTPUT_ERROR[out_n] * OUTPUT_W[out_n, neurons]; >> /*——————————————————————————————*/ /*———————————Коррекция ошибки—————————————-*/ /* Входной слой */ for (int n = 0; n < InputN; n++)< for (int w = 0; w < InputW; w++)< INPUT_W[n, w] += (etta * INPUT_ERROR[n] * vector[w]); >> /*Выходной слой*/ for (int n = 0; n < OutN; n++)< for (int w = 0; w < OutW; w++)< OUTPUT_W[n, w] += (etta * OUTPUT_ERROR[n] * INPUT_RESULT[w]); >> /*——————————————————————————————*/ /*возвращаем ошибку на текущем наборе. */ return calculate_global_error(); > static float calculate_global_error() < float error = 0.0f; for (int i = 0; i < OUTPUT_ERROR.Length; i++) < error += Math.Abs(OUTPUT_ERROR[i] * OUTPUT_ERROR[i]); >return error * 0.5f; > static void Learn() < int Iteration = 0; float Error = 0.0f; while (Iteration < 250000) < Error = 0.0f; for (int paterns = 0; paterns < Input_patern.Count; paterns++) < Error += BackPropagation(Input_patern[paterns], Out_patern[paterns]); >Console.WriteLine(Error); Iteration++; > > /*Пороговая функция активации*/ static float Sigma(float value) < if (value >= Sig) < return 1.0f; >return 0.0f; > Собственно ни чего сложного. Как мы видим из кода в качестве функции активации нейронов выбрана пороговая функция с параметром срабатывания 0,5.

Читайте также:
Краткая инструкция по работе с программой explorer

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

Это связанно с тем, что иногда сеть проваливается в локальные минимумы, при этом ошибка равно нулю, а вот качество обучения порой ужасное. Так что пришлось сделать так… Возможно я что то делаю неверно… если кто то заметит, скажите пожалуйста. Собственно код Arduino: #include #include #include #define R_F 5 #define L_F 8 #define R_R 6 #define L_R 9 #define TRIG 4 #define ECHO 7 #define INPUT_W_COUNT 5 // #define INPUT_N_COUNT 5 // #define OUTPUT_W_COUNT 5 // #define OUTPUT_N_COUNT 5 // #define INPUT_VECTOR_SIZE 5 // #define TMP_VECTOR_SIZE 50 // #define START_ANGLE 130 #define RIGHT_ANGLE 55 #define LEFT_ANGLE 180 #define SERVO_PIN 3 #define MIC_PIN 2 #define REZISTOR_PIN 0 /*Для чтения данных с флешки*/ File myFile; /*Датчик растояния*/ Ultrasonic dist(TRIG, ECHO); /*голова :)*/ Servo servo; float input[INPUT_VECTOR_SIZE]; float Wtmp[TMP_VECTOR_SIZE]; /*Веса первого слоя*/ float INPUT_W[INPUT_N_COUNT][INPUT_W_COUNT]; /*Веса выходного слоя*/ float OUTPUT_W[OUTPUT_N_COUNT][OUTPUT_W_COUNT]; float INPUT_OUT[INPUT_N_COUNT]; //Выход первого слоя float NET_OUT[OUTPUT_N_COUNT]; //Выход сети int FileCount = 0, CountW = 0; void setup() < servo.attach(SERVO_PIN); servo.write(START_ANGLE); delay(1000); ReadDataFromSD(); delay(1000); >void loop() < /*Получаем данные с датчиков*/ CreateVector(); /*Расчитываем выход сети*/ NeuralNetUpdate(); >/*Пересчет выхода сети*/ void NeuralNetUpdate() < /*Входной слой*/ for(int N = 0; N < INPUT_N_COUNT; N++)< INPUT_OUT[N] = 0.0; for(int W = 0; W < INPUT_W_COUNT; W++)< INPUT_OUT[N] += input[W] * INPUT_W[N][W]; >INPUT_OUT[N] = Sigmoid(INPUT_OUT[N]); > /*Выходной слой*/ for(int N = 0; N < OUTPUT_N_COUNT; N++)< NET_OUT[N] = 0.0; for(int W = 0; W < OUTPUT_W_COUNT; W++)< NET_OUT[N] += INPUT_OUT[W] * OUTPUT_W[N][W]; >NET_OUT[N] = Sigmoid(NET_OUT[N]); if(NET_OUT[N] == 1.0) < command(N); >> > float Sigmoid(float value) < if(value >0.50f) return 0.0f; > void CreateVector() < input[1] = (float)dist.Ranging(CM); input[3] = analogRead(0); input[4] = digitalRead(2); >void LoadInputW() < for(int N = 0; N < INPUT_N_COUNT; N++)< for(int W = 0; W < INPUT_W_COUNT; W++)< INPUT_W[N][W] = Wtmp[CountW]; CountW++; >> > void LoadOutputW() < for(int N = 0; N < OUTPUT_N_COUNT; N++)< for(int W = 0; W < OUTPUT_W_COUNT; W++)< OUTPUT_W[N][W] = Wtmp[CountW]; CountW++; >> > /*Заполняет временный массив коэфиуиентами с SD карты*/ void ReadDataFromSD() < pinMode(10, OUTPUT); if (!SD.begin(10)) myFile = SD.open(«test.txt»); if (myFile) < int i = 0; char tmp[32]; while (myFile.available()) < tmp[i] = myFile.read(); if(tmp[i] == ‘;’)< tmp[i] = ‘ ‘; Wtmp[FileCount] = atof(tmp); FileCount++; i = 0; >else < i++; >> myFile.close(); > /*Загружаем первый слой*/ LoadInputW(); /*Загружаем второй слой*/ LoadOutputW(); > void command(int value) < if(value == 0)< Run(); delay(2000); Stop(); return;>if(value == 1) < Stop(); delay(2000); return;>if(value == 2) < Revers(); delay(2000); Left(); delay(2000); Stop(); return; >if(value == 3) < Revers(); delay(2000); Right(); delay(2000); Stop(); return;>if(value == 4) < View(); return;>> void Stop() < pinMode(R_R, OUTPUT); pinMode(L_R, OUTPUT); pinMode(R_F, OUTPUT); pinMode(L_F, OUTPUT); >void Run() < input[2] = 0.0f; input[0] = 0.0f; pinMode(R_R, OUTPUT); pinMode(L_R, OUTPUT); pinMode(R_F, INPUT); pinMode(L_F, INPUT); >void Revers() < pinMode(R_R, INPUT); pinMode(L_R, INPUT); pinMode(R_F, OUTPUT); pinMode(L_F, OUTPUT); >void Left() < input[2] = 0.0f; input[0] = 0.0f; pinMode(R_R, OUTPUT); pinMode(L_F, OUTPUT); pinMode(L_R, INPUT); pinMode(R_F, INPUT); >void Right() < input[2] = 0.0f; input[0] = 0.0f; pinMode(R_R, INPUT); pinMode(L_F, INPUT); pinMode(L_R, OUTPUT); pinMode(R_F, OUTPUT); >void View()

Запустить сеть на Arduino возможно и она даже будет работать. Временами конечно же начинаются глюки. Я думаю они связаны с обучающей выборкой. Я не тратил особо много времени на ее подборку и по этому не могу утверждать точно. Возможно в скором времени будут внесены некоторые коррективы.

Ну а сейчас пока вот так. Спасибо за внимание.

Источник: diytimes.ru

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru