英文:
Optimize Center of Circle given noisy data points on arbitrary Plane
问题
我似乎找不到一种算法来计算在3D空间的任意平面上给定一组噪点数据(在圆周上)的圆心。我尝试过不同的方法,例如一次只取3个点然后对圆心求平均值,但我相信应该有更好的解决方案,通过最小化到所有点的距离来优化圆心。有人有什么想法吗?
任何想法或方法都将非常赞赏!
英文:
I don't seem to find an algorithm to calculate the center of a circle given a set of noisy data (on the circumference of the circle) on an arbitrary Plane in 3D.
I did try different approached as to calculating the center by only taking 3 points at a time and then averaging the centers, yet I believe there must be a better solution to optimize the center by minimizing the distance to all points.
Does anyone have an Idea?
Any Ideas or approaches would be highly appreciated!
答案1
得分: 1
终于,我找到了几种调整我的搜索词汇的解决方案:
<https://de.mathworks.com/matlabcentral/answers/475212-circle-least-squares-fit-for-3d-data>
<https://jekel.me/2015/Least-Squares-Sphere-Fit/>
这两种方法都非常有效。在第二个链接中,您首先需要计算平面,可以使用奇异值分解(SVD)来完成。基本上,我认为这两种方法非常相似,基本上产生相同的结果。
英文:
Finally I found several Solutions adjusting my search terms:
<https://de.mathworks.com/matlabcentral/answers/475212-circle-least-squares-fit-for-3d-data>
<https://jekel.me/2015/Least-Squares-Sphere-Fit/>
Both Approaches work very well. In the second link you have to calculate the plane first, which can be done using SVD. Basically I think both approaches are very similar and produce basically the same results.
答案2
得分: 0
以下是翻译好的部分,代码部分未翻译:
这是我的方法:
我有一个辅助结构(称为Aprox),其中包含中心、半径(为了速度优化我使用其平方)和边界点(两个或三个)。
我有以下函数:
CircumscribedCircle_2Points(P1, P2):Aprox(3D线段的中心)
CircumscribedCircle_3Points(P1, P2, P3):Aprox(绘制的圆的中心),由https://en.wikipedia.org/wiki/Circumscribed_circle#Cartesian_coordinates_from_cross-_and-dot-products编程
CircumscribedCircle_IsIn($Aprox, $Pnt):boolean(它是否在给定的Approx中)
我从前两个点计算Aprox,然后遍历其他点。如果在圆内,我什么也不做;如果在圆外,我计算一个新的Aprox并再次遍历(所有点)。
规则是:如果平面上有4个点,其中一个总是在其他三个点构成的圆内。
function CircumscribedCircle_2Points($P1, $P2, $Aprox=[])
{
$Aprox = as_New('Borders', [$P1, $P2]);
$Aprox['Center'] = v3d_AvgV3($P1, $P2);
$Aprox['RadSq'] = v3d_mV3AbsKv($Aprox['Center'], $P1);
$Aprox['RadSq'] = $Aprox['RadSq'] + 0.0001; //浮点数舍入误差
return $Aprox;
}
function CircumscribedCircle_3Points($P1, $P2, $P3, $Aprox=[], $JmP=0, $Jm=0, $u1=0, $u2=0, $u3=0)
{
$Aprox = as_New('Borders', [$P1, $P2, $P3]);
$JmP = v3d_Abs(v3d_vxV3(v3d_mV3($P1, $P2), v3d_mV3($P2, $P3)));
$Jm = 2 * $JmP**2;
$u1 = v3d_mV3AbsKv($P2, $P3) * v3d_sxV3(v3d_mV3($P1, $P2), v3d_mV3($P1, $P3)) / $Jm;
$u2 = v3d_mV3AbsKv($P1, $P3) * v3d_sxV3(v3d_mV3($P2, $P1), v3d_mV3($P2, $P3)) / $Jm;
$u3 = v3d_mV3AbsKv($P1, $P2) * v3d_sxV3(v3d_mV3($P3, $P1), v3d_mV3($P3, $P2)) / $Jm;
$Aprox['Center'] = v3d_pV3pV3(v3d_xSkl($P1, $u1), v3d_xSkl($P2, $u2), v3d_xSkl($P3, $u3));
$Aprox['RadSq'] = v3d_mV3AbsKv($P1, $P2) * v3d_mV3AbsKv($P2, $P3) * v3d_mV3AbsKv($P3, $P1) / (2*$JmP)**2;
$Aprox['RadSq'] = $Aprox['RadSq'] + 0.0001; //浮点数舍入误差
return $Aprox;
}
function CircumscribedCircle_IsIn($Aprox, $Pnt)
{
return v3d_mV3AbsKv($Aprox['Center'], $Pnt) <= $Aprox['RadSq'];
}
// 更多代码...
这段代码是通用的,可以在PHP和Javascript中使用。我将辅助变量声明为可选参数(以避免使用var关键字)。
as_*函数与关联数组相关,ar_*函数与数值数组相关,nm_*函数与数值相关,其功能从函数名称中就可以看出来。v3d_*函数与3D向量相关,并在脚注中列出。
这段代码已经翻译完毕,如果你需要更多帮助,请告诉我。
英文:
Here's how I do it:
I have an auxiliary structure (called Aprox) that contains Center, Radius (I use its square for speed optimization) and Boundary points (two or three).
I have the following functions:
CircumscribedCircle_2Points(P1, P2):Aprox (center of the line segment in 3D)
CircumscribedCircle_3Points(P1, P2, P3):Aprox (center of the circle traced ), programmed by https://en.wikipedia.org/wiki/Circumscribed_circle#Cartesian_coordinates_from_cross-_and_dot-products
CircumscribedCircle_IsIn($Aprox, $Pnt):boolean (It is in the given Approx)
I compute Aprox from the first two points and then run through the other points. If it's in circle I do nothing, if it's out I calculate a new Aprox and run through again (all points).
The rule is: If there are 4 points in the plane, one of them is always inside the circle given by the other three.
function CircumscribedCircle_2Points($P1, $P2,
$Aprox=[])
{
$Aprox = as_New('Borders', [$P1, $P2]);
$Aprox['Center'] = v3d_AvgV3($P1, $P2);
$Aprox['RadSq'] = v3d_mV3AbsKv($Aprox['Center'], $P1);
$Aprox['RadSq'] = $Aprox['RadSq'] + 0.0001; //Float rounding errors
return $Aprox;
}
function CircumscribedCircle_3Points($P1, $P2, $P3,
$Aprox=[],$JmP=0,$Jm=0,$u1=0,$u2=0,$u3=0)
//https://en.wikipedia.org/wiki/Circumscribed_circle#Cartesian_coordinates_from_cross-_and_dot-products
{
$Aprox = as_New('Borders', [$P1, $P2, $P3]);
$JmP = v3d_Abs(v3d_vxV3(v3d_mV3($P1,$P2),v3d_mV3($P2,$P3)));
$Jm = 2 * $JmP**2;
$u1 = v3d_mV3AbsKv($P2,$P3) * v3d_sxV3(v3d_mV3($P1,$P2),v3d_mV3($P1,$P3)) / $Jm;
$u2 = v3d_mV3AbsKv($P1,$P3) * v3d_sxV3(v3d_mV3($P2,$P1),v3d_mV3($P2,$P3)) / $Jm;
$u3 = v3d_mV3AbsKv($P1,$P2) * v3d_sxV3(v3d_mV3($P3,$P1),v3d_mV3($P3,$P2)) / $Jm;
$Aprox['Center'] = v3d_pV3pV3(v3d_xSkl($P1,$u1), v3d_xSkl($P2,$u2), v3d_xSkl($P3,$u3));
$Aprox['RadSq'] = v3d_mV3AbsKv($P1,$P2) * v3d_mV3AbsKv($P2,$P3) * v3d_mV3AbsKv($P3,$P1) / (2*$JmP)**2;
$Aprox['RadSq'] = $Aprox['RadSq'] + 0.0001; //Float rounding errors
return $Aprox;
}
function CircumscribedCircle_IsIn($Aprox, $Pnt)
{
return v3d_mV3AbsKv($Aprox['Center'], $Pnt) <= $Aprox['RadSq'];
}
function CircumscribedCircle_SetAprox($Aprox, $Pnt,
$HrLen=0,$i=0,$TestAprox=[],$Ok=true,$j=0, $TestRet=[])
{
$HrLen=ar_Count($Aprox['Borders']); //2 or 3
for ($i=0; $i<$HrLen; $i++)
{
$TestAprox = CircumscribedCircle_2Points($Aprox['Borders'][$i], $Pnt);
$Ok = true;
for ($j=0; $j<$HrLen; $j++)
{
if ($i==$j) continue;
if (CircumscribedCircle_IsIn($TestAprox, $Aprox['Borders'][$j])) continue;
$Ok = false;
break;
}
if ($Ok) return $TestAprox;
}
if ($HrLen==2)
{
return CircumscribedCircle_3Points($Aprox['Borders'][0], $Aprox['Borders'][1], $Pnt);
}
$TestRet = ar_New();
$TestAprox = CircumscribedCircle_3Points($Aprox['Borders'][0], $Aprox['Borders'][1], $Pnt);
if (CircumscribedCircle_IsIn($TestAprox, $Aprox['Borders'][2])) ar_Push($TestRet, $TestAprox);
$TestAprox = CircumscribedCircle_3Points($Aprox['Borders'][0], $Aprox['Borders'][2], $Pnt);
if (CircumscribedCircle_IsIn($TestAprox, $Aprox['Borders'][1])) ar_Push($TestRet, $TestAprox);
$TestAprox = CircumscribedCircle_3Points($Aprox['Borders'][1], $Aprox['Borders'][2], $Pnt);
if (CircumscribedCircle_IsIn($TestAprox, $Aprox['Borders'][0])) ar_Push($TestRet, $TestAprox);
ar_Sort($TestRet, function($a, $b){return $a['RadSq']-$b['RadSq'];});
return $TestRet[0];
}
function CircumscribedCircle($Arr,
$Aprox=[], $i=0, $l=0, $max=0)
{
$Aprox = CircumscribedCircle_2Points($Arr[0], $Arr[1]);
for ($i=0, $l=ar_Count($Arr); $i<$l; $i++)
{
if (CircumscribedCircle_IsIn($Aprox, $Arr[$i])) continue;
$Aprox = CircumscribedCircle_SetAprox($Aprox, $Arr[$i]);
if (!$Aprox) return false;
$i=-1;
}
return as_New('Center',$Aprox['Center'], 'Radius', nm_Sqrt($Aprox['RadSq']));
}
//-------------------------
in JS
const x = 0;
const y = 1;
const z = 2;
in PHP
define('x', 0);
define('y', 1);
define('z', 2);
function v3d_AvgV3($v, $w)
//Diameter of two vectors (center of the line segment)
{
return ar_New(($v[x]+$w[x])/2, ($v[y]+$w[y])/2, ($v[z]+$w[z])/2);
}
function v3d_Abs($v)
//Absolute vector size
{
return nm_Sqrt($v[x]**2 + $v[y]**2 + $v[z]**2);
}
function v3d_vxV3($v,$w)
//Vector product
{
return ar_New($v[y]*$w[z]-$w[y]*$v[z], $v[z]*$w[x]-$w[z]*$v[x], $v[x]*$w[y]-$w[x]*$v[y]);
}
function v3d_mV3Abs($v, $w)
//Vector difference
{
return nm_Sqrt(($v[x]-$w[x])**2 + ($v[y]-$w[y])**2 + ($v[z]-$w[z])**2);
}
function v3d_mV3AbsKv($v, $w)
//Square of vector difference
{
return ($v[x]-$w[x])**2 + ($v[y]-$w[y])**2 + ($v[z]-$w[z])**2;
}
function v3d_sxV3($v,$w)
//Scalar product
{
return $v[x]*$w[x] + $v[y]*$w[y] + $v[z]*$w[z];
}
function v3d_pV3pV3($v,$w,$u)
//Sum 3 3D vect
{
return ar_New($v[x]+$w[x]+$u[x], $v[y]+$w[y]+$u[y], $v[z]+$w[z]+$u[z]);
}
function v3d_xSkl($v,$k)
//3D vect multiple scalar
{
return ar_New($v[x]*$k, $v[y]*$k, $v[z]*$k);
}
The code is universal and works both in PHP and Javascript I declare auxiliary variables as optional parameters (to avoid using var keyword)
as_* are functions related to associative arrays, ar_* are functions related to numeric arrays, nm_* is numeric and their function is clear from their name. v3d_* are functions related to 3d vectors and are listed in the footnote.
Translated with www.DeepL.com/Translator (free version)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论