Сделал машинку а-ля танк. Думаю синхронизировать колеса. Как?

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

mu_ssina пишет:

Кстати, сейчас гуглю теорию по ПИД-регуляторам. Давно хотел их понять-а тут как раз повод. Пока что на данный момент, мой ламерский вопрос звучит так: "зачем он вообще нужен, не проще ли просто прибавить/убавить скорость двигателя и всё тут" :-)))

Ищу ответ...

"пилите Шура, пилите", точнее лучше тему "программа электронного дифференциала" пощупайте

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

mu_ssina пишет:

Кстати, сейчас гуглю теорию по ПИД-регуляторам. Давно хотел их понять-а тут как раз повод. Пока что на данный момент, мой ламерский вопрос звучит так: "зачем он вообще нужен, не проще ли просто прибавить/убавить скорость двигателя и всё тут" :-)))

Ищу ответ...

"Просто прибавить" это И-составляющая регулятора.

Izvekoff
Offline
Зарегистрирован: 02.03.2020

rkit пишет:

mu_ssina пишет:

Кстати, сейчас гуглю теорию по ПИД-регуляторам. Давно хотел их понять-а тут как раз повод. Пока что на данный момент, мой ламерский вопрос звучит так: "зачем он вообще нужен, не проще ли просто прибавить/убавить скорость двигателя и всё тут" :-)))

Ищу ответ...

"Просто прибавить" это И-составляющая регулятора.

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

nik182
Offline
Зарегистрирован: 04.05.2015

Прибавить убавить это уже П регулятор. Хотите Вы или нет. И регулятор нужен, что бы убрать маленькие ошибки, которые не видит П регулятор. ПИ регулятора во многих случаях достаточно. Д регулятор нужен если есть в наличии внешние воздействия большой амплитуды. Чтобы объект регулирования мог быстро отреагировать на изменившиеся обстоятельства. ПИД регулятор это собственно эти три регулятора в одном флаконе. Задав коэффициент ноль для любой из букв можно исключить букву из названия регулятора. Формула расчёта от этого не изменится.

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

Izvekoff пишет:

rkit пишет:

mu_ssina пишет:

Кстати, сейчас гуглю теорию по ПИД-регуляторам. Давно хотел их понять-а тут как раз повод. Пока что на данный момент, мой ламерский вопрос звучит так: "зачем он вообще нужен, не проще ли просто прибавить/убавить скорость двигателя и всё тут" :-)))

Ищу ответ...

"Просто прибавить" это И-составляющая регулятора.

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

Значит говоришь о том, чего не понимаешь. Как и в следующем сообщении.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

mu_ssina пишет:
не проще ли просто
Проще или не проще - это оценочные понятия, которые у каждого свои - как говаривали наши древнеримские партнёры: "suum cuique"

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

Короче говоря, изучил я его код-выложенный мной выше. Наворочено у него -по самое немогу. Да, всё круто и здорово. Но слишком круто. Нам не ракеты наводить :-).

У него 4 двигателя управляются- у меня 2. Ну и у него на freeRTOS. В целом,я ее понял. Но в деталях изучать ради одного случая-такое себе...

Убежден, что можно намного проще. В чем вижу минус, этого кода: робот становится "терминатором" которого не сбить ничем. Это плохо для моих целей (игра в хоккей-роботами). Почему: неинтересно. Все как влупили прямо -и хрен собъешь. Гораздо интересней, если робот в принципе едет прямо, но если его сбили или столкнули-едет прямо в другую сторону. Это гораздо интересней.

Сейчас еще раз снял показания с датчика-чтобы понимать его offset-ы. Попробую сам написать PID-регулятор, с учетом только поворота по Z (толчки и удары не учитываются-после удара робот поедет прямо "куда-то туда вбок". И это хорошо).

 

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

А вот это сложнее, чем тупо всегда возвращаться на прямую. (если работать по IMU).

mu_ssina
Offline
Зарегистрирован: 30.08.2013

Согласен. Определение момента "толчка", "удара"-и что это именно был толчок/удар. И калибровка на их силу и т.д. и.т.п. Я вообще стал думать-не проще ли присобачить на каждое колесо по энкодеру-просто стабилизировать вращение колес и хрен с ним...

Из того, что есть под рукой- цифровые датчики Холла (много) и магнитики неодимовые. Штуки по 4 на колесо поставить. Да, это маленькое разрешение. Больше-лучше. Ну и ладно...будет вихляться туда-сюда, -но ехать... :-)))

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

Сделать просто и проверенно и как было посоветовано еще месяц назад? Ну ты безумец.

