Помогите реализовать запись числа с точкой

Вопросы настройки, программирования, прошивки микроконтроллеров и микросхем программируемой логики
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение ARV »

1. речь идет об AVR? не так ли? этот тип МК нельзя назвать ни быстрым, ни обладающим просторным ОЗУ
2. и большие и маленькие числа можно использовать в предложенным мною варианте, надо просто продумать алгоритм вычислений так, чтобы не было потери точности.
если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе
при взгляде на многих сверху ничего не меняется...

Мой уютный бложик... заходите!
codenamehawk
Вымогатель припоя
Сообщения: 528
Зарегистрирован: Вт фев 09, 2010 17:52:26

Re: Помогите реализовать запись числа с точкой

Сообщение codenamehawk »

ARV писал(а):1. речь идет об AVR? не так ли? этот тип МК нельзя назвать ни быстрым, ни обладающим просторным ОЗУ

1.Да. Далее, МК быстрый ( первый комп с процом Интел был всего на 1 М), но не умеет он выполнять операции с плавающей точкой. ОЗУ мало, но не значит что не хватает.
2. и большие и маленькие числа можно использовать в предложенным мною варианте, надо просто продумать алгоритм вычислений так, чтобы не было потери точности.

2. Можно, но зачем, если автор подтвердил, что нет потери точности.

(По вашему, первому посту, сложно разобраться, как написать алгоритм программы.
Проще тогда уж проще знаки цифр, принятые до запятой, преобразовать в целостной тип и сохранить в одной переменной(целая часть числа),
а знаки цифр принятые после запятой, преобразовать в целостной тип и сохранить в второй переменной(дробная часть числа).
Дальше целочисленная арифметика, как на бумажке.
В данной ситуации надо ли это?)
Аватара пользователя
ARV
Ум, честь и совесть. И скромность.
Сообщения: 18544
Зарегистрирован: Чт дек 28, 2006 08:19:56
Откуда: Новочеркасск
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение ARV »

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

Мой уютный бложик... заходите!
Аватара пользователя
avreal
Опытный кот
Сообщения: 842
Зарегистрирован: Чт дек 31, 2009 19:27:45
Откуда: Бровари, Україна
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение avreal »

Andrey19888 писал(а):Спасибо. Заработало. Единственный момент, что в переменной sdf по прежнему остается неточное значение, а мне с ней нужно будет работать. Как там можно поправить?

А Вам не приходило в голову, что десятичное число 12.075, т.е. дробь 12075 / 1000, имеющая в знаменателе число, раскладываемое на 2-ки и 5-ки, может быть просто непредставимо точно в виде дроби X / 2^N (т.е. числа, имеющего в знаменателе только двойки) с конечным N?
Почитайте про представление чисел с плавающей запятой в двоичной форме.
Простой пример:
число 0.75 раскладывается на 0.5+0.25 и с фиксированной запятой записывается в двоичной форме как 0,11 (1/2 + 1/4)
А 0.76?
0.5+0.25+0,0078125 = 0,7578125, в двоичной форме
0,11000010
и остаток 0,0021875
Т.е. при фиксированной запятой и восьми двоичных знаках после запятой мы можем задать числа
0,11000010 -> 0,7578125
0,11000011 -> 0,76171875
А всё, что между ними - ну никак.
Можем добавить разрядности, получим что-то в духе 0.110000101000111101011100 ошибка уменьшится, но до нуля не упадёт.
0.110000101000111101011100 -> 0,75999999
0.110000101000111101011101 -> 0,76000005
Последний раз редактировалось avreal Ср янв 05, 2011 17:00:59, всего редактировалось 2 раза.
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
clawham
Поставщик валерьянки для Кота
Сообщения: 1957
Зарегистрирован: Пт окт 31, 2008 09:38:55
Откуда: Одесса
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение clawham »

Уважаемые знатоки математики, с моей-то идиократией не поможете обойтись без флоата? ибо кроме разрядности мне реально не хватает скорости...а ещё надо экспоненту....
камень мега 8 генератор 8 мегагерц....внешний кварц - расточительство...
Что нас не убило сделало нас осторожней
Не доверяйте русским лужам - это может быть вход в метро.
codenamehawk
Вымогатель припоя
Сообщения: 528
Зарегистрирован: Вт фев 09, 2010 17:52:26

