Алгоритм змішування аналогового входу на 2 осі для керування диференційним приводом двигуна


9

Я шукаю інформацію про те, як реалізувати правильне змішування 2 аналогових сигналів джойстика (вісь X і Y) для управління подвійним диференціальним приводом двигуна ("приводом, як танк"), використовуючи uC (ATMega328p в моєму випадку, але те саме має стосуватися і будь-який ЦК з входами АЦП та вихідними ШІМ):

У мене є аналогова паличка, яка дає 2 аналогові значення:

(напрямок) X: 0 до 1023
(дросель) Y: 0 до 1023

введіть тут опис зображення

Позиція відпочинку (напрямок і нейтраль дросельної заслінки) 512,512
Дросель вперед / напрямок ліворуч 0,0
Повний правий передній-повний правий 1023,0
і т.д.

Двигунами керують 2 драйвери H-мосту, по 2 ШИМ-шпильки для кожного (вперед, назад). Так:
Лівий двигун: від -255 до 255
Правий двигун: -255 до 255
(позитивні значення дозволяють ввести ШІМ-шпильку вперед, негативні включити назад ШИМ-штифт, 0 відключає обидва)

Метою є змішання джойстика аналогових сигналів для отримання наступної відповіді:

а) Дросель вперед, напрямок нейтральний = автомобіль рухається вперед
b) Дросель вперед, напрямок вліво = автомобіль рухається вперед і повертає вліво
в) Дросель нейтральний, напрямок вліво = автомобіль повертає ліво В МІСЦЕ, що правий мотор повний вперед, лівий двигун повний заднім ходом

... і аналогічно для інших комбінацій. Звичайно, вихід повинен бути "аналогом", тобто він повинен дозволяти поступовий перехід, наприклад, від варіанту а) до б) до с).

Концепція така:

http://www.lynxmotion.com/images/html/build123.htm


(1) Зауважте, що мій базовий алгоритм дозволяє контролювати швидкість «повороту на місці», коли джойстик натискається, наприклад, залишений на% від повної шкали. (2) Ця вимога, мабуть, вже багато разів повторюється. Модельне співтовариство повинно мати відповіді на це. (3) Якщо приймач переводить команди у швидкість треку, використовуючи зворотний зв'язок, транспортний засіб поводитиметься приблизно так само, як і зміни наземних умов. АЛЕ, якщо команди переводяться на потужність двигуна або напругу приводу тощо, продуктивність автомобіля буде змінюватися залежно від наземних умов. - імовірно 91) є кращим.
Рассел Макмахон

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

хороший день! двоюрідний брат Ренхо, який намагався інфантильного паралічу та конструкції інвалідного візка, їх програмування спрацювало добре, але вихідна напруга занадто низька! допоможи мені! Я використовую arduino uno.

@Johnny Ласкаво просимо до Electronics.Stackexchange! Перегляньте поширені запитання, щоб зрозуміти, як працює цей сайт, і якщо у вас виникли запитання, скористайтеся цією кнопкою у верхньому правому куті сторінки.
clabacchio

Це спрацювало ???
Рассел Макмахон

Відповіді:


4

"Правильне" змішування відкрите для дискусій :-).