mu_ssina
Offline
Зарегистрирован: 30.08.2013

Хотел как проще ;-)

Все-таки прикрепить плату акселерометра проще-чем городить 2 энкодера. 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

mu_ssina пишет:

Хотел как проще ;-)

Все-таки прикрепить плату акселерометра проще-чем городить 2 энкодера. 

Вы всерьез думаете, что софт под акселерометр будет проще софта под энкодеры?

mu_ssina
Offline
Зарегистрирован: 30.08.2013

Кстати, еще один вариант, который можно было бы применить (если не делать энкодер) - через каждые n миллисекунд - производить запись "начального положения". То есть не берем абсолют-который был в самом начале прямолинейного движения. А периодически пересчитываем этот абсолют. В этом случае, если произошел удар и существенное смещение-абсолютом будет считаться это новое положение. И робот не будет пытаться вернуться в "самое самое первое положение". А поедет прямо из текущего (в случайную сторону). Что и есть хорошо. Учитывая, что игровое поле маленькое- метра 3,5 в длину- то супер существенное накопление ошибки не должно происходить (при движении вперед, без воздействия ударов со стороны).

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

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

mu_ssina
Offline
Зарегистрирован: 30.08.2013

В общем, вроде синхронизировал колеса. Рассмотрел массу вариантов, - от магнитного энкодера на аналоговом датчике Холла, до оптического. Оптический оказался весьма даже неплох-фото приложил. Он выставлен точно таким образом-что "светит" инфракрасным лучом в гребенку колеса. Расстояние срабатывания настроено минимальное. Где то 5-6мм. Соответственно-есть гребенка-срабатывает. Провал между гребенками-нет. Датчик цифровой, всё "ну ащще четко и конкретно" :-))) Машинка-едет прямо.

Одного не пойму что-то никак.

У меня к пинам датчиков подключены прерывания. 

Выглядит так:   attachInterrupt(SensorPin2, sensor_impulse2, LOW);

То есть, функция срабатывает через раз(на каждое значение LOW). Машинке этого хватает для ровной езды. Пробовал ставить CHANGE-чтобы на любое изменение- энергопотребление выросло до небес и пошли глюки и отказы.

Но суть вопроса не в этом. В чем вопрос: как можно видеть выше, при возникновении прерывания-идет запуск функции sensor_impulse2

Вроде всё ок. Но есть момент, над которым ломаю голову: в составе этой функции sensor_impulse2 - есть Serial.println ("произошло прерывание, происходит всякое бла бла:  " + blabla)

И вот этот вывод на экран -не срабатывает! Не могу понять, почему так...

 

 

nik182
Offline
Зарегистрирован: 04.05.2015

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

b707
Offline
Зарегистрирован: 26.05.2017

mu_ssina пишет:

И вот этот вывод на экран -не срабатывает! Не могу понять, почему так...

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

mu_ssina
Offline
Зарегистрирован: 30.08.2013

Хорошая информация!

mu_ssina
Offline
Зарегистрирован: 30.08.2013

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

Пилил по этому мануалу:

wikihandbk.com/ruwiki/index.php?title=ESP32:Примеры/Машинка-робот_с_дистанционным_WiFi-управлением

Вроде все ок. Но есть минус: нажал кнопку,-и машинка едет вперед пока не нажмешь "стоп". И со всеми кнопками то же самое.

То есть нет режима "нажал-одно происходит; отпустил-другое".

Пытаюсь переделывать. Но пока не до конца допираю как. Пока допер только, что:

а) событие "onclick" - надо заменить на 2 разных: onkeydown и onkeyup - судя по вики по html.

б) не отключать клиента вебсервера-пока не произошло событие onkeyup.

Чего не получается сделать: чтобы одной и той же кнопке назначить onkeyup и onkeydown. 

Сам код этого места выглядит так:

           // Показываем веб-страницу:
            client.println("<!DOCTYPE HTML><html>");
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // При помощи CSS задаем стиль кнопок.
            // Попробуйте поэкспериментировать
            // с атрибутами «background-color» и «font-size»,
            // чтобы стилизовать кнопки согласно своим предпочтениям: 
            client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
            client.println(".button { -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; background-color: #4CAF50;");
            client.println("border: none; color: white; padding: 12px 28px; text-decoration: none; font-size: 26px; margin: 1px; cursor: pointer;}");
            client.println(".button2 {background-color: #555555;}</style>");
            client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script></head>");
            
            // веб-страница:        
            client.println("<p><button class=\"button\" onkeydown=\"moveForward()\">FORWARD</button></p>");
            client.println("<p><button class=\"button\" onkeydup=\"stopRobot()\">FORWARD</button></p>");
            client.println("<div style=\"clear: both;\"><p><button class=\"button\" onclick=\"moveLeft()\">LEFT </button>");
            client.println("<button class=\"button button2\" onclick=\"stopRobot()\">STOP</button>");
            client.println("<button class=\"button\" onclick=\"moveRight()\">RIGHT</button></p></div>");
            client.println("<p><button class=\"button\" onclick=\"moveReverse()\">REVERSE</button></p>");
