在下面的曲线(蓝线)中,我试图检测应该位于x = 2.5附近的“膝盖/肘部”
这是我正在使用的一组值:
x = { - 10,-9,-8,-7,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6, 7,8,9,10}
y = {0,10,20,30,40,50,60,70,80,90,100,107,122,145,176,215,262,317,380,451,530,617}
我尝试了Kneedle algorithm和formal definition of the curvature of a graph(有符号曲率)。我对Kneedle算法的问题是在实时应用程序(嵌入式系统)中我不知道哪个是y轴的最大值,所以我不能正确地对点进行归一化,也找不到斜率值适用于所有情况。当使用图形曲率的形式定义时,我尝试用5阶多项式(绿线)拟合曲线,然后得到导数的值来计算曲率。然而,由于多项式,该方法在x = -2附近找到曲率,因为该点周围存在曲率。
有人可以建议我检测膝盖/肘部吗?
答案 0 :(得分:2)
这不是连续曲线,也不是连续曲线的近似值,因此我们不必在此浪费时间:您有一个简单的多边形。相当于“曲率半径”的多边形是入射角和折射角之差:差值越小,“曲率”的半径越大。
如果您正确地对数据进行采样,我们可以计算每个数据点的角度差异:
for (i=1, i<p.length -1):
vector1 = p[i] - p[i-1] // assuming your language of choice has points as primitives
vector2 = p[1+1] - p[i] // if not, you'll have to extract x/y separately.
p[i].angle = findAngle(vector1,vector2)
findAngle
函数应该很容易实现,有一百万个关于如何用你喜欢的语言实现它的教程(它是许多语言中的基本语义,甚至是某些语言的内置语言)。就是这样,我们已经将我们的2D数据转换为具有(x,y,z)坐标的3D数据,其中z
表示穿过该点的行进中的局部角度。
然后找到任何“膝盖”,我们可以查看所有三个数据点(x-1),(x)和(x + 1)的集合,并考虑那些局部角度差异的x
大于其邻居的角度差异。 x
最大的区别是“胜利者”:你找到了你的膝盖。 (或者实际上,一个膝盖,因为点数据使零承诺不会上下多次,导致多边形有很多变形)
knee = undefined
max_diff = 0;
for (i=1, i<p.length -1):
a = p[i].angle
a1 = p[i-1].angle
d1 = a1-a
a3 = p[i+1].angle
d2 = a3-a
diff = ... // there's a few ways to compute this
p[i].diff = diff // always useful if we need to do more work later
if (diff > max_diff):
max_diff = diff
knee = p[i]
我已经将差异计算留给了你,因为你可能只想要d1 + d2或(d1 + d2)/ 2,或者你想根据d1或d2(但不是两者)来切换0等。
当然,重要的是,我们可以“一次性”完成所有这些事情,因为我们收集数据点,因为新点不会影响旧点的位置,所以在某些时候n
,我们已经知道最多n-1
的角度,并且我们已知道膝盖到n-2
,因此我们可以在一次线性传递中计算所有这些值。好又高效。
这种方法类似于找到数据拟合曲线的导数的根,但当我们只有数值数据时,有时最正确的方法是使用数据,而不是“假定重建你从中获取数据的原始事物”。在这种情况下,您没有了解数据源在采样点之间的作用。也许它的表现很好,也许不是,但是你不知道,所以不要浪费周期来解决问题,而是直接使用你知道的属性(当然, 它们对的真实含义:这些是传感器读数,您的传感器绝对会产生噪音甚至是错误的。)
答案 1 :(得分:1)
我一直在研究膝关节/肘关节检测。绝不是我是专家。不过,我测试了你到目前为止实现的代码的例子。
Kneedle -> 3.0
L-method -> 4.0
S-method -> 2.0
Menger Curvature -> 1.0
DFDT -> 3.0
DSDT -> 3.0
R-Method -> NA (R2 = NAN)
DK-method -> 1.0
K-needle似乎给出了一个很好的近似值,如DFDT / DSDT和S-方法。 如你所说,kneedle与你的用例无关。
DFDT代表动态第一导数阈值。 它计算一阶导数并使用阈值算法来检测膝/肘点。 DSDT类似,但使用二阶导数,我的评价表明他们有相似的表现。
S方法是L方法的扩展。 L方法在曲线上插入两条直线,两条线之间的截距是拐点/肘点。通过循环整个点,拟合线并评估MSE(均方误差)来找到最佳拟合。 S方法适合3条直线,这提高了精度,但也需要更多的计算。
我的所有代码都在github上公开发布。 此外,此article可以帮助您找到有关该主题的更多信息。它只有四页长,所以应该很容易阅读。 您可以使用代码,如果您想讨论任何方法,请随时这样做。
答案 2 :(得分:0)
这是一个算法:
1)将范围分成3rds。 A,B,C
2)对于每个范围(A,B,C),计算任意点的最大斜率减去任何点的最小斜率,并称之为SlopeDiversity。
3)无论哪个三分之一(A,B或C)具有最大的斜率多样性,请从步骤#1开始重复整个过程。这就像二分,但是是三分。
可能还有一些二分算法有效,但是分成3rds是有道理的,因为你想要丢掉坏部分并保留最好的部分,每次都需要分成3个范围。
另一种'陈述'或者做到这一点的方法是,当你将所有点与一个简单的线性回归模型进行差异时,选择具有“最大总误差”的第三种方法(只有THAT第3行的公式)
答案 3 :(得分:0)
答案取决于你如何获得这些价值观。以下答案假设您在输入中获得了一组这些XY数字(如您的示例中所示)。
还有你如何定义曲率。
对于曲率的几何意义(与轴方向无关),您可以将B点的曲率估计为abs( crossproduct(B-A, C-B) ) / length(C-A)
,其中A和C是两个相邻点。因为平方根(长度需要)可能是昂贵的,尤其是在嵌入式中,您可以使用该值的平方。
对于曲率的代数定义(二阶导数,很大程度上取决于轴的方向),请参阅以下公式:https://mathformeremortals.wordpress.com/2013/01/12/a-numerical-second-derivative-from-three-points/
对于曲率的几何意义, 更新:,如果点均匀分布,则上述公式才可以。
对于不同的点密度,最好使用a precise formula作为曲率半径。 4*K
= 2*abs( crossproduct(B-A, C-B) )
。不幸的是,你需要一个平方根(你可以先将所有三个边的正方形相乘,然后从产品中取一个根)。