Управление трактором с помощью джойстика

Я завтра возвращаюсь домой и смогу приступить к колибровке и смогу точно сказать линейную зависимость.
А по поводу ШИМ сигнала я его буду усиливаливать с помощью полевого транзистора мне дали схему и я ее уже спаял на 4 канала. А вход как раз реагиреюует на ШИМ.


остатки от машинки, на которой опробовал ДУ на модулях LORA. Управлялась джойстиком с кнопкой. Только PiPico была без ВиФи.

#include <Wire.h>
#define motorAspeedPIN 6  // motorA MOSFET
#define AreversPIN 13     //revers relay A
#define motorBspeedPIN 7  // motorB MOSFET
#define BreversPIN 12      //revers relay B
#define motorCspeedPIN 8  // motorC MOSFET
#define CreversPIN 11      //revers relay C
#define motorDspeedPIN 9  // motorD MOSFET
#define DreversPIN 10      //revers relay D
byte Xval=126;      // нач.значение с оси  Х джстк от ДУмодуля
byte Yval=127 ;     //начальное значение с оси У джстк от ДУмодуля
bool Zval;   // нач.значение кнопки дж -переключение скорости от ДУмодуля
byte diffSpeed = 0;   // не понадобилось
byte speedValP = 0;    
byte speedValPpoworot = 0;
byte speedValL = 0;
byte speedValLpoworot = 0;
byte ValP = 0;// угол поворота джойстика вправо от оси Х
byte ValL = 0;// угол поворота джойстика влево от оси Х
byte maxSpeed;//
bool reversARelay = 0;
bool reversBRelay = 0;
bool reversCRelay = 0;
bool reversDRelay = 0;
void setup() {
  Serial.begin(9600);
  pinMode(AreversPIN, OUTPUT);
  pinMode(BreversPIN, OUTPUT);
  pinMode(CreversPIN, OUTPUT);
  pinMode(DreversPIN, OUTPUT);
  pinMode(motorAspeedPIN, OUTPUT);
  pinMode(motorBspeedPIN, OUTPUT);
  pinMode(motorCspeedPIN, OUTPUT);
  pinMode(motorDspeedPIN, OUTPUT);
  digitalWrite(motorAspeedPIN, LOW);
  digitalWrite(motorBspeedPIN, LOW);
  digitalWrite(motorCspeedPIN, LOW);
  digitalWrite(motorDspeedPIN, LOW);
  analogWriteResolution(8);
  analogWriteFreq(5000);
  Wire.setSDA(0);
  Wire.setSCL(1);
  Wire.begin(4);                 // join i2c bus with address #4
  Wire.onReceive(receiveEvent);  // register event
  // Serial.begin(9600);            // start serial for output
}

