c++ 根据测量值创建反向查找表

hgb9j2n6  于 2022-12-30  发布在  其他
关注(0)|答案(3)|浏览(106)

在校准过程中,我记录了编码器(速度已知)的两个电压。创建这个(Angular 上的电压),然后我需要测量电压并得到相应的Angular 。

我也想找到记录点之间的值,以获得更高的精度,某种arccos查找表,但我被卡住了,无法找到有效的方法来做到这一点。
我需要通过比较很多数组来研究数组中的值吗?那会很慢,对吗?
我试过使用反余弦函数,但我想这不能补偿机械的不规则性,而且我已经计算了波的平均值和振幅。
编辑:如前所述,目标是测量FOC电机的位置。设置如下:

这是我用atan2函数得到的结果:

x = 2*(hallA.read() - moyenneA)/(maxA-minA);
y = 2*(hallB.read() - moyenneB)/(maxB-minB);

kd3sttzy

kd3sttzy1#

最后,除了更频繁地采样外,你无法向数据中添加更多的信息。也就是说,你可以用线性插值法来估计任意两个测量点之间的电压。2详细信息可以查线性插值法,但总结一下,如果你在A点和B点之间有一个时间点C,它是A点和B点之间距离的25%。然后计算加权相同的电压,即电压C =电压A + 25%(电压A -电压B)。
至于计算Angular ,这不是我的专业领域,但我想我会计算感兴趣点之后1/2采样时间的线性插值和感兴趣点之前1/2采样时间的插值,并计算斜率的Angular 。我不是信号处理Maven。关于这个主题的书很多。还有其他插值函数可用,阅读有关信号处理插值在互联网上可能会得到你吨更多的信息。
如果您担心数据中的机械或电气噪声,那么我建议您使用平滑算法。我知道快速傅里叶变换非常流行。但我肯定还有很多其他选择。互联网上肯定有过滤库和算法。但正如我所说,这不是我的专业领域。也许我的回答会引起其他人更专业的回答。消除高频噪声的一个方法就是减少采样频率。

g0czyy6m

g0czyy6m2#

正如前面提到的,你的数据包含噪声和振幅失真。然而,你的数据很可能也包含时间失真(正弦波的Angular 参数是非线性的,也不是精确/恒定的相移),这将使atan,acos,asin的使用非常不准确和有问题。
我会:
1.FIR滤波数据
所以使用以值为中心的滑动平均值,这样输出就不会相移。这是O(n),并且一个只有一次。不要忘记使用临时数组...
1.检测单调间隔
简单地检测信号只减少或只增加的间隔...这也是O(n),只做一次。
1.使用二进制搜索
只需遍历所有间隔,如果测得的电压在其范围内(间隔的第一个和最后一个值),则对间隔进行二进制搜索。间隔的选择为O(1),因为间隔数始终为45,具体取决于起始相位。
二分查找本身是O(log(n))

下面是一个C++/VCL小示例:

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
//---------------------------------------------------------------------------
// LUT data
const int n=1317;
double atan2_u0[n];         // u0 [V] red
double atan2_u1[n];         // u1 [V] blue
//  ix = ang*183/50         // index -> [deg]
// ang = ix*50/183          // [deg] -> index

// monotonic intervals
const int m=10;             // max interval count
int atan2_n=0;              // intervals count
int atan2_ix[m];            // interval start index
int atan2_s0[m];            // direction of atan2_u0
int atan2_s1[m];            // direction of atan2_u1
//---------------------------------------------------------------------------
double LUT_atan2(double _u0,double _u1)
    {
    int i,i0,i1,m;
    double u,u0,u1,U0,U1,s0,s1;
    // process interals
    for (i=0;i<atan2_n;i++)
        {
        i0=atan2_ix[i];
        i1=atan2_ix[i+1]-1;
        // test BBOX
        u0=atan2_u0[i0]; U0=atan2_u0[i1]; s0=atan2_s0[i];
        u1=atan2_u1[i0]; U1=atan2_u1[i1]; s1=atan2_s1[i];
        if (s0<0.0){ u=u0; u0=U0; U0=u; }
        if (s1<0.0){ u=u1; u1=U1; U1=u; }
        if ((_u0<u0)||(_u0>U0)) continue;
        if ((_u1<u1)||(_u1>U1)) continue;
        // binary search
        for (m=1;m<=(i1-i0);m<<=1); m>>=1; if (!m) m=1; // m = exp2(log2(i1-i0))
        for (;m;m>>=1)
            {
            i0+=m;
            if (i0>i1){ i0-=m; continue; }
            u0=atan2_u0[i0];
            u1=atan2_u1[i0];
            if (((_u0-u0)*s0<0.0)||((_u1-u1)*s1<0.0)) i0-=m;
            }
        return double(i0)*50.0/183.0;
        }
    return 0.0;
    }
