Acos и сложные математические вычисления на ардуино

Тема в разделе "Моторы, сервоприводы, робототехника", создана пользователем Dan Dan, 13 июн 2021.

  1. Dan Dan

    Dan Dan Нерд

    Здравствуйте! Возникла задача - сделать манипулятор с двумя суставами. Управлять планируется только последней точкой манипулятора. Схема: Снимок экрана 2021-06-13 в 12.30.52.png Отрезки c и a - "кости" между суставами, имеют постоянное значение, точка B - первый сустав, точка A - второй сустав, задается расположение точки С (конец манипулятора) с помощью значения l и значения h. Манипулятором является только BAC, остальное лишь вспомогательные отрезки. Нужно вычислить угол A и угол ABD.
    Нашел в интернете формулу расчета углов по сторонам треугольника. Получилось, что угол A равен арккосинусу из chart.png , а угол ABD равен сумме арккосинуса chart-2.png и арккосинуса chart-3.png . При этом chart-4.png . Написав код, несколько раз проверил, хотя до сих пор не выдает правильные значение. В чем проблема? Выдает значение либо nan или ovf на запрос о значении арккосинуса, либо просто неправильные значения еще до арккосинуса( выходит за рамки от -1 до 1). Заранее благодарен за помощь.
    Код (C++):
    #include <Servo.h>
    #include <Math.h>
    Servo serv1;
    Servo serv2;
    Servo serv3;
    Servo serv4;
    int Jox = A1;
    int Joy = A0;
    float c = 15.2;
    float a = 13.8;
    float b;
    float h;
    float l;
    void setup() {
       serv1.attach(11);
       serv2.attach(10);
       serv3.attach(9);
       serv4.attach(8);
    pinMode(Jox,INPUT);
    pinMode(Joy,INPUT);
    Serial.begin(9600);
    }
    void loop() {
      l = analogRead(Jox);
      h = analogRead(Joy);
      l = map(l,0,1023,1,180);
      h = map(h,0,1023,1,180);
      l = sq(l);
      h = sq(h);
      b = sqrt(l+h);
      float f = sq(a)+sq(c)-sq(b);
      float m = 2*c*a;
      float A =f/m;
      A = acos(A);
      float k = sq(c)+sq(b)-sq(a);
      float u = 2*c*b;
      float o = sq(b)+sq(l)-sq(h);
      float p = 2*b*l;
      float q = k/u;
      q = acos(q);
      float z = o/p;
      z = acos(z);
    float B = q+z;
    B = round(B);
      serv3.write(A);
      delay(50);
      serv2.write(B);
      delay(50);
     
    }
     

    Вложения:

    • image.png
      image.png
      Размер файла:
      252 КБ
      Просмотров:
      166
  2. Igor68

    Igor68 Гуру

    Код не проверяю - занят. А вот на вскидку: исключите любую возможность деления на ноль.
    Одним словом делитель не ноль!
    У вас там есть ряд мест... я при возможности такого условия к переменным с такой возможностью вероятности прибавляю очень маленькое значение(вроде и ноль, но не ноль вовсе). В ряде случаев и отрицательные значения не допускаются.
    Сами подсчитайте. Желаю успехов!
     
  3. a1000

    a1000 Гуру

    Проверил как считается у вас угол А.
    Для этого задал такие параметры. Обозначения согласно вашего чертежа
    а = 5
    c = 10
    угол А = 60 градусов. Для простоты, вель cos 60 = 0.5
    По теореме косинусов вычислил b. Получил 8,66.
    В прямоугольном треугольнике BCD принял I = 4. Тогда h будет 7,68.
    Подставил это напрямую в ваш скетч
    Код (C++):
    //#include <Servo.h>
    #include <Math.h>
    /*Servo serv1;
    Servo serv2;
    Servo serv3;
    Servo serv4;
    */

    int Jox = A1;
    int Joy = A0;
    float c = 10;
    float a = 5;
    float b;
    float h;
    float l;
    void setup() {
       /*
       serv1.attach(11);
       serv2.attach(10);
       serv3.attach(9);
       serv4.attach(8);
    pinMode(Jox,INPUT);
    pinMode(Joy,INPUT);
    */

    Serial.begin(9600);
    }
    void loop() {
      /*
      l = analogRead(Jox);
      h = analogRead(Joy);
      l = map(l,0,1023,1,180);
      h = map(h,0,1023,1,180);
      */

      l = 4;
      h = 7.68;
      l = sq(l);
      h = sq(h);
      b = sqrt(l+h);
      Serial.println(b);
      //b = 8.66;
      float f = sq(a)+sq(c)-sq(b);
      float m = 2*c*a;
      float A =f/m;
      A = acos(A);

    Serial.println(A);
      delay(2000);
      /*
      float k = sq(c)+sq(b)-sq(a);
      float u = 2*c*b;
      float o = sq(b)+sq(l)-sq(h);
      float p = 2*b*l;
      float q = k/u;
      q = acos(q);
      float z = o/p;
      z = acos(z);
    float B = q+z;
    B = round(B);
      serv3.write(A);
      delay(50);
      serv2.write(B);
      delay(50);
      */

    }
    В результате b = 8.66, угол А = 1,05 что соответствует 60 градусам в радианах.
    Математика у вас правильная. Смотрите, что вы получаете с аналоговых входов. Скорее всего проблема там.
     
  4. Dan Dan

    Dan Dan Нерд

    Спасибо большое за помощь! Обязательно проверю!
     
  5. Dan Dan

    Dan Dan Нерд

    Сначала не понял, как получить ненулевое значение, потом понял. Спасибо большое!
     
  6. Dan Dan

    Dan Dan Нерд

    У меня были подозрения по поводу самой функции acos(), сейчас опробовал ее отдельно и выводится не то, что я ожидал: при любых значениях выводится nan при следующем коде:
    Код (C++):
    void setup() {
    Serial.begin(9600);
    }
    void loop() {
    float n = Serial.read();
    n = acos(n);
    Serial.println(n);
    delay(1000);
    }
    Решил проверить, может проблема еще раньше и узнал, что в строке ввода монитора порта уже стоит значение -1. Сам арккосинус высчитывает все правильно ( подставил значения в коде : acos(-1) или acos(0.7) и т.д.), но что-то не так при вводе значений с внешних устройств, потому что всегда, когда пытаюсь посчитать арккосинус с заранее неизвестными, вводимыми числами, ардуина выставляет мне nan. Короче говоря, acos() выдает nan при вводимых числах.
    Пример 1:
    Код (C++):
    void setup() {
    Serial.begin(9600);
    }
    void loop() {
    float n = acos(0.7);
    Serial.println(n);
    delay(1000);
    }
    Выдает 0.80, тут все в порядке.
    Пример 2:
    Код (C++):
    void setup() {
    Serial.begin(9600);
    }
    void loop() {
      float a = Serial.read();
    float n = acos(a);
    Serial.println(n);
    delay(1000);
    }
    При вводе любых значений кидает nan.
    Теперь возникает вопрос, что происходит с функцией и как это исправить? Заранее благодарен за ответ,
     
  7. a1000

    a1000 Гуру

    Я на вскидку не помню, а искать сейчас некогда, но там передаются цифры в кодах ascii. 0 это 48, 1 - 49 и т.д. Дробные числа вообще передаются в виде строки.
    Ну и так как вы читаете, данные из Serial не принимают. Надо проверять наличие пришедших данных с помощью Serial.available(), и если там что-то есть, тогда читать.
     
  8. Dan Dan

    Dan Dan Нерд

    Спасибо большое, напишу что получилось сразу как опробую
     
  9. Dan Dan

    Dan Dan Нерд

    Получилось считать только нужные значения, а теперь вопрос - как их преобразовать в читаемые? То есть преобразовать ASCII в float? У меня выводится ( когда ввожу 1 ) 49 13 10. Я даже не знаю как эти числа из монитора порта выцепить. Заранее благодарен за ответ!
     
  10. akl

    akl Гуру

  11. Dan Dan

    Dan Dan Нерд

    Огонь. Спасибо! Сейчас опробую.
     
  12. Dan Dan

    Dan Dan Нерд

    Спасибо за помощь, но мне это не подходит, мне нужно именно перевод типа String в тип int для того, чтобы перевести числа из ASCII в число float.
     
  13. akl

    akl Гуру

    функция atof занимается переводом строки char* в float. переделать string в строку char* очень просто. при чем тут int непонятно
     
  14. Dan Dan

    Dan Dan Нерд

    Мне не нужно переводить строку сразу в float. Я пока не понимаю в целом путь значений от ввода до выход, поэтому экспериментирую и разбираюсь.
     
  15. Dan Dan

    Dan Dan Нерд

    Я хотел сделать следующее: у меня по цифрам выходят значения в ASCII ( например 49 13 10), преобразовать их в отдельные char, а затем склеить их в String, а уже данный String перевести в float.
     
  16. Dan Dan

    Dan Dan Нерд

    По крайней мере сейчас хочу
     
  17. b707

    b707 Гуру

    Вам советуют как это делать правильно.
    А хотите изврата - делайте как хотите, но тогда зачем спрашивать?
     
    Последнее редактирование: 15 июн 2021
  18. Dan Dan

    Dan Dan Нерд

    Думаю, мне решать что для меня изврат, а что нет. То, что мне посоветовали, переводить цифру в тип float, а мне сначала нужно расшифровать то, что приходит с ASCII, а потом уже переводить в нужный тип данных.
     
  19. akl

    akl Гуру

    оно переводит во float не цифру, а строку ASCII. если у вас приходит строка, в которой вместо например "12.34" написано что-то вроде "двенадцать целых тридцать четыре сотых", то таки придется поизвращаться.

    вернее не во float а в double, но это уже тонкости
     
    Последнее редактирование: 15 июн 2021
  20. akl

    akl Гуру