Управление мотором, RC-Switch

booroondook
Offline
Зарегистрирован: 19.01.2019

Собираю робота на гусеницах. Основа - Arduino Uno. Предполагается управление моторами с 4-хкнопочного пульта, работающего на частоте 433 МГц. Приемник - MX-RM-5V. Для приема сигналов используется библиотека rc-switch. Для управления моторами используется Adafruit Motor Shield и библиотека AFMotor.

Стоит задача - крутить моторы, пока нажата кнопка (в зависимости от конкретной кнопки - конкретное кручение). При отпускании кнопки моторы должны остановиться.

Написал вот такой скетч (чтобы сократить текст, урезал его здесь до одного мотора и одной кнопки):

#include <AFMotor.h>
#include <RCSwitch.h>
RCSwitch mySwitch = RCSwitch();

unsigned long oldTime;
unsigned long newTime;
unsigned long oldValue;
unsigned long newValue;
const unsigned long  btnA = 5592332;
AF_DCMotor motor(4);
//***********************
void setup() {
  Serial.begin(9600);
  motor.setSpeed(255);
  motor.run(RELEASE);
  mySwitch.enableReceive(0); //приемник на пине 2 (прерывание 0)
}
//***********************
void loop(){ 
  newTime = millis();  
  if ((newTime - oldTime) >= 100) {
    if (mySwitch.available()){
      newValue = mySwitch.getReceivedValue();
      mySwitch.resetAvailable();
    }else{
      newValue = 0;
    }
    if (newValue == btnA) {
        Serial.print("Forward");
        motor.run(FORWARD);
    }else if (newValue == 0) {
        Serial.print("Stop");
        motor.run(RELEASE);
    }
    Serial.println();
    oldTime = millis();
    oldValue = newValue;
  }
} 

То есть, мысль была такая - раз в 100 миллисекунд (я экспериментировал со значениями от 10 мс до 10 с, но разницы нет, см.ниже) проверяется наличие посылки кода с пульта-передатчика. И если кнопка продолжает быть нажатой (код есть, и он нужный), то мотор крутится, либо продолжает крутиться. Если же кнопка отпущена, то кода нет (пришел 0), и мотор останавливается. Ну, и для контроля шлем в последовательный порт текст-описание команды ("Forward" или "Stop").

Ну так вот - вся эта конструкция прекрасно работает до того момента, пока к контактам мотор-шилда не подключен реальный двигатель и на шилд не подано напряжение - то есть, пока кнопка нажата, порт выдает строчки "Forward" с периодичностью 100 мс, а после отпускания кнопки - строчки "Stop" с той же периодичностью.

Но как только мы включили в работу реальный двигатель, то начинается полный бардак, а именно - несмотря на постоянно нажатую кнопку, двигатель работает рывками, а в выводе в порт между строчками "Forward" регулярно попадаются строчки "Stop". То есть, хоть кнопка и постоянно нажата, все равно эпизодически проскакивают "нулевые" сигналы.

Что можно сделать в такой ситуации? Как избавиться от паразитных нулевых сигналов?

b707
Онлайн
Зарегистрирован: 26.05.2017

схему  выкладывайте.

От чего питатается ардуино (напряжение Бп и ток) и от чего питается мотор. Как они соединены

booroondook
Offline
Зарегистрирован: 19.01.2019

b707 пишет:
схему  выкладывайте.

От чего питатается ардуино (напряжение Бп и ток) и от чего питается мотор. Как они соединены

Мотор-шилд в зависимости от ситуации питается либо от лабораторного БП (6-7 вольт подаю), либо от пары аккумуляторов 18650, соединенных последовательно.

Arduino так же в зависимости от ситуации питается либо от USB-порта ПК  (джампер питания на мотор-шилде разомкнут), либо от упомянутых аккумуляторов (джампер замкнут).

Вариант питания на проявление проблемы на влияет.

Но вот, что интересно (причем, только сейчас заметил) - на проявление проблемы влияет направление вращения мотора. То есть, частота вылезания паразитных "нулевых" сигналов очень сильно разная, в зависимости от того, в какую сторону крутится ротор мотора.

Мое личное предположение - работающий мотор создает помехи, препятствующие прохождению радиосигнала на частоте 433 МГц.

Потребляемый ток от БП 6 вольт - 0,13 А при постоянно вращающемся моторе. При пуске скачки до 0,35 А.

b707
Онлайн
Зарегистрирован: 26.05.2017

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

И посмотрите, будет ли работать такая программа устойчиво.

booroondook
Offline
Зарегистрирован: 19.01.2019

Естественно, я такие опыты проводил. Все работает штатно.

ZXPirate
Offline
Зарегистрирован: 18.02.2020

Попробуй керамический конденсатор 0.1 - 1uF поставить параллельно мотору (припаивать на выводы мотора).

 

