曲线之间的 3D 插值

时间:2021-03-10 05:20:28

标签: c++ algorithm c++11 math interpolation

我有一组与温度相关的曲线。 即曲线 Mat1 是针对 310C 的温度,而 Mat2 是针对 420C 的。

enter image description here

如您所见,数据采用对数刻度时看起来更好;

enter image description here

现在我需要通过插入 Mat1 和 Mat2 曲线来获得温度为 370C 的 Mat3 曲线。解决此问题的最佳方法是什么?我猜我可能需要做某种 3D 插值。还需要考虑数据的性质(对数行为)。

这是 Mat1 的数据

9.43E+06    6.00E+04
3.96E+06    6.20E+04
1.78E+06    6.40E+04
8.52E+05    6.60E+04
4.28E+05    6.80E+04
2.25E+05    7.00E+04
1.23E+05    7.20E+04
6.95E+04    7.40E+04
4.05E+04    7.60E+04
2.43E+04    7.80E+04
1.49E+04    8.00E+04
9.39E+03    8.20E+04

这是 Mat2 的数据

5.14E+08    4.80E+04
1.35E+08    5.00E+04
4.36E+07    5.20E+04
1.64E+07    5.40E+04
6.90E+06    5.60E+04
3.18E+06    5.80E+04
1.58E+06    6.00E+04
8.35E+05    6.20E+04
4.64E+05    6.40E+04
2.69E+05    6.60E+04
1.62E+05    6.80E+04
1.01E+05    7.00E+04
6.47E+04    7.20E+04
4.25E+04    7.40E+04
2.86E+04    7.60E+04
1.96E+04    7.80E+04
1.37E+04    8.00E+04
9735.23     8.20E+04

任何帮助将不胜感激。

编辑: 我正在为两条附加曲线添加数据;

温度 21C 时的曲线

3.98E+07    6.30E+04
1.58E+07    6.40E+04
4.03E+06    6.60E+04
1.47E+06    6.80E+04
6.57E+05    7.00E+04
3.37E+05    7.20E+04
1.91E+05    7.40E+04
1.16E+05    7.60E+04
7.49E+04    7.80E+04
5.04E+04    8.00E+04
3.52E+04    8.20E+04
2.53E+04    8.40E+04
1.87E+04    8.60E+04
1.41E+04    8.80E+04
1.08E+04    9.00E+04
8.47E+03    9.20E+04

537C 温度下的曲线

7.91E+06    3.80E+04
3.29E+06    4.00E+04
1.51E+06    4.20E+04
7.48E+05    4.40E+04
3.95E+05    4.60E+04
2.20E+05    4.80E+04
1.28E+05    5.00E+04
7.77E+04    5.20E+04
4.87E+04    5.40E+04
3.14E+04    5.60E+04
2.08E+04    5.80E+04
1.41E+04    6.00E+04
9.73E+03    6.20E+04
6.85E+03    6.40E+04

有关曲线的更多信息 - 这些是不同温度下材料的交变应力(y 轴)、失效循环数(x 轴)曲线。

谢谢。

1 个答案:

答案 0 :(得分:1)

我设法让简单的示例工作。首先,您的数据必须排序,因此测量值必须按温度排序,并且每个测量值必须按 y(应力)排序。我使用升序。第一种算法:

  1. 计算 BBOX

    简单地计算所有测量值的最小和最大 x,y 坐标。这将用于对数刻度和线性刻度之间的转换以及对齐。

  2. 重新采样并对齐所有测量值

    因此将所有测量值转换为样本具有相同的 y 值(跨所有测量值)。我使用了均匀采样的 y 轴。所以简单的步骤是 (ymax-ymin)/(n-1),其中 n 是重采样数据的点数。因此,所有测量将具有相同的大小,并且所有 y 值在同一索引的测量中将相同。缺失的 x 数据将用 0 填充。

    重采样可以在线性范围内完成。我使用了 piecewise cubic interpolation

  3. 为新温度创建新的测量值

    因此只需再次创建包含 n 点的新测量。 y 值与之前相同(因此只需从任何对齐的测量中复制它),然后从与我们正在处理的同一点对应的 4 个测量中的每一个中取 1 个点,并对其位置进行三次插值.但是这必须以对数标度来完成!

    温度的有效范围在第 2 次和第 3 次测量温度之间。

