Расчет RMS D C смещение - PullRequest
       108

Расчет RMS D C смещение

0 голосов
/ 09 мая 2020

Мне нужно реализовать вычисления RMS синусоидальной волны в MCU (микроконтроллер, ресурсы ограничены). В MCU отсутствует FPU (модуль с плавающей запятой), поэтому я бы предпочел остаться в целочисленной области. Захваты дискретны через 10-битный AD C.

В поисках решения я нашел здесь отличное решение от Эдгара Б. onet: { ссылка }

Похоже, это полностью мне подходит. Но у меня есть вопросы.

  1. Вход сеть 230 ВА C, 50 Гц. Он преобразуется и смещается аппаратными средствами, чтобы стать синусоидальной волной 0-1 В (от пика до пика), которую я могу зафиксировать с помощью AD C, получая показания 0-1023. Аппаратные средства откалиброваны таким образом, что входное значение 260 VRMS (т.е. около -368: +368 от пика до пика) становится пиковым выходным напряжением 0–1 В. Как я могу «восстановить» исходное среднеквадратичное значение волны, если я тоже хочу оставаться в целочисленной области? Единицы измерения могут быть разными, милливольты тоже подойдут. Мое первое предположение заключалось в том, чтобы вычесть 512 из входной выборки (D C смещение), а затем выполнить этот сдвиг «magi c», как в ответе Эдгара B onet. Но я понял, что это неправильно, потому что смещение D C не фиксировано. Вместо этого он склонен начинать с 0 В. То есть, вход 130 ВА C будет давать выход от пика до пика 0-500 мВ (а не 250-750 мВ, которые до сих пор работали бы). С реальным среднеквадратичным значением для вычитания смещения D C мне нужно вычесть возведенную в квадрат сумму выборок из суммы квадратов. Как в этой формуле: RMS formula

Итак, я получил следующую функцию:

#define INITIAL 512
#define SAMPLES 1024
#define MAX_V 368UL // Maximum input peak in V ( 260*sqrt(2) )
/* K is defined based on equation, where 64 = 2^6,
 * i.e. 6 bits to add to 10-bit ADC to make it 16-bit 
 * and double it for whole range in -peak to +peak
 */
#define K (MAX_V*64*2)

uint16_t rms_filter(uint16_t sample)
{
    static int16_t rms = INITIAL;
    static uint32_t sum_squares = 1UL * SAMPLES * INITIAL * INITIAL;
    static uint32_t sum = 1UL * SAMPLES * INITIAL;

    sum_squares -= sum_squares / SAMPLES;
    sum_squares += (uint32_t) sample * sample;
    sum -= sum / SAMPLES;
    sum += sample;
    if (rms == 0) rms = 1;    /* do not divide by zero */
    rms = (rms + (((sum_squares / SAMPLES) - (sum/SAMPLES)*(sum/SAMPLES)) / rms)) / 2;
    return rms;
}

...
// Somewhere in a loop
getSample(&sample);
rms = rms_filter(sample);
...
// After getting at least N samples (SAMPLES * X?)
uint16_t vrms = (uint32_t)(rms*K) >> 16;
printf("Converted Vrms = %d V\r\n", vrms);

Выглядит нормально? Или я что-то не так делаю?

Как число ОБРАЗЦОВ (размер окна?) Соотносится с F (50 Гц) и моей скоростью захвата AD C (отсчетов в секунду)? Т.е. сколько реальных образцов мне нужно передать в rms_filter (), прежде чем я смогу получить реальное среднеквадратичное значение, при условии, что моя скорость захвата равна X sps? Хотя бы как оценить необходимое минимум N образцов?

1 Ответ

1 голос
/ 11 мая 2020

Я не тестировал ваш код, но мне кажется, что он должен работать нормально. Лично я бы так не реализовал функцию. Вместо этого я бы удалил D C часть сигнала до , пытаясь вычислить значение RMS. Часть D C можно оценить, посылая необработанный сигнал через фильтр нижних частот. В псевдокоде это будет

rms = sqrt(low_pass(square(x - low_pass(x))))

, тогда как то, что вы написали, в основном

rms = sqrt(low_pass(square(x)) - square(low_pass(x)))

Это не должно иметь большого значения. Однако первая формула избавляет вас от умножения. Кроме того, удаляя компонент D C перед вычислением квадрата, вы в конечном итоге умножаете меньшие числа, что может помочь в распределении битов для реализации с фиксированной точкой.

В любом случае я рекомендую вам протестировать фильтровать на вашем компьютере данные Syntheti c перед их передачей в MCU.

Как число SAMPLES (размер окна?) соотносится с F (50 Гц) и моей скоростью захвата ADC (выборок в секунду) )?

Константа SAMPLES управляет частотой среза фильтров низких частот. Эта отсечка должна быть достаточно маленькой, чтобы почти полностью удалить часть сигнала 50 Гц. С другой стороны, если сетевое питание не полностью стабильно, измеряемое количество будет медленно меняться со временем, и вы можете захотеть, чтобы ваше пороговое значение было достаточно высоким, чтобы учесть эти изменения.

Передача Функция этих однополюсных фильтров нижних частот:

H(z) = z / (SAMPLES * z + 1 − SAMPLES)

, где

  • z = exp (i 2 π f / f₀),
  • i - мнимая единица,
  • f - частота сигнала, а
  • f₀ - частота дискретизации

Если f₀ ≫ f (что желательно для хорошей выборки), это можно аппроксимировать с помощью аналогового фильтра:

H(s) = 1/(1 + SAMPLES * s / f₀)

, где s = i2πf, а частота среза равна f₀ / (2π * SAMPLES). Тогда усиление при f = 50 Гц будет

1/sqrt(1 + (2π * SAMPLES * f/f₀)²)

Соответствующий параметр здесь (SAMPLES * f / f₀), который представляет собой количество периодов сигнала 50 Гц, которые помещаются в ваше окно выборки. Если вы подходите к одному периоду, вы пропускаете около 15% сигнала через фильтр. В два раза меньше, если вы поместите два периода, и т. Д. c.

Вы можете получить идеальное отклонение сигнала 50 Гц, если спроектируете фильтр с вырезом на этой конкретной частоте. Если вы не хотите углубляться в теорию проектирования цифровых фильтров, простейшим из таких фильтров может быть простое скользящее среднее, которое усредняет за период ровно 20 мс. Однако это имеет нетривиальные затраты в ОЗУ, поскольку вы должны хранить полные 20-миллисекундные образцы в кольцевом буфере.

...