Проблема полягає в тому, що ви повинні приймати рішення про те, як швидко трек рухається за чистими сигналами з одного горщика і що робити, коли входять сигнали з іншого горщика. Наприклад, якщо ви натискаєте ФБ (Вперед-назад горщик повністю вперед, і якщо обидва двигуни потім працюють на повній швидкості вперед, як ви будете мати справу з додаванням невеликої кількості LR (зліва-вправо), що додається. для обертання вам потрібно, щоб одна доріжка йшла швидше, ніж інша. Отже, якщо ви вже біжите з максимальною швидкістю вперед на обох моторах, ви повинні зменшити ту або іншу швидкість треку, щоб повернути. Але, якщо ви стояли на місці, ви прискорив би ту чи іншу колію, щоб досягти того ж результату.

Отже, все, що сказано, ось просте запуску рішення з манжети з моєї голови, що здається гарним початком.

Якщо горщики механічно незалежні, то обидва можуть бути на 100% одночасно.
Якщо обидва є устрою типу джойстика, якщо Yaxis = 100% і Xaxis = 0%, то додавання деякого B зазвичай зменшить A. Джойстик може бути побудований там, де зазначене вище не відповідає дійсності, але це незвично.
Припустимо, що джойстик має такий тип, що збільшення Y%, коли X = 100%, зменшить X. Можна зробити інші припущення.

FB = фронтально-задній горщик. Центр нульовий, + Ve для руху вперед горщика

LR = лівий правий горщик. Центр нульовий. + Ve для горщика справа.

K - початковий масштабний коефіцієнт 1.
Якщо будь-який результат перевищує 100%, то відрегулюйте K, таким чином, результат = 100% і використовуйте те саме значення K для іншого двигуна.

  • наприклад, якщо результат лівого двигуна = 125 і результат правого мотора = 80, то.
    Як 125 х 0,8 = 100, задайте K = 0,8. Тоді.
    Зліва = 125 х 0,8 = 100%. Праворуч = 80 х 0,8 = 64%.

Тоді:

  • Лівий двигун = K x (Front_Back + Left_Right)

  • Правий двигун = K x (Фронт_Бак - Ліворуч_право)

Перевірки обгрунтованості:

  • LR = 0 (по центру), FB = full fwd -> Обидва мотори працюють повністю вперед.

  • LR = повністю ліворуч, FB = 0 ->
    Лівий мотор працює повністю назад,
    правий мотор працює повністю вперед.
    Автомобіль обертається проти годинникової стрілки.

  • FB був 100%, Lr = 0%. Додайте 10% LR праворуч.
    L = FB + LR = 100% - + 10% R = FB-LR = 100% - - 10%

Якщо найбільша вісь <100%, масштаб до = 100%.
Потім масштабуйте іншу вісь на ту саму суму.


Дякую Расселлу - я спробую реалізувати це на моїй установці моделі. BTW, Мій джойстик здатний тримати вперед, рухаючи його вліво-вправо і навпаки, він дуже схожий на це: static.sparkfun.com/images/products/09032-03-L_i_ma.jpg
Kamil

1
На даний момент мені покладено завдання вирішити ту саму проблему на роботі. У мене є двовісний контролер wii nunchuk, і йому потрібно керувати 2 двигунами точно так, як описано в питанні. У мене тут є проблеми з розумінням логіки. Що саме стосується k1 / K1? Одна - це малі, а одна - великі, - чим вони відрізняються? Що таке + Ve?
Тал

1
Класно - дякую за роз’яснення. Мені потрібно було це написано на Python, тому якщо я правильно зрозумів, це слід зробити: pastebin.com/sWDakvLp . Це схоже на те, що я нічого не пропускаю? Здається, що я працюю в моєму тестовому середовищі - мені потрібно буде фактично підключити його до кінцевих двигунів, якими я буду користуватися точно.
Тал

1
1) Швидкість двигуна регулюється ШІМ, яка приймає значення лише від 0 до 100, тому я використовував 100 як максимальне значення. 2) Я використовую abs, щоб знайти, чи потрібно масштабування (як ви сказали), і щоб отримати масштабний фактор. Наприклад, якщо я закінчую коефіцієнт масштабу 0,8 і використовую його за від’ємним числом, -125 * 0,8 = -100. Напрямок зберігається. Я думаю, що це працює, якщо я щось не пропускаю. У мене ще не було можливості спробувати це на останніх моторах - мій бос будує тестову платформу з приєднаними моторами, які я зможу випробувати.
Тал

1
Я не був впевнений, чи дійсно мій код спрацює, тому я встановив, що попереднє посилання пастебіну закінчується через тиждень. Оскільки це, здається, працює, ось постійне посилання з ще кількома коментарями, якщо хтось знову зіткнеться з проблемою: pastebin.com/EKguJ1KP . Я б поставив це у відповідь, але, мабуть, у мене недостатньо респондентів, щоб розмістити відповідь. Весь код ґрунтується на відповіді Русселя Макмахона - кредит йому належить - спасибі Рассел.
Тал

5

