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

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

<< < (28/35) > >>

VITAN:
Браво, браво  8)
Е, с 2 ардуино-та ще стане  :)

EDM electronics:
Сблъсках се със следния проблем:
Счетох, че вътрешната памет на Ардуино НАНО е твърде малка за запис на маршрути и поставих външна 24CL128. Това е огромна памет за целта.
Проблема обаче се състои в това, че езикът IDE /компилатора най-вече/ възприема най-голямо число с променлива запетая тип float. То е с големина 4 байта. Има и double, но то е същото float.
При смятане с такива числа или запис в паметта точността се ограничава до 7 разряда, независимо от запетаята. Т.е. точността на 4235672.3 е същата като 42.356723. Последната цифра /осмия разряд/ варира с 2-3 стойности.

Някой от програмистите, имате ли идея как да го направя с абсолютна точност до 8 разряд?

Пробвах при запис с умножение по сто, а при четене с деление на 100, но не става, запазва се точността до 7 разряд, осмия вече варира.

Записа правя, като записвам 4 отделни байта с разбиване на числото float на 4, а при четене се четат отделните байтове и се сумира в тип float . Ползвам две отделни функции за запис и четене.

#include <Eeprom24C128_256.h>
#define EEPROM_ADDRESS  0x50
static Eeprom24C128_256 eeprom(EEPROM_ADDRESS);

void setup()
{
  float  val_1 = 42.899751;
  float  val_2 = 0;
  Serial.begin(9600);
  eeprom.initialize();
 
  EEPROM_float_write(0, val_1);
  for (int ii = 0; ii<4; ii=ii+4)
  {
    val_2 = EEPROM_float_read(ii);

  // запис в серниния порт
 
  Serial.println(val_2,6);
  }
 
}

void loop(){}


void EEPROM_float_write (int addr, float  val) // запис в ЕЕПРОМ

  byte *x = (byte *)&val;
  for(byte i = 0; i < 4; i++)
  {
    eeprom.writeByte(i+addr, x);
    delay(10);
  }
}

float  EEPROM_float_read (int addr) // четене от ЕЕПРОМ
{   
  byte x[4];
  for(byte i = 0; i < 4; i++) x = eeprom.readByte(i+addr);
  float  *y = (float *)&x;
  delay(10);
  return y[0];
}

juliang:

--- Цитат на: juliang в Февруари 17, 2020, 09:28:03 pm ---Ако работиш с числа с плаваща запетая нещата вероятно ще са малко по-добри, но трябва да внимаваш изключително много при преминаването от абсолютни географски координати към разстояния. Не е изключено катастрофално да загубваш точност, защото ардуиното си работи с числа със 7-8 значещи цифри.

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

Аз като говоря не приказвам...
Решението е да вземеш една сравнително близка точка за "нула" и да работиш с цели числа спрямо нея. 4 байта са +/- 2 милиарда, което ако работиш в сантиметри са +/- 20 000 километра. Така че ако смяташ в цели числа дори и ако приемеш нулата за нула градуса северна и нула градуса източна... пак България ще влезе в обхвата. А ако хванеш една точка в България, или примерно 45 градуса северна и 45 градуса източна, то ще можеш да обхванеш цяла България дори и в милиметри (което е абсолютно безмислено).

Обаче ти искаш да ползваш синуси, тангенси, квадратни корени ... дето връщат нецели числа... Не че не става, но пак ще трябва да мислиш какво се случва и кога е възможно рязко да загубиш точност.

НЕ МОЖЕШ да работиш с 8-я разряд, защото сметките ти ще са грешни. Дробните числа се представят като а*1/2 + b*1/4 + c*1/8 + d*1/16 + .... (а, b, c, d.... са битове и са 0 или 1) и просто няма как да запишеш точно 1/3 или 1/7. Затова вътрешно се смята с 8 знака точност, но ти се дава само до 7-я, който със сигурност е точен, а 8-я се спестява защото не винаги отговаря на истината.

EDM electronics:
Ползвам формулата с тангенси, котангенси и т.н. предимно защото само с нея мога да реализирам котвата. Не че твоята идея не е добра, напротив. Дори съм решил, скоро като завърша проекта, да направя и твоя алгоритъм, винаги мога да направя отделен скеч, като сменя само алгоритъма на навигацията, друго се запазва.

Седем разряда са напълно достатъчни за точността, но аз малко прекалявам и се престаравам. Осмия разряд от координатите от 0-9 дава толеранс 1 м. Точността ми в този разряд бяга с до 3 единици или 30 см. Това говоря при запис на координатите в паметта. Такава точност при следване на маршрут не ми е нужна и бих казал практически невъзможна, защото има много други фактори, които ще пречат на такава точност, главния от които е GPS-приемника.

При него 8-ми разряд варира с до 10-15 единици и ако няма хубав сигнал дори повече - играе си там постоянно се мени. Мислих да му слагам медиантен филтър и може би ще му сложа, защото този филтър елиминира отскоците и не дава средна средна аритметична стойност, като сбор от някщолко, а отделя средното показание от няколкото, колкото задам - примерно 5-6, ако настроя GPS-a на 5-6 Hz опресняване.

Та с тая любителска техника не мога да постигна кой знае какво. Има много добри приемници, но са скъпи и у нас трудно се намират, а и не виждам чак такъв смисъл, да гоня сантиметри точност. Дори в тази връзка съм предвидил умишлено намаляване на точността с около 2 м. Т.е. при наближаване на отправната точка, ако разчитам на навигатора да мина през нея, елеминирайки вълни, вятър и т.н., няма да се случи често и ще подмина точката, тогава навигатора ще дава команда назад, кръгове и т.н., което е недопустимо. Зетова в скеча съм задал условие, ако текущото разстояние е по-малко или равно на 2 метра, се зарежда следващата запеметена точка от маршрута за следване. Два метра това е радиус, т.е. трябва да улуча кръг с размер 4 м, изпусна ли го почва кръжене.  ;)

Иначе има софтуерно решение на проблема с точността на 8-мия разряд, но все още не съм го осмислил. Функция, която свежда грешката на 8-мия разряд до 9.5, т.е. остава погрешност само 0,5 или това е да го наречем 5 см. Ако имаш интерес погледни и коментирай:

http://arduino.ru/forum/programmirovanie/etyudy-dlya-nachinayushchikh-tip-float-kak-ne-utonut#comment-341969

//   Возведение числа f в целую, неотрицательную степень n
//
float iPow(const float f, const int n) {
   if (n <= 0) return 1;
   if (n == 1) return f;
   float f2 = iPow(f, n / 2);
   f2 *= f2;
   return (n & 1) ? f2 * f : f2; // если n - нечётно, надо ещё раз домножить на f
}

nizo:
EDM, ако отвориш документацията:

https://www.arduino.cc/reference/en/language/variables/data-types/double/

пише, че double е обикновенно 4 байта, но Arduino Due има 8 байтов double с такава конструкция.

Навигация

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

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

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

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