【问题标题】:Creating a reverse lookup table from measured values从测量值创建反向查找表
【发布时间】:2021-03-29 11:52:38
【问题描述】:

我在校准期间记录了来自编码器(已知速度)的 2 个电压。创建这个(电压超过角度),然后我需要测量电压并获得相应的角度。

我也想在记录点之间找到值以获得更高的精度,某种 arccos 查找表,我被卡住了,无法找到有效的方法。

我是否必须通过比较大量数组来研究我在数组中的值?那会很慢吧?

我曾尝试使用 Arcos 函数,但我猜这无法补偿机械不规则性。 此外,我已经计算了波的平均值和振幅。

编辑:如前所述,目标是测量 FOC 的电机位置。 设置如下。

这是我使用 atan2 函数得到的结果: x = 2*(hallA.read() - moyenneA)/(maxA-minA); y = 2*(hallB.read() - moyenneB)/(maxB-minB);

【问题讨论】:

  • 您似乎有 sin 和 cos 曲线,因此您可以使用 atan2-function 来计算角度。你在说什么机械异常?
  • 我建议澄清你的答案,明确 Arccos 指的是反余弦函数。我花了一点时间才弄明白。
  • 是的,这是一个 sin 和 cos,霍尔传感器相隔 pi/2。也许转子的磁场不直,或者磁铁没有完全居中@Keijo
  • 我想您可以在记录的 sin 和 cos 值之间进行简单的多项式插值,以提高采样率。根据这些值,您可以估计不规则参数并将相应的校正应用于测量。我不完全确定您真正想要的结果是什么。
  • 好的,从你上次的评论开始。我认为您正在尝试计算电动机转子的角度。可能将其输入 PID(比例-积分-微分)控制器以控制电机速度。如果是这种情况,我建议将该目标添加到您的问题中。让人们知道计算的目标。

标签: c++ arrays math lookup


【解决方案1】:

最终,您无法在数据中添加比已有信息更多的信息。除了更频繁地采样。也就是说,您可以使用线性插值来估计任意两个测量点之间的电压。查找线性插值以获取详细信息,但总结一下 A 和 B 之间是否有一个时间点 C,它是 A 和 B 之间距离的 25%。然后计算加权相同的电压。所以电压 C = 电压 A + 25%(电压 A - 电压 B)。

至于计算角度,那不是我的专业领域。但我想我会计算我感兴趣的点后面的线性插值 1/2 采样时间和我感兴趣的点前面的插值 1/2 采样时间并计算斜率的角度。但是,我不是信号处理专家。有很多关于这个主题的书。还有其他可用的插值函数,在 Internet 上阅读有关信号处理插值的信息可能会获得更多信息。

如果您担心数据中的机械或电气噪声,那么我建议您使用平滑算法。我知道快速傅里叶变换非常流行。但我敢肯定还有很多其他选择。互联网上一定有过滤库和算法。但正如我所说,这不是我的专业领域。也许我的回答会触发其他人以更专业的方式回答。消除高频噪声的一种方法是减少采样频率。

【讨论】:

  • 线性插值是我的意思但是我将如何构造它?我做这样的桌子吗? angle[ ] = { 1mV 时的角度,2mV 时的角度,3mV 时的角度 }
【解决方案2】:

正如其他人已经提到的那样,您的数据包含噪声和幅度失真。但是,您的数据很可能还包含时间失真(正弦波的角度参数不是非线性的,也不是精确/恒定的相移)这将使atan,acos,asin 的使用非常不准确且有问题。

我愿意:

  1. FIR 过滤数据

    所以使用以值为中心的滑动平均值,这样输出就不会发生相移。这是O(n),只有一次。不要忘记使用临时数组...

  2. 检测单调区间

    只需检测信号仅减少或仅增加的间隔...这也是O(n),并且只执行一次。

  3. 使用二分搜索

    只需遍历所有间隔,如果您测量的电压在其范围内(间隔的第一个和最后一个值),则对间隔使用二进制搜索。间隔的选择是 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);

第一个预先计算所有需要的东西(只调用一次),第二个在[deg] 中返回角度u0,u1

但是,如果您的 u0,u1 接近区间边缘,其中一个电压在一个区间内,而另一个电压在下一个区间内,这可能会出现轻微的准确性问题,如果添加噪声,可能会发生这种情况。在这种情况下,您可以对u0u1 进行二元搜索,分别记住这两个位置的两个位置,然后检查4 个组合,哪一个是正确的。之后,只需使用找到的两个索引之间的中间位置。为此,您也可以将间隔分开(但每个信号只有 2 或 3 个)。

您还可以通过在二分搜索之后添加线性插值来添加 “子像素”精度(我太懒了,所以不在我的示例中)

这里预览:

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

【讨论】:

  • 第一:非常感谢!编程确实不是我的专业领域,但我试图理解假设我有一个数组,其中包含记录的每千分之一转双电压的电压[1000][2] = { {0.0, 1.0} , { 0.01, 0.99 },等 } 那么我可以做一个反向数组吗?
  • @antoineserry 它不是一个真正的反向数组,它仍然是相同的数组,二进制搜索只是在您的测量数组中找到对应于 2 个输入电压的索引(在 O(log(n) 迭代中,用于 1000 次测量 / 4间隔是〜8次迭代......)
【解决方案3】:

如果插值是您想要的,您将需要 EE 人所说的 重建滤波器 来从一组样本中重建测量信号,或者至少在两者之间获得(最佳)插值采样值以获得适合您需求的数据集。

最基本的方式是线性插值;例如,在两个测量值之间添加一个值,该值是这两个测量值的平均值。这是一个简单的算法,您可以从旧数据集生成一个新的、更大的数据集,或者动态插值。

大多数示波器使用 sin(x)/x 重构滤波器,它比线性插值提供更准确的采样输入信号表示。

如果您想消除不规则性,您可以使用移动平均滤波器或(更复杂的)FIR 滤波器,但是当您处理“实时”时,这会使事情变慢。

如果你想知道更多(可能比我知道的更多),你可以试着问他关于 EE 的问题;他们拥有我怀疑您想要实现的目标所需的所有理论。

【讨论】:

    猜你喜欢
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 2018-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多