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

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

  1. avex

    avex Нерд

    Отлично, теперь все сервоприводы работают! Спасибо огромное!
    Скажите, можно ли к Вам ещё обращаться? У Вас отлично получается решать сложные вопросы. В этой теме другие вопросы, наверное, будут не к месту. Можно, в личку или на почту?
     
  2. mcureenab

    mcureenab Гуру

    В личку можно, но на форуме много людей, которые могут помочь. Так что лучше тему открывать.
     
  3. avex

    avex Нерд

    Я так и сделаю - напишу в личку.
     
  4. mcureenab

    mcureenab Гуру

    Код (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

    // Переключатель джойстиков
    #define BTNSW 9

    // Алгоритм
    // Быстрый поиск 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();
      Servo *set_servo( Servo *servo_ ) { Servo *res(servo); servo = servo_; return res; }
      Servo *get_servo( ) { return servo; }
    };

    // Пары значений { 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);
        if ( servo )
        {
          servo->write(serv);
    //    Serial.println( String("serv=") + serv );
        }
    }

    class Button {
    private:
      uint8_t pin;
      void on_push();
      bool btn1;
      long relax;
      JoyAxe (*joax)[4];
    public:
      Button(uint8_t pin_, JoyAxe (*joax_)[4] ):
        pin(pin_),
        btn1(false),
        relax(millis()),
        joax(joax_)
      {
        pinMode(pin, INPUT_PULLUP);
      };
      void loop();
    };


    void Button::loop()
    {
      if ( millis() < relax ) return;
      bool btn = digitalRead(pin) == LOW; // Для INPUT_PULLUP замыкание на GND
      if ( btn == btn1 ) return; // Нет изменений
      relax = millis() + 100;
      btn1 = btn;
      if ( btn )
      {
        on_push();
      }
    }

    void Button::on_push()
    { // Циклический сдвиг управления парами осей.
        (*joax)[0].set_servo( (*joax)[2].set_servo( (*joax)[0].get_servo() ) );
        (*joax)[1].set_servo( (*joax)[3].set_servo( (*joax)[1].get_servo() ) );
    }


    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)
    };

    Button btnsw(BTNSW, &joax);

    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();
      }
      btnsw.loop();
    }
     
     
  5. mcureenab

    mcureenab Гуру

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

    Тут то что относится непосредственно к вашему приложению.

    Код (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

    // Переключатель джойстиков
    #define BTNSW 9


    // Пары значений { 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 Button::on_push()
    { // Циклический сдвиг управления парами осей.
        (*joax)[0].set_servo( (*joax)[2].set_servo( (*joax)[0].get_servo() ) );
        (*joax)[1].set_servo( (*joax)[3].set_servo( (*joax)[1].get_servo() ) );
    }


    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)
    };

    Button btnsw(BTNSW, &joax);

    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();
      }
      btnsw.loop();
    }
     
     
  6. mcureenab

    mcureenab Гуру

    Код для 2х, 4х или 6ти осей.
    Пины определены для 4х осей.
    Если надо не 4 оси, указать нужное значение JOYNUM, и поменять инициализацию таблиц
    servo_pins и joax.

    Код (C++):
    #include <Servo.h>

    // Количество осей
    #define JOYNUM 4

    // пины для подключения джойстиков
    #define JOY1X A0
    #define JOY1Y A1
    #define JOY2X A2
    #define JOY2Y A3

    // пины для подключения сервомашинок
    #define SER1X 5
    #define SER1Y 6
    #define SER2X 9
    #define SER2Y 10

    // Переключатель джойстиков
    #define BTNSW 11

    // Алгоритм
    // Быстрый поиск 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();
      Servo *set_servo( Servo *servo_ ) { Servo *res(servo); servo = servo_; return res; }
      Servo *get_servo( ) { return servo; }
    };

    // Пары значений { 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);
        if ( servo )
        {
          servo->write(serv);
    //    Serial.println( String("serv=") + serv );
        }
    }

    class Button {
    private:
      uint8_t pin;
      void on_push();
      bool btn1;
      long relax;
      JoyAxe (*joax)[JOYNUM];
    public:
      Button(uint8_t pin_, JoyAxe (*joax_)[JOYNUM] ):
        pin(pin_),
        btn1(false),
        relax(millis()),
        joax(joax_)
      {
        pinMode(pin, INPUT_PULLUP);
      };
      void loop();
    };


    void Button::loop()
    {
      if ( millis() < relax ) return;
      bool btn = digitalRead(pin) == LOW; // Для INPUT_PULLUP замыкание на GND
      if ( btn == btn1 ) return; // Нет изменений
      relax = millis() + 100;
      btn1 = btn;
      if ( btn )
      {
        on_push();
      }
    }

    void Button::on_push()
    { // Циклический сдвиг управления парами осей. a(b(a)), a(c(b(a)))
    #if ( 4 == JOYNUM )
        (*joax)[0].set_servo( (*joax)[2].set_servo( (*joax)[0].get_servo() ) );
        (*joax)[1].set_servo( (*joax)[3].set_servo( (*joax)[1].get_servo() ) );
    #endif
    #if ( 6 == JOYNUM )
        (*joax)[0].set_servo( (*joax)[4].set_servo( (*joax)[2].set_servo( (*joax)[0].get_servo() ) ) );
        (*joax)[1].set_servo( (*joax)[5].set_servo( (*joax)[3].set_servo( (*joax)[1].get_servo() ) ) );
    #endif
    }


    const uint8_t servo_pins[JOYNUM] = {
      SER1X,
      SER1Y,
      SER2X,
      SER2Y
    };

    Servo servo[JOYNUM];

    JoyAxe joax[JOYNUM] = {
      JoyAxe(&servo[0], JOY1X),
      JoyAxe(&servo[1], JOY1Y),
      JoyAxe(&servo[2], JOY2X),
      JoyAxe(&servo[3], JOY2Y)
    };

    Button btnsw(BTNSW, &joax);

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

    void loop() {
      // put your main code here, to run repeatedly:
      for ( uint8_t i = JOYNUM; i--; )
      {
        joax[i].loop();
      }
      btnsw.loop();
    }
     
     
  7. Domrist

    Domrist Нуб

    Я конечно не совсем понял суть вопроса в шапке.Как я понял - то Вам надо в зависимости от положения потенциометра крутить серву?Попробуйте значение потенциометра перевести в другой диапазон функцией map() и уже через функцию digitalWrite() отдавать на серву эти значения
     
  8. Tomasina

    Tomasina Сушитель лампочек Модератор

    map - линейная функция (поворот потенциометра на N градусов переводится в поворот сервы всегдав на k*N градусов), а по графику требуется нелинейная, т.е. в начале малый оборот потенциометра соответствует большому углу сервы, а в конце наборот.
     
  9. mcureenab

    mcureenab Гуру

    Добавил кнопки для создания помех. Собрал. Загрузил на Arduino micro. Работает.
    Распиновку поменял, чтобы наглядннее было.

    Код (C++):
    #include <Servo.h>

    // Количество осей
    #define JOYNUM 4

    // пины для подключения джойстиков
    #define JOY1X A0
    #define JOY1Y A1
    #define JOY2X A2
    #define JOY2Y A3

    // пины для подключения сервомашинок
    #define SER1X 5
    #define SER1Y 6
    #define SER2X 9
    #define SER2Y 10

    // Переключатель джойстиков
    #define BTNSW 13

    // Кнопки помех
    #define BTNNS1X 7
    #define BTNNS1Y 8
    #define BTNNS2X 11
    #define BTNNS2Y 12

    // Запас помех на кнопке
    #define AMMO 17

    // Помеха. Длительность (мс) и амплитуда (градусы)
    #define NOISE_TM 1000
    #define NOISE_DGR 15

    // Алгоритм
    // Быстрый поиск 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;

    struct IFilter // Интерфейс к функции искажения сигнала
    {
      virtual int apply( int signal ) = 0;
    };

    class JoyAxe {
    public:
      JoyAxe( Servo *servo_, uint8_t joystick_, IFilter * filter_ ):
        servo(servo_),
        joystick(joystick_),
        joy_zero(511),
        filter(filter_)
      {
        pinMode(joystick, INPUT);
      };
      void calibrate ( )
      {
        joy_zero = analogRead(joystick);
      }
      void loop();
      inline Servo *set_servo( Servo *servo_ )
      {
        Servo *res(servo);
        servo = servo_;
        return res;
      }
      inline Servo *get_servo( )
      {
        return servo;
      }
    private:
      Servo *servo;         // Управляемая сервомашинка
      uint8_t joystick;     // Пин джойстика
      int joy_zero;         // Нейтральное положение джойстика
      static const Pair pots[]; // Отображение позиции джойстика в позицию сервомашинки
      IFilter *filter;      // Функция помехи
    };

    // Пары значений { 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 = filter->apply( map( x, pots[i].key, pots[i+1].key, pots[i].val, pots[i+1].val) );
        if ( servo )
        {
          servo->write(serv);
    //    Serial.println( String("serv=") + serv );
        }
    }

    class Button;

    struct IEvent
    {
      virtual void process( Button * ) = 0;
    };

    class Button {
    private:
      uint8_t pin; // Пин с физической кнопкой
      bool btn1; // Предыдущее состояние кнопки
      unsigned long relax;  // Время успокоения дребезга
      IEvent * eh; // Обработчик событи
    public:
      Button(uint8_t pin_, IEvent * eh_ ):
        pin(pin_),
        btn1(false),
        relax(millis()),
        eh(eh_)
      {
        pinMode(pin, INPUT_PULLUP);
      };
      void loop();
    };


    void Button::loop()
    {
      if ( millis() < relax ) return;
      bool btn = digitalRead(pin) == LOW; // Для INPUT_PULLUP замыкание на GND
      if ( btn == btn1 ) return; // Нет изменений
      relax = millis() + 100;
      btn1 = btn;
      if ( btn )
      {
        eh->process(this);
      }
    }

    extern JoyAxe joax[JOYNUM];

    class Account {
    public:
      Account():x(10),y(10){};
    private:
        int x;
        int y;
    };

    class Noise : public IFilter // Функция помехи
    {
    public:
      struct Point {
        unsigned long expire; // Срок действия millis() с момента start
        int ampl;             // Амплитуда отклонения
      };
    public:
      virtual int apply(int signal)
      {
        while( phase )
        {
          unsigned long ms( millis() );
          if ( ms < start_tm ) break;
          ms -= start_tm;
          if ( ms < phase->expire )
          {
             signal += phase->ampl;
             break;
          };
          if ( ++phase == &graf[2] ) phase = 0;
        }
        return signal;
      };
      void start( unsigned long time_ )
      {
        start_tm = time_;
        phase = graf;
      };
    private:
      unsigned long start_tm; // Время запуска помехи
      Point *phase;           // Текушая фаза помехи
      static Point graf[2];   // График помехи
    };

    // График помехи
    Noise::Point Noise::graf[] =
    { // ms, градусы
      { NOISE_TM,  NOISE_DGR },
      { NOISE_TM, -NOISE_DGR }
    };

    class BtnSw : public IEvent // Циклический сдвиг управления
    {
    public:
      virtual void process( Button * )
      { // Циклический сдвиг управления парами осей. a(b(a)), a(c(b(a)))
    #if ( 4 == JOYNUM )
        joax[0].set_servo( joax[2].set_servo( joax[0].get_servo() ) );
        joax[1].set_servo( joax[3].set_servo( joax[1].get_servo() ) );
    #endif
    #if ( 6 == JOYNUM )
        joax[0].set_servo( joax[4].set_servo( joax[2].set_servo( joax[0].get_servo() ) ) );
        joax[1].set_servo( joax[5].set_servo( joax[3].set_servo( joax[1].get_servo() ) ) );
    #endif
      };
    };

    class BtnNoise : public IEvent // Запуск помехи
    {
    public:
      virtual void process( Button * )
      {
        if ( acc )
        {
          --acc;
          filter->start( millis() );
        }
      };
      BtnNoise ( Noise *filter_ )
      :acc(AMMO),
       filter(filter_)
      {};
    private:
      unsigned int acc; // Запас помех
      Noise *filter;   // Целевая помеха
    };

    const uint8_t servo_pins[JOYNUM] = {
      SER1X,
      SER1Y,
      SER2X,
      SER2Y
    };

    Servo servo[JOYNUM];

    Noise noise[JOYNUM];

    JoyAxe joax[JOYNUM] = {
      JoyAxe(&servo[0], JOY1X, &noise[0]),
      JoyAxe(&servo[1], JOY1Y, &noise[1]),
      JoyAxe(&servo[2], JOY2X, &noise[2]),
      JoyAxe(&servo[3], JOY2Y, &noise[3])
    };

    // Кнопка пользователя шумит другому пользователю.
    BtnNoise btnns_h[JOYNUM] = {
      BtnNoise(&noise[2]),
      BtnNoise(&noise[3]),
      BtnNoise(&noise[0]),
      BtnNoise(&noise[1])
    };

    BtnSw btnsw_h;

    Button btns[JOYNUM + 1] = {
      Button (BTNNS1X, &btnns_h[0]),
      Button (BTNNS1Y, &btnns_h[1]),
      Button (BTNNS2X, &btnns_h[2]),
      Button (BTNNS2Y, &btnns_h[3]),
      Button (BTNSW,  &btnsw_h)
    };


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

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