首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在C中有更快的RMS值计算吗?

在C中有更快的RMS值计算吗?
EN

Stack Overflow用户
提问于 2015-03-02 10:29:41
回答 5查看 16.3K关注 0票数 10

我正在为一个小型8位微控制器编写一个软件,在C中,部分代码是读取电流互感器(ZCT)的ADC值,然后计算RMS值。流过ZCT的电流是正弦的,但它会被扭曲。我的代码如下:

代码语言:javascript
复制
float       adc_value, inst_current;
float       acc_load_current;           // accumulator = (I1*I1 + I2*I2 + ... + In*In)
double      rms_current;

// Calculate the real instantanous value from the ADC reading
inst_current = (adc_value/1024)*2.5;    // 10bit ADC, Voltage ref. 2.5V, so formula is: x=(adc/1024)*2.5V                           

// Update the RMS value with the new instananous value:
// Substract 1 sample from the accumulator (sample size is 512, so divide accumulator by 512 and substract it from the accumulator)
acc_load_current -= (acc_load_current / 512);       
inst_current *= inst_current;          // square the instantanous current
acc_load_current += inst_current;      // Add it to the accumulator

rms_current = (acc_load_current / 512);  // Get the mean square value. (sample size is 512)
rms_current = sqrt(rms_current);         // Get RMS value

// Now the < rms_current > is the real RMS current

然而,它有许多浮点计算。这给我的小型单片机增加了很大的负担。我发现sqrt()函数在我的编译器中不起作用。

有什么代码可以运行得更快吗?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2015-03-02 14:32:32

当您需要在缺乏FPU的处理器上获得更快的速度时,最好的选择是用不动点代替浮点计算。将此与joop的建议(一牛顿-拉斐逊平方米)相结合,您将得到如下内容:

代码语言:javascript
复制
#define INITIAL 512  /* Initial value of the filter memory. */
#define SAMPLES 512

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

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

只需通过这个过滤器运行您的原始ADC样本即可。为了获得更高的分辨率,您可以在这里和那里添加几个位移位,但是您必须小心,不要溢出变量。我怀疑你真的需要额外的决心。

过滤器的输出与其输入在同一个单元中。在这种情况下,它是你的模数转换器的单位:2.5V/1024的副≈,2.44mV。如果您可以在以后的计算中保留此单元,则可以通过避免不必要的转换来节省周期。如果你真的需要电压值(这可能是I/O的要求),那么你必须转换成浮点。如果您想要毫伏,您可以停留在整数域:

代码语言:javascript
复制
uint16_t rms_in_mV = rms_filter(raw_sample) * 160000UL >> 16;
票数 9
EN

Stack Overflow用户

发布于 2015-03-02 11:17:12

由于您的平方和值acc_load_current在迭代之间变化不大,它的平方根几乎是恒定的。Newton-Raphson sqrt()函数通常只在几个迭代中收敛。通过每步使用一次迭代,对计算结果进行模糊化处理。

代码语言:javascript
复制
static double one_step_newton_raphson_sqrt(double val, double hint)
{
double probe;
if (hint <= 0) return val /2;
probe = val / hint;
return (probe+hint) /2;
}

static double      acc_load_current = 0.0;           // accumulator = (I1*I1 + I2*I2 + ... + In*In)
static double      rms_current = 1.0;
float       adc_value, inst_current;
double      tmp_rms_current;

// Calculate the real instantanous value from the ADC reading
inst_current = (adc_value/1024)*2.5;    // 10bit ADC, Voltage ref. 2.5V, so formula is: x=(adc/1024)*2.5V                           

// Update the RMS value with the new instananous value:
// Substract 1 sample from the accumulator (sample size is 512, so divide accumulator by 512 and substract it from the accumulator)
acc_load_current -= (acc_load_current / 512);
inst_current *= inst_current;          // square the instantanous current
acc_load_current += inst_current;      // Add it to the accumulator

tmp_rms_current = (acc_load_current / 512);
rms_current = one_step_newton_raphson_sqrt(tmp_rms_current, rms_current);         // Polish RMS value

// Now the <rms_current> is the APPROXIMATE RMS current

备注:

  • 我将一些数据类型从float更改为double (这在通用计算机/桌面上是正常的),如果double在您的微型计算机上非常昂贵,您可以将它们改回来。
  • 我还添加了static,因为我不知道代码是来自函数还是循环。
  • 我使函数static强制它内联。如果编译器没有内联静态函数,则应手动将其内联。
票数 6
EN

Stack Overflow用户

发布于 2015-03-02 10:39:22

希望您的项目是测量“大”交流电压(而不是像9v的电机控制)。)如果碰巧是这种情况,那么您就可以作弊,因为您的错误可以在可接受的范围内。

以整数计算所有数学,并为sqrt操作使用一个简单的查找映射。(您可以在启动时预先计算,如果您正在执行3个阶段,那么您只需要大约600个奇数。)

这也引出了一个问题,你是否真的需要VAC、RMS或其他一些衡量能力的指标?(例如,你能用一个简单的盒子-平均计算法-逃脱吗?)

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/28807537

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档