void loop() {

  if (Zval == 0) {
    maxSpeed=90;
 //   speedValP = 0;
  //  speedValL = 0;
  } else {
maxSpeed=255;
  }
    if (Xval > 125) {
      if (Yval > 125 && Yval < 128) {
        speedValP = map(Xval, 125, 255, 0, maxSpeed);
        speedValL = map(Xval, 125, 255, 0, maxSpeed);
        ValL = 0;
        ValP = 0;
      } else if (Yval > 128) {
        ValL = map(Yval, 125, 255, 0, 90);//значение с джст переводим в градусы отклонения
        speedValLpoworot = map(Xval, 125, 255, 0, maxSpeed);
        speedValL = speedValLpoworot * cos(ValL);
        speedValP = map(Xval, 125, 255, 0, maxSpeed);
        ValP = 0;
      } else if (Yval < 125) {
        ValP = map(Yval, 125, 0, 0, 90);
        speedValPpoworot = map(Xval, 125, 255, 0, maxSpeed);
        speedValP = speedValPpoworot * cos(ValP);
        speedValL = map(Xval, 125, 255, 0, maxSpeed);
        ValL = 0;
      }
      reversARelay = 0;
      reversBRelay = 0;
      reversCRelay = 0;
      reversDRelay = 0;
    } else if (Xval < 125) {

      if (Yval > 125 && Yval < 128) {
        speedValP = map(Xval, 125, 0, 0, maxSpeed);
        speedValL = map(Xval, 125, 0, 0,maxSpeed);
        ValL = 0;
        ValP = 0;
      } else if (Yval > 128) {
        ValL = map(Yval, 125, 255, 0, 90);
        speedValLpoworot = map(Xval, 125, 0, 0, maxSpeed);
        speedValL = speedValLpoworot * cos(ValL);
        speedValP = map(Xval, 125, 0, 0, maxSpeed);
        ValP = 0;
      } else if (Yval < 125) {
        ValP = map(Yval, 125, 0, 0, 90);
        speedValPpoworot = map(Xval, 125, 0, 0, maxSpeed);
        speedValP = speedValPpoworot * cos(ValP);
        speedValL = map(Xval, 125, 0, 0, maxSpeed);
        ValL = 0;
      }
      reversARelay = 1;
      reversBRelay = 1;
      reversCRelay = 1;
      reversDRelay = 1;
    }

    else {
      if (Yval > 125 && Yval < 129) {
        speedValP = 0;
        speedValL = 0;
        reversARelay = 0;
        reversBRelay = 0;
        reversCRelay = 0;
        reversDRelay = 0;
      } else if (Yval > 125) {
        speedValP = map(Yval, 125, 255, 0, maxSpeed);
        speedValL = map(Yval, 125, 255, 0, maxSpeed);
        reversARelay = 0;
        reversBRelay = 1;
        reversCRelay = 1;
        reversDRelay = 0;
      } else if (Yval < 125) {
        speedValP = map(Yval, 127, 0, 0, maxSpeed);
        speedValL = map(Yval, 127, 0, 0, maxSpeed);
        reversARelay = 1;
        reversBRelay = 0;
        reversCRelay = 0;
        reversDRelay = 1;
      }
 //   }

    //speedValP = Xval;
    //speedValL = Xval;
  }
  Amotor(speedValP, reversARelay);
  Bmotor(speedValL, reversBRelay);
  Cmotor(speedValP, reversCRelay);
  Dmotor(speedValL, reversDRelay);
  Serial.print("Xval=");
  Serial.print(Xval);
  Serial.print(",Yval=");
  Serial.println(Yval);
}

void Amotor(byte speed, bool revers) {
  analogWrite(motorAspeedPIN, speed);
  digitalWrite(AreversPIN, revers);
}

void Bmotor(byte speed, bool revers) {
  analogWrite(motorBspeedPIN, speed);
  digitalWrite(BreversPIN, revers);
}
void Cmotor(byte speed, bool revers) {
  analogWrite(motorCspeedPIN, speed);
  digitalWrite(CreversPIN, revers);
}
void Dmotor(byte speed, bool revers) {
  analogWrite(motorDspeedPIN, speed);
  digitalWrite(DreversPIN, revers);
}

void receiveEvent(int howMany) {
  while (1 < Wire.available())  // loop through all but the last
  {
    byte c = Wire.read();
    Yval = c;
    byte a = Wire.read();
    Xval = a;
    bool x = Wire.read();  // receive byte as an integer
    Zval = x;
  }
}

Бегала очень быстро . Поэтому использовалась кнопка для выбора скорости. Все делалось через MAP функцию и через ИФы с ELSE. Для тестирования ДУ и такой говнокод прокатил.Моторчики управлялись мосфетами и реле (для реверсирования моторчика).
Для четырех соленоидов еще проще. Вперед - 2 соленоида , назад -2 других. И ШИМ.

Это неверная последовательность.
Сначала - мультиметр.
Потом - алгоритм.
И только в самом конце - код.

Да конечно, я наверное так и хотел сказать примерно. Сначала околибровать измерить что-то примерно построить. Ну а потом ставить на трактор. Возможно опять снимать. И ещё настраивать. Если не получится ноутбук настроить.

Спасибо.
Я думаю на основе ваших и предыдущих советав, что нибудь сделаю. Но все равно придется спрашивать.
Ведь это мой первый блин. :slight_smile:

Даже не знаю как сообщения исправлять. Отправишь потом ёлки палки.
Советов

