Как запрограммировать угол поворота сервы по приведённому графику?

Тема в разделе "Моторы, сервоприводы, робототехника", создана пользователем avex, 7 май 2017.

  1. ostrov

    ostrov Гуру

    Так делать не делать? Если надо то давайте зависимость показаний джойстика к углу сервы. 0-1024 к 0-180. Может вообще линейно сделать? Уверяю, разницы не заметите.
     
  2. avex

    avex Нерд

    Делать! Нужно поставить точку в теме, это поможет заинтересованным любителям в будущем.
    Если линейно, чем будет отличать от обычного кода?
    mcurenab в последних комментариях говорит, что там и не эллипс и не парабола, а, если не ошибаюсь, кубическая парабола.
     
  3. ostrov

    ostrov Гуру

    Если линейно, то обычный map() решит все.

    Вообще смысл страданий в чем если не секрет? Сделать плавный разгон и торможение? Если в этом, то круглый график не поможет.
     
  4. mcureenab

    mcureenab Гуру

    Сейчас скетч почти готовый класс.
    Вот сделать это класс и в 4х экщемплярах поместить в новый скетч.
     
  5. avex

    avex Нерд

    От сервы в ходе работы будет требоваться отклонение от 90 градусов в основном на небольшие углы - 15-20 градусов (потом откорректирую). БОльшие углы почти не потребуются. Поэтому возникла необходимость этим углам отклонения прописать бОльший ход джойстика.
    Чтобы точнее управлять сервой в пределах этих углов.
     
  6. avex

    avex Нерд

    Спасибо еще раз за помощь!
    Придётся повторять прочитанное однажды - как создаются классы, но это уже не так сложно.
     
  7. ostrov

    ostrov Гуру

    Максимальных ход джойстика на который он он реагирует эти примерно сантиметр по верхушке грибка. Я уже писал об этом. А замапимть можно хоть на 1 градус. Не понятно зачем нужны гиперболические зависимости?
     
  8. avex

    avex Нерд

    Нужно, чтобы бОльшая часть этого сантиметра (70%) управляла меньшими углами отклонения вала сервы от 90 градусов, а, соответственно, остальная часть - оставшимся углом (90-20=70 градусов).
     
  9. ostrov

    ostrov Гуру

    Давайте уже таблицу готовьте тогда.
     
  10. avex

    avex Нерд

    Сделаю.
    Градусы: 0, 30, 35, 45, 55, 60, 90
    Потенциометр: 0, 171, 342, 512, 683, 952, 1023
    Числа соответственные - 0-0, 30-171 и т.д.
    Их потом можно откорректировать, главное, чтобы код был рабочим.
     
    Последнее редактирование: 8 май 2017
  11. avex

    avex Нерд

    Готово.
     
  12. mcureenab

    mcureenab Гуру

    Для 4х осей

    Код (C++):
    #include <Servo.h>
    #define joy1x A0
    #define joy1y A1
    #define joy2x A2
    #define joy2y A3

    #define ser1x 5
    #define ser1y 6
    #define ser2x 7
    #define ser2y 8


    class JoyAxe {
    private:
      Servo &servo;
      uint8_t joystick;
      int joy_zero;
      float a2;
      float b2;
    public:
      JoyAxe( Servo &servo_, uint8_t joystick_ ):
        servo(servo_),
        joystick(joystick_),
        joy_zero(511),
        a2(pow(512, 2)),
        b2(pow(90, 2))
      {};
      void calibrate ( )
      {
        joy_zero = analogRead(joystick);
        a2 = pow( max( joy_zero, 1023 - joy_zero ), 2 ); // диаметр эллипса примем больше большего возможного значения pot1x
        b2 = pow( 90.0, 2 );
      }
      void loop()
      {
        const int pot ( analogRead(joy1x) );
        float x = pot - joy_zero;  // калибровка 0.
        float serv = sqrt( b2 - b2 / a2 * pow( x, 2) );
        servo.write(serv);
      }
    };

    const uint8_t servo_pins[4] = {
      ser1x,
      ser1y,
      ser2x,
      ser2y
    };

    Servo servo[4];

    JoyAxe joax[4] = {
      JoyAxe(servo[0], joy1x),
      JoyAxe(servo[1], joy1y),
      JoyAxe(servo[2], joy2x),
      JoyAxe(servo[3], joy2y)
    };

    void setup() {
      // put your setup code here, to run once:
      for ( uint8_t i = 4 ; i--; )
      {
        servo[i].attach( servo_pins[i] );
        joax[i].calibrate();
      }
    }

    void loop() {
      // put your main code here, to run repeatedly:
      for ( uint8_t i = 4 ; i--; )
      {
        joax[i].loop();
      }
    }
     
     
    avex нравится это.
  13. mcureenab

    mcureenab Гуру

    Только на 1/4 оборота нужно крутить (0 - 90) ?
     
  14. avex

    avex Нерд

    Спасибо за класс!
    Две трети отклонения джойстика в одну сторону дают 15 градусов отклонения сервы от середины (90 градусов), оставшаяся треть - оставшиеся 30 градусов.
    К сожалению, должен съездить в соседний город, буду вечером, тогда и испробую все четыре сервы.
     
  15. mcureenab

    mcureenab Гуру

    Таблично заданная функция с кусочно линейной апроксимацией для 4х осей.
    Таблица пар
    Код (C++):
    JoyAxe::pots[]
    .


    Класс JoyAxe координирует взаимодействие джойстика и серво машинки.

    Структура Pair - вспомогательная для таблицы pots.

    Код (C++):
    #include <Servo.h>
    // пины для подключения джойстиков
    #define joy1x A0
    #define joy1y A1
    #define joy2x A2
    #define joy2y A3

    // пины для подключения сервомашинок
    #define ser1x 5
    #define ser1y 6
    #define ser2x 7
    #define ser2y 8

    // Алгоритм
    // Быстрый поиск key в массиве keys от 0 до b-1. у KEY должен быть bool operator<(const KEY&) const
    template <typename KEY, typename INDEX> INDEX search (
      const KEY *keys, // массив ключей.
      INDEX b, // размер массива *keys.
      const KEY key // искомый ключ.
    )
    {
      INDEX a(0);
      --b;
      do {
        INDEX i( (a + b) >> 1 );
        if ( key < keys[i] )
        {
          if ( b == i ) return i;
          b = i;
        } else {
          if ( a == i ) return i;
          a = i;
        }
      } while( true );
    }


    typedef struct Pair {
      int key;
      int val;
      bool operator < ( const Pair& r ) const { return this->key < r.key ; }
    } Pair;

    class JoyAxe {
    private:
      Servo &servo;
      uint8_t joystick;
      int joy_zero;
      static const Pair pots[];
    public:
      JoyAxe( Servo &servo_, uint8_t joystick_ ):
        servo(servo_),
        joystick(joystick_),
        joy_zero(511)
      {};
      void calibrate ( )
      {
        joy_zero = analogRead(joystick);
      }
      void loop();
    };

    // Пары значений { joystick, servo }. Последнее значение key больше 1023. val совпадает с предпоследним.
    // Нейтральное положение калибруется на значение key = 511.
    const Pair JoyAxe::pots[] = { {0, 1}, {10, 15}, {30, 20}, {70, 30}, {120, 40}, {456, 50}, {670, 60}, {883, 70}, {884, 75}, {901, 90}, {940, 100}, {1010, 110}, {1023, 120}, {1024, 120} };

    void JoyAxe::loop()
    {
        const int pot ( analogRead(joy1x) );
        int x ( constrain( pot - joy_zero + 511, 0, 1023) ) ;  // калибровка 0. Нейтральное положение 511.
        uint8_t i ( search( pots, (uint8_t) sizeof(pots)/sizeof(*pots), { x, 0 } ) );
        int serv = map( x, pots[i].key, pots[i+1].key, pots[i].val, pots[i+1].val);
        servo.write(serv);
        Serial.println( String("serv=") + serv );
    }

    const uint8_t servo_pins[4] = {
      ser1x,
      ser1y,
      ser2x,
      ser2y
    };

    Servo servo[4];

    JoyAxe joax[4] = {
      JoyAxe(servo[0], joy1x),
      JoyAxe(servo[1], joy1y),
      JoyAxe(servo[2], joy2x),
      JoyAxe(servo[3], joy2y)
    };

    void setup() {
      // put your setup code here, to run once:
      Serial.begin(250000);
      while(!Serial){}
      Serial.println("Starting...");
      for ( uint8_t i = 4 ; i--; )
      {
        servo[i].attach( servo_pins[i] );
        joax[i].calibrate();
      }
    }

    void loop() {
      // put your main code here, to run repeatedly:
      for ( uint8_t i = 4 ; i--; )
      {
        joax[i].loop();
      }
    }
     
     
    Последнее редактирование: 8 май 2017
    avex нравится это.
  16. avex

    avex Нерд

    Спасибо за табличный код! Пока пробую предыдущий. Поправил пины (вместо 7 и 8 - 9 и 10). Но все четыре сервы слушаются только одну ось одного джойстика.
    Да, и крутятся они в одну сторону.
    В последнем примере тоже слушаются только одну ось одного джойстика, но крутятся уже, как надо, в обе стороны. И движение более плавное!
     
    Последнее редактирование: 8 май 2017
  17. mcureenab

    mcureenab Гуру

    Таблицу я от балды забивал.
     
  18. avex

    avex Нерд

    Плацебо, подозревал) Эти пары значений я настрою. А почему слушают только одну ось одного джойстика? Для меня код - квантовая физика, самому не разобрать.
     
  19. mcureenab

    mcureenab Гуру

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

    С осью не понятно. Попробую стенд собрать, поразбираться
     
  20. mcureenab

    mcureenab Гуру

    Пофиксил ошибку. Убрал вывод в Serial.
    Отключил калибровку на 0
    Код (C++):
    //   joax[i].calibrate();

    Код (C++):
    // version 1.1
    #include <Servo.h>
    // пины для подключения джойстиков
    #define joy1x A0
    #define joy1y A1
    #define joy2x A2
    #define joy2y A3

    // пины для подключения сервомашинок
    #define ser1x 5
    #define ser1y 6
    #define ser2x 7
    #define ser2y 8

    // Алгоритм
    // Быстрый поиск key в массиве keys от 0 до b-1. у KEY должен быть bool operator<(const KEY&) const
    template <typename KEY, typename INDEX> INDEX search (
      const KEY *keys, // массив ключей.
      INDEX b, // размер массива *keys.
      const KEY key // искомый ключ.
    )
    {
      INDEX a(0);
      --b;
      do {
        INDEX i( (a + b) >> 1 );
        if ( key < keys[i] )
        {
          if ( b == i ) return i;
          b = i;
        } else {
          if ( a == i ) return i;
          a = i;
        }
      } while( true );
    }


    typedef struct Pair {
      int key;
      int val;
      bool operator < ( const Pair& r ) const { return this->key < r.key ; }
    } Pair;

    class JoyAxe {
    private:
      Servo &servo;
      uint8_t joystick;
      int joy_zero;
      static const Pair pots[];
    public:
      JoyAxe( Servo &servo_, uint8_t joystick_ ):
        servo(servo_),
        joystick(joystick_),
        joy_zero(511)
      {
        pinMode(joystick, INPUT);
      };
      void calibrate ( )
      {
        joy_zero = analogRead(joystick);
      }
      void loop();
    };

    // Пары значений { joystick, servo }. Последнее значение key больше 1023. val совпадает с предпоследним.
    // Нейтральное положение калибруется на значение key = 511.
    const Pair JoyAxe::pots[] = { {0, 1}, {10, 15}, {30, 20}, {70, 30}, {120, 40}, {456, 50}, {670, 60}, {883, 70}, {884, 75}, {901, 90}, {940, 100}, {1010, 110}, {1023, 120}, {1024, 120} };

    void JoyAxe::loop()
    {
        const int pot ( analogRead(joystick) );
        int x ( constrain( pot - joy_zero + 511, 0, 1023) ) ;  // калибровка 0. Нейтральное положение 511.
        uint8_t i ( search( pots, (uint8_t) sizeof(pots)/sizeof(*pots), { x, 0 } ) );
        int serv = map( x, pots[i].key, pots[i+1].key, pots[i].val, pots[i+1].val);
        servo.write(serv);
    //    Serial.println( String("serv=") + serv );
    }

    const uint8_t servo_pins[4] = {
      ser1x,
      ser1y,
      ser2x,
      ser2y
    };

    Servo servo[4];

    JoyAxe joax[4] = {
      JoyAxe(servo[0], joy1x),
      JoyAxe(servo[1], joy1y),
      JoyAxe(servo[2], joy2x),
      JoyAxe(servo[3], joy2y)
    };

    void setup() {
      // put your setup code here, to run once:
    //  Serial.begin(250000);
    //  while(!Serial){}
    //  Serial.println("Starting...");
      for ( uint8_t i = 4 ; i--; )
      {
        servo[i].attach( servo_pins[i] );
    //   joax[i].calibrate();
      }
    }

    void loop() {
      // put your main code here, to run repeatedly:
      for ( uint8_t i = 4 ; i--; )
      {
        joax[i].loop();
      }
    }
     
     
    avex нравится это.