Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Кафедра «Мехатронные системы»

 

 

Методические указания к лабораторной работе

"Применение джойстика, энкодера в роботах"

по дисциплине

"Электронные устройства мехатронных и робототехнических систем"

 

 

Ижевск 2017


Задача: разработать алгоритм и программу управления объектом с помощью джойстика. В качестве объекта может выступать робот с дифференциальным управлением. Для имитации нужно использовать 2 светодиода, соответствующих 2 приводам.

Разработать алгоритм и программу для чтения данных с энкодера и вывода значений на последовательном мониторе.

 

Как правило, игрушки комплектуются пультом управления с кнопками. Но управлять кнопками неудобно, т.к. машинка резко разгоняется и резко тормозит. Системы с таким управлением называют дискретными. Для возможности плавного управления применяют джойстики. Системы с плавным управлением называют пропорциональными. Для работы с Arduino есть специальные модули джойстиков, они имеют ось X, Y и кнопку, которую могут обозначать как ось Z. Кроме управления моделями, джойстик можно применить для управления меню, или для плавного регулирования какого- либо параметра, которое видится как более сильное отклонение джойстика – приводящее к более быстрому изменению числа. К примеру, необходимо изменять число от 0 до 3000 с шагом 1. Если мы применим для этого кнопку, то придется или нажать ее 3000 раз или писать специальный алгоритм автоматического увеличения шага, зависящего от времени. Если использовать джойстик, то реализовать такой алгоритм значительно проще.

 

Рисунок 1 - Джойстик

 

Джойстик состоит из двух переменных резисторов. Когда положение рукоятки смещается – это вызывает изменение сопротивления резисторов. Чем сильнее отклоняется рукоятка, тем сильнее изменяется сопротивление.
На плате эти резисторы уже соединены нужным образом с 0 и +5V, поэтому для подключения к Arduino, нужно подать питание на соответствующие клеммы и подключить выходы осей к соответствующим аналоговым портам.

 

Рисунок 2 - Подключение джойстика

 

Пример 1.

Написать программу, которая устанавливает все светодиоды в выключенное состояние; светодиоды по порядку начинают светиться и гаснут.

Шаг 1. Подключить матрицу, как изображено на схеме.

 

Рисунок 3 – Схема подключения

 

Шаг 2. Написать программу.

 

#include "LedControl.h"
LedControl lc = LedControl(4, 2, 3, 1);
unsigned long delaytime = 100;

void setup()
{
lc.shutdown(0, false);
lc.setIntensity(0, 8);
lc.clearDisplay(0);
}

void loop()
{
for (int row = 0; row < 8; row ++)
for (int col = 0; col < 8; col ++)
{
delay(100);
lc.setLed(0, row, col, true);
}
for (int row = 0; row < 8; row ++)
for (int col = 0; col < 8; col ++)
{
delay(100);
lc.setLed(0, row, col, false);
}
lc.setRow(0, 0, B11111111);
lc.setRow(0, 7, B11111111);
lc.setColumn(0, 0, B11111111);
lc.setColumn(0, 7, B11111111);
delay(500);
lc.setRow(0, 0, B00000000);
lc.setRow(0, 7, B00000000);
lc.setColumn(0, 0, B00000000);
lc.setColumn(0, 7, B00000000);
delay(500);
}

 

Пример 2.

Реализовать игру «Змейка» с использованием джойстика и светодиодной матрицы.Реализовать будильник c использованием часов реального времени и LCD – дисплея и пьезоизлучателя.

 

Шаг 1. Собираем схему, состоящую из джойстика и RGB – светодиода.

 

Рисунок 4 – Схема подключения

 

Шаг 2. Написать программу.

 

#include

// Display
int din = 8;
int clk = 10;
int cs = 9;

// Joystick click
int sw = 5;

// Joystick move
int x = 3;
int y = 4;

int tilt_max = 700;
int tilt_min = 300;

LedControl matrix(din, clk, cs);

int food_x;
int food_y;
int head_last_x;
int head_last_y;
int width = 8;
boolean needNewFood = true;
int max_body_length = 20;
int head = 0;
int tail = 0;
int body_xs[20];
int body_ys[20];
boolean gameOver = false;

void setup()
{
Serial.begin(9600);
randomSeed(analogRead(0));
start();
}