Re: Помогите реализовать запись числа с точкой

Сообщение codenamehawk »

avreal писал(а):
Andrey19888 писал(а):Спасибо. Заработало. Единственный момент, что в переменной sdf по прежнему остается неточное значение, а мне с ней нужно будет работать. Как там можно поправить?


Если почитаете, то это уже опровергли, т.е. точность, для приведенных данных, не теряется.

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

avr писал(а):умножение двух флоатов длится долго, использовать их в прерывании, например, становится весьма сложно


Тут, как говорится и спору нет. А делить еще хуже.
Аватара пользователя
avreal
Опытный кот
Сообщения: 842
Зарегистрирован: Чт дек 31, 2009 19:27:45
Откуда: Бровари, Україна
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение avreal »

Код: Выделить всё

 (float)(Gas_in-Mem_Min_Gas)/(Mem_Max_Gas-Mem_Min_Gas)*0x03ff;
Это приблизительно то же самое, что и

Код: Выделить всё

 (long)(Gas_in-Mem_Min_Gas) * 0x03ff / (Mem_Max_Gas-Mem_Min_Gas);
Или можно так записать

Код: Выделить всё

 0x03ffUL * (Gas_in-Mem_Min_Gas)  / (Mem_Max_Gas-Mem_Min_Gas);

(long) чтобы хватило разрядности
В конкретных ксловиях может оказхаться выгодным выписать на ассемблере (ассемблерными вставками) свои умножения, скажем, 16бит*16бит -> 24 бита или даже 16бит * 8 бит = 24 бита, в зависимости от возможного диапазона изменения как входных, так и выходных переменных.

В данном случае ещё надо глянуть - может (Mem_Max_Gas-Mem_Min_Gas), которые, как я понимаю, берутся из каких-то настроек и меняются относительно медленно, сразу пересчитывать. Т.е. заранее рассчитать коэффициент (перерассчитывать при введении коррекции)

Код: Выделить всё

gas_coeff = 0x03FFUL * SCALE / (Mem_Max_Gas-Mem_Min_Gas);
где SCALE это удобная степень двойки. И дальше считать

Код: Выделить всё

 gas_coeff * (Gas_in-Mem_Min_Gas)  / SCALE;
Деление на SCALE превратится в сдвиг. Если SCALE = 256 или 65536, то сдиг превратится в пересылку байтов.
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Аватара пользователя
avreal
Опытный кот
Сообщения: 842
Зарегистрирован: Чт дек 31, 2009 19:27:45
Откуда: Бровари, Україна
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение avreal »

codenamehawk писал(а):Если почитаете, то это уже опровергли, т.е. точность, для приведенных данных, не теряется.
Что опровергли?

Ну дже если не теряется точность для 12.075 то будет теряться для 12.074. Или прикажете вводить с пульта только те числа, для котрых тоность не теряется?
Согласен, для конкретных цеелй float может быть выше крыши по точности, ошибка будет меньше имеющей смысл для рассмотрения (ох-ох, 12.0749999 отличается от 12.075 аж на одну десятимиллионную), но я бы посоветовал не надеяться, что любое даже относительно "короткое" десятичное число будет точно представлено относительно "длинным" двоичным float точно.

Что я и сделал.
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Andrey19888
Родился
Сообщения: 12
Зарегистрирован: Вс июл 16, 2006 23:52:49

Re: Помогите реализовать запись числа с точкой

Сообщение Andrey19888 »

И всетаки вы правы. После экспериментов с разными числами, ошибка реально есть. Уже даже не знаю как выйти из ситуации. Разложение на 2 целых числа не вариант т.к. если ввести 23.005 в одну переменную пойдет 23, а в другую то чушь. Или как представить мелкое число, например 0.002?
Жизнь как рояль: клавиша белая, клавиша черная, крышка
Аватара пользователя
avreal
Опытный кот
Сообщения: 842
Зарегистрирован: Чт дек 31, 2009 19:27:45
Откуда: Бровари, Україна
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение avreal »