//---------------------------------------------------------------------------
void LUT_atan2_init()       // filter u0,u1 and compute intervals
    {
    int i,j,k,r;
    double u0,u1,du0,du1,U0,U1,tmp0[n],tmp1[n];
    // centered sliding avg, window is <-r,+r>
    r=25; du0=1.0/double(r+r+1);
    for (i=0;i<n;i++)   // all samples
        {
        u0=0.0; u1=0.0;
        for (j=i-r;j<=i+r;j++)
            {
            k=j;
            if (k< 0) k+=n;
            if (k>=n) k-=n;
            u0+=atan2_u0[k];
            u1+=atan2_u1[k];
            }
        tmp0[i]=u0*du0;
        tmp1[i]=u1*du0;
        }
    for (i=0;i<n;i++)
        {
        atan2_u0[i]=tmp0[i];
        atan2_u1[i]=tmp1[i];
        }
    // find monotonic intervals
    atan2_n=0; atan2_ix[0]=0;
    u0=atan2_u0[0]; U0=atan2_u0[1]; atan2_s0[0]=0.0;
    u1=atan2_u1[0]; U1=atan2_u1[1]; atan2_s1[0]=0.0;
    for (i=2;i<n;i++)
        {
        // actual delta du0,du1
        u0=U0; U0=atan2_u0[i]; du0=U0-u0; if (du0<0.0) du0=-1.0; else if (du0>0.0) du0=+1.0;
        u1=U1; U1=atan2_u1[i]; du1=U1-u1; if (du1<0.0) du1=-1.0; else if (du1>0.0) du1=+1.0;
        if (fabs(atan2_s0[atan2_n])<1e-3) atan2_s0[atan2_n]=du0;
        if (fabs(atan2_s1[atan2_n])<1e-3) atan2_s1[atan2_n]=du1;
        // if sign changed add new interval
        if ((du0*atan2_s0[atan2_n]<-0.1)||(du1*atan2_s1[atan2_n]<-0.1))
            {
            if (atan2_n>=m-1) break;
            atan2_n++;
            atan2_ix[atan2_n]=i;
            atan2_s0[atan2_n]=du0;
            atan2_s1[atan2_n]=du1;
            }
        }
    // add end of table as end of intervals
    atan2_n++;
    atan2_ix[atan2_n]=n;
    atan2_s0[atan2_n]=0.0;
    atan2_s1[atan2_n]=0.0;
    }
//---------------------------------------------------------------------------
void data_generate() // extract mesurede values from image
    {
    int x,y,x0=44,y0=478,dx=227-x0,dy=y0-424,r,g,b,i;
    double u0,u1,du=1.0/double(dy);
    BYTE *c; DWORD *q;
    // convert bmp -> pnt[]
    Graphics::TBitmap *bmp=new Graphics::TBitmap;
    bmp->LoadFromFile("in.bmp");
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    for (x=x0;x<bmp->Width;x++)
        {
        u0=u1=-1.0;
        for (y=0;y<=y0;y++)
            {
            q=(DWORD*)bmp->ScanLine[y];
            c=(BYTE*)(q+x);
            b=c[0]; g=c[1]; r=c[2]; i=r+g+b;
            if ((r>200)&&(i<500)){ u0=y0-y; u0*=du; break; }    // [V]
            }
        for (y=0;y<=y0;y++)
            {
            q=(DWORD*)bmp->ScanLine[y];
            c=(BYTE*)(q+x);
            b=c[0]; g=c[1]; r=c[2]; i=r+g+b;
            if ((b>200)&&(i<500)){ u1=y0-y; u1*=du; break; }    // [V]
            }
        if (u0<-0.5) u0=u1;
        if (u1<-0.5) u1=u0;
        i=x-x0;
        atan2_u0[i]=u0+1.0;
        atan2_u1[i]=u1+1.0;
        }
    delete bmp;
    }
