如何找到一条线的斜率变化的点?

时间:2014-08-26 18:00:52

标签: c# algorithm line

我有一些传感器数据的图表。在某些时候,整体图表(不计算数据中的小凹凸;我的意思是整线)将显示斜率的突然变化并且变得更加陡峭。这一点在图表的开头永远不会正确,但它也从未到达过一半。我需要找到斜率变化的点(或尽可能接近它)并且线变得更陡峭。这是我到目前为止的代码。它有效,但不一致---有些图形返回(0,0)作为点,这是不正确的,而其他图形返回的点远远超过图形的初始向上,这也是不正确的。任何帮助,将不胜感激;有没有人知道是否有支票或我遗失的任何东西?

Point Location = new Point(0, 0);
float lastSlope = 0;
float slope = 0;

for (int i = 6; i+6 < cnt/2; i+=6)
{
    lastSlope = (display.DataSources[0].Samples[i].y - display.DataSources[0].Samples[i-6].y) / (display.DataSources[0].Samples[i].x - display.DataSources[0].Samples[i-6].x);
    slope = (display.DataSources[0].Samples[i+6].y - display.DataSources[0].Samples[i].y) / (display.DataSources[0].Samples[i+6].x - display.DataSources[0].Samples[i].x);

    //lastSlope is significantly different from slope AND (i is more than a quarter of cnt)
    if (((slope*100)-45 > lastSlope*100) && (i > cnt / 4) )
    {
        Debug.WriteLine("changing location!");
        Location.X = (int) display.DataSources[0].Samples[i].x;
        Location.Y = (int) display.DataSources[0].Samples[i].y;
    }

}

for (int i = 6; i + 6 < cnt/2; i += 6)
{
    if (Location.X == (int)display.DataSources[0].Samples[i].x && Location.Y == (int) display.DataSources[0].Samples[i].y)
    {
        display.DataSources[1].Samples[i].y = 65;  //adjust the did-we-edit-the-data line to show the location of the point
    }
}

//convert y value to inches
//Location.Y = (int)(96 - (((double)Location.Y) / 25.4));

return Location;

1 个答案:

答案 0 :(得分:1)

找到一个模拟传感器数据部分的函数应该是Math包的例程,至少如果你a)有一个,b)可以熟练使用它。

由于我在这两点都失败了,这就是我要做的事情:计算衍生物不是来自我没有的函数,而是来自我所拥有的数据点。

  • 通过仔细查看数据和/或反复试验来确定分组编号n。我首先估计从较低坡度S1变为陡坡S2所需的点数,然后取一小部分,比如1/3。

  • 计算每组n个点的平均值。这减少了从N到N / n的总点数,并且应该摆脱颠簸。

  • 计算每个平均值之间的斜率,得到N / n - 1个斜率;这基本上是一阶导数。打折数据序列的(对我不知道)开头,列表中应该有两个不同的斜率S1和S2,以及从S1到S2上升的少量1-3个斜率。

  • 我们寻找的点位于这些中间斜率的中间位置。

  • 可以重复最后一步并将第二个导数点计算为斜率之间的斜率。这应该导致两个范围,其中(“第二阶”)斜率接近0,并且在两个范围之间,它们是非常正的。我们寻找的点是在最大值之后的某个地方。

要找到一个好的n不会那么难,只要每个批次之间的数据密度相似,但数据的“颠簸”也是找到一个好的n的因素。

这是一个示例代码;

备注

  • 此解决方案对数据进行2或3次传递。有时准备数据会使事情变得更容易处理..

  • 较大的部分是创建测试数据的代码。实际计算只有八行代码!

  • 您需要使用 n cutOff 的值。

  • 斜坡之前或之后我没有任何数据点。您必须从计算中排除这些部分,因为可能会有更多的斜率变化。

在实际计算之前,您将跳过大部分代码..:

// test data
List<PointF> oData = new List<PointF>();  // orginal data values
List<PointF> D0 = new List<PointF>();     // reduced smoothed data
List<PointF> D1 = new List<PointF>();     // 1st derivative
List<PointF> D2 = new List<PointF>();     // 2nd derivative
List<PointF> M = new List<PointF>();      // reasonably large values from D2

int N1 = 255;            // number of data points with slope S1
int N2 = 15;             // number of data points between S1 and S2
int N3 = 40;             // number of data points with slope S2

int n = N2 / 3;          // grouping number

float S1 = 0.2f;         // Slope 1
float S2 = 1.3f;         // Slope 2
float S12 = (S2 - S1) / N2; 
float P1y = N1 * S1;
float cutOff = 2f;      // cutoff value to detemine 'reasonably' large slope changes
int roughness = 10;
float smoothness = 15f;

// create the data points
for (int i = 1; i <= N1; i++) oData.Add(new PointF(
         i, i * S1 + (R.Next(roughness) - roughness/2) / smoothness));
for (int i = 1; i <= N2; i++) oData.Add(new PointF(
         i + N1, P1y + i * S12 + (R.Next(roughness) - roughness/2) / smoothness));
for (int i = 1; i <= N3; i++) oData.Add(new PointF(
         i + N1 + N2, P1y + i * S2 + (R.Next(roughness) - roughness/2) / smoothness));

// display them
chart1.ChartAreas.Add("data");
Series s = chart1.Series.Add("data");
s.ChartType = SeriesChartType.Line;
foreach (PointF p in oData) s.Points.Add(new DataPoint(p.X, p.Y));

// smoothen the data
for (int i = 1; i < oData.Count / n; i++) 
{
    float ysum = 0.0f;
    for (int j = 0;j<n; j++) ysum += oData[i*n+j].Y;
    D0.Add(new PointF(i, ysum/n));
}

// 1st derivative
for (int i = 1; i < D0.Count; i++) D1.Add(new PointF(i, D0[i - 1].Y - D0[i].Y));

// 2nd derivative
for (int i = 1; i < D1.Count; i++) D2.Add(new PointF(i, D1[i - 1].Y - D1[i].Y));

// collect 'reasonably' large values from D2
foreach (PointF p in D2) if (Math.Abs(p.Y / cutOff ) > 1) M.Add(p);

// our target is n after the last one
int targetX = (int) (M[M.Count -1 ].X * n) + n;

// display as annotation
VerticalLineAnnotation LA = new VerticalLineAnnotation();
LA.LineColor = Color.Red;
LA.AnchorDataPoint = s.Points[targetX];
LA.IsInfinitive = true;
LA.ClipToChartArea = "data";
chart1.Annotations.Add(LA);

以下是结果图片:

slopeChange

相关问题