codenamehawk писал(а):
Andrey19888 писал(а):Безрезультатно. На экране всеравно 12,074999. Если в sprintf(s,"%.3f",sdf); на экране правильно, но в переменной же всеравно нет :(

Это похоже функция sprintf выдает такой результат, а переменная sdf равна 12.075
Просмотрел ещё раз тему, и так и не понял, что "уже опровергли".

Andrey19888 писал(а):С выводом я уже разобрался. Волновала только проблема что в переменной хранится не правильное число. Но эксперимент показал что функция sprintf не корректно отрабатывает и приходится округлять
Нормально отрабатывает функция sprintf.
Число 12.075 не представимо в виде конечной двоичной дроби, кто не верит, может проверить.
Так что ни в 32-битном float, ни в 64-битном double точно записано быть не может.
Другое дело, что я не пойму, что так волнует - ну округляйте до нужной разрядности, а точности должно хватить.
sprintf не обязан угадывать, "что приблизительно было" и выводить "красивое" число.
Вот на PC

Код: Выделить всё

#include <stdio.h>
float f = 12.075;
int main()
{
        printf("%.8f\n", f);
        printf("%.6f\n", f);
        printf("%.4f\n", f);
        printf("%.2f\n", f);
        printf("%.1f\n", f);
}
12.07499981
12.075000 ; 8-ка в 7-мом знаке после запятой пошла в округление, поэтому вместо 3 девяток вышло 3 нуля
12.0750
12.07 ; 7-ка нечётное, 5-ка округялется вниз, для 12.085 будет 12.09
12.1 ; ну тут, понятно, и 7-ка округлилась

Единственная некорректность Вашего sprintf — так это то, что без указания разрядности, т.е. при формате %f, по умолчанию должно быть 6 знаков после запятой и он должен был округлить до 6 знаков и вывести как во второй строке выдачи выше. Но переменной не 12.075, это двоичный код...
Последний раз редактировалось avreal Ср янв 05, 2011 18:35:45, всего редактировалось 1 раз.
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Аватара пользователя
avreal
Опытный кот
Сообщения: 842
Зарегистрирован: Чт дек 31, 2009 19:27:45
Откуда: Бровари, Україна
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение avreal »

Andrey19888 писал(а):Разложение на 2 целых числа не вариант т.к. если ввести 23.005 в одну переменную пойдет 23, а в другую то чушь. Или как представить мелкое число, например 0.002?
У Вас все числа должны быть не больше 3 десятичных знаков после запятой?
Напишите свою функцию разбора строки в число, которая будет выдавать целое число, но умноженное на 1000. Т.е. при вводе числа точку игнорировать, но считать, сколько вышло символов после десятичной точки. И если их меньше 3, то домножать на 10 столько раз, сколько не хватило.
И дальше все рассчёты вести с учётом того, что числа умножены на 1000.
При выводе

Код: Выделить всё

  int a = 12075;
  sprintf( "%d.%03u", a / 1000, a % 1000);


upd - с отрицательными числами, пожалуй, придётся врукопашную, как-то так

Код: Выделить всё

  int a = 12075;
  char sig;
  int b;
  if( a > 0) {
    sig = ' '; // или '+', по вкусу
    b = a;
  } else {
    sig = '-';
    b = -a;
  }
  sprintf( "%c%d.%03u", sig, a / 1000, a % 1000);


Тху!!! Уже ж 5-е число!!!!!!
Конечно, везде sprintf-у буфер указывать :-) :beer:
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
codenamehawk
Вымогатель припоя
Сообщения: 528
Зарегистрирован: Вт фев 09, 2010 17:52:26

Re: Помогите реализовать запись числа с точкой

Сообщение codenamehawk »

Andrey19888 писал(а):Разложение на 2 целых числа не вариант т.к. если ввести 23.005 в одну переменную пойдет 23, а в другую то чушь.


Во вторую уйдет 005

Andrey19888 писал(а): Или как представить мелкое число, например 0.002?

Мелкое число 0.002
в первой переменной (целая часть) равна 0 , во второй переменной (дробная часть) равна 002
(Приняли цифры до точки, загнали в первую переменную. Далее, после того, как пульт принял точку, начинаете принимать дробную часть.)
codenamehawk
Вымогатель припоя
Сообщения: 528
Зарегистрирован: Вт фев 09, 2010 17:52:26

Re: Помогите реализовать запись числа с точкой

Сообщение codenamehawk »

Да с ними, как с целыми числами не поработаешь.
clawham
Поставщик валерьянки для Кота
Сообщения: 1957
Зарегистрирован: Пт окт 31, 2008 09:38:55
Откуда: Одесса
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение clawham »