//---------------------------------------------------------------------------
void TMain::draw()
    {
    if (!_redraw) return;

    // clear buffer
    bmp->Canvas->Brush->Color=clBlack;
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));

    int i;
    double x,y,dy=ys/10.0,r=5.0;
    // LUT
    bmp->Canvas->Pen->Color=clRed;
    for (i=0;i<n;i++)
        {
        x=(i*xs)/n;
        y=ys-(atan2_u0[i]*dy);
        if (!i) bmp->Canvas->MoveTo(x,y);
        else    bmp->Canvas->LineTo(x,y);
        }
    bmp->Canvas->Pen->Color=clBlue;
    for (i=0;i<n;i++)
        {
        x=(i*xs)/n;
        y=ys-(atan2_u1[i]*dy);
        if (!i) bmp->Canvas->MoveTo(x,y);
        else    bmp->Canvas->LineTo(x,y);
        }
    // intervals
    bmp->Canvas->Pen->Color=clDkGray;
    for (i=0;i<=atan2_n;i++)
        {
        x=(atan2_ix[i]*xs)/n;
        bmp->Canvas->MoveTo(x,0);
        bmp->Canvas->LineTo(x,ys);
        }

    // atan2
    double u0=8.0,u1=1.7,a;
    a=LUT_atan2(u0,u1);     // [V,V] -> [deg]
    x=(183.0*a*xs)/(50.0*n);    // [deg] -> [pixel]

    bmp->Canvas->Pen->Color=clWhite;
    bmp->Canvas->MoveTo(x,0);
    bmp->Canvas->LineTo(x,ys);

    y=ys-(u0*dy);
    bmp->Canvas->Pen->Color=clWhite;
    bmp->Canvas->Brush->Color=clRed;
    bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);

    y=ys-(u1*dy);
    bmp->Canvas->Pen->Color=clWhite;
    bmp->Canvas->Brush->Color=clBlue;
    bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);

    // render backbuffer
    Main->Canvas->Draw(0,0,bmp);
    _redraw=false;
    }
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner)
    {
    data_generate();
    LUT_atan2_init();
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    pyx=NULL;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
    {
    if (pyx) delete[] pyx;
    delete bmp;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender)
    {
    xs=ClientWidth;  xs2=xs>>1;
    ys=ClientHeight; ys2=ys>>1;
    bmp->Width=xs;
    bmp->Height=ys;
    if (pyx) delete[] pyx;
    pyx=new int*[ys];
    for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender)
    {
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender)
    {
    draw();
    }
//---------------------------------------------------------------------------

我从你的图像中提取输入数据并转换为[V][deg]。你可以忽略VCL渲染和窗口的东西。这里唯一重要的是这些函数:

void LUT_atan2_init();
double LUT_atan2(double _u0,double _u1);

第一个预先计算所有需要的填充(只调用一次),第二个返回电压u0,u1[deg]中的Angular 。
然而,如果您的u0,u1接近间隔边缘,其中一个电压在一个间隔中,另一个在下一个间隔中,如果添加噪声,可能会发生这种情况,则可能会有轻微的准确性问题。在这种情况下,您可以分别对u0u1进行二元搜索,记住两者的2个位置,然后检查4个组合中哪一个是正确的。之后,只需使用找到的两个索引之间的中间位置。为此,你也可以将间隔分开(但每个信号只有2或3个)。
你也可以通过在二进制搜索后添加线性插值来增加**“亚像素”精度**(我太懒了,所以不在我的例子中)
这里预览:

红色、蓝色曲线是u0,u1信号,灰色线是找到的间隔边缘,点是测试(测量)电压,白色是找到的Angular

6ie5vjzr

6ie5vjzr3#

如果您需要插值,则需要 * 重构滤波器 * 从一组样本重构测量信号,或至少在采样值之间获得(最佳)插值,以获得符合您需要的数据集。
最基本的方法是线性插值;例如,在两个测量值之间加上一个值,即这两个测量值的平均值。这是一个简单的算法,你可以从旧的数据集生成一个新的、更大的数据集,或者动态插值。
大多数示波器使用sin(x)/x重构滤波器,与线性插值法相比,该滤波器可提供更精确的采样输入信号重构。
如果你想摆脱不规则,你可以使用移动平均滤波器,或(更复杂的)FIR滤波器,但这会使事情变慢,当你处理“现场”。
如果你想知道更多(可能比我知道的更多),你可以试着问他关于EE的问题;他们有我怀疑你想要达到的所有理论。

相关问题