Motor
Учим робота ориентироваться в пространстве. Робот, объезжающий препятствия.
Обычно говорят «Скоро сказка сказывается, да не скоро дело делается». В нашем случае все с точностью до наоборот. Этого робота мы закончили (ну или почти закончили) делать в мае, а «сказка» появляется только сейчас, почти через три месяца.
Долго ли, коротко ли, пришло время научить робота принимать какие-то осмысленные решения самостоятельно. Пока робот умеет только обнаруживать препятствия и объезжать их слева. Необходимо, чтобы он объезжал препятствие либо слева, либо справа, в зависимости от того, с какой стороны больше места для маневра. Если же робот заезжает в тупик (т. е. невозможно проехать ни вперед, ни вправо, ни влево), он должен задним ходом выехать из тупика.
Из всего вышеописанного следует, что нам необходимо не только контролировать расстояние до препятствия впереди, но и расстояние до препятствия слева и справа.
Вариантов несколько:
- установить ультразвуковые сенсоры впереди, слева и справа,
- сделать так, чтобы сеносор мог вращаться и смотреть в разные стороны.
Второй вариант немного сложнее, но кажется более правильным.
Для того, чтобы сенсор мог смотреть в разные стороны мы использовали сервопривод вот такой.
Сервопривод закрепили в передней части робота, на него поставили сенсор. Таким образом сенсор может смотреть вперед, поворачиваться на 90 градусов направо и налево.
Для подсоединения сервопривода используется трехконтактная колодка, как и для сенсора (плюс, минус и сигнал). Для подключения использовали Sensor Shield (на фотографии это хорошо видно).
Для того, чтобы робот смог объезжать препятствия был придуман такой алгоритм:
- Робот все время мониторит свободное пространство перед собой.
- Как только расстояние до препятствия становится меньше определенного минимального значения, робот останавливается.
- Сенсор поворачивается на 90 градусов налево и измеряется расстояние до препятствия, затем на 90 градусов направо и тоже измеряет расстояние.
- Робот должен повернуться в том направлении, где расстояние до ближайшего препятствия больше, при этом оно должно быть больше определенного минимального значения.
- После завершения маневра поворота робот продолжает движение прямо, и алгоритм исполняется с пункта 1.
- В случае если и справа и слева расстояние до препятствия меньше определенного минимального значения, робот включается задний ход. Алгоритм исполняется с пункта 3.
Для управления сервоприводом мы использовали стандартную библиотеку Arduino. В этом месте выяснился непрятный момент, что стандартная библиотека для сервопривода конфликтует с библиотекой IRremote.h, которую мы использовали для работы с инфракрасным приемником. Они обе используют один таймер (9-й пин), а второй подключить не прадставляется возможным (так как 3-й пин занят двигателем). В общем испробовали разные библиотеки для сервопривода, которые не используют таймер, но как-то нормально ни одна не заработала. В результате поисков мы набрели на очень интересную библиотеку, которая позволяет работать с разными устройствами, в т.ч. и с ИК приемником (http://www.zbotic.com).
Вот так это работает:
Здесь уже робота поджидают испытания по сложнее:
Программа:
#include "Device.h"
#include "PCInterrupt.h"
#include "irController.h"
#include "myHardware.h"
#include <Servo.h>
#include <ArduMoto.h>
#include <NewPing.h>
int key;
IRController controller;
const int mindist=15;
const int pingpin=4; // Arduino pin tied to trigger pin on the ultrasonic sensor.
const int maxdist=400 ;// Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
Servo servoping;
NewPing sonar(pingpin, pingpin, maxdist); // NewPing setup of pins and maximum distance.
ArduMoto Moto; //создаем объект для работы с моторами
const float kB=0.55 ; //коэффициент для мотора B, так как он крутится быстрее
const float kA=1;
const float kAll=0.7;//общий коэффициент
const int delayServo=200;
int distcmAhead;
int distcmRight;
int distcmLeft;
void setup()
{
servoping.attach(A0);
Moto.begin();//подключаем моторы
Moto.setSpeed('A',0);//останавливаем моторы на всякий случай
Moto.setSpeed('B',0);
servoping.write(90);
Serial.begin(9600);
int res = controller.begin(IR_PIN, OTHER_DEVICE);
forward(0);
}
void stopall(){
forward(0);
}
int wheretogo()
{
if( (distcmLeft>=distcmRight)&&(distcmLeft>mindist)||(distcmLeft=0))//можем ли мы ехать налево
{
return 1;//go left
}
else {
if( (distcmLeft<distcmRight)&&(distcmRight>mindist)||(distcmRight=0))////можем ли мы ехать направо
{
return 2;//go right
}
else{
return 3;//go back
}
}
}
int lookahead()
{
servoping.write(90);
delay(delayServo);
int distcm=sonar.ping_cm();
return distcm;
}
int lookleft(){
servoping.write(180);
delay(delayServo*2);
int distcm=sonar.ping_cm();
return distcm;
}
int lookright(){
servoping.write(0);
//SoftwareServo::refresh();
delay(delayServo*2);
int distcm=sonar.ping_cm();
return distcm;
}
void left(int velocity)
{
Moto.setSpeed('A',velocity*kAll*kA);
Moto.setSpeed('B',-velocity*kAll*kB);
}
void right(int velocity)
{
Moto.setSpeed('A',-velocity*kAll*kA);
Moto.setSpeed('B',velocity*kAll*kB);
}
void forward(int velocity)
{
Serial.println('go forward');
Moto.setSpeed('A',velocity*kAll*kA);
Moto.setSpeed('B',velocity*kAll*kB);
}
void backward(int velocity)
{
Moto.setSpeed('A',-velocity*kAll*kA);
Moto.setSpeed('B',-velocity*kAll*kB);
}
void forwardleft(int velocity)
{
Moto.setSpeed('A',velocity*kAll*kA);
Moto.setSpeed('B',0.4*velocity*kAll*kB);
}
void forwardright(int velocity)
{
Moto.setSpeed('B',velocity*kAll*kB);
Moto.setSpeed('A',0.4*velocity*kAll*kA);
}
void backwardright(int velocity){
Moto.setSpeed('B',-velocity*kAll*kB);
Moto.setSpeed('A',-0.4*velocity*kAll*kA);
}
void backwardleft(int velocity){
Moto.setSpeed('A',-velocity*kAll*kA);
Moto.setSpeed('B',-0.4*velocity*kAll*kB);
}
void loop() {
int dir;
distcmAhead=lookahead();
if( (distcmAhead<=mindist)&&(distcmAhead>0)){
stopall();
label1:
distcmLeft=lookleft();
distcmRight=lookright();
dir=wheretogo();
switch (dir){
case 1://едем налево
left(100);
delay(500);
forward(100);
break;
case 2://едем направо
right(100);
delay(500);
forward(100);
break;
case 3:
backward(100);
do{
distcmLeft=lookleft();
distcmRight=lookright();
}
while( (distcmLeft<mindist)&&(distcmRight<mindist)&&(distcmRight>0)&&(distcmLeft>0));
goto label1;
}
}
key = controller.read();
if (key>=0){//если получили сигнал то надо что-то сделать
switch (key){
case 16128://едем вперед
forward(100);
break;
case 16192:
backward(100);
break;
case 14726:
forward(0);
break;
case 15427 :
forwardleft(100);
break;
case 14662:
forwardright(100);
break;
case 16256:
backwardright(100);
break;
case 10200 :
backwardleft(100);
break;
case 15746 :
left(100);
break;
case 14471 :
right(100);
break;
}
}
}
Добавляем к роботу сенсоров
Для того, чтобы робот мог объезжать препятствия, необходим специальный сенсор. Мы использовали ультразвуковой датчик расстояния. Под названием PING))) Ultrasonic Distance Sensor.
Принцип работы этого сенсора следующий: cенсор излучает короткий ультразвуковой импульс (в момент времени 0), который отражается от объекта и принимается сенсором. Расстояние рассчитывается исходя из времени до получения эха и скорости звука в воздухе.
Для подключения сенсора к Arduino мы использовали расширение Sensor Shield:
C помощью этого расширения очень удобно подключать к Arduino разные сенсоры и актуаторы (например сервомоторы). Кроме того, что на расширении есть все пины Arduino в обычном виде, как на самой плате, все они дополнительно выведены дополнительно и каждый пин дополнен двумя выводами питания «плюс» и «минус». Это позволят очень легко подключать сенсоры. Как правило все сенсоры имеют для подключения штекеры с тремя выводами «плюс»-«минус»-«сигнал»
На этой фотографии видно, как мы подсоединили к одной группе контактов ультразвуковой сенсор. Сам сенсор мы неподвижно закрепили в передней части робота.
Первая задача, которую необходимо решить — объезжать препятствия, встречающиеся роботу на пути, объезжая их либо справа, либо слева (без какой-либо логики).
Для получения сигналов с сенсора мы использовали библиотеку NewPing. Если расстояние до препятствия 10 сантиметров или меньше, робот пытается объехать его слева. При этом остается возможность управлять роботом с пульта.
Программа:
#include <IRremote.h>
#include <ArduMoto.h>
#include <NewPing.h>
int pingpin=4; // Arduino pin tied to trigger pin on the ultrasonic sensor.
int maxdist=200 ;// Maximum distance we want to ping for (in centimeters). Maximum sensor distance is rated at 400-500cm.
NewPing sonar(pingpin, pingpin, maxdist); // NewPing setup of pins and maximum distance.
ArduMoto Moto; //Object to work with motors
int RECV_PIN = 7; //Pin with IR sensor
IRrecv irrecv(RECV_PIN); //object to work with IR sensor
decode_results results; //object to work with results of IR sensor
const float kB=0.74 ; //коэффициент для мотора B, так как он крутится быстрее
const float kA=1;
const float kAll=0.7;//общий коэффициент
void setup()
{
//Serial.begin(9600);
irrecv.enableIRIn(); // подключаем ИК приемник
Moto.begin();//подключаем моторы
Moto.setSpeed('A',0);//останавливаем моторы на всякий случай
Moto.setSpeed('B',0);
}
void left(int velocity)
{
Moto.setSpeed('A',velocity*kAll*kA);
Moto.setSpeed('B',-velocity*kAll*kB);
}
void right(int velocity)
{
Moto.setSpeed('A',-velocity*kAll*kA);
Moto.setSpeed('B',velocity*kAll*kB);
}
void forward(int velocity)
{
Moto.setSpeed('A',velocity*kAll*kA);
Moto.setSpeed('B',velocity*kAll*kB);
}
void backward(int velocity)
{
Moto.setSpeed('A',-velocity*kAll*kA);
Moto.setSpeed('B',-velocity*kAll*kB);
}
void forwardleft(int velocity)
{
Moto.setSpeed('A',velocity*kAll*kA);
Moto.setSpeed('B',0.4*velocity*kAll*kB);
}
void forwardright(int velocity)
{
Moto.setSpeed('B',velocity*kAll*kB);
Moto.setSpeed('A',0.4*velocity*kAll*kA);
}
void backwardright(int velocity){
Moto.setSpeed('B',-velocity*kAll*kB);
Moto.setSpeed('A',-0.4*velocity*kAll*kA);
}
void backwardleft(int velocity){
Moto.setSpeed('A',-velocity*kAll*kA);
Moto.setSpeed('B',-0.4*velocity*kAll*kB);
}
void loop() {
int distcm;
distcm=sonar.ping_cm();//измеряем расстояние в см.
if( (distcm<=10)&&(distcm>0) ){//если до препятствия менее 10 см.
forward(0);
delay(50);
backward(100);
delay(100);
left(100);
delay(100);
forward(100);
}
if (irrecv.decode(&results)) {//если получили сигнал то надо что-то сделать
switch (results.value){
case 0x807fc03f://едем вперед
forward(100);
break;
case 0x807f40bf:
backward(100);
break;
case 0x807f9867:
forward(0);
break;
case 0x807f708f:
forwardleft(100);
break;
case 0x807f58a7:
forwardright(100);
break;
case 0x807f807f:
backwardright(100);
break;
case 0x807f06f9 :
backwardleft(100);
break;
case 0x807f906f :
left(100);
break;
case 0x807fb847 :
right(100);
break;
}
irrecv.resume();
}
}
Теперь робот умеет объезжать препятствия:
Небольшая фотосессия на вкусное:
Продолжаем строить робота
Строим дальше нашего робота.
После того как собрано шасси, мы начали собирать «корпус» робота. Для этого использовали детали из обычного металлического конструктора.
Для скрепления деталей использовали пластиковые стяжки. Для питания робота сначала мы использовали батарейки на 9В (типа «Крона»), но они достаточно быстро разряжались, поэтому использовали аккумулятор от гоночной машинки, тоже на 9В. Для включения/выключения использовали ... выключатель:
Сейчас немного отвлечемся от сборки робота. Для дистанционного управления роботом на первом этапе мы решили использовать пульт от медиа плеера. Для получения сигнала мы будем использовать ИК приемник VS1838B. Прежде чем включать ИК приемник в общую схему робота и писать программу управления, необходимо уточнить каким кнопкам пульта какое действие робота будет соответствовать, и какие коды, полученные Arduino, соответствуют этим кнопкам. Для выяснения этого мы собрали небольшую схему с ИК приемником и подключили его к Arduino:
Как водится, для общения с ИК приемником взяли одну из имеющихся в открытом доступе библиотек. Программа получилась такая:
#include <IRremote.h>
int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results results;
void setup()
{
Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
}
void loop() {
if (irrecv.decode(&results)) {
Serial.println(results.value, HEX);
irrecv.resume(); // Receive the next value
}
}
Дальше дело не хитрое — нажимай кнопки и записывай коды:
Продолжаем собирать управляющую часть робота. Для управления двигателями нам нужен H-bridge, таким мы уже пользовались в проекте Зоотроп. Но здесь мы не будем использовать непосредственно саму микросхему и пучок проводов к ней. Умные люди придумаю расширения к Arduino, называются они Shield, или по-русски шилды. Представляют собой эти шилды платы размером с Arduino или чуть больше, на этих платах реализуется какая-то функциональность, но самое главное, что подсоединяется она к Arduino с использованием установленных на них штыревых разъёмов. Получается такой бутерброд из плат. На основе H-bridge делают разные MotorShield'ы, как следует из названия, предназначены для управления моторами. Мы взяли Ardumoto L298P Motor Driver Shield. Как я уже говорил, шилд легко подсоединяется к Arduino, имеет удобные клеммы для подключения двигателей и питания (от них же питается и сам Arduino).
Шилд использует пины 12 и 13 для включения/выключения двигателей, и пины 3 и 11 для управления скоростью вращения двигателей. Специально для этого шилда уже есть куча библиотек с полезными функциями, мы выбрали первую попавшуюся, и она заработала. Вроде бы все готово для программирования первой версии дистанционно управляемого робота. Но в процессе отладки программы мы столкнулись с неожиданной проблемой. Начали появляться странности: после выполнения нескольких команд, робот отказывался реагировать на поступающие сигналы, в некоторых случаях отказывался работать один из двигателей (исправный). По отдельности оба компонента работали хорошо, ИК исправно принимал команды, двигатели крутились в разные стороны. При объединении схем, опять появлялись проблемы. Логика подсказывала, что проблема появляется из библиотек, поскольку программа на тот момент была простейшая. При более детальном изучении библиотеки был обнаружен следующий текст
// define which timer to use
//
// Uncomment the timer you wish to use on your board. If you
// are using another library which uses timer2, you have options
// to switch IRremote to use a different timer.
// Arduino Duemilanove, Diecimila, LilyPad, Mini, Fio, etc
#else
// #define IR_USE_TIMER1 // tx = pin 9
#define IR_USE_TIMER2 // tx = pin 3
#endif
Спинным мозгом получилось догадаться, что если двигатель использует третий пин, то, наверное, не совсем правильно использовать тот же пин в таймере для IR приемника. По поводу таймеров в Arduino это отдельная большая тема, сейчас главное то, что если используется таймер (а их в Arduino uno два), то на этот же пин лучше ничего не вешать. Вобщем, следуя велению того же спинного мозга первая строчка была раскомментирована, вторая наоборот. Вот так выглядит финальная версия программы робота версии 1.2:
#include <IRremote.h>
#include <ArduMoto.h>
ArduMoto Moto; //создаем объект для работы с моторами
int RECV_PIN = 7; //пин где сидит ИК приемник
IRrecv irrecv(RECV_PIN); //создаем объект для работы с ИК приемником
decode_results results; //создаем объект для обработки результатов работы ИК приемника
const float kB=0.85; //коэффициент для мотора B, так как он крутится быстрее
const float kA=1;
const float kAll=0.7;/общий коэффициент
void setup()
{
irrecv.enableIRIn(); //подключаем ИК приемник
Moto.begin();//подключаем моторы
Moto.setSpeed('A',0);//останавливаем моторы на всякий случай
Moto.setSpeed('B',0);
}
void left(int velocity)
{
Moto.setSpeed('A',velocity*kAll*kA);
Moto.setSpeed('B',-velocity*kAll*kB);
}
void right(int velocity)
{
Moto.setSpeed('A',-velocity*kAll*kA);
Moto.setSpeed('B',velocity*kAll*kB);
}
void forward(int velocity)
{
Moto.setSpeed('A',velocity*kAll*kA);
Moto.setSpeed('B',velocity*kAll*kB);
}
void backward(int velocity)
{
Moto.setSpeed('A',-velocity*kAll*kA);
Moto.setSpeed('B',-velocity*kAll*kB);
}
void forwardleft(int velocity)
{
Moto.setSpeed('A',velocity*kAll*kA);
Moto.setSpeed('B',0.4*velocity*kAll*kB);
}
void forwardright(int velocity)
{
Moto.setSpeed('B',velocity*kAll*kB);
Moto.setSpeed('A',0.4*velocity*kAll*kA);
}
void backwardright(int velocity){
Moto.setSpeed('B',-velocity*kAll*kB);
Moto.setSpeed('A',-0.4*velocity*kAll*kA);
}
void backwardleft(int velocity){
Moto.setSpeed('A',-velocity*kAll*kA);
Moto.setSpeed('B',-0.4*velocity*kAll*kB);
}
void loop() {
if (irrecv.decode(&results)) {//если получили сигнал то надо что-то сделать
switch (results.value){
case 0x807fc03f://едем вперед
forward(100);
break;
case 0x807f40bf:
backward(100);
break;
case 0x807f9867:
forward(0);
break;
case 0x807f708f:
forwardleft(100);
break;
case 0x807f58a7:
forwardright(100);
break;
case 0x807f807f:
backwardright(100);
break;
case 0x807f06f9 :
backwardleft(100);
break;
case 0x807f906f :
left(100);
break;
case 0x807fb847 :
right(100);
break;
}
irrecv.resume();
}
}
И все стало работать, как предполагалось. В этом ролике робот еще не умеет делать команды поворотов направо и налево c одновременным движением назад, а так же повоторы на месте. Работает на аккумуляторе от гоночной машинки.
После робот был немного усовершествован. Мы установили на него более емкий аккумулятор, при этом меньших размеров:
Для зарядки мы используем «умное» зарядное устройство:
В программу добавили команды поворотов, указанные раннее. И робот стал двигаться намного веселее (на заднем и на переднем плане слышно и видно Фёдора, который возмущается, что ему не дают поиграть с роботом):
Небольшое слайдшоу:
Строим робота
Мы строим робота.
Идея робота такая:
- Робот должен быть более-менее автономным;
- Необходима возможность дистанционного управления роботом (в идеале напрямую с телефона);
- Робот должен самостоятельно объезжать препятствия;
- Хочется иметь на борту видеокамеру, которая тоже должна дистанционно управляться и передавать изображение в режиме реального времени (хотя, опцию записи видео тоже можно предусмотреть);
- Изображение желательно получать также на телефон (но это, наверное, мечты).
Работы по созданию робота мы разделили на следующие этапы:
- Сборка движущего механизма.
- Установка «доп. оборудования» (сенсоры и актуаторы).
- Программирование микроконтроллера
- Программирование внешних устройств (телефона или компьютера) для управления роботом и получения и обработки информации, поступающей с сенсоров робота.
В процессе сборки и совершенствования робота пункты 2 и 3 будут выполнятся в цикле.
В принципе, можно купить уже готового робота или готовый комплект для сборки и программировать уже его. Но, во-первых, это не так интересно. Процесс проектирования и создания робота своими руками сам по себе затягивает. Во-вторых, при самостоятельном проектировании робота можно предусмотреть какие-то конструктивные особенности, чтобы робот удовлетворял потребностям его создателя. Правда, с точки зрения затрат, этот способ может выйти дороже.
Мы выбрали средний путь — узловая сборка, приобрели узлы и из них сделали робота. Решили делать гусеничного, поскольку проще в управлении, ну и «внедорожные» качества имеются. Для сборки использовали следующие компоненты:
- Редукторная коробка для двух моторов, и сами моторы. Tamiya Twin Motor Gearbox (TAM70097).
- Платформа для крепления и крепеж. — Tamiya Universal Plate Set (TAM70157).
- Гусеницы и колеса с осями. — Tamiya Track and Wheel Set (TAM70100)
- Металлический конструктор.
Приступаем к сборке. Все узлы комплектуются очень подробными инструкция по сборке, написанные мелким японским шрифтом с картинками.
Редукторную коробку можно собрать с разным передаточным числом: 58:1 или 203:1. Мы собрали 203:1, чтобы нагрузка на моторы была меньше, да и с учетом груза, который мы хотим запихать в тележку, тяга у шасси должна быть побольше. Коробка состоит из двух симметричных блоков шестеренок.
После сборки коробки в нее вставляются моторчики. К моторам надо не забыть припаять провода.
Блок устанавливается на платформу. После этого к валам, идущим от редуктора, крепятся ведущие колеса, остальные колеса крепятся на платформу.
После сборки подсоединили к проводам для проверки батарейку, использовали обычную крону на 9В — ездит.
На этом сборка шасси окончена, дальше будем собирать управляющие конструкции.
В процессе эксплуатации выяснилось, что моторы — это самое слабое звено этой конструкции. Пришлось несколько раз их разбирать для очистки нагара со счеток, в итоге, на одном из них щётки сгорели окончательно. Пришлось взять такой же из комплекта одномоторного редуктора (типа такого), который мы заказали про запас для более других экспериментов.
IMG_0321.jpg
Зоотроп (Zoetrope)
В этом проекте мы будет делать «движущиеся картинки». Они будут двигаться вперед и назад с помощью Arduino, к которому мы подключим электромотор, H-bridge и ... неподвижные картинки.
До того, как появилось кино, движущиеся картинки создавались с помощью прибора, называемого зоотроп. Зоотроп создает иллюзию движения из статических картинок, которые немного отличаются друго от друга.
Конструкция его довольно простая. Это цилиндр, в верхней части цилинда делаются небольшие прямоугольные отвестия-прорези. В нижней части цилиндра (там где прорезей нет) на внутренней его стороне закрепляется полоска с картинками, которые будут «двигаться». Надо смотреть на картинки через прорези. Когда цилиндр начинает вращаться картинки начинают «оживать». Выглядит вот так:
Раньше зоотропы приводились в движение руками или при помощи вращающего механизма. Мы будем вращать цилиндр с помощью моторчика. Чтобы сделать систему более продвинутой, мы добавим выключатель, для того, чтобы контролировать направление вращения, еще один, чтобы включать и выключать двигатель и потенциометр для управления скоростью вращения.
В нашем проекте «Вертушка с моторчиком» мотор вращался только в одном направлении. Если поменять полярность питания, подаваемого на двигатель, то он начнет вращаться в противоположную сторону. Не очень практично каждый раз, когда нам требутся поменять направление вращения двигателя, переключать проводки двигателя. Для автоматизации этой задачи придумали специальный компонент, который называется H-bridge. H-bridge — это интегральная микросхема, схема, которая содержит множество элементов, плотно «упакованных» в в один комнонент. С помощью интегральных микросхем можно составлять достаточно сложные схемы, состоящие из легко заменяемых компонентов. Например, H-bridge, который мы будем использовать в этом проекте, содержит внутри несколько транзисторов. Чтобы собрать его, может понадобится еще одна макетная плата.
Для доступа к «внутренностям» интегральной схемы используются пины (выходы), расположенные по ее бокам. Разные компоненты имеют разное количество выходов, и не все из них используются в конкретной схеме. Для удобства выходы интегральных схем нумеруются, нумерация выводов ведется против часовой стрелки, начиная от первой ножки, находящейся под ключом, который может иметь вид засечки или углубления.
В нашем проекте мы используем микросхему L293D. Назначение пинов можно посмотреть на схеме. С ее помощью можно управлять двумя двигателями, каждый из которых можно будет вращать либо в одну, либо в другую сторону. Можно также подключить четыре двигателя, тогда каждый из них можно будет вращать только в одну сторону. Мы будем вращать двигатель в две стороны, поэтому используем схему подключения для двух двигателей.
Выходы 1 и 9 предназначены для включения и выключения двигателей, если их подключить к выходам Arduino с ШИМ, то можно управлять скоростью вращения двигателей. Выходы 2 и 7, и 10 и 15 управляют направлением вращения двигателей, первого и второго соответственно (парами). Если на выход 2 подать HIGH, а на выход 7 LOW, то мотор будет вращаться в одну сторону, наоборот — в другую сторону. Если на эти выходы подать одинаковый сигнал (LOW или HIGH) то мотор вращаться не будет. То же и для второго двигателя. К выходам 3 и 6 подключается один мотор (напряжением на этих выходах управляют 2-й и 7-й), к выходам 10 и 15 — второй мотор. Выход 8 соединяют с положительным полюсом источника питания двигателей. Выход 16 — с положительным полюсом источника питания микросхемы (5В). Выходы 4, 5, 12, 13 подключаются на «землю», они же используются для теплоотвода от схемы (в нашем случае нагрев был небольшой, так как подлючили только один двигатель и не надолго, а вообще греется она хорошо, может понадобиться радиатор).
Схема, которую мы будет собирать с использованием H-Bridge L293D:
В схеме присутствуют два выключателя. Один для включения и выключения моторчика (если мотор включен, то нажатием на кнопку он выключается, если выключен, то нажатием включается). Второй — для изменения направления вращения двигателя. Потенциометр используется для регулировки скорости двигателя.
Собираем схему
Собрать такую схему было, конечно, не очень просто. Но написать программу, оказалось труднее, и заняло это гораздо больше времени.
const int motorenablepin=9;//пин для пуска мотора const int directionpin1=3;//первый пин для направления мотора const int directionpin2=2;//второй пин для направления мотора const int onoffswitchpin=5;//пин с кнопкой для пуска/останова мотора const int directionswitchpin=4;//пин с кнопкой для смены направления мотора const int potens=A0;//пин с потенциометром int potensVal=0;//значение с потенциометра //переменные для отражения текущего состояния мотора int onoffswitchSt=0;//текущее состояние кнопки для пуска мотора int directionswitchSt=0;//текущее состояние кнопки для смены направления мотора int previosonoffswitchst=0;//предыдущее состояние кнопки запуска мотора (надо для определения смены состояния кнопки int motorenabled=0;//запущен ли в текущий момент мотор int motorSp=0;//скорость мотора int motordirection=1;//направление мотора int prevdirectionswitchpinst=0;//предыдущее состояние кнопки смены направления //мотора (надо для определения смены состояния кнопки void setup(){ pinMode (motorenablepin,OUTPUT);//инициализация пинов pinMode (directionpin1, OUTPUT); pinMode (directionpin2,OUTPUT); pinMode (onoffswitchpin,INPUT); pinMode (directionswitchpin,INPUT); digitalWrite(motorenablepin,LOW);//выключаем мотор на всякий случай } void loop (){ onoffswitchSt=digitalRead(onoffswitchpin);//текущее состояние кнопки запуска мотора delay(1); directionswitchSt=digitalRead(directionswitchpin );//текущее состояние кнопки смены направления мотора potensVal= analogRead(potens);//считываем значение потенциометра motorSp=map(potensVal,0,1023,0,254);//преобразуем значение в скорость мотора if (onoffswitchSt != previosonoffswitchst){//если состояние кнопки поменялось if(onoffswitchSt==HIGH){//и она нажата motorenabled=!motorenabled;//меняем состояние мотора на противоположное } previosonoffswitchst=!previosonoffswitchst;//меняем предыдущее состояние кнопки на противоположное } if (directionswitchSt != prevdirectionswitchpinst){//аналогично для направления вращения if(directionswitchSt==HIGH){ motordirection=!motordirection; } prevdirectionswitchpinst=!prevdirectionswitchpinst; } //в зависимости от значения переменной, определяющей состояние мотора запускаем //мотор с нужной скоростью или останавливаем его if(motorenabled==1){ analogWrite(motorenablepin,motorSp); } else{ digitalWrite(motorenablepin,LOW); } //в зависимости от значения переменной, определяющей направление вращения мотора устанавливаем направление if(motordirection==1){ digitalWrite(directionpin1,HIGH); digitalWrite(directionpin2,LOW); } else{ digitalWrite(directionpin1, LOW); digitalWrite(directionpin2,HIGH); } }
Когда собрана схема и написана программа, можно сделать тестовый пуск моторчика. И так, с помощью первой кнопки мы пускам/останавливаем мотор, потенциометром регулируем скорость вращения, второй кнопкой управляем направлением вращения. мотора.
Теперь осталось собрать непоследственно сам Зоотроп. Используем диск, который мы делали для предыдущего проекта. Приделаем к нему цилиндр с прорезями и картинки.
На этом видео можно попытаться увидеть как работает Зоотроп.
Вертушка с моторчиком
В этом проекте Arduino будет управлять вращающейся с помощью моторчика вертушки.
Управлять моторами с помощью Arduino намного сложнее чем управлять светодиодами. Во-первых, моторы требуют больше тока чем может дать Arduino. Во-вторых, моторы могут сами генерировать электрический ток за счет являния, называемого индукцией, и этот ток может повредить элементы схемы. Однако, моторы приводят в движение разные вещи, и от этого проекты становяться интереснее. В общем оно того стоит.
Для того, чтобы что-то двигать, требуется много энергии. Кроме того, что моторы требуют больше тока, чем дает Arduino (только 40мА на цифровом выходе), некоторым так же нужно более высокое напряжение. В момент начала движения или при высоких нагрузках, потребление тока также может значительно возрастать.
С помощью транзисторов, подсоединенных к Arduino можно решить эту проблему. Транзисторы позволяют управлять большим током и источниками питания с высоким напряжением с помощью небольшого управляющего тока, который уже может идти от Arduino. Существует можество разновидностей транзисторов, но принцип действия у всех одинаковый. В нашем примере транзистор лучше представить как цифровой выключатель. Есть подать напряжение на один из выходов транзистора (затвор), то он замыкает два других выхода (сток и исток). Таким образом можно включать и выключать высоковольтные и высокоамперные моторы при помощи Arduino.
Электромотор — это индуктивное устройство. Индукция — это являние возникновения изменяющегося магнитного поля вокруг провода, по которому протекает изменяющийся электрический ток. Внутри электромотора есть катушка с плотно намотанным на нее медным проводом. Когда на мотор подается электрический ток, внутри этой катушки возникает магнитное поле. Это магнитное поле заставляет вал двигателя вращаться.
В обратную сторону индукция тоже работает: мотор может вырабатывать электричество, если вращать его вал. Если подключить к выходам мотора светодиод и начать вращать вал двигателя, то диод будет светиться. Так можно превратить мотор в маленький электрогенератор.
Если прекратить подавать электричество, то он по инерции продолжает какое-то время вращаться. Пока он вращается, он вырабатывает электрический ток, противоположный по направлению тому, который его вращал. Этот ток называют «обратным током», он может вывести из строя транзистор. Чтобы избежать этого, параллельно с мотором надо подключить диод и обратный ток будет проходить через диод.
С учетом всего вышесказанного схема для управления мотором получается такая:
Поскольку для мотора требуется много энергии, он питается от дополнительного источника — 9-ти вольтовой батерейки. И так получается, что управление транзистором производится с помощью Arduino, а сам мотор запитан от дополнительного мощного источника электроэнергии. Обязательное правило, которое следует соблюдать если в схеме используется несколько источников энергии — минусы всех источников должны быть соединены вместе.
Теперь, когда макет готов, осталось написать программу:
int motor=9; //пин управления мотором int switchButn=2; //кнопка включения мотора int switchButnVal=0;//значение считанное с кнопки включения мотора void setup(){ Serial.begin(9600); pinMode (switchButn,INPUT); pinMode(motor,OUTPUT); } void loop(){ switchButnVal=digitalRead(switchButn);//считываем состояние кнопки Serial.println(switchButnVal); if(switchButnVal==HIGH){ digitalWrite(motor,HIGH);//если кнопка нажата включаем мотор } else{ digitalWrite(motor,LOW);//если не нажата — выключаем } }
Работает вот так:
Чтобы за вращением наблюдать было интереснее, мы сделали деревянный диск, наклеили на него разноцветный рисунок, и подсоединили к мотору.
Если скорость вращения мотора уменьшить, то на диске с рисунком, который крутиться, можно увидеть интересный оптический эффект. Для управления скоростью вращения в схему добавили потенциометр, и на затвор транзистора подаем напряжение от 0 до 5В, в зависимости от положения потенциометра. Программа чуть-чуть увеличилась.
int motor=9; //пин управления мотором int switchButn=2; //кнопка включения мотора int switchButnVal=0;//значение считанное с кнопки включения мотора int pot=A0; // пин с потенциометром int potVal=0;//значение считанное с потенциометра int Val=0;//значение выдаваемое на пин управления мотором void setup(){ Serial.begin(9600); pinMode (switchButn,INPUT); pinMode(motor,OUTPUT); } void loop(){ potVal=analogRead(pot);//читаем значение с потенциометра Val=map(potVal,0,1023,0,254); // в значение для вывода switchButnVal=digitalRead(switchButn);//считываем состояние кнопки Serial.println(switchButnVal); if(switchButnVal==HIGH){ analogWrite(motor,Val);//если кнопка нажата включаем мотор } else{ digitalWrite(motor,LOW);//если не нажата — выключаем } }