我有一些传感器数据的图表。在某些时候,整体图表(不计算数据中的小凹凸;我的意思是整线)将显示斜率的突然变化并且变得更加陡峭。这一点在图表的开头永远不会正确,但它也从未到达过一半。我需要找到斜率变化的点(或尽可能接近它)并且线变得更陡峭。这是我到目前为止的代码。它有效,但不一致---有些图形返回(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;
答案 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);
以下是结果图片: