Как сделать обработку энкодера мотора более "чувствительной"?

Daminski

✩✩✩✩✩✩✩
26 Май 2021
23
1
Приветствую. Пытаюсь написать обработчик для энкодела, чтобы при натыкании вала мотора с энкодером на препятствие, он начинал вращаться в обратном направлении. Кое что у меня получилось (код приведу ниже), но энкодер не достаточно чувствителен с таким кодом. Хотел бы узнать как можно сделать так, чтобы при малейшем замедлении штатной скорости вращения вала мотора (но не на долю секунды, чтобы не было ложных срабатываний), ардуино это фиксировало и давало команду на вращение вала мотора в обратном направлении?

Пока пошел по такому принципу, как изложу далее, но может знающие люди подскажут более оптимальный принцип. С помощью прерываний в переменную "Step" записывается количество тиков энкодера. Затем раз в 25мс запоминается предыдущее значение переменной "Step" в переменную "Step_Compare". А потом появляется 3-я переменная "Step_Compared_Val", в которой от текущего количества тиков энкодера в переменной "Step" отнимается предыдущее значение, которое находится в переменной "Step_Compare". То есть "Step_Compared_Val = Step - Step_Compare;" И срабатывание вращения в обратном направлении происходит когда "Step_Compared_Val" уменьшается на несколько пунктов. Например "Step_Compared_Val = 30" и в прерываниях это считывается, и если оно меньше чем 28, происходит реверс.

Но в этом принципе или его реализации есть два минуса:
1) Прежде чем "Step_Compared_Val" упадёт на 2 пункта, мотор может достаточно сильно прижать препятствие. А если сделать чтобы срабатывание обратного хода вала было при снижении на 1 пункт, то происходят ложные срабатывания. То есть он не достаточно чувствительный. Мне кажется надо запрограммировать чтобы срабатывание реверса вала было когда замедление вала об препятствие продолжалось какое-то время, например 50-100мс, но мои попытки реализовать эту идею не увенчались успехом.
2) С изменением скорости вращения вала меняется и значение переменной "Step_Compared_Val". Как вы увидите в коде ниже, я сравниваю эту переменную с "жестким числом". И при изменении скорости вала мотора в большую сторону, реверс может вообще не сработать. Хотелось бы узнать как можно динамически задавать это число, чтобы оно подстраивалось под переменную, но с разницей в два пункта, например (мои попытки так сделать не удались).

Вот сам код:
C++:
#define INTERRUPT_PIN 2     /* Пин прерываний */
#define ENCODER 50          /* Сигнальный пин энкодера на моторе */
int Start_Motor_Button;     /* Кнопка запуска мотора */
int Rotate_Back = 4;        /* Вращение вала мотора по часовой стрелке */
int Rotate_Forward = 13;    /* Вращение вала мотора против часовой стрелки */
int Motor_State = 0;
long Step = 0;              /* Переменная для запоминания количества тиков с энкодера */
long Step_Compare = 0;      /* Переменная для запоминания предыдущего кол-ва тиков с энкодера */
long Step_Compared_Val = 0; /* Переменная для получения разницы с предыдущих двух переменных */
long Compare_Time = 0;

void setup() {
  pinMode(Rotate_Back, OUTPUT);
  pinMode(Rotate_Forward, OUTPUT);
  pinMode(Start_Motor_Button, INPUT);
 
  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN), Read_Enc, RISING);
  Serial.begin(9600);

}

void loop() {
  Start_Motor_Button = digitalRead(41);
 
  if  (Start_Motor_Button == HIGH) { Motor_State = 1; }
    
  switch (Motor_State)
   {
    case 1:
           rotate_Forward();
           break;
    case 2:
           rotate_Back();
           break;
   }
}

void Read_Enc() {         /* Функция считывания тиков энкодера по прерыванию */
  if (digitalRead(ENCODER))
   {
     Step++;
     if (Step_Compared_Val < 29) { Motor_State = 2; }  /* При свободном вращении Step_Compared_Val имеет значение 30-31 */
   }
}

void rotate_Forward() {
  analogWrite(Rotate_Back, LOW);
  analogWrite(Rotate_Forward, 200);

  Step_Compared_Val = Step - Step_Compare;

  if (millis() - Compare_Time >= 25)
   {
     Step_Compare = Step;
     Compare_Time = millis();
     Serial.print("Шаг сравнения энкодера: "); 
     Serial.println(Step_Compared_Val);        /* Почему-то когда убираю эту строчку, пропадает реакция на препятствие.
                                                  Возможно это глюк версии Arduino IDE 1.8.16? */
   }
}


void rotate_Back() {
  analogWrite(Rotate_Back, 200);
  analogWrite(Rotate_Forward, LOW);
 
}
 

rkit

★★★✩✩✩✩
5 Фев 2021
508
127
Считать нужно интервал между индивидуальными шагами энкодера. Ждать, пока там накапает 30 шагов это заведомо дико медленно.
 

Daminski

✩✩✩✩✩✩✩
26 Май 2021
23
1
@rkit,

Считать нужно интервал между индивидуальными шагами энкодера
А как это?
Там не ожидается пока "накапает" 30 шагов. 30 шагов это разница между текущим количеством шагов и тем, которое было зафиксировано 25мс назад.
 

Bruzzer

★★★✩✩✩✩
23 Май 2020
472
133
@Daminski,
В данном коде вы раз в 25 мс выводите в Serial от 24 до примерно 26 плюс длина в символах числа
Step_Compared_Val . На скорости в 9600 это может приводить к заполнению буфера и как следствие к незапланированным задержкам и "загадочному" поведению . Увеличьте скорость Serial для большей предсказуемости.
По алгоритму тоже есть вопросы. На мой взгляд логичнее перенести расчеты по Step... из
rotate_Forward() в прерывание.

Добавление. Количество выводимых байт скорее всего больше. Так как текст русский и скорее всего используется кодировка UTF-8, то каждая русская буква выводится двумя байтами.
 
Изменено: