Справочници, схемотехника, теория > Програмиране на микроконтролери, програматори, цифрови проекти

GPS-автопилот - алгоритъм

<< < (34/35) > >>

juliang:
Ще те оставя да го пробваш в реални условия за да се убедиш че не е най-доброто решение.
В почти всички случаи е много по-добре интегралната част да ползва записани данни от много по-стари събития от деривативната. В твоя случай деривативната ще измери скоростта на изменение за много голям период назад, и повече ще пречи отколкото да помага. Ей-таака на усет в твоя случай бих я взел спрямо 4-я или 5-я елемент, не от 9-я.
Предопоследния ред същи нещо не го схващам какво прави...
И още нещо - гледам ползваш float. Замисли се дали да не реализираш изискващия се от добрата практика anti-windup контрол на интегралната поправка (какъвто аз нямам в моя код :) ) Примерно взимаш само дробната част на грешката - частта след дес. запетайка. Със знака, естествено. Ако грешката е голяма - пропорционалната сама ще се справи, няма смисъл и интегралната да напъва заедно с нея. А когато грешката е малка, интегралната вече ще може да  коригира това, което пропорционалната не е успяла.
Но всичко зависи от това в какъв диапазон ще са ти входните и изходните данни.

dmitarp:
Аз бих го направил така:

int PID_regulator(float input, float setpoint, float kp, float ki, float kd, int minOut, int maxOut) {
 
  static float error;                                          // текуща грешка
  float derivative = 0;                                             //дефиринциална съставна
  static int Output = 0;                                           //изход - връща значение

  error = setpoint - input;                                     //текущо значение на грешката
 
  integral  +=  Ki*error;                                                         //интегрална съставна
  derivative = (error - error_old) * Kd;                  //деферинциална съставна
  Output = error* kp + integral + derivative;       //събираме трите съставни в изхода
  if(Output < minOut) Output = minOut;                    //ограничаване по минимум
  else if(Output > maxOut) Output  = maxOut;          //ограничаване по максимум
  else Output;                                                              //четем изхода без ограничения
  error_old = error                                                      // запазване на старата стойност на грешката
  return Output;                                                          //връщаме значението от изхода
 }


Променливите integral и error_old са глобални. Времето за интегриране и диференциране е замаскирано в стойностите на Ki и Kd съответно.
Процеса на регулиране е непрекъснат затова не е добра идея тази функция да се извиква през 5 секунди. Дори системата да е много инертна за тези 5 секунди остава валидно Output без промяна.

Настройката на регулатора се прави по следният начин:
Ki = 0
Kd = 0
Задаваш само стойност на Kp. От тази стойност зависи времето на реакция, и в известна степен крайната грешка след установяване. Колкото е по-голямо Kp и времето за реакция ще се намали, разбира се това време няма как да е по-малко от възможностите на изпълнителният механизъм. След като си удволетворен от времето на реакция, задаваш стойност на Kd, от него зависи пулсациите преди крайното установяване, както и времето за крайното установяване, колкото е по голямо Kd пулсациите намаляват и времето за крайното установяване намалява. И накрая като дадеш стойност на Ki, ще можеш да нагласиш грешката след установяването, така че да бъде близка до нула. Трябва да се избягват големи стойности на Ki защото се увеличава времето за установяване и пулсациите.

EDM electronics:

--- Цитат на: juliang в Април 27, 2022, 07:08:28 pm ---
1. Предопоследния ред същи нещо не го схващам какво прави...

2. И още нещо - гледам ползваш float. Замисли се дали да не реализираш изискващия се от добрата практика anti-windup контрол на интегралната поправка (какъвто аз нямам в моя код :) )

--- Край на цитат ---

1. Този ред е ненужен, сложил съм го по инерция. Всъщност това е реализация на една функция от С++, това е constrain(x, min, max) за оразмеряване на променлива и този трети ред има смисъл, ако функцията се ползва самостоятелно. Тук я ползвам вътре в друга функция и този ред се явява излишен, защото всъщност го чете от сбора по-горе.

2. Ползвам float защото входните ми данни са метри /разстояние от една GPS-точка до друга/ и там числото е веществено, за да отчита и дециметри. Нужно е, защото диапазона на входа който ме интересува е 3-4 м. Това е максимално допустимото разстояние на което ще ме мести течението, да го наречем дрейф, а и то е съобразено с точността на GPS-а, който за граждански цели е 3-5 м. но това е точност, която отговаря на самата география, иначе се надявам точността между 2 точки да е поне 1 м., без оглед на географията. Та входа ми е от 0-4 м число float.

