Почему переменные PWM в этом фрагменте кода RC обрабатываются таким образом? Что такое магические числа?

tcommand=(pwm_value-480.0)/8.22;
flapmag=(pwm_value-880)/41.0+10;

В этом фрагменте кода, используемом в радиоуправляемом самолете arduino, я не понимаю, почему значение ШИМ вычитается и делится следующим образом. Это не мой код, он пришел отсюда. Любое объяснение было бы очень кстати. Я читал о ШИМ, но все они имеют разные объяснения.

//Arduino servo flap system
//copyright Steve Morris 10-25-16
#include <Servo.h>
volatile int pwm_value = 0;
volatile int prev_time = 0;
static int servo_comm1 = 0;
static int servo_comm2 = 0;
volatile int flapangle1 = 0;
volatile int flapangle2 = 0;
volatile int millisold = 0;
int millinow = 0;
float dt = 0;
float flapmag = 0;
static float flapdeg = 0;
float tcommand = 0;
float floattime = 0;
float temp = 0;
float glide_deg = -10.0;
static int pwm_value_temp = 0;
float omegadot = 0.0;
float thetadot = 0.0;
static float omega = 0.0;
static float theta = 0.0;
static float k0 = 1.0;
static float k2 = 10.0;
static float servo_zero1 = -4;
static float servo_zero2 = 0;

Servo myservo1, myservo2; // create servo object to control a servo

void setup() {
  //Serial.begin(115200);
  // when pin D2 goes high, call the rising function
  attachInterrupt(0, rising, RISING);
  myservo1.attach(5); // attaches the servo on pin 5 to the servo object
  myservo2.attach(6); // attaches the servo on pin 6 to the servo object
}
void loop() {

  millinow = millis();
  floattime = millinow / 1000.0;
  dt = (millinow - millisold) / 1000.0;
  millisold = millinow;

  tcommand = (pwm_value - 480.0) / 8.22;
  omegadot = k0 * tcommand - k2 * omega;
  thetadot = omega;
  flapdeg = sin(theta);
  theta = theta + omega * dt;
  omega = omega + omegadot * dt;

  flapmag = (pwm_value - 880) / 41.0 + 10;
  flapdeg = flapmag * sin(theta); //variable amplitude+freq


  flapangle1 = (int)((30.0 - flapdeg + servo_zero1) * 2.0 + 25.0);
  flapangle2 = (int)((30.0 + flapdeg + servo_zero2) * 2.0 + 25 .0);

  if (pwm_value > 930) {
    servo_comm1 = flapangle1;
    servo_comm2 = flapangle2;
  }

  //Glide Lock
  if (pwm_value < 910) {

    servo_comm1 = (int)((30.0 - glide_deg + servo_zero1) * 2.0 + 25.0);
    servo_comm2 = (int)((30.0 + glide_deg + servo_zero2) * 2.0 + 25.0);

  }

  myservo1.write(servo_comm1); // tell servo to go to position in variable 'pos'
  myservo2.write(servo_comm2); // tell servo to go to position in variable 'pos'

  //Serial.println(servo_comm1);
  //Serial.println(floattime);
  //Serial.println(dt);
  //Serial.println(flapangle);
  //Serial.println(flapdeg);
  //Serial.println(temp);
  //Serial.println(tcommand);
  //Serial.println(pwm_value);

}

void rising() {
  attachInterrupt(0, falling, FALLING);
  prev_time = micros();
}

void falling() {
  attachInterrupt(0, rising, RISING);
  pwm_value = micros() - prev_time;
}

, 👍-1

Обсуждение

Вы не предоставляете никакого контекста для этих двух строк. Как мы должны их понимать? Это всего лишь расчет. То, что одна задействованная переменная называется "pwm_value", не делает вычислений о PWM в целом. Мы не можем знать, что такое tcommand и "flapmag". Пожалуйста, предоставьте полный код. Тогда мы могли бы понять это, @chrisl

Это код для приема радиоуправляемой связи и управления моделью самолета или что-то в этом роде?, @Majenko

Как сказал @chrisl, нам нужно достаточно контекста, чтобы понять, что означают эти ценности. Это прекрасный пример того, почему магические числа не приветствуются в любом стиле кода., @the busybee

Вам нужно исправить свой вопрос, чтобы предоставить больше информации, или удалить его., @Duncan C


1 ответ


5

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

Итак, имея это в виду, давайте возьмем эту строку:

flapmag=(pwm_value-880)/41.0+10;

Я предполагаю, что это делается для того, чтобы рассчитать, на сколько закрылки должны быть выдвинуты или убраны.

RC-контроллеры, а точнее приемники, которые улавливают сигналы, обычно выводят ШИМ-сигнал (квадратная волна с переменным рабочим циклом), где рабочий цикл напрямую связан с положением джойстика.

Джойстик обычно равен нулю в центре и идет плюс или минус вверх и вниз (или вправо и влево). Но вы не можете иметь отрицательное значение ШИМ, так как это не имеет смысла (как вы можете иметь сигнал, который выключен менее чем на 0% времени?!). Таким образом, выход "смещен" на определенную величину (это не совсем так - на самом деле джойстик равен 0 для полного опускания и 100% для полного подъема, причем "0" находится где-то посередине, так что смещение применяется джойстиком, а не приемником, но это действительно так сейчас это не имеет для нас значения).

Таким образом, у нас есть сигнал, который в любой момент находится где-то между 0% и 100%, а "ноль" находится где-то посередине. Какое фактическое значение имеет "100%" или где именно находится "ноль", может варьироваться в зависимости от многих факторов, поэтому мы просто будем использовать "100" для максимального и "50" для среднего смещения "нуля".

Первое, что вы хотите сделать, это удалить это "нулевое" смещение. А это так же просто, как вычесть его. В приведенном выше примере, если вы вычтете "нулевое" смещение "50" из входящего сигнала, он теперь будет находиться между -50 и +50 вместо 0 и 100. Теперь это гораздо лучше относится к тому, как выглядит джойстик - 0-50 в направлении вверх (+) и 0-50 в направлении вниз (-).

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

Далее идет деление. Это масштабирует входящее значение до нужного размера для устройства, которым вы управляете. В нашем примере мы имеем сигнал ±50. Но что, если нашим закрылкам требуется сигнал ±10, чтобы дать правильный диапазон движения? Таким образом, простое деление значения на 5 изменит ±50 на ±10. Это именно то, что делает /41.0 - масштабирование результата в соответствии с диапазоном целевого устройства.

Тогда есть еще один последний +10. Это просто добавляет небольшое смещение к результату. Вы можете думать об этом как о значении "обрезки". Он устанавливает фактическую "нулевую" точку целевого устройства - в данном случае, когда джойстик центрирован, это означает, что закрылки установлены в положение +10.

Что касается того, как создается само значение pwm_value, то это обычно делается путем захвата восходящих и нисходящих краев входящего сигнала от приемника и определения времени нахождения сигнала в ВЫСОКОМ или НИЗКОМ состоянии. Это может быть выполнено какой-то неопределенной библиотекой.

,

Собственно, именно об этом я и спрашивал. Спасибо за информацию., @Shivam Goswami