此处使用您的数据和 370 C 进行预览:

preview 370C

这里是 C++/VCL 示例(忽略 VCL 内容):

//$$---- Form CPP ----
//---------------------------------------------------------------------------
#include <vcl.h>
#include <math.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
int xs,ys;              // screen resolution
Graphics::TBitmap *bmp; // back buffer bitmap for rendering
//---------------------------------------------------------------------------
// here starts the important stuff
//---------------------------------------------------------------------------
float in[4][40]=        // input measureements format is: { temperature,x0,y0,x1,y1...,-1 }
    {{ 21.0,
    3.98E+07,6.30E+04,
    1.58E+07,6.40E+04,
    4.03E+06,6.60E+04,
    1.47E+06,6.80E+04,
    6.57E+05,7.00E+04,
    3.37E+05,7.20E+04,
    1.91E+05,7.40E+04,
    1.16E+05,7.60E+04,
    7.49E+04,7.80E+04,
    5.04E+04,8.00E+04,
    3.52E+04,8.20E+04,
    2.53E+04,8.40E+04,
    1.87E+04,8.60E+04,
    1.41E+04,8.80E+04,
    1.08E+04,9.00E+04,
    8.47E+03,9.20E+04,
    -1.0 },
    { 310.0,
    9.43E+06,6.00E+04,
    3.96E+06,6.20E+04,
    1.78E+06,6.40E+04,
    8.52E+05,6.60E+04,
    4.28E+05,6.80E+04,
    2.25E+05,7.00E+04,
    1.23E+05,7.20E+04,
    6.95E+04,7.40E+04,
    4.05E+04,7.60E+04,
    2.43E+04,7.80E+04,
    1.49E+04,8.00E+04,
    9.39E+03,8.20E+04,
    -1.0 },
    { 420.0,
    5.14E+08,4.80E+04,
    1.35E+08,5.00E+04,
    4.36E+07,5.20E+04,
    1.64E+07,5.40E+04,
    6.90E+06,5.60E+04,
    3.18E+06,5.80E+04,
    1.58E+06,6.00E+04,
    8.35E+05,6.20E+04,
    4.64E+05,6.40E+04,
    2.69E+05,6.60E+04,
    1.62E+05,6.80E+04,
    1.01E+05,7.00E+04,
    6.47E+04,7.20E+04,
    4.25E+04,7.40E+04,
    2.86E+04,7.60E+04,
    1.96E+04,7.80E+04,
    1.37E+04,8.00E+04,
    9735.23 ,8.20E+04,
    -1.0 },
    { 537.0,
    7.91E+06,3.80E+04,
    3.29E+06,4.00E+04,
    1.51E+06,4.20E+04,
    7.48E+05,4.40E+04,
    3.95E+05,4.60E+04,
    2.20E+05,4.80E+04,
    1.28E+05,5.00E+04,
    7.77E+04,5.20E+04,
    4.87E+04,5.40E+04,
    3.14E+04,5.60E+04,
    2.08E+04,5.80E+04,
    1.41E+04,6.00E+04,
    9.73E+03,6.20E+04,
    6.85E+03,6.40E+04,
    -1.0 }};             
