Обратная связь для коллекторного двигателя
- Войдите на сайт для отправки комментариев
Добрый день. Интересует вопрос программно организовать обратную связь для коллекторного двигателя с помощью потенциометра. Есть потенциометр, который напрямую крутится от вала коллекторного двигателя, который подключен к драйверу BTS7960. Драйвер по пинам +,-, ШИМ лево, ШИМ право, лево, право подключен к ардуино, так же к ардуино подключен потенциометр. Хотелось бы как-то удобно организовать программно работу этих двух устройств чтобы они были похожи на обычный серво-привод. Мне нужно регулировать угол в котором находится двигатель. Вообще, задумка такая: стоит компас, по нажатию кнопки фиксируется положение компаса, а дальше двигатель должен крутиться в противоположную сторону на столько градусов насколько ушел компас, чтобы стабилизировать положение.
Третий день ломаю голову, ничего не могу придумать.
На всякий случай приложу код.
#include <Wire.h> #include <Adafruit_Sensor.h> #include <Adafruit_BNO055.h> #include <utility/imumaths.h> #include <GyverButton.h> #define BNO055_SAMPLERATE_DELAY_MS (100) #define EN 3 // № вывода Arduino к которому подключены входы драйвера L_EN и R_EN. Можно указать любой вывод Arduino поддерживающий ШИМ. #define L_PWM 4 // № вывода Arduino к которому подключён вход драйвера L_PWM. Можно указать любой вывод Arduino, как цифровой, так и аналоговый. #define R_PWM 5 // № вывода Arduino к которому подключён вход драйвера R_PWM. Можно указать любой вывод Arduino, как цифровой, так и аналоговый. #define fixButton 2 // Кнопка фиксации значения координаты fixedVal #define led1 6 // Индикатор работы автопилота #define relay 7 // Реле двигателя #define potInput A0 // Резистор руля Adafruit_BNO055 bno = Adafruit_BNO055(-1, 0x29); float fixedVal = 0; // Принимает значение координаты float tempVal = 0; // Временная переменная зафиксированной координаты float xVal = 0; float engPosition = 0; // Позиция руля int engSpeed = 0; bool apstate = 0; bool setpos = 0; int maxSpeed = 100; int minSpeed = 1; GButton butt1(2); // Кнопка void setup(){ pinMode(EN, OUTPUT); // Конфигурируем вывод EN как выход (выход Arduino, вход драйвера) pinMode(L_PWM, OUTPUT); // Конфигурируем вывод L_PWM как выход (выход Arduino, вход драйвера) pinMode(R_PWM, OUTPUT); // Конфигурируем вывод R_PWM как выход (выход Arduino, вход драйвера) pinMode(fixButton, INPUT); // Кнопка фиксации значения координаты fixedVal pinMode(led1, OUTPUT); // Индикатор работы автопилота pinMode(relay, OUTPUT); // Реле двигателя digitalWrite(led1, LOW); digitalWrite(relay, LOW); Serial.begin(115200); Serial.println("Orientation System 25.04.2020"); Serial.println(""); Wire.begin(); /* Initialise the sensor */ if(!bno.begin()) { Serial.print("Ooops, no BNO055 detected ... Check your wiring or I2C ADDR!"); while(1); } delay(1000); bno.setExtCrystalUse(true); Serial.println("Loading done!"); } void loop(){ sensors_event_t event; bno.getEvent(&event); xVal = event.orientation.x-180; engPosition = map(analogRead(potInput), 0.00, 1023.00, -100.00, 100.00); butt1.tick(); if(butt1.isPress()){ if(apstate == 1){ Serial.println("OFF!"); digitalWrite(led1, LOW); digitalWrite(relay, LOW); engStop(); engNeutral(); apstate = 0; } else if(apstate == 0){ Serial.println("ON!"); digitalWrite(led1, HIGH); digitalWrite(relay, HIGH); bno.begin(); xVal = event.orientation.x-180; fixedVal = xVal; apstate = 1; } } if(apstate == 1){ if(xVal > -179 && xVal < 0){ // ЛЕВО tempVal = xVal - fixedVal; Serial.print("1: "); Serial.print(tempVal); if(engPosition < tempVal){ engStart(); engLeft(); } else engNeutral(); } if(xVal < 179 && xVal > 0){ // ПРАВО tempVal = fixedVal + xVal; Serial.print("2: "); Serial.print(tempVal); if(engPosition > tempVal){ engStart(); engRight(); } else engNeutral(); } if(xVal > 179 || xVal < -179){ Serial.print("3: "); Serial.print(tempVal); if(engPosition > 2){ engRight(); engStart(); } else if(engPosition < -2){ engLeft(); engStart(); } else engNeutral(); } /* if(fixedVal > 0){ if(fixedVal < xVal){ tempVal = fixedVal - xVal; Serial.print(" LEFT+ "); } else if(xVal < fixedVal){ tempVal = xVal + fixedVal - 360; Serial.print(" RIGHT+ "); } } else if(fixedVal < 0){ if(fixedVal < xVal){ tempVal = fixedVal - xVal + 360; Serial.print(" LEFT- "); } else if(xVal < fixedVal){ tempVal = xVal + fixedVal; Serial.print(" RIGHT- "); } } if(apstate == 1){ if(engPosition < -2.00 && engPosition > -100){ digitalWrite(L_PWM, HIGH); digitalWrite(R_PWM, LOW); analogWrite (EN, 30); Serial.print("MODE 1 "); } else if(engPosition > 2.00 && engPosition < 100){ digitalWrite(L_PWM, LOW); digitalWrite(R_PWM, HIGH); analogWrite (EN, 30); Serial.print("MODE 2 "); } else { digitalWrite(L_PWM, LOW); digitalWrite(R_PWM, LOW); analogWrite (EN, LOW); Serial.print("MODE 3 "); } if(xVal < 179 && xVal > 0){ if(engPosition <= tempVal){ digitalWrite(L_PWM, HIGH); digitalWrite(R_PWM, LOW); analogWrite (EN, 35); Serial.print("MODE 1 "); } } if(xVal > -179 && xVal < 0){ //right tempVal = (-xVal-180)*8; //Serial.print(tempVal); Serial.print(" RIGHT "); if(engPosition >= tempVal){ digitalWrite(L_PWM, LOW ); digitalWrite(R_PWM, HIGH); analogWrite (EN, 50); setpos = 0; } else if(engPosition <= tempVal){ setpos = 1; } else { digitalWrite(L_PWM, LOW ); digitalWrite(R_PWM, LOW); analogWrite (EN, LOW); Serial.print(" STOP R "); } } else if(xVal < 179 && xVal > 0){ //left tempVal = (-xVal+180)*8; //Serial.print(tempVal); Serial.print(" LEFT "); if(engPosition > tempVal){ digitalWrite(L_PWM, HIGH); digitalWrite(R_PWM, LOW); analogWrite (EN, 100); } else { analogWrite (EN, LOW); Serial.print(" STOP L "); } */ /* } Serial.print(" xVal: "); Serial.print(xVal); Serial.print(" fixedVal: "); Serial.print(fixedVal); Serial.print(" engPosition: "); Serial.print(engPosition); Serial.print(" tempVal: "); Serial.println(tempVal); */ } delay(BNO055_SAMPLERATE_DELAY_MS); Serial.print(" engPosition: "); Serial.print(engPosition); Serial.print(" xVal: "); Serial.println(xVal); } int engStart(){ if(engSpeed < maxSpeed){ engSpeed++; analogWrite(EN, engSpeed); delay(1); engStart(); } analogWrite(EN, engSpeed); return engSpeed; Serial.print(" (engStart) Done! "); } int engStop(){ if(engSpeed >= minSpeed){ engSpeed--; analogWrite(EN, engSpeed); delay(1); engStop(); } analogWrite(EN, engSpeed); return engSpeed; Serial.print(" (engStop) Done! "); } int engLeft(){ digitalWrite(L_PWM, HIGH); digitalWrite(R_PWM, LOW); Serial.print(" (engLeft) Done! "); } int engRight(){ digitalWrite(R_PWM, HIGH); digitalWrite(L_PWM, LOW); Serial.print(" (engRight) Done! "); } int engNeutral(){ digitalWrite(R_PWM, LOW); digitalWrite(L_PWM, LOW); Serial.print(" (engNeutral) Done! "); } int engExtraStop(){ digitalWrite(R_PWM, HIGH); digitalWrite(R_PWM, HIGH); analogWrite(EN, HIGH); delay(100); digitalWrite(R_PWM, LOW); digitalWrite(L_PWM, LOW); analogWrite(EN, LOW); Serial.print(" (engExtraStop) Done! "); }
Базовая схема управления серво
П-регулятор <искомое положение> - <реальное положение> = искомая скорость
ПИД-регулятор <искомая скорость> - <реальная скорость> = шим на моторе
ПИД-регулятор <искомая скорость> - <реальная скорость> = шим на моторе
Посмотрел, спасибо за совет, попробую - отпишу.
ПИД-регулятор <искомая скорость> - <реальная скорость> = шим на моторе
Посмотрел, спасибо за совет, попробую - отпишу.
Нашёл вот такое видео: https://www.youtube.com/watch?v=K7FQSS_iAw0
Мне оно подходит как раз, но там энкодер, а мне нужен потенциометр...
Ну дак выкинь энкодер, поставь потацометр.
Вообще то дешевые серво-приводы для Ардуино так и устроены - двигатель (скорее всего коллекторный, но это не точно) понижающий привод, потенциометр на оконечном валу, и микроконтроллер в корпусе девайса для управления этой системой. Так что смотрите, какой МК там стоит и ищите прошивку к нему.
Тут засада в слове коллекторный. Очень трудно повернуть его на угол меньше 360 градусов. Проблема в том, что тронуться с места коллекторному двигателю нужен больший ток, чем потом на вращение. Поэтому регулятор сначала поднимает напряжение, потом сбрасывает, а движок крутится. В рулевых машинках это решается редуктором, который выбеги коллекторного двигателя уменьшает до приемлемых значений.
Вот я в своём коде
Организовал вот такой участок кода:
Который отвечает за позиционирования двигателя, но вот обратно он возвращается только в положении по середине, как мне сделать из потенциометра и двигателя (двигатель через редуктор медленно крутит потенциометр) - серво, который будет принимать положение которое ему будет указывать компас? Вообще ничего не могу понять, пытался разобраться с ПИД-регулятором, все равно ничего не получается, даже переделать программу, вообще не врубаюсь как организовать ПИД для двигателя и потенциометра... Подскажите кто что может пожалуйста
Зачем ПИД? Зачем PWM? Сначала пишем скетч только с кнопкой. Нажали - едет на градусов на 40 влево или вправо, доехал по потенциометру - останавливаем и смотрим сколько переехал (это выбег)- на столько нужно выключать мотор раньше точки уставки. Потом берём компас и точно также - если угол больше выбега - включаем мотор на сколько надо, что бы остановился с учётом выбега. Не надо включать мотор часто. После пуска мотора ничего не проверять (кроме потенциометра) пока не остановился.
Зачем ПИД? Зачем PWM? Сначала пишем скетч только с кнопкой. Нажали - едет на градусов на 40 влево или вправо, доехал по потенциометру - останавливаем и смотрим сколько переехал (это выбег)- на столько нужно выключать мотор раньше точки уставки.
Давайте не будем давать бредовых советов. С этим подходом никогда в жизни не получится стабильного результата.
Ну может совет и бредовый, но именно по этому алгоритму отлаживал самодельную рулевую машинку на основе движка от шуруповёрта и стоящую на детской машинке. Уже полгода рулится с пульта. Сбоев не было.
Зачем ПИД? Зачем PWM? Сначала пишем скетч только с кнопкой. Нажали - едет на градусов на 40 влево или вправо, доехал по потенциометру - останавливаем и смотрим сколько переехал (это выбег)- на столько нужно выключать мотор раньше точки уставки. Потом берём компас и точно также - если угол больше выбега - включаем мотор на сколько надо, что бы остановился с учётом выбега. Не надо включать мотор часто. После пуска мотора ничего не проверять (кроме потенциометра) пока не остановился.
Мне нужно точное позиционирование руля в зависимости от угла на который повернулось судно, а не лево право
Что значит точное позиционирование руля? Относительно чего? Куда указывает компас это опорная точка? Нужно устранить разницу между чем? Курсом и показанием компаса? Показанием компаса и углом руля? В любом случае сервомашинка может крутиться в одну или в другую сторону. Команду на поворот формируете Вы в своей программе. Замените лево-право, на по часовой-против. Вам нужно рассчитать на сколько нужно повернуть сервомашинку, на какой-то угол, что бы устранить отклонение от заданного параметра. На этом основана работа регуляторов. Есть уставка - точка к которой надо стремиться, есть текущее положение, которое дают датчики, регуляторы по разным алгоритмам дают команду на исполнительный механизм для устранении разницы между уставкой и текущим положением. Как только Вы всё это опишите в своей программе, всё будет работать так как Вы хотите. Тут нужно точно понимать что такое для Вас уставка и текущее положение. Ещё нужно знать постоянную времени регулирования - время между выполнением команды регулятора и переходом всей системы в новое положение. Если это время не учитывает регулятор, то может возникнуть ситуация перерегулирования, когда за время выполнения первой команды регулятора, регулятор выдаст ещё насколько команд в том же направлении.
Разработал такой код, теперь он отвечает всем моим критериям.
Ещё подскажите пожалуйста как можно сделать плавный запуск и остановку двигателя? Чтобы значение engSpeed нарастало и убывало наверное
На форуме были темы по плавному пуску коллекторных двигателей. Поиск по плавный & пуск