это то понятно что можно некоторые коэффициенты рассчитать "до того как"

некоторые это разве что дельтагаз

тоесть из кода

Код: Выделить всё

TempA = (float)(In_Gas);
TempA = TempA - (float)(Mem_Min_Gas);
Delta_Gas = Mem_Max_Gas;
Delta_Gas-= Mem_Min_Gas;
TempA = TempA/Delta_Gas;
TempA = TempA * 500.0;


вынести за рамки основного цикла кусочек

Код: Выделить всё

Delta_Gas = Mem_Max_Gas;
Delta_Gas-= Mem_Min_Gas;

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

как-то от них можно избавиться?
может ввести некую предварительно расчитанную переменную delta_gas_div_500 = Delta_Gas/500.0
и сократить

Код: Выделить всё

TempA = TempA/Delta_Gas;
TempA = TempA * 500.0;

до

Код: Выделить всё

TempA = TempA/delta_gas_div_500;


всёравно это деление флоатов...тобиш еруньдя...

А вот ещё на последок вторая математическая головоломка:
есть такое понятие как експонента...в авиамоелизме подразумевает нелинейную зависимость выхода от входа а хитрую кривую и с третьего(или 4-го) канала задавать процент влияния этой кривой на выход тоесть при 0 процентах вход=выходу, при 100% выход равен експоненте, а при 50% выход равен среднему между входом и експонентой)

Изображение

Вот этот график...
есть в екселе все его циферки )
есть формула но она сложная и не думаю что это реально - впихать её в МК
Вложения
экспонента.xls
тот самый екселик
(30.5 КБ) 157 скачиваний
Что нас не убило сделало нас осторожней
Не доверяйте русским лужам - это может быть вход в метро.
Аватара пользователя
avreal
Опытный кот
Сообщения: 842
Зарегистрирован: Чт дек 31, 2009 19:27:45
Откуда: Бровари, Україна
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение avreal »

clawham писал(а):это то понятно что можно некоторые коэффициенты рассчитать "до того как"
...
вынести за рамки основного цикла кусочек

Код: Выделить всё

Delta_Gas = Mem_Max_Gas;
Delta_Gas-= Mem_Min_Gas;
и всё...а я не думаю что вычитание двух интов получаются очень затратными по времени процессора....
гораздо круче по времени занимают последние две строки - деление и умножение флоата
Я имел ввиду другое - по виду имени переменные Mem_* навевают мысль о том, что это некие корректирующие коэффициенты, которые меняются редко и сохраняются где-то.
Если это так, то можно при изменении сразу пересчитывать в более сложные коэффициенты и уже их использовать каждый раз в формулах.
Ну и к фиксированной запятой перейти аккуратно.
Любое деление выполняется гораздо дольше, чем умножение. Для AVR даже целочисленное деление 32 бит может выполняться дольше плавающего умножения. Так что если есть возможность пересчитать 1/coeff заранее, то лучше так сделать и умножать, чем делить.

clawham писал(а):А вот ещё на последок вторая математическая головоломка:
есть такое понятие как експонента...в авиамоелизме подразумевает нелинейную зависимость выхода от входа а хитрую кривую и с третьего(или 4-го) канала задавать процент влияния этой кривой на выход тоесть при 0 процентах вход=выходу, при 100% выход равен експоненте, а при 50% выход равен среднему между входом и експонентой)
Попробуйте аппроксимировать кубической параболой. Что-то в духе

Код: Выделить всё

     y = 50 + ((x-50)^3) / 250
Ну и опять таки, с учётом доп канала k это будет

Код: Выделить всё

     y = 50 +  k * ((x-50)^3) / 250 / 100 + (100 - k) * (x - 50) / 100 
если я правильно понял.
Порисуйте графики, гляньте отличие в %-тах, может устроит.

И, опять таки, смотря сколько раз в секунду пересчитывается y из x и сколько раз в секунду меняется k - можно заготавливать коэффициенты заранее, кроме k туда включать и константы и только множить, а в конце только поделить один раз.
Скажем (это математически формулы, а не запись на С)

Код: Выделить всё

 y * 65536 = 50 * 65536 +  65536 * k * ((x-50)^3) / 25000 + 65536 * (100 - k) * (x - 50) / 100
           = 50 * 65536  +  65536 * k * ((x-50)^3) / 8 / 3125 + 655 * (100 - k) * (x - 50)