//            client.println("<p>Motor Speed: <span id=\"motorSpeed\"></span></p>");          
//            client.println("<input type=\"range\" min=\"0\" max=\"100\" step=\"25\" id=\"motorSlider\" onchange=\"motorSpeed(this.value)\" value=\"" + valueString + "\"/>");


            client.println("<script>$.ajaxSetup({timeout:1000});");
            client.println("function moveForward() { $.get(\"/forward\"); {Connection: close};}");
            client.println("function moveLeft() { $.get(\"/left\"); {Connection: close};}");
            client.println("function stopRobot() {$.get(\"/stop\"); {Connection: close};}");
            client.println("function moveRight() { $.get(\"/right\"); {Connection: close};}");
            client.println("function moveReverse() { $.get(\"/reverse\"); {Connection: close};}</script>");
//            client.println("var slider = document.getElementById(\"motorSlider\");");
//            client.println("var motorP = document.getElementById(\"motorSpeed\"); motorP.innerHTML = slider.value;");
//            client.println("slider.oninput = function() { slider.value = this.value; motorP.innerHTML = this.value; }");
//            client.println("function motorSpeed(pos) { $.get(\"/?value=\" + pos + \"&\"); {Connection: close};}</script>");
              
            
//            client.println("<script>$.ajaxSetup({timeout:1000});");
//            client.println("function moveForward() { $.get(\"/forward\"); {Connection: close};}");
//            client.println("function moveLeft() { $.get(\"/left\"); {Connection: close};}");
//            client.println("function stopRobot() {$.get(\"/stop\"); {Connection: close};}");
//            client.println("function moveRight() { $.get(\"/right\"); {Connection: close};}");
//            client.println("function moveReverse() { $.get(\"/reverse\"); {Connection: close};}");
//            client.println("var slider = document.getElementById(\"motorSlider\");");
//            client.println("var motorP = document.getElementById(\"motorSpeed\"); motorP.innerHTML = slider.value;");
//            client.println("slider.oninput = function() { slider.value = this.value; motorP.innerHTML = this.value; }");
//            client.println("function motorSpeed(pos) { $.get(\"/?value=\" + pos + \"&\"); {Connection: close};}</script>");
           
            client.println("</html>");
            
            // Пример HTTP-запроса: «GET /?value=100& HTTP/1.1»;
            // Он задает коэффициент заполнения ШИМ на 100% (255):
//                    if(header.indexOf("GET /?value=")>=0) {
//                      pos1 = header.indexOf('=');
//                      pos2 = header.indexOf('&');
//                      valueString = header.substring(pos1+1, pos2);
//                      // Задаем скорость мотора:
//                      if (valueString == "0") {
//                        ledcWrite(pwmChannel, 0);
//                        digitalWrite(motor1Pin1, LOW); 
//                        digitalWrite(motor1Pin2, LOW); 
//                        digitalWrite(motor2Pin1, LOW);
//                        digitalWrite(motor2Pin2, LOW);   
//                      }
//                      else { 
//                        dutyCycle = map(valueString.toInt(), 25, 100, 200, 255);
//                        ledcWrite(pwmChannel, dutyCycle);
//                        Serial.println(valueString);
//                      } 
//                    }         
            // HTTP-ответ заканчивается еще одной пустой строкой:
            client.println();
            // Выходим из цикла while():
            break;
          } else {  // Если получили символ новой строки,
                    // то очищаем переменную «currentLine»:
            currentLine = "";
          }
        } else if (c != '\r') {  // Если получили что-либо,
                                 // кроме символа возврата каретки...
          currentLine += c;      // ...добавляем эти данные
                                 // в конец переменной «currentLine»
        }
      }
    }
    // Очищаем переменную «header»:
    header = "";
    // Отключаем соединение:
    client.stop();
    Serial.println("Client disconnected.");  // "Клиент отключен."
    Serial.println("");
  }

 

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

Это делается вебсокетами.

mu_ssina
Offline
Зарегистрирован: 30.08.2013

Ок, покопаю в эту сторону.