|
Манипулятор «рука» Задача: Разработать систему дистанционного управления многозвенным манипулятором типа «рука».Описание первоначального устройства Изначально манипулятор Robotic Arm Edge фирмы OWI KIT управлялся проводным пультом управления, который менял полярность питания каждого мотора в звеньях устройства, т.е. представлял из себя набор механических реле. Общая схема создаваемой системы управления Механические реле заменяются драйверами L293DNE, управляемые логическими слаботочными сигналами от микроконтроллера и питаемые внешним источником питания. Пользовательские команды отправляются с ios устройства посредствам беспроводной сети wifi в виде сообщений протокола OSC (Open Sound Control), который имеет простую структуру. Затем сообщения обрабатываются на серверной части, установленной на компьютере, и преобразуются в другой формат данных, удобный для передачи в микроконтроллер. Исходя из выбранной схемы, возникают две главные задачи в плане программирования: 1.Обеспечение передачи команд с пользовательского устройства (клиента) на персональный компьютер (сервер) и передача их на микроконтроллер. 2.Обеспечение связи между микроконтроллером и компьютером для получения команд и последующая интерпретация команд в управляющие сигналы на драйверы. Клиент отправляет сообщения на сервер по протоколу OSC. Приложение на сервере в свою очередь непрерывно опрашивает один из портов, на который должны поступить команды и при их получении передаёт соответствующее команды на arduino. Серверное приложение написано на языке processing. // импорт библиотеки для работы с COM-портом import processing.serial.*; // импорт библиотек для работы с протоколом OSC и работы с сетью import oscP5.*; import netP5.*; Serial s; OscP5 oscP5; NetAddress myRemoteLocation; // два управляющих байта, отправляемые в arduino byte sendBytes[]={0, 0}; // начальная инициализация void setup() { // установка работы COM-порта s = new Serial(this, "COM5", 9600); size(400,400); // создание объекта OscP5 для прослушивания порта 12000 и обработки приходящих сообщений oscP5 = new OscP5(this, 12000); } void draw() { background(0); } // обработка сообщений, инициализируемая методом oscEvent void oscEvent(OscMessage theOscMessage) { // получение сообщения String adr = theOscMessage.addrPattern(); // выделение значения параметра int value = int(theOscMessage.get(0).floatValue()); // выделение из сообщения номера двигателя int motor = adr.charAt(adr.length()-2)-48; // выделение из сообщения направления поворота int dir = (int(adr.charAt(adr.length()-1) == \'R\')+1)*value; // вывод сообщения в консоль (для отладки) println(adr+" "+value); print((motor-1)*2+dir); println(value); // отдельная обработка управления светодиодом if(adr.length() == 6) { upr[5] = byte(value); } else { upr[motor-1] = byte(dir); } // формирование двух управляющих байтов sendBytes[0] = 0; for(int i=0; i<4; i++) sendBytes[0] = (byte)(sendBytes[0]|(upr[i] << 2*i)); sendBytes[1] = upr[5-1]; sendBytes[1] |= (upr[6-1] << 2); // запись управляющих байтов в COM-порт (arduino) s.write(sendBytes); println(sendBytes); Программа для микроконтроллера (располагаемого на плате arduino) должна принимать два управляющих байта со стороны компьютера, выделить из них данные для каждого мотора и светодиода и выставить на выходных портах соответствующее логические уровни. // установка соответствий между выходами платы и переменными int m1EN = 13; int m1DIR = 12; int m2EN = 11; int m2DIR = 10; int m3EN = 9; int m3DIR = 8; int m4EN = 7; int m4DIR = 6; int m5EN = 5; int m5DIR = 4; int LED_EN= 3; int byteNum = 0; // принимаемые управляющие байты byte incomingBytes[2]; int controlIndex[] = {m1DIR, m2DIR, m3DIR, m4DIR, m5DIR, LED_EN}; byte control[6]; // начальная инициализация void setup() { // включение серийного порта Serial.begin(9600); // настройка управляющих пинов как цифровых и выходных for(int i=0; i<6; i++) pinMode(controlIndex[i], OUTPUT); pinMode(m1EN, OUTPUT); pinMode(m2EN, OUTPUT); pinMode(m3EN, OUTPUT); pinMode(m4EN, OUTPUT); pinMode(m5EN, OUTPUT); } // главный цикл void loop() { // если получены данные по серийному порту while (Serial.available()) { // побайтное чтение incomingBytes[byteNum] = (byte)Serial.read(); byteNum++; } // если прочитаны оба байта if (byteNum > 1){ // выделение управляющих сигналов для каждого двигателя из полученных байт и формирование управляющего вектора control for(int i=0; i<4; i++) { control[i] = (incomingBytes[0] >> i*2)&3; } control[5-1] = incomingBytes[1]&3; control[6-1] = (incomingBytes[1] >> 2)&1; // обновление состояний управляющих выходов в соответствии с управляющим вектором reLoadStatus(); byteNum = 0; } } // обновление состояний управляющих выходов в соответствии с управляющим вектором void reLoadStatus() { // каждый двигатель находится в одном из трёх состояний for(int i = 0; i<5; i++) switch (control[i]) { // включен и вращается вправо case 1: digitalWrite(controlIndex[i], LOW); digitalWrite(controlIndex[i]+1, HIGH); break; // включен и вращается влево case 2: digitalWrite(controlIndex[i], HIGH); digitalWrite(controlIndex[i]+1, HIGH); break; // выключен case 0: digitalWrite(controlIndex[i]+1, LOW); break; } // состояния светодиода обрабатывается отдельно if(control[6-1] == 1) digitalWrite(controlIndex[6-1], HIGH); else digitalWrite(controlIndex[6-1], LOW); Силовая часть представлена на следующей схеме Способы подключения двигателя к драйверу описаны в datasheet к L293DNE. Общий вид Три драйвера L293DNE Соединение Arduino и драйверов Видео выполнил студент Плотицын А 4 курс (2011 год ) |