21 отличается от 65526 / 3125 на 0.13%
655 отличается от 65536 / 100 на 0.05%
           = 3276800 +  21 * k * ((x-50)^3) / 8  + 655 * (100 - k) * (x - 50)     // [1]
k1 = 21 * k
k2 =  655 * (100-k)
           = 3276800 +  k1 * (x - 50) * (x - 50) * (x - 50) / 8  + k2 * (x - 50)  // 4 умножения + 3 сдвига на бит
           = 3276800 +  (x - 50) * (k1 *  (x - 50) * (x - 50) / 8  + k2)              // 3 умножения + 3 сдвига на бит
и вот теперь, если я нигде не ошибся, в конйе рассчёта для получения y делим результат на 65536, т.е. сдвигаем на 16 бит вправо, т.е. берём старшие два байта. Точнее, третий байт.
Почему я и говорил о выписывании арифметики на 24 бита -- надо внимательно глянуть, и если промежуточные результаты не выходят за пределы 25 бит, то может быть выгодно выписать всё на 3 байта.
Теперь при изменении k пересчитываются k1 и k2 - это будет выгодно, если k меняется в несколько раз реже, чем производится пересчёт y.
Если нет - считать как по формуле [1] с учётом схемы Горнера для полинома для уменьшения умножений хотя бы на одно :-)

В таком варианте всё в целых числах и без делений.
Лень в виде мании величия: «ты гений, зачем стараться?». В виде комплекса: «всё равно не выйдет, зачем упираться?». Как логика: «если достаточно, зачем знать и уметь больше?». Цель одна: остановить. Не любит тепло работающих мышц и шум работающего мозга.
Аватара пользователя
Jack_A
Друг Кота
Сообщения: 6307
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Re: Помогите реализовать запись числа с точкой

Сообщение Jack_A »

avreal писал(а):Попробуйте аппроксимировать кубической параболой.


А она и есть кубическая парабола ( или квадратичная со знаком y = sign(x) * x^2 ), бо экспонента при отрицательных аргументах асимптотически приближается к 0 ( для чисел > 1 ).

Вообще-то я не представляю, что это за устройство ( если не прецизионный вольтметр ) , где точность 10^-6 уже напрягает. Авиамодель ? Хм... Видимо, какой-то ПИД-регулятор с запасом точности в 3 порядка.

