Регулирование скорости двигателя

kim84sm
Offline
Зарегистрирован: 28.05.2019

Всем привет!

   Пишу скетч на управление маленьким движком(24в) с отслеживанием скорости с инкрементального энкодера(600 имп/об х 2 канала = 1200 имп/об) на Уно. При вращении 1000 об/мин 328-я Атмега уже не успевает всё выводить,вопрос - почему? Оба канала завёл через встроенные аппаратные прерывания ардуинки и эта проблема ушла но, теперь эти прерывания останавливают остальную часть кода и я не могу даже дать задание на мосфет, не говоря уже о пид-регулировании.

   Возможно как-то без прерываний нормально отследить скорость с двухканального энкодера или как-то обойти прерывания, чтобы параллельно выполнялась другая часть кода?

rkit
Offline
Зарегистрирован: 23.11.2016

Можно написать код так, чтобы он не тормозил безбожно. Тогда все будет успевать

Duino A.R.
Offline
Зарегистрирован: 25.05.2015

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

-NMi-
Онлайн
Зарегистрирован: 20.08.2018

kim84sm пишет:

Возможно как-то без прерываний нормально отследить скорость с двухканального энкодера

У таймеров есть возможность тактироваться от внешних сигналов, причём всё будет аппаратно! Уотт туда и нужно смотреть.

ИМХО - 1200имп/оборот это явный перебор :)))

kim84sm
Offline
Зарегистрирован: 28.05.2019

  Благодарю за ответ.

   Нужно аппаратно добавить некий делитель частоты. На его вход приходит 1200 имп x 1000 об/мин = 20 КГц , делитель делит к примеру на 100, правильно?

Duino A.R.
Offline
Зарегистрирован: 25.05.2015

kim84sm пишет:

  Благодарю за ответ.

   Нужно аппаратно добавить некий делитель частоты. На его вход приходит 1200 имп x 1000 об/мин = 20 КГц , делитель делит к примеру на 100, правильно?

Да, идея такова. Коэффициент деления зависит от желаемой точности, скорости вращения и возможностей Ардуино. Если поддерживать нужно скорость в районе 1000 об/мин, то коэф. деления пусть будет 100, а если в районе 100 об/мин, то коэф. деления должен быть 10. Цифры условные, конкретика будет зависеть от того как будет справляться Ардуино, и как быстро и точно Вам нужно будет поддерживать скорость вращения.

Когда нужно поддерживать скорость в большом диапазоне, т.е. и 1000, и 100 об/мин, то аппаратный делитель делают с переменным коэф. деления, управляемым от самой Ардуино.

Если нужно поддерживать очень низкие скорости, например 1 об/мин, то подсчитывать импульсы долго, их мало приходит в единицу времени. Тогда подсчитывают не частоту следования импульсов, а период следования (время до каждого нового импульса).

kim84sm
Offline
Зарегистрирован: 28.05.2019

  Ну на совсем низких уместен редуктор)..Понятно!Есть вариант использовать две Ардуино - управлять с одной, а читать скорость и прочее с другой, но про делитель подумаем.

  И всё-таки не пойму, как Атмега328 с тактовой частотой в 16 МГц не может справиться с 20 КГц...код элементарный, кручу рукой вал энкодера, а он выдаёт бред, может быть что-то в настройках ардуинки???

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

kim84sm пишет:

  Ну на совсем низких уместен редуктор)..Понятно!Есть вариант использовать две Ардуино - управлять с одной, а читать скорость и прочее с другой, но про делитель подумаем.

  И всё-таки не пойму, как Атмега328 с тактовой частотой в 16 МГц не может справиться с 20 КГц...код элементарный, кручу рукой вал энкодера, а он выдаёт бред, может быть что-то в настройках ардуинки???

А может быть что-то в коде обработчика прерывания?

PS я правда 20 килогерц не пробовал, но шимить пин INT и обрабатывать справляется (490 герц)

Duino A.R.
Offline
Зарегистрирован: 25.05.2015

kim84sm пишет:
Ну на совсем низких уместен редуктор)..Понятно!

И это правильно! :)) Только если у вас есть готовый мотор-редуктор, то датчик скорости можно поставить только на низкооборотный выходной вал. :))

SLKH
Offline
Зарегистрирован: 17.08.2015

Duino A.R. пишет:

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

и нужно ли для измерения/регулирования только скорости вообще обрабатывать второй канал?

Duino A.R.
Offline
Зарегистрирован: 25.05.2015

SLKH пишет:
и нужно ли для измерения/регулирования только скорости вообще обрабатывать второй канал?

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

kim84sm
Offline
Зарегистрирован: 28.05.2019

с прерываниями как раз работает, но у прерываний есть свойство - прерывать весь код и выполнять только свою функцию. А что если если весь код внести в функцию обработчика прерываний?..вариант, но геморно как-то

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

kim84sm пишет:

с прерываниями как раз работает, но у прерываний есть свойство - прерывать весь код и выполнять только свою функцию. А что если если весь код внести в функцию обработчика прерываний?..вариант, но геморно как-то

в прерывании надо быть наоборот очень недолго, вы бы код привели, что вы там в прерывании делаете?
По идее завести переменную U long и там её ++, в основной программе с ней оперировать

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

ua6em пишет:

По идее завести переменную U long и там её ++

volatile U long. 

SLKH
Offline
Зарегистрирован: 17.08.2015

без внешнего делителя и торможения в обработчике:

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()
{

}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

DetSimen пишет:

ua6em пишет:

По идее завести переменную U long и там её ++

volatile U long. 

ну так жеж как рубль, волатильные...

kim84sm
Offline
Зарегистрирован: 28.05.2019

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

//  Код считывает импульсы с инкрементального энкодера(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;
}
ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

какой физический смысл рассчитывать куда мотор крутится, если интересуют только обороты?

Может есть смысл ограничиться простым частотомером от 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;
}

 

SLKH
Offline
Зарегистрирован: 17.08.2015

Duino A.R. пишет:

kim84sm пишет:

  Благодарю за ответ.

   Нужно аппаратно добавить некий делитель частоты. На его вход приходит 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()
{

}
kim84sm
Offline
Зарегистрирован: 28.05.2019

   да, на данном этапе направление вращения не принципиально. Не могли бы  вы дать ссылку на полный код, с этим частотомером,а то знаний по си уже не хватает )))

SLKH
Offline
Зарегистрирован: 17.08.2015

kim84sm пишет:

   да, на данном этапе направление вращения не принципиально. Не могли бы  вы дать ссылку на полный код, с этим частотомером,а то знаний по си уже не хватает )))

полный код писать надо. 

Неполный может начинаться примерно так:

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();
		}
}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

kim84sm пишет:

   да, на данном этапе направление вращения не принципиально. Не могли бы  вы дать ссылку на полный код, с этим частотомером,а то знаний по си уже не хватает )))

куда уж полнее ))) на 5 пин подаем импульсы, в переменной получаем их частоту, код заимствован из генератора от DIMAX

kim84sm
Offline
Зарегистрирован: 28.05.2019

Спасибо, буду ковырять)