booroondook
Offline
Зарегистрирован: 19.01.2019

Короче, кое-что прояснилось, но обо всем по-порядку. Сначала я решил использовать для RF-приема другую библиотеку - не RCSwitch, а RemoteSwitch. Там функции организованы несколько по другому. В частности, требуется явно указать процедуру, обслуживающую прерывание. ну и, соответственно, написать эту процедуру. Как следствие - отпала необходимость запускать вызов RF-приема в loop'е.

Немного пооптимизировав код, я перенес все процедуры запуска двигателей (а их 4 варианта) непосредственно в эту самую процедуру.

Получилось как-то так:

void handleCode(unsigned long receivedCode, unsigned int period) {
    if (receivedCode == A) {
      Serial.print("Forward/");
      move_f();
    }else if (receivedCode == B) {
      Serial.print("Right/");
      move_r();
    }else if (receivedCode == C) {
      Serial.print("Left/");
      move_l();
    }else if (receivedCode == D) {
      Serial.print("Backward/");
      move_b();
    }else{
      Serial.print("Stop/");
      move_s();
    }
    Serial.println();
}

Как видим, здесь вообще нет обработки "нулевого" сигнала, поскольку раз прерывание сработало, то сигнал никак не может быть нулевым. Вместо этого (временно) был включен обработчик "неизвестного" сигнала, вызывающий "стоп" - т.е., останов двигателей. Получить с пульта "неизвестный" сигнал можно, нажав одновременно две или более кнопок (собственно говоря, 4-хкнопочный пульт может выдать 15 разных сигналов, если использовать еще и комбинации кнопок).

Ну так вот. Запускаю скетч, жму кнопку А (Forward), моторы заработали, последовательный порт выдает "Forward", и... на этом все. Никакой реакции на кнопки нет, новая строчка в порту не появляется, моторы продолжают крутиться.

И тут до меня доходит: во время работы моторов прерывания не работают. То есть, вообще, от слова "никак". Для проверки гипотезы прописываю запуск моторов в setup'е, загоняю скетч в Ардуину, запускаю. Моторы включились, последовательный порт молчит, хотя я жму на пульте кнопки по-всякому.

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

Попробую вместо Мотор-шилда поставить HW95.

b707
Онлайн
Зарегистрирован: 26.05.2017

booroondook пишет:

И тут до меня доходит: во время работы моторов прерывания не работают. То есть, вообще, от слова "никак".

простите, не ясно, откуда вы это взяли. В библиотеке AFMotor.h я ничего подобного не вижу, да и ваш опыт со скетчем из первого сообщения говорит об обратном - там у вас RC-приемник работал во время запуска моторов, так что никакие прерывания моторы не выключают.

 

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

Попробуйте переписать последнюю программу, обрабатывая внутри прерывания только получение кода с приемника, а все действия делать в основной программе

booroondook
Offline
Зарегистрирован: 19.01.2019

b707 пишет:
опыт со скетчем из первого сообщения говорит об обратном - там у вас RC-приемник работал во время запуска моторов
Это не так. Внимательно посмотрите на скетч. Вот ключевой кусок из loop'a:

    if (mySwitch.available()){
      newValue = mySwitch.getReceivedValue();
      mySwitch.resetAvailable();
    }else{
      newValue = 0;
    }

То есть, если было прерывание, и его функция вернула ненулевое значение (а она может возвращать по определению как раз только ненулевые значения), то переменная newValue принимает это значение. А если прерывания не было (в заданный промежуток времени), то переменная принимает нулевое значение.

Вот, собственно, и получается - если мотор работает, то он "отсекает" прослушивание прерывания, затем проходит заданный тайм-аут, значение из функции прерывания не получено, и поэтому переменная newValue обнуляется.

b707 пишет:
сама идея засунуть всю программу в прерывание - тупиковая
Это-то понятно. Это было сделано чисто ради эксперимента, но никак не для реальной работы.

b707
Онлайн
Зарегистрирован: 26.05.2017

вы ошибаетесь. Откройте код библиотеки моторов и покажите мне место. где она отключает прерывания на время движения. Я ничего подобного не нашел.. А то, что у вас не проходят сигналы - может быть вызвано десятком других причин

Bruzzer
Offline
Зарегистрирован: 17.03.2020

booroondook пишет:

    if (mySwitch.available()){
      newValue = mySwitch.getReceivedValue();
      mySwitch.resetAvailable();
    }else{
      newValue = 0;
    }

А если прерывания не было (в заданный промежуток времени), то переменная принимает нулевое значение.

Если бы были запрещены ВСЕ прерывания, то перестало бы считаться millis и вы никгда бы не попали в блок

if ((newTime - oldTime) >= 100) {

booroondook
Offline
Зарегистрирован: 19.01.2019

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