Регулирование скорости двигателя
- Войдите на сайт для отправки комментариев
Чт, 28/11/2019 - 17:40
Всем привет!
Пишу скетч на управление маленьким движком(24в) с отслеживанием скорости с инкрементального энкодера(600 имп/об х 2 канала = 1200 имп/об) на Уно. При вращении 1000 об/мин 328-я Атмега уже не успевает всё выводить,вопрос - почему? Оба канала завёл через встроенные аппаратные прерывания ардуинки и эта проблема ушла но, теперь эти прерывания останавливают остальную часть кода и я не могу даже дать задание на мосфет, не говоря уже о пид-регулировании.
Возможно как-то без прерываний нормально отследить скорость с двухканального энкодера или как-то обойти прерывания, чтобы параллельно выполнялась другая часть кода?
Можно написать код так, чтобы он не тормозил безбожно. Тогда все будет успевать
Сотни, а временами и тысячи импульсов на оборот используются в станках ЧПУ для точного позиционирования. Я полагаю, что Вы хотите управлять не положением, а скоростью вращения двигателя. Если интересуют скорости порядка 1000 об/мин, то с учетом динамики двигателя, тем более нагруженного, достаточно получать данные один раз за оборот, используя для измерения скорости не частоту, а период следования импульсов. Поделите аппаратно частоту с датчиков на несколько десятков и подайте на Ардуино. Если диапазон скоростей большой, то коэффициент деления аппаратных делителей можно изменять программно для сохранения точности измерений.
Возможно как-то без прерываний нормально отследить скорость с двухканального энкодера
У таймеров есть возможность тактироваться от внешних сигналов, причём всё будет аппаратно! Уотт туда и нужно смотреть.
ИМХО - 1200имп/оборот это явный перебор :)))
Благодарю за ответ.
Нужно аппаратно добавить некий делитель частоты. На его вход приходит 1200 имп x 1000 об/мин = 20 КГц , делитель делит к примеру на 100, правильно?
Благодарю за ответ.
Нужно аппаратно добавить некий делитель частоты. На его вход приходит 1200 имп x 1000 об/мин = 20 КГц , делитель делит к примеру на 100, правильно?
Да, идея такова. Коэффициент деления зависит от желаемой точности, скорости вращения и возможностей Ардуино. Если поддерживать нужно скорость в районе 1000 об/мин, то коэф. деления пусть будет 100, а если в районе 100 об/мин, то коэф. деления должен быть 10. Цифры условные, конкретика будет зависеть от того как будет справляться Ардуино, и как быстро и точно Вам нужно будет поддерживать скорость вращения.
Когда нужно поддерживать скорость в большом диапазоне, т.е. и 1000, и 100 об/мин, то аппаратный делитель делают с переменным коэф. деления, управляемым от самой Ардуино.
Если нужно поддерживать очень низкие скорости, например 1 об/мин, то подсчитывать импульсы долго, их мало приходит в единицу времени. Тогда подсчитывают не частоту следования импульсов, а период следования (время до каждого нового импульса).
Ну на совсем низких уместен редуктор)..Понятно!Есть вариант использовать две Ардуино - управлять с одной, а читать скорость и прочее с другой, но про делитель подумаем.
И всё-таки не пойму, как Атмега328 с тактовой частотой в 16 МГц не может справиться с 20 КГц...код элементарный, кручу рукой вал энкодера, а он выдаёт бред, может быть что-то в настройках ардуинки???
Ну на совсем низких уместен редуктор)..Понятно!Есть вариант использовать две Ардуино - управлять с одной, а читать скорость и прочее с другой, но про делитель подумаем.
И всё-таки не пойму, как Атмега328 с тактовой частотой в 16 МГц не может справиться с 20 КГц...код элементарный, кручу рукой вал энкодера, а он выдаёт бред, может быть что-то в настройках ардуинки???
А может быть что-то в коде обработчика прерывания?
PS я правда 20 килогерц не пробовал, но шимить пин INT и обрабатывать справляется (490 герц)
И это правильно! :)) Только если у вас есть готовый мотор-редуктор, то датчик скорости можно поставить только на низкооборотный выходной вал. :))
Сотни, а временами и тысячи импульсов на оборот используются в станках ЧПУ для точного позиционирования. Я полагаю, что Вы хотите управлять не положением, а скоростью вращения двигателя. Если интересуют скорости порядка 1000 об/мин, то с учетом динамики двигателя, тем более нагруженного, достаточно получать данные один раз за оборот, используя для измерения скорости не частоту, а период следования импульсов. Поделите аппаратно частоту с датчиков на несколько десятков и подайте на Ардуино. Если диапазон скоростей большой, то коэффициент деления аппаратных делителей можно изменять программно для сохранения точности измерений.
На оптических датчиках на диск датчика наносят две группы одинаковых рисок, сдвинутых одна относительно другой на полшага. Благодаря этому можно определить направление движения. Если нужно управлять только скоростью, то достаточно обрабатывать только один канал.
с прерываниями как раз работает, но у прерываний есть свойство - прерывать весь код и выполнять только свою функцию. А что если если весь код внести в функцию обработчика прерываний?..вариант, но геморно как-то
с прерываниями как раз работает, но у прерываний есть свойство - прерывать весь код и выполнять только свою функцию. А что если если весь код внести в функцию обработчика прерываний?..вариант, но геморно как-то
в прерывании надо быть наоборот очень недолго, вы бы код привели, что вы там в прерывании делаете?
По идее завести переменную U long и там её ++, в основной программе с ней оперировать
По идее завести переменную U long и там её ++
volatile U long.
без внешнего делителя и торможения в обработчике:
volatile uint32_t period; void setup() { pinMode(2, INPUT); attachInterrupt(0, intr256, RISING); } void intr256() { static uint32_t oldT=micros(); static uint32_t newT=0; static byte tick=1; tick++; if (tick==0) { newT=micros(); period=newT-oldT; oldT=newT; } } void loop() { }По идее завести переменную U long и там её ++
volatile U long.
ну так жеж как рубль, волатильные...
Вот код. Можно вращать вал энкодера от руки и он выдаст текущие обороты, но дать задание на силовой ключ с этого кода я уже не могу
// Код считывает импульсы с инкрементального энкодера(600 имп/об) при вращении вала, при помощи двух аппаратных прерываний, установленных на цифровых входах платы Arduino Uno - // D2(прерывание 0) и D3(прерывание 1) и подключенных к каналам А и B энкодера. Условием поворота вала энкодера по часовой стрелке является передний положительный фронт импульса // канала А при отсутствии импульса на канале B и передний положительный фронт канала B при наличии импульса на канале A ; против часовой стрелки - передний положительный фронт // канала B при отсутствии импульса на канале A и передний положительный фронт канала A при наличии импульса на канале B. При этом, код считает не 600 имп/об, а 1200, т.к. при // прохождении периода от переднего фронта импульса до переднего фронта следующего импульса в контроллер передаётся 2 сигнала - прописано в функциях aio() и ai1(). // В функции circle() рассчитывается формат одного оборота энкодера(0 - 1199 имп), в функции speed() из угла поворота рассчитывается значение частоты вращения в об/мин, выводится // в Serial - порт при помощи библиотеки <TimerOne.h> и внутреннего таймера Timer1 в Ардуино и на экран lcd - дисплея (библиотека <LiquidCrystal.h>), прописывется время опроса - 1 с. #include <TimerOne.h> #include <LiquidCrystal.h> LiquidCrystal lcd(4,5,6,7,8,9); volatile unsigned long counter = 0; int speedCnt, pred_speedCnt, rpm; void setup() { lcd.begin(16,2); Serial.begin (9600); pinMode(2, INPUT); pinMode(3, INPUT); pinMode(10, OUTPUT); digitalWrite(2, HIGH); digitalWrite(3, HIGH); attachInterrupt(0, ai0, RISING); attachInterrupt(1, ai1, RISING); } void loop() { //circle(); Timer1.initialize(1000000); Timer1.attachInterrupt(speed); } void ai0() { if(digitalRead(3)==LOW) { counter++; }else{ counter--; } } void ai1() { if(digitalRead(2)==LOW) { counter--; }else{ counter++; } } void circle() { if (counter > 1199) { counter = 0; } else if (counter < 0) { counter = 1199; } } void speed() { speedCnt = counter; rpm = (int(abs(speedCnt - pred_speedCnt)/3.333)*1*0.0175)*60/6.28; Serial.print('\t'); Serial.println(rpm); lcd.print(" "); lcd.setCursor(0,0); lcd.print("rpm"); lcd.setCursor(0,1); lcd.print(rpm); pred_speedCnt = speedCnt; }какой физический смысл рассчитывать куда мотор крутится, если интересуют только обороты?
Может есть смысл ограничиться простым частотомером от DIMAX в его генераторе примменён, на выходе частота в секунду, разделить на число импульсов на оборот и 60 и будут обороты, ну или просто накапливаем 1 секунду,запрещаем прерывания, рассчитываем, сбрасываем счетчик, разрешаем прерывания и так по кругу ну или затактировать счётчик от энкодера
/*************** Ч А С Т О Т О М Е Р **********************/ volatile unsigned int int_tic=0; float freq_tic=0; volatile unsigned long tic; ISR (TIMER1_OVF_vect){ int_tic++; } void freq_meter() { pinMode (5,INPUT); // вход сигнала T1 (only для atmega328) TCCR1B = (1<<CS10)|(1<<CS11)|(1<<CS12);//тактирование от входа Т1 delay(1000); TCCR1B=0; // Остановить счетчик tic= ((uint32_t)int_tic<<16) | TCNT1; //сложить что натикало int_tic=0; TCNT1 = 0; freq_tic =(float) tic/1000.0; }Благодарю за ответ.
Нужно аппаратно добавить некий делитель частоты. На его вход приходит 1200 имп x 1000 об/мин = 20 КГц , делитель делит к примеру на 100, правильно?
Да, идея такова. Коэффициент деления зависит от желаемой точности, скорости вращения
без внешнего делителя, с переменным коэффициентом деления:
volatile uint32_t period; byte startCount = 250; void setup() { pinMode(2, INPUT); attachInterrupt(0, intr256, RISING); } void intr256() { static uint32_t oldT = micros(); static uint32_t newT = 0 ; static byte tick = startCount; tick++; if (tick==0) { newT = micros(); period = newT-oldT; oldT = newT; tick = startCount; } } void loop() { }да, на данном этапе направление вращения не принципиально. Не могли бы вы дать ссылку на полный код, с этим частотомером,а то знаний по си уже не хватает )))
да, на данном этапе направление вращения не принципиально. Не могли бы вы дать ссылку на полный код, с этим частотомером,а то знаний по си уже не хватает )))
полный код писать надо.
Неполный может начинаться примерно так:
volatile uint16_t tick = 0; uint16_t adjustmentInterval = 100; uint16_t displayInterval =1000; void adjust() { // регулируем обороты static uint16_t newTick, preTick; /* что-то вычисляем и изменяем напряжение на моторе */ } void display() { static uint16_t newTick, preTick; /* вычисляем и выводим обороты на дисплей */ } void setup() { pinMode(2, INPUT); attachInterrupt(0, intrTick, RISING); } void intrTick() { tick++; } uint32_t prevMillisAdjust = millis(); uint32_t prevMillisDisplay = millis(); uint32_t newMillis =0; void loop() { newMillis = millis(); if (newMillis - prevMillisAdjust > adjustmentInterval) { prevMillisAdjust = newMillis; adjust(); } if (newMillis - prevMillisDisplay > displayInterval) { prevMillisDisplay = newMillis; display(); } }да, на данном этапе направление вращения не принципиально. Не могли бы вы дать ссылку на полный код, с этим частотомером,а то знаний по си уже не хватает )))
куда уж полнее ))) на 5 пин подаем импульсы, в переменной получаем их частоту, код заимствован из генератора от DIMAX
Спасибо, буду ковырять)