使用整数运算的正交信号发生器

信息处理 控制系统 一体化 嵌入式系统
2022-02-01 05:35:34

我在使用整数运算在微控制器上实现正交信号发生器 (OSG) 算法时遇到问题。我将此算法用于单相锁相环 (PLL) 算法,为此我需要电网电压的正交分量。

OSG算法定义如下:

ddtvx=ω^((vgvx)vy)
ddtvy=ω^vx

其中是测量的电网电压,是估计的电网频率,是估计的分量,对于理想估计等于为此,我们假设电网频率是已知的。vgω^vxvyvxvg

数值积分器实现如下:

yk=Ts12(23uk116uk2+5uk3)+yk1

其中是采样时间。Ts=50 μs

现在,该算法在浮点实现中运行良好,但在整数算术实现中效果不佳。在这里,我给出了两种实现:


浮点实现

代码声明。

float w = (2*PI)*50;
float Ts = 50e-6;
float i1u1, i1u2, i1u3, i1y1;
float i2u1, i2u2, i2u3, i2y1;

float NumInt3rd(float u1, float u2, float u3, float y1) {
    return (Ts/12)*(23*u1-16*u2+5*u3)+y1;
}

主功能。

float vg = floor(Input(0));

float vg_x = NumInt3rd(i1u1,i1u2,i1u3,i1y1);
float vg_y = NumInt3rd(i2u1,i2u2,i2u3,i2y1);

i1u3 = i1u2;
i1u2 = i1u1;
i1u1 = ((vg-vg_x)-vg_y)*w;
i1y1 = vg_x;

i2u3 = i2u2;
i2u2 = i2u1;
i2u1 = vg_x*w;
i2y1 = vg_y;

Input(0)的正弦波)的宏2048


整数算术实现

代码声明。

int w = 643398L; // (2*PI)*50*2048
int i1u1, i1u2, i1u3, i1y1;
int i2u1, i2u2, i2u3, i2y1;

int NumInt3rd(int u1, int u2, int u3, int y1) {
    int iu = 23*u1-16*u2+5*u3;
    int iy = 240000L*y1;
    return (iu+iy)/240000L;
}

主功能。

int vg = (int) Input(0);

int vg_x = NumInt3rd(i1u1,i1u2,i1u3,i1y1);
int vg_y = NumInt3rd(i2u1,i2u2,i2u3,i2y1);

i1u3 = i1u2;
i1u2 = i1u1;
i1u1 = ((vg-vg_x)-vg_y)*w/2048;
i1y1 = vg_x;

i2u3 = i2u2;
i2u2 = i2u1;
i2u1 = vg_x*w/2048;
i2y1 = vg_y;

请注意,我已经检查了可能的溢出,它永远不会发生。此外,有趣的是相同的算法适用于Ts=300 μs

我对整数算术实现没有那么有经验。你能给我一个建议如何解决这个问题吗?谢谢!


这是两种实现的估计误差(在 y 轴上:估计误差的百分比)。整数算法实现的估计误差约为±5%

在此处输入图像描述

1个回答

我设法找到了我的问题的答案。

问题在于整数除法的舍入。例如,-4/3将四舍五入为-1,就像-5/3因此,积分误差不断累积。这里我没有解释,而是给出了一个如何解决这个问题的代码。

代码声明。

// Global variables
int w = 643398L; // (2*PI)*50*2048
int i1u1=0, i1u2=0, i1u3=0;
int i2u1=0, i2u2=0, i2u3=0;
short i1y1=0, i2y1=0;

// Numerical integrator implementation
short NumInt3rd(int u1, int u2, int u3, short y1) {
    int iu = 23*u1-16*u2+5*u3;
    int iy = 240000L*y1;
    short y0 = (((iu+iy)>>12)*2237+65536)>>17;
    return y0;
}

主功能。

// Get voltage measurements (-2048 to +2048)
short vg = (short) Input(0);

// Numerical integrators, 3rd order
short vg_x = NumInt3rd(i1u1,i1u2,i1u3,i1y1);
short vg_y = NumInt3rd(i2u1,i2u2,i2u3,i2y1);

// Downsample frequency to prevent overflow
short wb = w>>5;

// Update integrator #1 states
i1u3 = i1u2;
i1u2 = i1u1;
i1u1 = ((int)((vg-vg_x)-vg_y)*wb)>>6;
i1y1 = vg_x;

// Update integrator #2 states
i2u3 = i2u2;
i2u2 = i2u1;
i2u1 = ((int)vg_x*wb)>>6;
i2y1 = vg_y;

应该注意的是,我宁愿使用位移运算,而不是使用在所需指令周期数方面非常昂贵的整数除法。例如,1/240000可以很好地近似为2237/2^29- 近似精度为0.00169277%

这是正弦波估计误差。如您所见,结果现在好多了。

在此处输入图像描述