Ось рішення, яке не вимагає складних ланцюгів if / else, не зменшує потужність при повному переміщенні вперед або обертанні на місці, а також дозволяє плавні криві та переходи від переміщення до прядіння.

Ідея проста. Припустимо, що (x, y) значення джойстика є декартовими координатами на площині квадрата. Тепер уявіть собі меншу квадратну площину, обернуту на 45 ° всередині неї.

Приклад площині

Координати джойстика дають точку у великому квадраті, а та сама точка, накладена на менший квадрат, дає значення двигуна. Вам просто потрібно перетворити координати від одного квадрата до іншого, обмеживши нові значення (x, y) сторонами меншого квадрата.

Існує багато способів перетворення. Мій улюблений метод:

  1. Перетворіть початкові (x, y) координати в полярні координати.
  2. Поверніть їх на 45 градусів.
  3. Перетворіть полярні координати назад до декартових.
  4. Пересуньте нові координати до -1,0 / + 1,0.
  5. Закріпіть нові значення до -1,0 / + 1,0.

Це передбачає, що початкові (x, y) координати знаходяться в діапазоні -1,0 / + 1,0. Сторона внутрішнього квадрата завжди буде дорівнює l * sqrt(2)/2, тому крок 4 - це як раз множення значень на sqrt(2).

Ось приклад реалізації Python.

import math

def steering(x, y):
    # convert to polar
    r = math.hypot(x, y)
    t = math.atan2(y, x)

    # rotate by 45 degrees
    t += math.pi / 4

    # back to cartesian
    left = r * math.cos(t)
    right = r * math.sin(t)

    # rescale the new coords
    left = left * math.sqrt(2)
    right = right * math.sqrt(2)

    # clamp to -1/+1
    left = max(-1, min(left, 1))
    right = max(-1, min(right, 1))

    return left, right

Початкова ідея цього методу - із значно складнішим методом трансформації - виникла з цієї статті .


0

Нижче наведено приклад реалізації алгоритму змішування, як описано у відповіді Рассела Макмахона:

http://www.youtube.com/watch?v=sGpgWDIVsoE

//Atmega328p based Arduino code (should work withouth modifications with Atmega168/88), tested on RBBB Arduino clone by Modern Device:
const byte joysticYA = A0; //Analog Jostick Y axis
const byte joysticXA = A1; //Analog Jostick X axis

const byte controllerFA = 10; //PWM FORWARD PIN for OSMC Controller A (left motor)
const byte controllerRA = 9;  //PWM REVERSE PIN for OSMC Controller A (left motor)
const byte controllerFB = 6;  //PWM FORWARD PIN for OSMC Controller B (right motor)
const byte controllerRB = 5;  //PWM REVERSE PIN for OSMC Controller B (right motor)
const byte disablePin = 2; //OSMC disable, pull LOW to enable motor controller

int analogTmp = 0; //temporary variable to store 
int throttle, direction = 0; //throttle (Y axis) and direction (X axis) 

int leftMotor,leftMotorScaled = 0; //left Motor helper variables
float leftMotorScale = 0;

int rightMotor,rightMotorScaled = 0; //right Motor helper variables
float rightMotorScale = 0;

float maxMotorScale = 0; //holds the mixed output scaling factor

int deadZone = 10; //jostick dead zone 

void setup()  { 

  //initialization of pins  
  Serial.begin(19200);
  pinMode(controllerFA, OUTPUT);
  pinMode(controllerRA, OUTPUT);
  pinMode(controllerFB, OUTPUT);
  pinMode(controllerRB, OUTPUT);  

  pinMode(disablePin, OUTPUT);
  digitalWrite(disablePin, LOW);
} 