А тут вообще считать нечего не надо: задал таблично десяток точек и пот`ом апроксимация полиномом не очень большой степени.
clawham
Поставщик валерьянки для Кота
Сообщения: 1957
Зарегистрирован: Пт окт 31, 2008 09:38:55
Откуда: Одесса
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение clawham »

Mem_Max_Gas и прочие @mem@ это инициализация...только при включении модифицируются а в последней версии вообще из еепромины кешируются...
сколько раз в секунду? всего 50
а саму экспоненту я хочу вообще в таблицу занести...всего-то 100 значений...помоему 100 байт памяти программ это пыль :)
кстати эта экспонента - всего лишь примерное действо...нужно просто чтоб в начале движения ручки на 10% влево вправо вызывало только 1 процент изменения выхода а если дернуть до 70% ручку то и выход должен уже быть 70% :) ну примерно...тоесть вначале как бы более высокая точность....

все Delta_Gas и прочие это числа от 1000 до 2000 не более...так что попробую покумекать..мож и вправду нафик этот флоат ведь всёравно у меня результат - это значение регистра pwm 500 максимум целого числа

что это за устройство? контроллер соосного вертолёта
суть в том что есть два мотора один крутит нижний винт по часовой стрелке
второй мотор - крутит верхний винт против часовой стрелки

когда ты с пульта даёш плавно газу то моторы раскручиваются и создают крутящие моменты на валу и сворачивают корпус в противоположную сторону ... обои эти усилия равны и верт никуда(в идеале) не дрейфует вокруг оси вращения винтов

ещё на борту есть гироскоп....маханькое цифровое пьезоэлектрическое устройство....суть его работы проста как 2х2:
изначально при включении питания он на выход даёт 1520 микросекунд....
когда вертолёт взлетел и чтото или ктото заставил нарушиться балансу сил двух моторов(провернуло корпус по часовой стрелке) то этот гироскоп(в зависимости от чувствительности которая тоже настраивается плавно с пульта) начинает уменьшать длительность выхода накапливая ошибку) мой контроллер принимает это отклонение от 1520 и начинает уменьшать мощность ( скважностью) двигателя который крутит верхний винт против часовой стрелке(а корпус по часовой) и соответственно поддавать газу второму мотору....корпус получит ускорение в обратную сторону и будет получать это ускорение до тех пор пока выход с гироскопа снова не прийдёт к 1520(ну или сколько там получится)
гироскоп при этом сам знает скорость и инерцию вертолёта, на подходе к расчетному направлению - может выдать в обратную сторону импульс чтоб компенсировать инерциюи остановиться там же где и начал...
даже если один мотор пригорел или одby винт поврежден - гирик всёравно будет стараться держать первоначальное направление смотря на свой датчик....правда датчик хреновенький...и от перепада температур появляется дрейф(самопроизвольное смещение центра но то уже мои проблемы...
с пульта в гиру приходят сигнал уровня чувствительности и сигнал требуемой скорости поворота в заданную сторону...

вот такое вот кино :)
есть проблемы:
1) иногда входит в самоколебания перелетая нужное направление
2) иногда сильно обезточенный мотор долго раскручивается(хотя усилие уже создаёт) и вертолёт проседает по высоте при рулении
3) когда батарея на 70% кончилась ты невольно задираеш газ до 80-90% и тут уде начинаются упирания "подгазовывающего" мотора в максимум газа...и работа по стабилизации ложится на притормаживающий.....

но меня в данный момент бесит и мешает именно задержка в управлении...реакции...изза этого и гира не может нормально фунциклировать и высота плавает...
Что нас не убило сделало нас осторожней
Не доверяйте русским лужам - это может быть вход в метро.
Andrey19888
Родился
Сообщения: 12
Зарегистрирован: Вс июл 16, 2006 23:52:49

Re: Помогите реализовать запись числа с точкой

Сообщение Andrey19888 »

avreal писал(а):
Andrey19888 писал(а):Разложение на 2 целых числа не вариант т.к. если ввести 23.005 в одну переменную пойдет 23, а в другую то чушь. Или как представить мелкое число, например 0.002?
У Вас все числа должны быть не больше 3 десятичных знаков после запятой?
Напишите свою функцию разбора строки в число, которая будет выдавать целое число, но умноженное на 1000. Т.е. при вводе числа точку игнорировать, но считать, сколько вышло символов после десятичной точки. И если их меньше 3, то домножать на 10 столько раз, сколько не хватило.
И дальше все рассчёты вести с учётом того, что числа умножены на 1000.
При выводе

Код: Выделить всё

  int a = 12075;
  sprintf( "%d.%03u", a / 1000, a % 1000);


upd - с отрицательными числами, пожалуй, придётся врукопашную, как-то так

Код: Выделить всё

  int a = 12075;
  char sig;
  int b;
  if( a > 0) {
    sig = ' '; // или '+', по вкусу
    b = a;
  } else {
    sig = '-';
    b = -a;
  }
  sprintf( "%c%d.%03u", sig, a / 1000, a % 1000);


Тху!!! Уже ж 5-е число!!!!!!
Конечно, везде sprintf-у буфер указывать :-) :beer:



Числа у меня не больше 3 знаков после запятой ... пределы изменения это [0.001-99.999]
Числа вводятся как дробные так и целые
Отрицательных значений не будет
Помимо вывода на экран, введенное число необходимо будет сравнивать и если попадает в той или иной предел выполняется действие.

Если честно я немного не понял с этими домножениями. Как быть когда вводится например 0,001 т.е. 0 постоянно множатся и в итоге 1 умножится на 1000 т.е. число 0,001 эквивалентно 1000?

Если не трудно подскажите подробнее алгоритм, что-то я уже совсем запутался :oops:
Жизнь как рояль: клавиша белая, клавиша черная, крышка
Аватара пользователя
Jack_A
Друг Кота
Сообщения: 6307
Зарегистрирован: Вт апр 24, 2007 07:45:40
Откуда: Minsk

Re: Помогите реализовать запись числа с точкой

Сообщение Jack_A »

clawham писал(а):есть проблемы:
1) иногда входит в самоколебания перелетая нужное направление


Как я и предполагал - это ПИД регулятор. И это явление объяснимо: астатичность системы с интегратором в цепи обратной связи и обеспечивается именно колебаниями относительно расчетной точки. Настройка ПИД регулятора - тонкая штука, я хоть и руководил дипломными проектами студней специальности "Автоматика", в реале этим не занимался. Но нам хоть удалось убедить автора, что запредельная точность и головоломные Float-вычисления ни к чему.

Желаю успешных полетов :-)

PS надеюсь, ничего противозаконного на борту не будет ? ;-)
clawham
Поставщик валерьянки для Кота
Сообщения: 1957
Зарегистрирован: Пт окт 31, 2008 09:38:55
Откуда: Одесса
Контактная информация:

Re: Помогите реализовать запись числа с точкой

Сообщение clawham »

это суперумный пидрегулятор на арме 133 мегагерца и 32 разрядном АЦП :)
150 доларов стоит штучка...гирик всмысле...
в общем я обошелся вообще без вычислений задрав частоту камня до 16 мегагерц кварцем получилось что вход в два раза меньший нормы а pwm один-в-один задаёт длительность выхода в микровекундах и код расчета превратился в

Код: Выделить всё


                //ждём пока вход Газ упадёт в нуль
          while(Gas_in);                   
                //теперь ждём поднятия в 1                           
                //TCCR2=TimOn;
          TCCR0     = 0x00;                   // таймер глушим
          Timer_ext = 0;                      // обнуляем накопитель таймера
          TCNT0     = 0;
          while(!Gas_in);
                //запускаем таймер
          TCCR0     = TimOn;
                // ждём пока вход Газ упадёт вниз
          while(Gas_in);
                //срочно тормозим таймер
          TCCR0     = 0x00;
                // расчитываем кол-во отсчетов от нуля
          In_Gas    = TCNT0+Timer_ext;
          In_Gas    = In_Gas>>1;           
         
                //ждём пока вход Гиры упадёт в нуль
          while(Gyro_in);                   
                //теперь ждём поднятия в 1                           
                //TCCR2=TimOn;
          TCCR0     = 0x00;                   // таймер глушим
          Timer_ext = 0;                      // обнуляем накопитель таймера
          TCNT0     = 0;                      // обнулили и сам таймер
          while(!Gyro_in);
                //запускаем таймер
          TCCR0     = TimOn;
                // ждём пока вход Гирo упадёт вниз
          while(Gyro_in);
                //срочно тормозим таймер
          TCCR0     = 0x00;           
                // расчитываем кол-во отсчетов от нуля
          In_Gyro   = TCNT0+Timer_ext; 
         
          In_Gyro   = In_Gyro>>1;
         
          if((In_Gas>2200)||(In_Gyro>2200)||(In_Gyro<800))
            continue;
           
          #asm("wdr")
         
          if(In_Gas<1050)
            continue;
           
          TempA = In_Gas;
          TempB = In_Gas;
         
          if(In_Gyro>1520)
          {
            // гирик скрутило вверх - первый канал увеличиваем, второй уменьшаем
            TempC=In_Gyro-1520; // получили микросекунды смещения
            TempA+=(TempC>>1);
            TempB-=(TempC>>1);
            if(TempA>2000)
            {
                // перелетели максимум - вернем его в минус второго канала
                TempB-=TempA-2000;
            }
          }
         
          if(In_Gyro<1520)
          {
            // гирик скрутило вниз - второй канал увеличиваем, первый уменьшаем
            TempC=1520-In_Gyro; // получили микросекунды смещения
            TempA-=(TempC>>1);
            TempB+=(TempC>>1);
            if(TempB>2000)
            {
                // перелетели максимум - вернем его в минус первого канала
                TempA-=TempB-2000;
            }
          }
       
                 
        if(TempA<1000)
            TempA = 1000;
        if(TempA>2000)
            TempA = 2000;
        if(TempB<1000)
            TempB = 2000;
        if(TempB>2000)
            TempB = 2000;
           
        OCR1A = TempA;
        OCR1B = TempB;



вот такой вот безвычислительный блочек...всё на интах..деления не используется....скорость реакции - 8 миллисекунд(по сути время требуемое на замер длительности входных каналов
Что нас не убило сделало нас осторожней
Не доверяйте русским лужам - это может быть вход в метро.
Закрыто

Вернуться в «Микроконтроллеры и ПЛИС»