//---------------------------------------------------------------------------
// temp and output data
//---------------------------------------------------------------------------
const n=40;                         // points to resmaple curves with
float dat[4][2+n+n];                // resampled input curves
float out[2+n+n];                   // interpolated curve
float xmin,xmax,ymin,ymax;          // BBOX
void resample(float *out,float *in,float y0,float y1)   // resample and align y to range and n points and store it to out
    {
    float t,d1,d2,a0,a1,a2,a3,x,y,x0,x1,x2,x3;
    int i,ii,i0,i1,i2,i3,nn;
    // scan how many points in[] has
    for (nn=0,i=1;in[i]>=0.0;i+=2) nn++;
    // resample input curves to n points
    out[0]=in[0];           // copy T
    out[n+n+1]=-1;          // end of data
    for (i=0;i<n;i++)
        {
        // y uniformly distributed and aligned in the dat array
        y=y0+((y1-y0)*float(i)/float(n-1));
        ii=1+i +i ;
        // check if range present
        if ((y<in[1+1])||(y>in[1+nn-1+nn-1+1]))
            {
            out[ii+0]=0.0;
            out[ii+1]=y;
            continue;
            }
        // find i1 so in[i1] <= y < in[i1+1]
        // linear search, can be replaced with binary search
        for (i1=0;i1<nn;i1++) if (in[1+i1+i1+1]>=y) break;
        if (in[1+i1+i1+1]>y) i1--;
        // neigboring indexes
        i0=i1-1; if (i0<  0) i0=   0;
        i2=i1+1; if (i2>=nn) i2=nn-1;
        i3=i1+2; if (i3>=nn) i3=nn-1;
        // convert to array index
        i0=1+i0+i0;
        i1=1+i1+i1;
        i2=1+i2+i2;
        i3=1+i3+i3;
        // parameter is based on y value
        d1=y-in[i1+1];
        d2=in[i2+1]-in[i1+1];
        if (fabs(d2)>1e-6) t=d1/d2; else t=0.0;
        // points to interpolate
        x0=in[i0];
        x1=in[i1];
        x2=in[i2];
        x3=in[i3];
        // cubic interpoaltion of x
        d1=0.5*(x2-x0);
        d2=0.5*(x3-x1);
        a0=x1;
        a1=d1;
        a2=(3.0*(x2-x1))-(2.0*d1)-d2;
        a3=d1+d2+(2.0*(-x2+x1));
        x=a0+(a1*t)+(a2*t*t)+(a3*t*t*t);
        if (x<0.0) x=0.0;   // just to be sure data is not messed up
        // copy point
        out[ii+0]=x;
        out[ii+1]=y;
        }
    }
//---------------------------------------------------------------------------
void interpolate(float *out,float T) // interpolate out[] as n point curve from dat[4][] matching temperature T
    {                                // dat[][] must be ordered ascending by T,x,y
    int i,ii;                        // valid T range is <dat[1][0],dat[2][0]>
    float t,d1,d2,a0,a1,a2,a3,x,x0,x1,x2,x3,t0,t1,t2,t3;
    out[0]=T;               // copy T
    out[n+n+1]=-1;          // end of data
    // parameter from T
    t=(T-dat[1][0])/(dat[2][0]-dat[1][0]);
    t0=dat[0][0];
    t1=dat[1][0];
    t2=dat[2][0];
    t3=dat[3][0];
    // cubic interpolation between curves
    for (i=0;i<n;i++)
        {
        // points to interpolate
        ii=1+i+i;
        x0=dat[0][ii];
        x1=dat[1][ii];
        x2=dat[2][ii];
        x3=dat[3][ii];
        // logarithm scale
        (x0>=xmin)?x0=log(x0/xmin)/log(xmax/xmin):x0=0.0;
        (x1>=xmin)?x1=log(x1/xmin)/log(xmax/xmin):x1=0.0;
        (x2>=xmin)?x2=log(x2/xmin)/log(xmax/xmin):x2=0.0;
        (x3>=xmin)?x3=log(x3/xmin)/log(xmax/xmin):x3=0.0;
        out[ii+1]=dat[0][ii+1]; // copy y
        // too much missing data
        if ((x1<=0.0)||(x2<=0.0)){ out[ii+0]=0; continue; }
        // mirror missing data
        if (x0<=0.0) x0=x1-((x2-x1)*(t1-t0)/(t2-t1));
        if (x3<=0.0) x3=x2+((x2-x1)*(t3-t2)/(t2-t1));
        // interpolate x
        d1=0.5*(x2-x0);
        d2=0.5*(x3-x1);
        a0=x1;
        a1=d1;
        a2=(3.0*(x2-x1))-(2.0*d1)-d2;
        a3=d1+d2+(2.0*(-x2+x1));
        x=a0+(a1*t)+(a2*t*t)+(a3*t*t*t);
        if (x<0.0) x=0.0;   // just to be sure data is not messed up
        else x=exp(x*log(xmax/xmin))*xmin; // back to linear scale
        out[ii+0]=x;
        }
    }