void loop()  { 
  //aquire the analog input for Y  and rescale the 0..1023 range to -255..255 range
  analogTmp = analogRead(joysticYA);
  throttle = (512-analogTmp)/2;

  delayMicroseconds(100);
  //...and  the same for X axis
  analogTmp = analogRead(joysticXA);
  direction = -(512-analogTmp)/2;

  //mix throttle and direction
  leftMotor = throttle+direction;
  rightMotor = throttle-direction;

  //print the initial mix results
  Serial.print("LIN:"); Serial.print( leftMotor, DEC);
  Serial.print(", RIN:"); Serial.print( rightMotor, DEC);

  //calculate the scale of the results in comparision base 8 bit PWM resolution
  leftMotorScale =  leftMotor/255.0;
  leftMotorScale = abs(leftMotorScale);
  rightMotorScale =  rightMotor/255.0;
  rightMotorScale = abs(rightMotorScale);

  Serial.print("| LSCALE:"); Serial.print( leftMotorScale,2);
  Serial.print(", RSCALE:"); Serial.print( rightMotorScale,2);

  //choose the max scale value if it is above 1
  maxMotorScale = max(leftMotorScale,rightMotorScale);
  maxMotorScale = max(1,maxMotorScale);

  //and apply it to the mixed values
  leftMotorScaled = constrain(leftMotor/maxMotorScale,-255,255);
  rightMotorScaled = constrain(rightMotor/maxMotorScale,-255,255);

  Serial.print("| LOUT:"); Serial.print( leftMotorScaled);
  Serial.print(", ROUT:"); Serial.print( rightMotorScaled);

  Serial.print(" |");

  //apply the results to appropriate uC PWM outputs for the LEFT motor:
  if(abs(leftMotorScaled)>deadZone)
  {

    if (leftMotorScaled > 0)
    {
      Serial.print("F");
      Serial.print(abs(leftMotorScaled),DEC);

      analogWrite(controllerRA,0);
      analogWrite(controllerFA,abs(leftMotorScaled));            
    }
    else 
    {
      Serial.print("R");
      Serial.print(abs(leftMotorScaled),DEC);

      analogWrite(controllerFA,0);
      analogWrite(controllerRA,abs(leftMotorScaled));  
    }
  }  
  else 
  {
  Serial.print("IDLE");
  analogWrite(controllerFA,0);
  analogWrite(controllerRA,0);
  } 

  //apply the results to appropriate uC PWM outputs for the RIGHT motor:  
  if(abs(rightMotorScaled)>deadZone)
  {

    if (rightMotorScaled > 0)
    {
      Serial.print("F");
      Serial.print(abs(rightMotorScaled),DEC);

      analogWrite(controllerRB,0);
      analogWrite(controllerFB,abs(rightMotorScaled));            
    }
    else 
    {
      Serial.print("R");
      Serial.print(abs(rightMotorScaled),DEC);

      analogWrite(controllerFB,0);
      analogWrite(controllerRB,abs(rightMotorScaled));  
    }
  }  
  else 
  {
  Serial.print("IDLE");
  analogWrite(controllerFB,0);
  analogWrite(controllerRB,0);
  } 

  Serial.println("");

  //To do: throttle change limiting, to avoid radical changes of direction for large DC motors

  delay(10);

}

Цікаво, що цей код виглядає так, що він подає 2 аналогових штифта на 2 різних контролера двигуна. Я спробую адаптувати код і змінити для моїх налаштувань. Arduino Uno + 1 борт водія Sabertooth. 1 джойстик до аналогового pinA0 (x) pinA1 (y) зчитування та передачі значень на ШІМ-контакт 10 та 3, що йде до S1 та S2 Sabertooth. Я думаю, що я близький, але я заплутався в тому, як встановити перемикач на дошці Sabertooth. Наразі я налаштований на налаштування комутатора для отримання аналогового входу, перемикач 4 все ще знаходиться в положенні для диференціального приводу, але пізніше поверне його в незалежний режим для подальшого тестування. Я думаю, що це orig

@ user20514 Ласкаво просимо до electronics.stackexchange! Як ви могли помітити, це не форум, а веб-сайт із запитаннями, тому простір відповідей не призначений для обговорення. Будь ласка, не соромтесь задавати нове запитання, якщо у вас є що запитати, або використовуйте коментарі, щоб (дійсно) коментувати існуючі питання та відповіді.
clabacchio

1
@Kamil - відео показане як приватне. Це все ще доступно? youtube.com/watch?v=sGpgWDIVsoE
Рассел Макмахон

@RussellMcMahon відновився :)
Kamil Zadora
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.