InProgress
УПРАВЛЕНИЕ КОЛЛЕКТОРНЫМ ДВИГАТЕЛЕМ
Обсуждение данной темы
Задачи, отрабатываемые в рамках данной темы (в порядке предполагаемой реализации):
- управленние коллекторным двигателем постоянного тока контроллером Atmel AVR с использованием ШИМ;
- организация обратной связи по частоте вращения;
- управление двигателем по командам от внешнего контроллера или компьютера
- по интерфейсу I2C или TWI;
- по интерфейсу RS-485.
Чтобы работа над данной темой была практически полезной, сделаем ее а рамках тем РоботДляКросса или
РоботДляСумо.
В качестве базового возьмем один из наиболее часто используемых для этой цели контроллеров - ATtiny2313?. При небольной стоимости и габаритах, он обладает вполне достаточными ресурсами для управления парой коллекторынх двигателей. Программа, написанная для ATtiny2313? с минимальными переделками сможет быть использована на контроллерах семейства megaAVR.
Чтобы сократить время на разработку, возьмем готовый модуль на базе ATtiny2313?, удовлетворяющий нашим требованиям:
Характеристики модуля следующие:
- Рабочее напряжение от 12 до 50В
- Рабочий ток 10A, импульсный до 30А (до 10мс)
- Реверсивное управление, рекуперация при торможении
- 6 входов/выходов для подключения внешних устройств
- Два управляемых выхода (открытый сток) на 4А.
- Разъем интерфейса RS-485
Схема контроллера:
ML2313-485.pdf
Выбор данного контроллера обесловлен следующими соображениями:
- Здесь достаточно типовая схема включения tiny2313, а суть данного открытого проекта - в первую очередь разработка софта;
- Те, кто хочет участвовать в проекте, но не хочет или не может делать контроллер самостоятельно, сможет воспользоваться готовым модулем.
Использование ШИМ для управления коллекторным двигателем
Частоту вращения коллекторного двигателя обычно регулируют изменяя подаваемое на него напряжение. При постоянном значении напряжения источника питания, напряжение на двигателе можно менять изменяя сопротивление в цепи двигателя, к примеру, с помощью реостата или транзистора. Однако такой способ при управлении мощными приводами приводит к выделению большой тепловой мощности на сопротивлении и снижению КПД системы.
Повысить КПД можно, подавая на двигатель полное напряжение, но на ограниченное время. Если это делать с большой частотой, то при постоянной частоте, меняя длительность включения, можно фактически менять среднее напряжение, подаваемое на двигатель. Такой способ управления двигателем называется ШИМ — широтно-импульсная модуляция.
Подробнее о регулировании частоты вращения двигателя с помощью ШИМ см.
статью на старой версии сайта. Там же приводится алгоритм программной реализации ШИМ.
Микроконтроллер ATtiny1213 имеет четыре линии, на которых ШИМ может быть сгенерирован без непосредственного участия процессора, "аппаратно".
Аппаратная реализация ШИМ дает безусловные преимущества перед программной, так как значительно разгружает процессор, освобождает память от лишнего кода, а также дает больше возможностей при генерации ШИМ. Дальнейшее описание будет посвящено именно аппаратной реализации ШИМ в контроллерах семейства AVR.
Упомянутые четыре линии по две связаны с одним из двух таймеров микроконтроллера. Первым таймером управляются выводы pwm_a0 (PB2?) и pwm_b0 (PD5?), вторым - pwm_a1 (PB3?) и pwm_b1 (PB4?).
Каждая из линий, на которой генерируется ШИМ, в свою очередь подключена к драйверу МОП-транзисторов, управляющему соответствующим полумостом. Линии pwm_a0 и pwm_b0 управляют первым двигателем, pwm_a1 и pwm_b1 - вторым.
Драйвер МОП-транзисторов начинает работать при подаче на него сигнала разрешения работы (линии ena_0 (PB0?) и ena_1 (PB1?), соответственно). Если работа драйвера разрешена, то при высоком уровне на его входе верхний МОП-транзистор полумоста открыт, а нижний закрыт, а при низком уровне - наоборот, верхний закрыт, а нижний открыт. Если работа драйвера запрещена, то оба транзистора закрыты.
Таким образом, если на линии pwm_a высокий уровень, а на pwm_b - низкий, то двигатель будет вращаться в одну сторону, если на pwm_a низкий, а на pwm_b высокий, то в противоположную. Если на обеих линиях одинаковый уровень сигнала, то мотор будет стоять.
Такая логика работы позволяет управлять двигателем за счет разности фаз сигналов на линиях pwm_a и pwm_b. В контроллере ATtiny1213 такую разность фаз очень удобно получить, просто меняя скважность на линиях ШИМ, управляемых от одного таймера.
Другими словами, если сигналы на линиях pwm_a и pwm_b имеют одинаковую скважность (т.е. синхронны), то мотор остановлен, если скважность pwm_a больше, чем pwm_b, то мотор вращается в одну сторону, если pwm_b больше, чем pwm_a, то в другую (см. рис: pwm_a - канал 1 (нижний), pwm_b - канал 2 (верхний), в середине (М) - результирующий сигнал).
Пример реализации на ассемблере
Продолжение следует
Пример реализации на Си
Скважность ШИМ (т.е., фактически, напряжение, выдаваемое на двигатель) будем задавать с помощью функции
void setPWM(unsigned char motorDescriptor, char value);
Диапазон значений мощности value установим от -99 до 99, где отрицательные значения соответствуют реверсу мотора. ( Примечание: конструкция данного контроллера не позволяет задать 100%-ный ШИМ).
Значения motorDescriptor зададим мнемониками, определяемыми через директивы препроцессора:
- M1 – первый мотор;
- M2 – второй мотор;
- M0 – команда адресована обоим моторам одновременно.
Примечание: применение групповых команд в ответственных системах не рекомендуется!
Таким образом, вызовы функции могут выгядить так:
setPWM(M0,99); – оба мотора вперед, максимальная мощность;
setPWM(M1,-50); – первый мотор назад, скважность ШИМ 50%;
setPWM(M2,0); – отключить второй мотор.
Подпрограмма задания ШИМ, реализующая описанный выше алгоритм, может выглядить, например, так:
void setPWM(unsigned char motorDescriptor, char value){
if (value > MaxPWM) value = MaxPWM;
if (value < -MaxPWM) value = -MaxPWM;
if (motorDescriptor == 0) {
OCR0A = (unsigned char) (midPoint + value);
OCR0B = (unsigned char) (midPoint - value);
} else {
OCR1AL = (unsigned char) (midPoint + value);
OCR1BL = (unsigned char) (midPoint - value);
}
}
Чтобы данная процедура была работоспособной, следует до ее вызова определить значения M0, M1, midPoint и MaxPWM, например так:
#define M0 0
#define M1 1
#define MaxPWM 120
char midPoint=128;
Значение MaxPWM определяет максимальную скважность ШИМ, поскольку при попытке полностью открыть верхний ключ перестанет заряжаться бутстропный конденсатор (см. документацию к драйверу МОП-транзисторов), так что задать 100%-ную скважность нельзя.
Кроме того, предварительно следует "запустить" ШИМ, к примеру, так:
void initPWM(void){
// Инициализация выходых линий
SetBit(DDRB, PB2); // OC0A - pwm a0 (pin14)
SetBit(DDRD, PD5); // OC0B - pwm b0 (pin9)
SetBit(DDRB, PB0); // ena 0
SetBit(DDRB, PB3); // OC1A - pwm a1 (pin15)
SetBit(DDRB, PB4); // OC1В - pwm b1 (pin16)
SetBit(DDRB, PB1); // ena 1
TCNT0 = 0;
TCNT1 = 0;
TCCR0A = _BV(WGM00) // 8-bit Phase Correct PWM
| _BV(COM0A1) // 0 при TCNT1=0 и 1 при TCNT1=OCR1A
| _BV(COM0B1);
TCCR0B = _BV(CS01); // частота CK/8
TCCR1A = _BV(WGM10) // 8-bit Phase Correct PWM
| _BV(COM1A1) // 0 при TCNT1=0 и 1 при TCNT1=OCR1A
| _BV(COM1B1);
TCCR1B = _BV(CS11); // частота CK/8
}
Таким образом, главная программа, использующая процедуру setPWM, будет выглядеть примерно так:
int main (void)
{
initPWM();
PORTB|= (1<<PB0); // motor 0 enable
PORTB|= (1<<PB1); // motor 1 enable
..........
setPWM(M0, ...);
setPWM(M1, ...);
..........
return (0);
}
Обратная связь по частоте вращения
Поскольку все моторы имеют некоторые индивидуальные отличия, при подаче одинакового напряжения они могут вращаться с разной скоростью. Чтобы точно управлять частотой вращения двигателя, необходимо ввести обратную связь, т.е. дать контроллеру управления двигателем информцию о фактической частоте его вращения.
В нашем случае высокая точность регулирования не требуется, поэтому будем мерять непосредственно частоту вращения колес. Для этого установим на выходном валу редуктора энкодер, состоящий из зубчатого колеса (см. фото) и оптического датчика.
В качестве датчика возьмем широко распространенный Vishay TCUT1200, используемый, к примеру, в компьютерных мышах и трекболах. Он имеет один излучатель, два приемника сдвинутых на 0.8мм, корпус SMD, расстояние между щеками 2мм. Наличие двух каналов дает возможность определять не только частоту, но и направление вращения.
Используемый контроллер имеет две линии, ориентированых на подключение оптических энкодеров или датчиков, работа с которыми требует обработки внешних прерываний - ein0(PD2/INT0) и ein1(PD3/INT1). Таким образом, контроллер дает возможность ввести сигнал с одного двухканального энкодера (т.е. с определением направления вращения), либо с двух однаканальных (т.е. без определения направления вращения).
Поскольку на данной платформе установлены моторедукторы с червячной передачей, проскальзывание колес невозможно и направление вращения колес однозначно определяется направлением вращения мотора. Поэтому в определении направления вращения колес по сигналу энкодера нет неодходимости и мы используем входы ein0(PD2/INT0) и ein1(PD3/INT1) для ввода сигналов от левого и правого моторов (и колес!), соответственно. Еще две линии для подключения датчиков (rin0 (PD6) и rin1(PD3)) остаются пока свободными для последующего использования.
Пример реализации на Си
Частоту вращения двигателя будем задавать с помощью функции
void setMotorSpeed(unsigned char motorDescriptor, char value);
Диапазон значений скорости value (аналогично заданию мощности) установим от -99 до 99, где отрицательные значения соответствуют реверсу мотора.
Значения motorDescriptor зададим мнемониками, определяемыми через директивы препроцессора:
- M1 – первый мотор;
- M2 – второй мотор;
- M0 – команда адресована обоим моторам одновременно.
Таким образом, вызовы функции могут выгядить так:
setMotorSpeed(M0,99); – оба мотора вперед, максимальная скорость;
setMotorSpeed(M1,-50); – первый мотор назад, частота вращения 50% от максимальной;
setMotorSpeed(M2,0); – остановить второй мотор.
Под нагрузкой фактическое значение скорости вращения может отличаться от заданного. Фактическое значение скорости можно получить с помощью функции
char getMotorSpeed(void);
Наличие датчика обратной связи позволяет не только поддерживать заданную скорость, но управлять положением вала двигателя. Функции:
void setPosition(unsigned char motorDescriptor, int value);
int getPosition(unsigned char motorDescriptor);
void clearPosition(unsigned char motorDescriptor);
дают возможность, соответственно, установить требуемое положение вала двигателя, считать фактическое положение вала, а также обнулить значение текущего положения.
Поскольку в данном случае контроллер управляет ходовыми двигателями мобильного робота, функция setPosition по сути позволяет задать значение пути, кторое должен пройти робот, getPosition - узнать фактически пройденный на данный момент путь, а clearPosition - сбросить соответствующий счетчик пути.
Продолжение следует
Прием команд от внешнего управляющего устройства
На базе данного контроллера, с использованием описываемого подхода и приемов, вполне можно построить несложный автономный робот. Однако ресурсы контроллера ограничены, поэтому логичнее использовать его именно в качестве интеллектуального контроллера управления двигателями, т.е. модуля, который получает команду от внешнего управляющего устройства и обеспечивает ее исполнение "наилучшим образом".
Данный контроллер может принимать внешние команды через USI по интерфейсу I2C или TWI или через UART и далее через по интерфейсам RS-232, RS-485 и т.п.
В любом случае, для управления контроллером используется последовательная линия связи, поэтому возьмем за основу протокола обмена данными с контроллером широко распространенный протокол последовательной передачи данных MODBUS/ASCII компании Modicon, который поддерживают многие фирмы-производители контроллеров технологического оборудования.
Протокол предполагает одно активное (ведущее) устройство в линии (master), которое может обращаться к одному или нескольким подчиненным (ведомым) устройствам (slave), обращаясь к ним по уникальному, в пределах данной линии, адресу. Синтаксис команд (сообщений) протокола позволяет адресовать 254 устройства.
Инициатива проведения обмена всегда исходит от ведущего устройства (мастера). Мастер выдает в линию запрос (сообщение) – последовательность байт определенного формата, – и переходит в состояние прослушивания линии связи. Ведомые устройства прослушивают линию связи. Обнаружив запрос, пришедший в его адрес, ведомое устройство предпринимает необходимые действия и отвечает на запрос.
В MODBUS/ASCII каждое сообщение начинается с "двоеточия" (':', код ASCII 3A) и заканчивается последовательностью "возврат каретки – перевод строки" (CRLF, код ASCII 0D и 0A). Допустимые символы для передачи – это шестнадцатеричные цифры 0-9, A-F.
Типичный формат сообщения в MODBUS/ASCII имеет вид:
| Старт | Адрес | Функция | Данные | Контрольная сумма | Конец |
| 1 символ | 2 символа | 2 символа | n символов | 2 символа | 2 символа |
| ":" | | | | | "CRLF" |
Каждое из подчиненных устройств в сети непрерывно отслеживает символ "двоеточие". Когда он принят, устройство декодирует следующее поле сообщения (поле адреса) и определяет, адресовано ли сообщение именно ему. Если да – интерпретирует функцию и данные, если нет – продолжает «слушать» линию и т.д.
Для того чтобы сделать возможным управление нашим контроллером непосредственно с компьютера вручную через программу HyperTerminal, откажемся от контрольной суммы, чтобы ее не приходилось ее вычислять каждый раз (однако в расчете на перспективу сохраним данное поле). Кроме того, для упрощения программирования и отладки сделаем общую длину сообщения (команды) постоянной:
| Старт | Адрес | Функция | Данные | Контрольная сумма | Конец |
| 1 символ | 2 символа | 2 символа | 2 символа | 2 символа | 2 символа |
| ":" | | | | "00" | "CRLF" |
Наконец, расширим число возможных символов в полях «Адрес» и «Функция» до 0-9, A-Z.
Адреса устройств контроллера зададим так:
- M1 – первый мотор;
- M2 – второй мотор;
- M0 – команда адресована обоим моторам одновременно.
Зададим также перечень функций устройств контроллера и их допустимые значения:
- PF – задать мощность при вращении вперед
- PR – задать мощность при вращении назад
- SF – задать частоту вращения вперед
- SR – задать частоту вращения назад
Конечно же, команды задания частоты вращения будут корректны только при наличии обратной связи.
Диапазон значений скорости и мощности установим от 00 до 99.
Таким образом, команды контроллеру могут выгядить так:
:M0PF9900CRLF – оба мотора вперед, 100% мощности;
:M1SR5000CRLF – первый мотор назад, частота вращения 50% от максимальной;
:M2SF0000CRLF – остановить второй мотор;
:M2PF0000CRLF – отключить второй мотор.
Примечание: при управлении контроллером из программы HyperTerminal, в ее настройках следует установить флажок Дополнять символы перевода каретки (CR) переводами строк (LF). Тогда после набора команды достаточно будет просто нажать <Ввод>:
:M0PF9900<Enter>
:M1SF5000<Enter>
:M2SF0000<Enter>
:M2PF0000<Enter>
Подчиненное устройство, приняв и обработав команду, выдает в линию подтверждение в виде пакета примерно того же формата, что и запрос:
| Старт | Отправитель | Данные1 | Данные2 | Контрольная сумма | Конец |
| 1 символ | 2 символа | 2 символа | 2 символа | 2 символа | 2 символа |
| ":" | | | | "00" | "CRLF" |
Для обозначения отправителя, используем те же мнемоники, но в нижнем регистре, т.е.:
- m1 – первый мотор;
- m2 – второй мотор;
Примечание: при групповых командах (в нашем случае "М0") отсылка подтверждения не производится. Применение групповых команд в ответственных системах не рекомендуется!
В поле Данные1 возвращаем текущее значение ШИМ, в поле Данные1 - текущее значение частоты вращения; и то, и другое в процентах. [Это обсуждается!]
Продолжение следует