Изхода ще е стойността на числото, което регулира оборотите на двигателя, да ме върне в запаметената точка с такава скорост, каквото е течението в момента, т.е. да отговаря на обстановката. От опит знам какъв трябва де е диапазона и скоростта - от 0 до 3 км/ч или това е число от 0-40. Изхода вече е не веществено число, не е необходим float. Имам вход 0-4 и изход от 0-40.

Системата както казах е доста инертна. Току така изневиделица течение не се появява, има само порив на вятъра, който също ще ме бута. Та един такъв порив не трябва да ми сменя скоростта, само тенденция.

Понеже съм наясно с входно-изходните параметри, ще мога да направя настройката на ПИД-а директно с плотера на компилатора. Само трябва да симулирам с един таймер различни стойности на входа и на графика ще виждам идеално какво става с изходните данни. Това ще го направя съвсем скоро.

EDM electronics:

--- Цитат на: dmitarp в Април 28, 2022, 08:42:54 am ---Аз бих го направил така:

int PID_regulator(float input, float setpoint, float kp, float ki, float kd, int minOut, int maxOut) {
 
  static float error;                                          // текуща грешка
  float derivative = 0;                                             //дефиринциална съставна
  static int Output = 0;                                           //изход - връща значение

  error = setpoint - input;                                     //текущо значение на грешката
 
  integral  +=  Ki*error;                                                         //интегрална съставна
  derivative = (error - error_old) * Kd;                  //деферинциална съставна
  Output = error* kp + integral + derivative;       //събираме трите съставни в изхода
  if(Output < minOut) Output = minOut;                    //ограничаване по минимум
  else if(Output > maxOut) Output  = maxOut;          //ограничаване по максимум
  else Output;                                                              //четем изхода без ограничения
  error_old = error                                                      // запазване на старата стойност на грешката
  return Output;                                                          //връщаме значението от изхода
 }


Променливите integral и error_old са глобални. Времето за интегриране и диференциране е замаскирано в стойностите на Ki и Kd съответно.
Процеса на регулиране е непрекъснат затова не е добра идея тази функция да се извиква през 5 секунди. Дори системата да е много инертна за тези 5 секунди остава валидно Output без промяна.

Настройката на регулатора се прави по следният начин:
Ki = 0
Kd = 0
Задаваш само стойност на Kp. От тази стойност зависи времето на реакция, и в известна степен крайната грешка след установяване. Колкото е по-голямо Kp и времето за реакция ще се намали, разбира се това време няма как да е по-малко от възможностите на изпълнителният механизъм. След като си удволетворен от времето на реакция, задаваш стойност на Kd, от него зависи пулсациите преди крайното установяване, както и времето за крайното установяване, колкото е по голямо Kd пулсациите намаляват и времето за крайното установяване намалява. И накрая като дадеш стойност на Ki, ще можеш да нагласиш грешката след установяването, така че да бъде близка до нула. Трябва да се избягват големи стойности на Ki защото се увеличава времето за установяване и пулсациите.

--- Край на цитат ---

dmitarp това което представяш е всъщност кода, който се подвизава навсякъде в Интернет за ПИД-регулатор. Т.е. имаме за сравнение само две стойност - текуща и стара, предишна. Аз възприех идеята на Юлиан, защото той ме убеди с тази оценка на бъдещето по повече на брой измервания в миналото.

Обаче така както си го представил променливата integral нищо не я нулира при всяка итерация тя ще натрупва стойност, като брояч. После мисля, че няма нужда да е глобална, умишлено не съм писал глобални, за да се ползва функцията универсално, без да се добят извън нея променливи и затова си има лесно - правят се статични вътре в самата функция. Така "живота" им се запазва.

В случая обаче променливата integral не е нужно да запазва стойността си, може да се нулира предварително автоматично, когато не е статична или дори да е статична ще приема следващата стойност на сбора.

dmitarp:
В случая интегралната съставна използва всички минали стойности, и няма как да се натрупа безкрайно стойност, защото грешката може да бъде и положителна и отрицателна. За дефиренциалната съставка може да се ползва и по стари данни, всяка от коита да идва с нейната си тежест, но това не променя почти нищо, всичко зависи от времето за актулизация, т.е. от тези 5 секунди, ако станат примерно 0,05 секунди тогава не ти трябват повече от една стара стойност, но ако си останат 5 секунди, колкото и стари стойности да вземеш, по-голяма точност няма да постигнеш.

Навигация

[0] Списък на темите

[#] Следваща страница

[*] Предходна страница

Премини на пълна версия