ну вы опять прямо на вопрос ответить не хотите, догадываться приходится (( ШИМ знаете, разный бывает. Частота, период, минимальная и максимальная длина импульса там всякие… А у вас, получается, клапан на 5 вольт начинает открываться, а на 24 вольтах открыт полностью, да? И это на транзисторе. То есть если я догадался правильно, то вы просто analogWrite можете делать на затвор транзистора и оно будет работать как надо.

Штош. Насчет таблиц, вот смотрите. Если нормализуете показания датчиков холла, например, в диапазон 0…255 (analogWrite как раз такие и принимает) и введете вычисление значений каждой полуоси, то есть у вас будет четыре полуоси джойстика:

вперед = 0…255
назад = 0…255
влево = 0…255
вправо = 0…255

ВАЖНО У вас одновременно будут ненулевые максимум два значения (одно из вперед/назад и одно из влево/вправо). Ваш алгоритм без таблиц нахреналиард значений будет следующим:

если (вперед > 0) {
  левыйПереднийКлапан = вперед > влево ? вперед - влево : 0
  правыйПереднийКлапан = вперед > вправо ? вперед - вправо : 0
  левыйЗаднийКлапан = 0
  правыйЗаднийКлапан = 0
} иначе если (назад > 0) {
  левыйПереднийКлапан = 0
  правыйПереднийКлапан = 0
  левыйЗаднийКлапан = назад > влево ? назад - влево : 0
  правыйЗаднийКлапан = назад > вправо ? назад - вправо : 0
} иначе {
  // если оси вперед и назад обе равны нулю то тогда ща будет прикольно:
  левыйПереднийКлапан = вправо
  правыйПереднийКлапан = влево
  левыйЗаднийКлапан = влево
  правыйЗаднийКлапан = вправо
  // помните, да, что у вас только одно из влево и вправо может быть НЕ равно нулю?
  // вот вам и танковый разворот без доп. оси джойстика
}

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

Если что записи вида “вперед > влево ? вперед - влево : 0” называются инлайн-условиями, читается как “условие ? если да : если нет” это для сокращения записи

кстати насчет полного привода. Я бы переднее и заднее колесо на каждой стороне соединил цепью или ремнем

а смысл, они и так соединены общим гидравлическим потоком, жидкости жеж не сжимаемы

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

у него 4-ре мотора, борта включены последовательно

а я так понял что по 2 мотора на каждом колесе: 1 по часовой стрелке вращает и 1 против, таким образом вперед/назад открытием 1 из 2х клапанов реализовано

блин, понял, гидрораспределитель хитрая штука походу. Получается трубка выхода одного мотора вставлена во вход второго мотора, а гидроуселитель по этой цепи жижу либо в 1 либо в другую сторону гоняет, и это управляется двумя соленоидами. Вполне изящное решения, рассинхрониться им не даст сцепление с дорогой ещё.

ссорян. Ну алгоритм выше это никак не меняет))

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

нужна, и ее там нет. Контроллеру же плевать, что трактор едет 20 км/ч вперед - дерни ручку назад до упора, потому что пчела укусила руку, и он клапана в реверс на полную откроет… Потому что контроллер не знает текущую скорость и направление движения трактора. Или я чего-то не понимаю?

Опять же открытие клапанов на 100% же не значит, что трактор сразу на максимальную скорость выйдет, а лишь то, что на моторах максимальный крутящий момент появится. Скорость ему еще нужно набрать, под действием силы, приложенной к массе трактора

а как он едет? скорость равна текущему значению расходов, на кранах (где гидравлика) стиком управляется и вот управление самая сложная операция

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

Добавлю: максималка в горку равна максималке по прямой?

в пределах производительности насоса, она тоже регулируется, на стадии настройки гидросистемы (типа ограничение максимального тока) еще и двигатель обороты сбросит от нагрузки
Пытать надо ТС на предмет даташита регулятора, что там и как, 150 штук это же не за просто так берут

А по мне стик для управления это блажь, но я ни разу не лётчик, но даже для моделей авто используется руль и педаль газа, хотя стик проще - FS-GT3C к примеру

Да, согласен. Там и обратка со стравливающим клапаном где-нибудь может быть.

Но. Давление в системе зависит от производительности насоса на моторе. Клапана на распредах контролируют поток. Когда клапан открывается, давление начинает давить на крыльчатку пневмомоторов, т.е. прикладывая силу. Чем больше открыт клапан - тем больше сила. На оси появляется момент, который может вращать колесо. А может и не вращать, если колесо ломом заблокировано. Под действием такой силы, приложенной к массе трактора, при условии достаточного сцепления с дорогой, трактору будет придано ускорение и он начнет разгоняться. Это как в машине, даже если у вас всего одна передача, скорость не равна положению педали газа, верно?

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

это уже вкусовщина… пусть хоть 2 потенциометра использует, с щелчком в центре. Суть задачи не меняется))

это безопасность