//---------------------------------------------------------------------------
void minmax(float *dat,bool _reset) // compute BBOX of the curves
    {
    int i;
    float x,y;
    for (i=1;dat[i]>=0.0;)
        {
        x=dat[i]; i++;
        y=dat[i]; i++;
        if (x<=0.0) continue;
        if (_reset){ xmin=xmax=x; ymin=ymax=y; _reset=false; }
        if (xmin>x) xmin=x;
        if (xmax<x) xmax=x;
        if (ymin>y) ymin=y;
        if (ymax<y) ymax=y;
        }
    }
//---------------------------------------------------------------------------
void toscr(float &x,float &y)   // convert x,y from plot data to screen coordinates (just for rendering)
    {
    float x0,dx,y1,dy;
    // range <0,1>
//  x=(x-xmin)/(xmax-xmin);                         // linear
//  y=(y-ymin)/(ymax-ymin);                         // linear
    (x>=xmin)?x=log(x/xmin)/log(xmax/xmin):x=0.0;   // logarithmic
    (y>=ymin)?y=log(y/ymin)/log(ymax/ymin):y=0.0;   // logarithmic
    // view
    x0=0.1*xs; dx=0.8*xs;
    y1=0.9*ys; dy=0.8*ys;
    // [pixels]
    x=x0+x*dx;
    y=y1-y*dy;
    }
//---------------------------------------------------------------------------
void plot(float *dat,TColor col)// renders measurement data (just for rendering)
    {
    int i,e;
    float x,y,r=2;
    // curve
    bmp->Canvas->Pen->Color=col;
    bmp->Canvas->Font->Color=col;
    for (e=1,i=1;dat[i]>=0.0;)
        {
        x=dat[i]; i++;
        y=dat[i]; i++;
        if (x<=0.0) continue;
        toscr(x,y);
        if (e)
            {
            bmp->Canvas->TextOutA(x,y,AnsiString().sprintf("%.0f C",dat[0]));
            bmp->Canvas->MoveTo(x,y);
            e=0;
            }
        else bmp->Canvas->LineTo(x,y);
        }
    // points
    for (i=1;dat[i]>=0.0;)
        {
        x=dat[i]; i++;
        y=dat[i]; i++;
        if (x<=0.0) continue;
        toscr(x,y);
        bmp->Canvas->Ellipse(x-r,y-r,x+r,y+r);
        }
    }
//---------------------------------------------------------------------------
void draw() // just render of my App
    {
    bmp->Canvas->Brush->Color=clWhite;
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));

    plot(dat[0],clRed);
    plot(dat[1],clGreen);
    plot(dat[2],clBlue);
    plot(dat[3],clBlack);

    plot(out,clMaroon);

    Form1->Canvas->Draw(0,0,bmp);
//  bmp->SaveToFile("out.bmp");
    }
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner):TForm(Owner) // init of my app
    {
    // init backbuffer
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    // here prepare data (important)
    int i;
    for (i=0;i<4;i++) minmax(in[i],i==0);
    for (i=0;i<4;i++) resample(dat[i],in[i],ymin,ymax);
    // here create new data for T=370[C]
    interpolate(out,370.0);
    // and also include it to the BBOX for rendering
    minmax(out,false);
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormDestroy(TObject *Sender) // not important just destructor of my App
    {
    delete bmp;
    }
//---------------------------------------------------------------------------
void __fastcall TForm1::FormResize(TObject *Sender) // not important just resize event
    {
    xs=ClientWidth;
    ys=ClientHeight;
    bmp->Width=xs;
    bmp->Height=ys;
    draw();
    }
//-------------------------------------------------------------------------
void __fastcall TForm1::FormPaint(TObject *Sender) // not important just repaint event
    {
    draw();
    }
//---------------------------------------------------------------------------

请参阅函数 TForm1::TForm1(TComponent* Owner) 了解如何使用它。

但是物理有效性值得怀疑 您应该通过 5 次测量来测试这种插值是否会产生有效数据。使用 4 来插值 5 并检查它们是否重叠如果没有,那么这可能需要额外的调整,例如增加插值多项式次数,或者也使用对数刻度进行重采样等......