Таймер и вычисление среднеквадратичного значения сигнала
- Войдите на сайт для отправки комментариев
Доброго всем времени суток!
Задача такая:
Имеется развертка синусоидального сигнала (с течением времени меняется частота и амплитуда) или широкополосный случайный сигнал. Необходимо вычислить его СКЗ (среднеквадротичное значение).
Этот синал смаштабировал и сместил, т.е. 0 бит = -Umаx, 512 бит = 0 В, 1024 бит = Umax
АЦП ардуино - максимальная частота дискретизации 10 кГц, т.е. 10000 измерений за секунду
Далее например в течение 1 секунды я вычисляю сумму (U1^2+U2^2+..U10000^2) - набираю статистику
Как бы мне организовать цикл с таймером где бы все это считалось? Как только посчитал за секунду, результат отдал и по новой считает, набросочек программы необходим?
СКЗ = КОРЕНЬ((U1^2+U2^2+..U10000^2)/10000)
Или есть какие-нибудь другие способы реализации этого дела?
PS формула взята такая для простоты, не стал с скользящем СКЗ заморачиваться.
Попробуйте так:
С таймером действительно нет смысла заморачиваться. Если во время счета Вам что-нибудь еще делать надо, то придется переходить на расчет скользящего в прерывании от АЦП. Программку maksimа я бы подправил (не уверен, что ровно 10000 отсчетов пройдет):
Попробовал!
Если сделать по этому варианту, то реально будет около 4316 отчетов за 1 секунду. Операция возведения в кадрат занимает много времени, если например просто суммировать то будет порядка 8000 отчетов.
Вероятно заявленные 10000 отчетов в секунду - это теоретический максимум работы ацп без учета других операции.
Как бы сделать так, запрос на ацп обрабатывается, в это время я считаю. Ацп отработало, я забрал данные, ацп начинает отрабатывать новые данные, в это время, я считаю полученное предыдущее значение с ацп.
Для чего мне это надо, у меня на входе сигнал с частотой от 5 до 2500 Гц. Это может быть и синусоидальный сигнал, так и случайный. Как раз последнее и накладывает подобную формулу вычисления СКЗ (для любого сигнала). Например если частота на входе будет порядка 2500 Гц, то 4316 отчетов в секунду мало, будет большая ошибка.
Есть какие-нибудь предложения?
PS еще попробовал организовать массив, данное с АЦП сажаю в массив и так далее... предложенной программой . В этом случае программа загружается в контроллер, но с компьютером я не могу установить связь (через TCP/IP). Может памяти не хватает ОЗУ... хз что там...
Могу предложить такие варианты:
Улучшаем AnalogRead()
#define FASTADC 1
// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
void setup() {
int start ;
int i ;
#if FASTADC
// set prescale to 16
sbi(ADCSRA,ADPS2) ;
cbi(ADCSRA,ADPS1) ;
cbi(ADCSRA,ADPS0) ;
#endif
Serial.begin(9600) ;
Serial.print("ADCTEST: ") ;
start = millis() ;
for (i = 0 ; i < 30000 ; i++)
analogRead(0) ;
Serial.print(millis() - start) ;
Serial.println(" msec (30000 calls)") ;
}
void loop() {
}
Результат: скорость 18,2 мкс против бывших 110 мкс.
Кстати, максимальная скорость АЦП Атмеги как раз 16мкс. Как вариант — использовать другую микросхему, заточенную именно под АЦП, которая позволит уменьшить скорость до 0,2мкс (читать ниже, почему)
Я так понял, вот этот кусок я вставляю в свою программу и в основном цикле как обычно работаем?
То, что жирным курсивом выделено - выбрасываем?
Предложенный вами 1 вариант, я врятли осилю, в асемблере не очень разбираюсь;(
Умножение не как нельзя подускорить?
Есть какие-нибудь предложения?
Вектор прерывания 22 (известный также под именем ADC_vect) - ваше все. Это прерывание генерируется в момент завершения аналогово-цифрового преобразования. Естественно, если прерывание разрешено и все подготовительные операции выполнены надлежащим образом.
Рыба проекта - примерно такая.
Есть какие-нибудь предложения?
Вектор прерывания 22 (известный также под именем ADC_vect) - ваше все.
ммм продлите мысль пожалуйста?
В ISR, которая будет вызываться в момент завершения АЦ-преобразования, т.е. когда новое значение готово для дальнейшей обработки, это только что полученное значение записывается в буфер (лучше взять массив и добавить в ISR проверку на его переполнение), а затем тут же запускается новое преобразование, после чего управление возвращается основной программе. Ну а та может непрерывно брать данные из буфера, возводить их в квадрат и т.д. и т.п. - совсем не заморачиваясь запуском АЦ-преобразования и ожиданием его завершения.
Предложенный вами 1 вариант, я врятли осилю, в асемблере не очень разбираюсь;(
Умножение не как нельзя подускорить?
Предложенный вариант 1 - это более краткое изложение Вашего же варианта из #3, а ассемблер тут непричем, там сишная функция, которую тупо нужно разрезать на две, но чтобы понять, где резать, нужно понять, что она делает.
Для организации прерывания по окончанию преобразования придется существенно заглубиться в даташит или в программирование проца на "С" без ардуино-IDE (точнее, без ее библиотечных функций). Но складывать квадраты отсчетов Вы все равно не сможете быстрее АЦП. Так что, если хотите что-то делать параллельно работе АЦП - придется поднапрячь мозг и разобраться, как это работает (я не про канал Дискавери :) )
Если хотите просто ускорить АЦП, Вам из приведенного текста потребуются только дефайны и секция "иф фастадц"
Ускорить умножение (точнее, вычисления, связанные с умножением) можно если перейти к целочисленным расчетам. Если устроит точность результата 10 бит (как у АЦП), то позаботтесь о том, чтобы все операнды расчетов имели тип "long int"
P.S. И не надо бояться ассемблера - он не сложнее программируемого микрокалькулятора... хотя в наш продвинутый компьютеризированный 21 век о них уже никто и не помнит, наверно :)
Но складывать квадраты отсчетов Вы все равно не сможете быстрее АЦП.
Хотите верьте, хотите нет, но для 10000 операций возведения в квадрат и суммирования мне потребовалось 234 мсек, т.е. за секунду можно успеть выполнить более 40000 таких операций. А это раз в 5 больше скорости работы АЦП (8-10 тыс. считываний в сек).
Хотите верьте, хотите нет, но для 10000 операций возведения в квадрат и суммирования мне потребовалось 234 мсек, т.е. за секунду можно успеть выполнить более 40000 таких операций. А это раз в 5 больше скорости работы АЦП (8-10 тыс. считываний в сек).
Действительно, это я ошибся, операция умножения выполняется быстро.
Попробовал "оптимизацию АЦП" (по ссылке выше), АЦП действительно стало работать быстрее примерно в 6 раз.
При опросе 2-х АЦП за 1 секунду проходит более 13000 опросов каждого. Это меня более чем устраивает.
Спасибо огромное за помощь!
Я тоже ошибся, точнее, перезаложился с накладными расходами (как-бэ потери времени на операциях в прерывании).
Стоит закомментировать проверку
if(i%1000==0) Serial.println(i);
как выполнение ускоряется в 10 (!) раз. 10000 умножений за 23 мсек!!!
Что лишний раз говорит о том, что деление зна-а-а-а-ачительно медленнее умножения и от него необходимо избавляться любыми способами.
Вот эта информация передается на компьютер, я дума тогда на компьютер можно отправлять сумму квадратов и число отчетов а тот пускай уже делить и извлекает корень.
В реале проверю как все это работает, посмотрю там...
Какие проблеммы? Это сумму квадратов Вы накапливаете в цикле, а деление и извлечение корня проводите 1 раз по окончании цикла замеров.
Это не проблема, можно и в контроллере поделить можно и на компьюторе. Ничего страшного;)
Всем привет, пропадал маленько... Дела были. Этот проект для меня снова стал актуален в новом ключе. В кратце: я завел таймер на 100 мкс, в прерывание обрабатываю ацп... считаю скз. Переменная аккумулятор накапливает сумму квадратов 10000 отсчетов. И возникла проблема - размер аккумулятора в 4 байта не хватает для всех значений... Как его можно увеличить или создать свой тип данных например 12 байт типа int? чтобы туда поместилось число равное произведению 1023*1023*10000.
вот тема, тут считали среднеквадратичное значение...
вот тема, тут считали среднеквадратичное значение...
там састота 50 Гц. У меня развертка от 5 до 2500 Гц или случайный сигнал в диапазоне 20-2500 Гц. Для правильной, корректной оцифровки сигнала по теореме Котельникова-Найкаеста частота опроса должна быть не меньше 5000 Гц, чтобы хотябы приходилось два отчета на период наивысшей частоты. А лучше 4 :) или больше. Но больше уже врятли. Амплитуда сигнала и частота неизвестны ардуине. По этому используется формула вычисления скз. При сумме квадратов 10000 отчетов аккумулятор переполняется... И значение недостоверное. Поэтому мне нужна переменная размером 8 или 12 байт :D как ее получить, я хз:(
Суммируйте первые 2000 отсчетов в одном накопителе, вторые - во втором, и так далее до пятого.
Как только накопители "заполнятся" ("прилетело" 10000 отсчетов) делите каждую сумму на 10000 и складывайте результаты (ну или делайте это последовательно - по мере заполнения накопителей).
Не забывайте сбрасывать накопители после выполнения вычислений с находящимися в них суммами.
Здравствуйте, всеравно результат некорректный получается, какая частота дискретизации ацп у ардуины, как ее можно поменять, боюсь что в прерывание таймера он не успевает получить корректное значение?!
ну вроде как у AVR на 1 перобразование уходит 13 тактов процессора, т.е при всех делителях частоты выборку в десятки кГц сделать можно...
Что вам мешает результата оцифровки хранить в 4байтной переменной(long, float). В обработчике принимаем 2байта с АЦП и складываем в массив, а в основном теле программы считаем всю медленную математику.
На конец можно вообще термоэлектрический вольтметр замутить на термопарах :))
Добрый день!
К каким результам я пришел:
Считать скз на 10 и более КГц с 2-х аналоговых каналов и при этом работать с ethernet - нереально.
Команды работы с ethernet выполняются приличное время (до 300 мкс)...
Поэтому скорее всего будет так: будет дополнительный контроллер, который будет считать СКЗ и передавать по UARTу инфу в основной контроллер который уже будет опрашивать переферию, а потом уже передавать по ethernet информацию в компьютер.
Модуль вычисления СКЗ тока и напряжения (с двух АЦП)
Все в принципе получилось хорошо, интервал таймера 51 мкс, ~19600 отчетов/сек. В программе я беру сумму квадратов девяти тысяч восьмисот отчетов, и отправляю в последовательный порт... Взял 9800, т.к. переменная summU или summI ограничена 4 байтами.
Интересно, почему-то при интервале таймера 50 мкс и при n=10000 отчетов, ничего не видно в мониторе последовательного порта, такое ощущение что программа не работает.