平面圆的一般方程为:
x 2 + y 2 + a x + b y + c = 0 (1) x^2 + y^2 + ax + by +c = 0\tag 1 x2+y2+ax+by+c=0(1)
其中, a , b , c ∈ R a,b,c\in R a,b,c∈R。
式(1)配方,可以得到:
( x + a / 2 ) 2 + ( x + b / 2 ) 2 = a 2 / 4 + b 2 / 4 − c (2) (x + a/2)^2 + (x + b/2)^2 = a^2/4 + b^2/4 - c \tag 2 (x+a/2)2+(x+b/2)2=a2/4+b2/4−c(2)
对于给定的一系列二维数据 ( x i , y i ) , i = 0 , . . . , n (x_i,y_i),i=0,...,n (xi,yi),i=0,...,n,根据式(1)可以列出 n + 1 n+1 n+1个线性方程,然后可以采用最小二乘法求解,非常简单有效。
不经过给定起点与终点的2D圆弧拟合另一种方法可参考:最小二乘法拟合圆。
有时候我们需要约束圆弧精确经过给定起点与终点,设起点坐标为 ( x 0 , y 0 ) (x_0,y_0) (x0,y0),终点坐标为 ( x n , y n ) (x_n,y_n) (xn,yn),则有约束等式:
{ x 0 2 + y 0 2 + a x 0 + b y 0 + c = 0 x n 2 + y n 2 + a x n + b y n + c = 0 (3) \begin{cases} x_0^2 + y_0^2 + ax_0 + by_0 +c = 0 \\ x_n^2 + y_n^2 + ax_n + by_n +c = 0 \\ \tag 3 \end{cases} {x02+y02+ax0+by0+c=0xn2+yn2+axn+byn+c=0(3)
(1)若起点与终点坐标重合,则式(3)退化为一个约束等式,将参数 c c c的表达式代入到式(1),利用最小二乘法求解参数 a , b a,b a,b,再计算参数 c c c。
(2)若 x 0 ≠ x n x_0\ne x_n x0=xn,根据式(3)解出参数 a , c a,c a,c的表达式,然后代入到式(1),利用最小二乘法求解参数 b b b,再计算参数 a , c a,c a,c。
(3)若 y 0 ≠ y n y_0\ne y_n y0=yn,根据式(3)解出参数 b , c b,c b,c的表达式,然后代入到式(1),利用最小二乘法求解参数 a a a,再计算参数 b , c b,c b,c。
circle_fitting_2D.m
function [ center, R, fittingError ] = circle_fitting_2D( points, pointCount, crossStartAndEndPointFlag )
%{
Function: circle_fitting_2D
Description: 2D圆弧拟合
Input: 二维点points, 点个数pointCount, 是否经过起点/终点标志位crossStartAndEndPointFlag
Output: 圆心center, 半径R, 拟合误差fittingError
Author: Marc Pony(marc_pony@163.com)
圆的方程:x^2 + y^2 + a*x + b*y +c = 0 -> (x + a/2)^2 + (x + b/2)^2 = a^2/4 + b^2/4 - c
%}
if crossStartAndEndPointFlag == 0
x = points(:, 1);
y = points(:, 2);
A = [x, y, ones(size(x))];
B = -x.^2 - y.^2;
temp = A \ B;
a = temp(1);
b = temp(2);
c = temp(3);
else
x0 = points(1, 1);
y0 = points(1, 2);
xn = points(pointCount, 1);
yn = points(pointCount, 2);
x = points(2 : pointCount - 1, 1);
y = points(2 : pointCount - 1, 2);
EPS = 1.0e-4;
if abs(x0 - xn) < EPS && abs(y0 - yn) < EPS %起点与终点重合
A = [x - x0, y - y0];
B = x0^2 + y0^2 - x.^2 - y.^2;
temp = A \ B;
a = temp(1);
b = temp(2);
c = -x0^2 - y0^2 - a * x0 - b * y0;
else
if abs(x0 - xn) > abs(y0 - yn)
A = x * (yn - y0) / (x0 - xn) + y - (x0 * yn - xn * y0) / (x0 - xn);
B = -x.^2 - y.^2 - x * (xn^2 + yn^2 - x0^2 - y0^2) / (x0 - xn) + ((xn^2 + yn^2) * x0 - (x0^2 + y0^2) * xn) / (x0 - xn);
b = A \ B;
P = -x0^2 - y0^2 - b * y0;
Q = -xn^2 - yn^2 - b * yn;
a = (P - Q) / (x0 - xn);
c = -(P * xn - Q * x0) / (x0 - xn);
else
A = x * (xn - x0) / (y0 - yn) + y - (y0 * xn - yn * x0) / (y0 - yn);
B = -x.^2 - y.^2 - x * (yn^2 + xn^2 - y0^2 - x0^2) / (y0 - yn) + ((yn^2 + xn^2) * y0 - (y0^2 + x0^2) * yn) / (y0 - yn);
a = A \ B;
P = -y0^2 - x0^2 - a * x0;
Q = -yn^2 - xn^2 - a * xn;
b = (P - Q) / (y0 - yn);
c = -(P * yn - Q * y0) / (y0 - yn);
end
end
end
R = sqrt(a^2 / 4 + b^2 / 4 - c);
center(1) = -a / 2;
center(2) = -b / 2;
fittingError = sqrt((points(:, 1) - center(1)).^2 + (points(:, 2) - center(2)).^2) - R;
end
test_circle_fitting_2D.m
clc
clear
close all
%% 绘参考圆
figure
axis([0 100 0 100])
theta = linspace(0, 2*pi, 1000);
r = 30;
x = 50 + r*cos(theta);
y = 50 + r*sin(theta);
plot(x,y,'g--')
hold on
axis equal
%% 左键点击取点,按回车键退出
pos = ginput();
%pos = [pos;pos(1,1), pos(1,2)]; %起点与终点重合
pointCount = size(pos, 1);
plot(pos(1, 1), pos(1, 2), 'k+')
plot(pos(pointCount, 1), pos(pointCount, 2), 'k+')
plot(pos(2:pointCount-1, 1), pos(2:pointCount-1, 2), 'o')
%% 圆最小二乘拟合
crossStartAndEndPointFlag = 1; %0:不经过给定起点与终点; 1:精确经过给定起点与终点
[ center, R, fittingError ] = circle_fitting_2D( pos, pointCount, crossStartAndEndPointFlag )
x = center(1) + R * cos(theta);
y = center(2) + R * sin(theta);
plot(x, y, 'b')
plot(center(1), center(2), 'b+')
plot(center(1), center(2), 'bo')
3D圆弧拟合可以分解两个问题:
(1)三维点的球面拟合(见博文:最小二乘法球面拟合(附完整代码))
(2)平面拟合
球面拟合后,球心便为3D圆弧的圆心,平面拟合则可以得到3D圆弧所在平面的法向量,3D圆弧的方程便确定。