最小二乘法拟合空间直线
空间直线的一种表达方式:(点向式)
x
−
x
0
m
=
y
−
y
0
n
=
z
−
z
0
k
\frac {x-x_0}{m}= \frac{y-y_0}{n}= \frac{z-z_0}{k}
mx−x0=ny−y0=kz−z0
方法一
可以将该公式转换为如下形式:
{
x
=
x
0
+
m
k
(
z
−
z
0
)
y
=
y
0
+
n
k
(
z
−
z
0
)
\left\{ \begin{aligned} x&=x_0+\frac{m}{k}(z-z_0) \\ \\ y&=y_0+\frac{n}{k}(z-z_0) \end{aligned} \right.
⎩
⎨
⎧xy=x0+km(z−z0)=y0+kn(z−z0)
令 方程为
{
x
=
k
1
z
+
b
1
y
=
k
2
z
+
b
2
\left\{ \begin{aligned} x&=k_1z+b_1 \\ y&=k_2z+b_2 \end{aligned} \right.
{xy=k1z+b1=k2z+b2
其中
k
1
=
m
k
,
b
1
=
x
0
−
m
k
z
0
k
2
=
n
k
,
b
2
=
y
0
−
n
k
z
0
\begin{aligned} k_1 &= \frac{m}{k},&b_1 &= x_0 -\frac{m}{k}z_0 \\\\ k_2 &= \frac{n}{k},&b_2 &= y_0 -\frac{n}{k}z_0 \end{aligned}
k1k2=km,=kn,b1b2=x0−kmz0=y0−knz0
利用残差的平方和
Q
1
=
∑
i
=
1
n
(
x
i
−
k
1
z
i
−
b
1
)
2
Q
2
=
∑
i
=
1
n
(
y
i
−
k
2
z
i
−
b
2
)
2
Q_1=\displaystyle\sum_{i=1}^n(x_i-k_1z_i-b_1)^2 \\ Q_2=\displaystyle\sum_{i=1}^n(y_i-k_2z_i-b_2)^2
Q1=i=1∑n(xi−k1zi−b1)2Q2=i=1∑n(yi−k2zi−b2)2
为使得
Q
1
Q_1
Q1,
Q
2
Q_2
Q2最小,则应满足:
{
∂
Q
1
∂
k
1
=
0
,
∂
Q
1
∂
b
1
=
0
∂
Q
2
∂
k
2
=
0
,
∂
Q
2
∂
b
2
=
0
\left\{ \begin{aligned} \frac{\partial Q_1}{\partial k_1}=0, \frac{\partial Q_1}{\partial b_1}=0\\ \frac{\partial Q_2}{\partial k_2}=0, \frac{\partial Q_2}{\partial b_2}=0\\ \end{aligned} \right.
⎩
⎨
⎧∂k1∂Q1=0,∂b1∂Q1=0∂k2∂Q2=0,∂b2∂Q2=0
即
{
∑
i
=
1
n
2
(
x
i
−
k
1
z
i
−
b
1
)
×
(
−
z
i
)
=
0
∑
i
=
1
n
2
(
x
i
−
k
1
z
i
−
b
1
)
×
(
−
1
)
=
0
∑
i
=
1
n
2
(
y
i
−
k
2
z
i
−
b
2
)
×
(
−
z
i
)
=
0
∑
i
=
1
n
2
(
y
i
−
k
2
z
i
−
b
2
)
×
(
−
1
)
=
0
\left\{ \begin{aligned} &\displaystyle\sum_{i=1}^n2(x_i-k_1z_i-b_1)\times (-z_i) =0\\ &\displaystyle\sum_{i=1}^n2(x_i-k_1z_i-b_1)\times (-1) =0\\ &\displaystyle\sum_{i=1}^n2(y_i-k_2z_i-b_2)\times (-z_i) =0\\ &\displaystyle\sum_{i=1}^n2(y_i-k_2z_i-b_2)\times (-1) =0\\ \end{aligned} \right.
⎩
⎨
⎧i=1∑n2(xi−k1zi−b1)×(−zi)=0i=1∑n2(xi−k1zi−b1)×(−1)=0i=1∑n2(yi−k2zi−b2)×(−zi)=0i=1∑n2(yi−k2zi−b2)×(−1)=0
联立上面的方程,得:
{
k
1
=
n
∑
x
i
z
i
−
∑
x
i
∑
z
i
n
∑
z
2
−
∑
z
i
∑
z
i
b
1
=
k
1
∑
z
i
−
∑
x
i
n
k
2
=
n
∑
y
i
z
i
−
∑
y
i
∑
z
i
n
∑
z
2
−
∑
z
i
∑
z
i
b
2
=
k
2
∑
z
i
−
∑
y
i
n
\left\{ \begin{aligned} &k_1=\frac{n\sum x_iz_i-\sum x_i\sum z_i}{n\sum z^2-\sum z_i\sum z_i}\\\\ &b_1=\frac{k1\sum z_i-\sum x_i}{n}\\\\ &k_2=\frac{n\sum y_iz_i-\sum y_i\sum z_i}{n\sum z^2-\sum z_i\sum z_i}\\\\ &b_2=\frac{k2\sum z_i-\sum y_i}{n} \end{aligned} \right.
⎩
⎨
⎧k1=n∑z2−∑zi∑zin∑xizi−∑xi∑zib1=nk1∑zi−∑xik2=n∑z2−∑zi∑zin∑yizi−∑yi∑zib2=nk2∑zi−∑yi
求解出
k
1
,
k
2
,
b
1
,
b
2
k_1,k_2,b_1,b_2
k1,k2,b1,b2,即得出直线方程
当直线不平行于xoy平面时
(
\big(
(该直线过点(
b
1
b_1
b1,
b
2
b_2
b2, 0),法向量为(
k
1
k_1
k1,
k
2
k_2
k2, 1)
)
\big)
)
代码中的结构体
// (x-x0)/m = (y-y0)/n = (z-z0)/k
struct SpaceLine
{
double x;
double y;
double z;
double m;
double n;
double k;
SpaceLine() = default;
SpaceLine(double x, double y, double z, double m, double n, double k) : x(x), y(y), z(z), m(m), n(n), k(k) { }
};
struct Point3d
{
double x;
double y;
double z;
Point3d() = default;
Point3d(double x, double y, double z) : x(x), y(y), z(z) { }
};
c++代码
using std::vector;
SpaceLine fit3dline(vector<Point3d> const & data)
{
int n = data.size();
double sum_xz = 0, sum_x = 0, sum_z = 0, sum_zz = 0, sum_yz = 0, sum_y = 0;
for (Point3d const& p : data)
{
sum_xz += p.x * p.z;
sum_x += p.x;
sum_z += p.z;
sum_zz += std::pow(p.z, 2);
sum_yz += p.z * p.y;
sum_y += p.y;
}
double k1 = (n * sum_xz - sum_x * sum_z) / (n * sum_zz - pow(sum_z, 2));
double b1 = (sum_x - k1 * sum_z) / n;
double k2 = (n * sum_yz - sum_y * sum_z) / (n * sum_zz - pow(sum_z, 2));
double b2 = (sum_y - k2 * sum_z) / n;
return SpaceLine(b1, b2, 0, k1, k2, 1);
}
方法二
当直线不平行于xoy平面时,令
z
0
z_0
z0 = 0,k =1,简化该方程为:
x
−
x
0
m
=
y
−
y
0
n
=
z
1
\frac {x-x_0}{m}= \frac{y-y_0}{n}= \frac{z}{1}
mx−x0=ny−y0=1z
{
x
=
m
z
+
x
0
y
=
n
z
+
y
0
\left\{ \begin{aligned} x &=mz+x_0 \\ y&=nz+y_0 \end{aligned} \right.
{xy=mz+x0=nz+y0
表现成矩阵形式为:
[
m
x
0
n
y
0
]
[
z
1
]
=
[
x
y
]
\begin{bmatrix} m & x_0 \\ n & y_0\\ \end{bmatrix} \begin{bmatrix} z \\ 1\\ \end{bmatrix}= \begin{bmatrix} x\\y\\ \end{bmatrix}
[mnx0y0][z1]=[xy]
当有n个点时第i个点的方程为:
[
m
x
0
n
y
0
]
[
z
i
1
]
=
[
x
i
y
i
]
\begin{bmatrix} m & x_0 \\ n & y_0\\ \end{bmatrix} \begin{bmatrix} z_i \\ 1\\ \end{bmatrix}= \begin{bmatrix} x_i\\y_i\\ \end{bmatrix}
[mnx0y0][zi1]=[xiyi]
并联n个方程得到:
[
m
x
0
n
y
0
]
[
z
⋯
z
n
1
⋯
1
]
=
[
x
1
⋯
x
n
y
1
⋯
y
n
]
\begin{bmatrix} m & x_0 \\ n & y_0\\ \end{bmatrix} \begin{bmatrix} z & \cdots &z_n\\ 1& \cdots &1\\ \end{bmatrix}= \begin{bmatrix} x_1 & \cdots & x_n\\ y_1 & \cdots & y_n\\ \end{bmatrix}
[mnx0y0][z1⋯⋯zn1]=[x1y1⋯⋯xnyn]
最小二乘拟合:
[
m
x
0
n
y
0
]
[
z
⋯
z
n
1
⋯
1
]
[
z
1
1
⋮
⋮
z
n
1
]
=
[
x
1
⋯
x
n
y
1
⋯
y
n
]
[
z
1
1
⋮
⋮
z
n
1
]
\begin{bmatrix} m & x_0 \\ n & y_0\\ \end{bmatrix} \begin{bmatrix} z & \cdots &z_n\\ 1& \cdots &1\\ \end{bmatrix} \begin{bmatrix} z_1 &1\\ \vdots & \vdots\\ z_n& 1\\ \end{bmatrix}= \begin{bmatrix} x_1 & \cdots & x_n\\ y_1 & \cdots & y_n\\ \end{bmatrix} \begin{bmatrix} z_1 &1\\ \vdots & \vdots\\ z_n& 1\\ \end{bmatrix}
[mnx0y0][z1⋯⋯zn1]
z1⋮zn1⋮1
=[x1y1⋯⋯xnyn]
z1⋮zn1⋮1
化简为:
[
m
x
0
n
y
0
]
=
[
∑
x
i
z
i
∑
x
i
∑
y
i
z
i
∑
y
i
]
[
∑
z
i
2
∑
z
i
∑
z
i
n
]
−
1
\begin{bmatrix} m & x_0 \\ n & y_0\\ \end{bmatrix} = \begin{bmatrix} \sum x_iz_i & \sum x_i\\ \sum y_iz_i & \sum y_i\\ \end{bmatrix} \begin{bmatrix} \sum z_i^2 & \sum z_i\\ \sum z_i & n\\ \end{bmatrix} ^{-1}
[mnx0y0]=[∑xizi∑yizi∑xi∑yi][∑zi2∑zi∑zin]−1
求出
m
,
n
,
x
0
,
y
0
m,n,x_0,y_0
m,n,x0,y0后 ,该直线过(
x
0
x_0
x0,
y
0
y_0
y0, 0),法向量为(m,n,1)
c++代码
using std::vector;
// 还运用了eigen库,进行矩阵的运算
SpaceLine fit3dline(vector<Point3d> const & data)
{
int n = data.size();
double sum_xz = 0, sum_x = 0, sum_yz = 0, sum_y = 0, sum_zz = 0, sum_z = 0;
for (Point3d const & p : data)
{
sum_xz += p.x*p.z;
sum_x += p.x;
sum_yz += p.y*p.z;
sum_y += p.y;
sum_zz += pow(p.z, 2);
sum_z += p.z;
}
Eigen::MatrixXf mat1(2, 2);
mat1 <<
sum_xz, sum_x,
sum_yz, sum_y;
Eigen::MatrixXf mat2(2, 2);
mat2 <<
sum_zz, sum_z,
sum_z, n;
Eigen::MatrixXf mat_result = mat1 * mat2.inverse();
Eigen::MatrixXf &r = mat_result;
return SpaceLine(r(0, 1), r(1, 1), 0, r(0, 0), r(1, 0), 1);
}