void loop()
{
if (!gameOver)
{
head_last_x = body_xs[head];
head_last_y = body_ys[head];
moveJoystick();
// Move head
ledOn(body_xs[head], body_ys[head]);
delay(100);

// Generate new food
if (needNewFood)
newFood();
if (headAndFoodCollide())
{
Serial.println("Om-nom-nom");
growSnake();
needNewFood = true;
}
if(headCollidesWithBody())
{
Serial.println("Collision!");
gameOver = true;
}
}
else
{
for (int i = 0; i < width; i++)
{
matrix.setRow(0, i, B11111111);
delay(500);
}
if(digitalRead(sw) == 0)
{
Serial.println("Reset");
gameOver = false;
for (int i = 0; i < width; i++)
{
matrix.setRow(0, i, B00000000);
delay(500);
}
start();
}
}
}

void start()
{
matrix.shutdown(0, false);
matrix.setIntensity(0, 2); // 0-16
head = 0;
tail = 0;
food_x = 0;
food_y = 0;
head_last_x = 0;
head_last_y = 0;
needNewFood = true;
randomStart();
}

void moveJoystick()
{
// Movements
if (body_xs[head] < 7 && analogRead(x) > tilt_max)
{
moveSnake();
body_xs[head]++;
}
else if (body_xs[head] > 0 && analogRead(x) < tilt_min)
{
moveSnake();
body_xs[head]--;
}
else if (body_ys[head] < 7 && analogRead(y) < tilt_min)
{
moveSnake();
body_ys[head]++;
}
else if (body_ys[head] > 0 && analogRead(y) > tilt_max)
{
moveSnake();
body_ys[head]--;
}
}

void newFood()
{
do
{
food_x = random(width);
food_y = random(width);
}
while (bodyAndFoodCollide());
Serial.print("New food at (");
Serial.print(food_x + 1);
Serial.print(", ");
Serial.print(food_y + 1);
Serial.print(")");
Serial.println();
needNewFood = false;
ledOn(food_x, food_y);
}

boolean headAndFoodCollide()
{
if(food_x == body_xs[head] && food_y == body_ys[head])
{
// Reset values to prevent multiple loop events
food_x = -1;
food_y = -1;
return true;
}
return false;
}

boolean bodyAndFoodCollide()
{
for (int i = 0; i <= tail; i++)
{
if(food_x == body_xs[i] && food_y == body_ys[i])
{
// Reset values to prevent multiple loop events
food_x = -1;
food_y = -1;
return true;
}
return false;
}
}

boolean headCollidesWithBody()
{
for (int i = 1; i <= tail; i++)
{
if (body_xs[head] == body_xs[i] && body_ys[head] == body_ys[i])
{
return true;
}
}
return false;
}

void moveSnake()
{
ledOff(body_xs[tail], body_ys[tail]);
shiftArray();
}

void growSnake()
{
tail++;
shiftArray();
body_xs[1] = head_last_x;
body_ys[1] = head_last_y;
}

void shiftArray()
{
for (int i = tail; i > 0; i--)
{
body_xs[i] = body_xs[i - 1];
body_ys[i] = body_ys[i - 1];
}
}

void printSnake()
{
Serial.println("Snake:");
for (int i = 0; i <= tail; i++)
{
Serial.print("(");
Serial.print(body_xs[i] + 1);
Serial.print(", ");
Serial.print(body_ys[i] + 1);
Serial.println(")");
}
}

void ledOn(int x, int y)
{
matrix.setLed(0, x, y, true);
}

void ledOff(int x, int y)
{
matrix.setLed(0, x, y, false);
}

void ledOnDebug(int x, int y)
{
Serial.print("Led ");
Serial.print("(");
Serial.print(x + 1);
Serial.print(", ");
Serial.print(y + 1);
Serial.print(")");
Serial.println(" set ON");
matrix.setLed(0, x, y, true);
}

void ledOffDebug(int x, int y)
{
Serial.print("Led ");
Serial.print("(");
Serial.print(x + 1);
Serial.print(", ");
Serial.print(y + 1);
Serial.print(")");
Serial.println(" set OFF");
matrix.setLed(0, x, y, false);
}

void randomStart()
{
// Start from a random cell
body_xs[head] = random(width);
body_ys[head] = random(width);
ledOn(body_xs[head], body_ys[head]);
Serial.print("Starting at (");
Serial.print(body_xs[head] + 1);
Serial.print(", ");
Serial.print(body_ys[head] + 1);
Serial.print(")");
Serial.println();
}

 

Рисунок 5 – Энкодер

 

Рисунок 6 – Схема подключения

 

 

В примере иллюстрируется подключение модуля энкодера к контроллеру, и определение текущего значения поворота. Значение поворота выводится в монитор Serial – порта

 

//Подключение библиотеки для энкодера

#include <Encoder.h>

 

//пины

const int CLK_PIN = 10;

const int DT_PIN = 9;

 

 

long position = -999; //установка значения переменной для хранения текущей позиции

 

//создание объекта типа Экнеодер

Encoder myEnc(DT_PIN, CLK_PIN);

 

void setup() {

Serial.begin(9600); //инициализация Serial-порта

}

 

void loop() {

long newPosition = myEnc.read(); //считать новую позицию

if (newPosition!= position) { //если значение отличается от старого

position = newPosition; //установить новое текущее значение

Serial.println(position); //вывести значение в Serial - порт

}

// Если в код добавить существенную задержку,

// Энкодер сможет отследить только очень медленное движение.

// Для проверки, можно расскоментировать нижнюю строчку:

 

//delay(50);

}

 

 

Пример 2: В примере иллюстрируется подключение модуля энкодера к контроллеру, и определение текущего значения поворота. Также проверяется нажатие кнопки энкодера. Значения поворота и нажатия кнопки выводятся в монитор Serial - порта. (Примеры тестировались на контроллере Genuino UNO)

Схема подключения:

Рисунок 7 – Схема подключения

Скетч для загрузки:

//Подключение библиотеки для энкодера

#include <Encoder.h>

 

//пины

const int CLK_PIN = 10;

const int DT_PIN = 9;

const int SW_PIN = 8;

 

long position = -999; //установка значения переменной для хранения текущей позиции

 

//создание объекта типа Экнеодер

Encoder myEnc(DT_PIN, CLK_PIN);

 

void setup() {

Serial.begin(9600); //инициализация Serial-порта

}

 

void loop() {

long newPosition = myEnc.read(); //считать новую позицию

if (newPosition!= position) { //если значение отличается от старого

position = newPosition; //установить новое текущее значение

Serial.println(position); //вывести значение в Serial - порт

}

 

// Если в код добавить существенную задержку,

// Энкодер сможет отследить только очень медленное движение.

// Для проверки, можно расскоментировать нижнюю строчку:

 

//delay(50);

 

if (digitalRead(SW_PIN) == LOW) { //проверка нажатия на кнопку

Serial.println("Pressed"); //вывод сообщения

}

 

}

 

 

Рассмотрим работу Arduino с энкодером (который служит для преобразования угла поворота в эл. сигнал). С энкодера мы получаем 2 сигнала (А и В), которые противоположны по фазе. В данном уроке мы будем использовать энкодер фирмы SparkFun COM-09117, который имеет 12 положений на один оборот (каждое положение 30°). На приведенной ниже диаграмме вы можете видеть, как зависят выход А и В друг от друга при вращении энкодера по часовой или против часовой стрелки.

Рисунок 8 – Импульсы А и В

 

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

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

Как было сказано выше, мы будем использовать энкодер sparkfun. Первое, что необходимо сделать, это определить как часто нам нужно обращаться к выходам энкодера для считывания значений. Итак, представим себе, что в лучшем случае, мы можем повернуть ручку энкодера на 180° за 1/10 сек, т.е. это будет 6 импульсов за 1/10 сек или 60 импульсов в секунду. В реальности быстрее вращать не сможете. Т.к. нам необходимо отслеживать все полупериоды, то частота должна быть минимум 120 Герц. Для полной уверенности, давайте примем 200 Гц. (Примечание: т.к. у нас механический энкодер, то возможен дребезг контактов, а низкая частота позволяет отфильтровывать дребезг).

Рисунок 9 – Схема подключения

 

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

 

Рисунок 10 – Схема подключения

 

Программа для данного урока приведена ниже. Она построена на использовании функции millis() для задания временных интервалов. Временной интервал у нас будет 5 мс (200 Гц).

 

/*

** Энкодер

** Для управлением яркостью LED используется энкодер Sparkfun

*/

 

int brightness = 120; // яркость LED, начинаем с половины

int fadeAmount = 10; // шаг изменения яркости LED

unsigned long currentTime;

unsigned long loopTime;

const int pin_A = 12; // pin 12

const int pin_B = 11; // pin 11

unsigned char encoder_A;

unsigned char encoder_B;

unsigned char encoder_A_prev=0;

 

void setup() {

// declare pin 9 to be an output:

pinMode(9, OUTPUT); // устанавливаем pin 9 как выход

pinMode(pin_A, INPUT);

pinMode(pin_B, INPUT);

currentTime = millis();

loopTime = currentTime;

}

 

void loop() {

currentTime = millis();

if(currentTime >= (loopTime + 5)){ // проверяем каждые 5мс (200 Гц)

encoder_A = digitalRead(pin_A); // считываем состояние выхода А энкодера

encoder_B = digitalRead(pin_B); // считываем состояние выхода B энкодера

if((!encoder_A) && (encoder_A_prev)){ // если состояние изменилось с положительного к нулю

if(encoder_B) {

// выход В в полож. сост., значит вращение по часовой стрелке

// увеличиваем яркость, не более чем до 255

if(brightness + fadeAmount <= 255) brightness += fadeAmount;

}

else {

// выход В в 0 сост., значит вращение против часовой стрелки

// уменьшаем яркость, но не ниже 0

if(brightness - fadeAmount >= 0) brightness -= fadeAmount;

}

 

}

encoder_A_prev = encoder_A; // сохраняем значение А для следующего цикла

 

analogWrite(9, brightness); // устанавливаем яркость на 9 ножку

 

loopTime = currentTime;

}

}

 


Литература

а) основная литература

1. Глинкин Е.И. Схемотехника аналоговых интегральных схем: учебное пособие [Электронный ресурс]. / Е.И. Глинкин. - 2-е изд., доп. - Тамбов: Изд-во ФГБОУ ВПО "ТГТУ", 2012. - 152 с. ISBN/ISSN:978-5-8265-1072-8 // Единое окно доступа к информационным ресурсам. URL: http://window.edu.ru/resource/453/76453 (дата обращения: 11.07.2015).

2. Кузнецов Б.Ф. Электронные промышленные устройства: Учебное пособие [Электронный ресурс]. - Изд-во: Ангарской государственной технической академии, 2010. - 151 с. ISBN/ISSN:978-5-89864-072-9 // Единое окно доступа к информационным ресурсам. URL:http://window.edu.ru/resource/335/73335 (дата обращения: 11.07.2015).

3. Кулик В.Д. Силовая электроника. Автономные инверторы, активные преобразователи: Учебное пособие [Электронный ресурс]. - СПб.: СПбГТУРП, 2010. - 90 с. // Единое окно доступа к информационным ресурсам. URL: http://window.edu.ru/resource/330/76330 (дата обращения: 11.07.2015).

4. Муромцев Д.Ю. Основы проектирования электронных средств: учебное пособие [Электронный ресурс] / Д.Ю. Муромцев, И.В. Тюрин. - Тамбов: Изд-во ГОУ ВПО ТГТУ, 2011. - Ч. 1. - 80 с. ISBN/ISSN:978-5-8265-0980-7 // Единое окно доступа к информационным ресурсам. URL:http://window.edu.ru/resource/512/76512 (дата обращения: 11.07.2015).

5. Чернышова Т.И., Чернышов Н.Г. Моделирование электронных схем: Учебное пособие [Электронный ресурс]. - Тамбов: Издательство ТГТУ, 2010. - 80 с. ISBN/ISSN:978-5-8265-0965-4 // Единое окно доступа к информационным ресурсам. URL:http://window.edu.ru/resource/209/73209 (дата обращения: 11.07.2015).

б) дополнительная литература

1. Афанасьева Н.А., Булат Л.П. Электротехника и электроника: Учебное пособие [Электронный ресурс]. - 2-е изд., перераб. и дополн. - СПб.: СПбГУНиПТ, 2009. - 181 с. ISBN/ISSN:5-89565-117-8// Единое окно доступа к информационным ресурсам. URL: http://window.edu.ru/resource/926/77926 (дата обращения: 11.07.2015).

2. Гаврилов С. А. Искусство схемотехники. Просто о сложном. – СПб.: Наука и Техника, 2011. – 352 с.

3. Майер Р.В. Основы электроники. Курс лекций: Учебно-методическое пособие. – Глазов: ГГПИ, 2011. – 80 с.

4. Топильский В. Б. Схемотехника измерительных устройств / В. Б. Топильский. – М.: БИНОМ. Лаборатория знаний, 2010. – 232 с.

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...