blog
工业测量拟合
工业测量中的圆拟合
在工业拟合中,有些设备是平面二次曲线。圆形作为二次曲线的一种这里进行拟合方法阐述。 圆形是工业产品中常见的形状,如管子、圆盘等。一般质量控制工程师会对生产线上的产品是否符合工业标准进行测试。 使用传感器可以记录工业产品圆周上点的坐标,进而利用坐标进行拟合。对于一般测量系统如果是非线性问题,则可以进行线性化处理。在同济大学王解先教授的《工业测量拟合》中对圆进行线性化处理。 因此需要微分改正,并进行迭代处理。
这里对圆拟合方程进行简单变换,可以使得非线性问题化为线性问题处理。因此只需要求解超定线性方程即可,无需迭代即可得到数值解精确结果。下面简述方法。
为拟合一个圆 \[{\left( {x - {c_1}} \right)^2} + {\left( {y - {c_2}} \right)^2} = {r^2}\] 对于n个样本坐标,我们需要确定圆形和半径。改写以上方程得到 \[2x{c_1} + 2y{c_2} + \left( {{r^2} - c_1^2 - c_2^2} \right) = {x^2} + {y^2}\]
令 \[{c_3} = {r^2} - c_1^2 - c_2^2\] 则方程可以写为 \[2x{c_1} + 2y{c_2} + {c_3} = {x^2} + {y^2}\] 将数据代入方程得到超定方程组 \[\left[ {\begin{array}{*{20}{c}} {2{x_1}}&{2{y_1}}&1 \\ {2{x_1}}&{2{y_2}}&1 \\ \vdots & \vdots & \vdots \\ {2{x_n}}&{{2_n}}&1 \end{array}} \right]\left[ \begin{gathered} {c_1} \hfill \\ {c_2} \hfill \\ {c_3} \hfill \\ \end{gathered} \right] = \left[ \begin{gathered} x_1^2 + y_1^2 \hfill \\ x_2^2 + y_2^2 \hfill \\ \vdots \hfill \\ x_n^2 + y_n^2 \hfill \\ \end{gathered} \right]\] 求的最小二乘解后,则最小二乘意义下最优圆心为\[\left[ {{c_1},{c_2}} \right]\],半径为 \[r = \sqrt {{c_3} + c_1^2 + c_2^2} \]
go语言代码为
package main
import (
"bufio"
"fmt"
"io"
"math"
"os"
"strconv"
"strings"
)
func Vec(N int) (V []float64) {
/*------------------------------------------------------
! Author : Song Yezhi
! verison : 2020-5-24 17:50
!
!
! -----------------------------------------------------
! Input Parameters :
!
! Output Parameters :
!
------------------------------------------------------*/
V = make([]float64, N)
for i := 0; i < N; i++ {
V[i] = 0.0
}
return
}
func Mat(M int, N int) (A [][]float64) {
/*------------------------------------------------------
! Author : Song Yezhi
! verison : 2020-5-23 23:43
!
! creat a two dimention slices (matrix)
! -----------------------------------------------------
! Input Parameters :
!
! Output Parameters :
!
------------------------------------------------------*/
A = make([][]float64, M)
for i := 0; i < M; i++ {
A[i] = make([]float64, N)
}
for i := 0; i < M; i++ {
for j := 0; j < N; j++ {
A[i][j] = 0.0
}
}
return
}
func MatDim(A [][]float64) (M int, N int) {
/*------------------------------------------------------
! Author : Song Yezhi
! verison : 2020-5-24 12:45
------------------------------------------------------*/
M = len(A)
N = len(A[M-1])
return
}
func VecOutput(V []float64) {
N := len(V)
for i := 0; i < N; i++ {
fmt.Printf("%18.6f\n", V[i])
}
}
func VecDot(x1 []float64, x2 []float64) (dot float64) {
/*------------------------------------------------------
! Author : Song Yezhi
! verison : 2020-5-19 22:53
!
! dot product
------------------------------------------------------*/
N := len(x1)
var i int
dot = 0.0
for i = 0; i < N; i++ {
dot = dot + x1[i]*x2[i]
}
return
}
func MatVec(A [][]float64, x []float64) (b []float64) {
/*------------------------------------------------------
! Author : Song Yezhi
! verison : 2020-5-19 22:25
! b = Ax
!
! -----------------------------------------------------
! Input Parameters :
! A[M][N] ----MATRIX
! Output Parameters :
! b[M]-----vector
------------------------------------------------------*/
M := len(A)
N := len(A[0])
//the size of mat
//b = make([]float64, M)
b = Vec(M)
var sum float64
for i := 0; i < M; i++ {
sum = 0.0
for j := 0; j < N; j++ {
sum = sum + A[i][j]*x[j]
}
b[i] = sum
}
return
}
func MatMul(A [][]float64, B [][]float64) (C [][]float64) {
/*------------------------------------------------------
! Author : Song Yezhi
! verison : 2020-5-23 23:27
! C = A*B
!
! -----------------------------------------------------
! Input Parameters :
! A[M][N]
! B[N][P]
! Output Parameters :
! C[M][P]
------------------------------------------------------*/
M := len(A)
N := len(A[M-1])
P := len(B[N-1])
var i, j, k int
C = Mat(M, P)
var sum float64
for i = 0; i < M; i++ {
for j = 0; j < P; j++ {
sum = 0.0
for k = 0; k < N; k++ {
sum = sum + A[i][k]*B[k][j]
C[i][j] = sum
}
}
}
return
}
func MatOutput(A [][]float64) {
/*------------------------------------------------------
! Author : Song Yezhi
! verison : 2020-5-24 16:54
!
------------------------------------------------------*/
M, N := MatDim(A)
for i := 0; i < M; i++ {
for j := 0; j < N; j++ {
fmt.Printf("%18.6f ", A[i][j])
}
fmt.Printf("\n")
}
}
func LDL(A [][]float64) (L [][]float64, D []float64) {
/*------------------------------------------------------
! Author : Song Yezhi
! verison : 2020-5-24 17:24
!
! A=LDL'
! -----------------------------------------------------
! Input Parameters :
! A
! Output Parameters :
! L
! D
------------------------------------------------------*/
M, N := MatDim(A)
if M != N {
fmt.Printf("the rows and cols is not equal")
}
G := Mat(N, N)
L = Mat(N, N)
D = Vec(N)
var i, j, k int
D[0] = A[0][0]
var tmp1 float64
for i = 1; i < N; i++ {
for j = 0; j <= i-1; j++ {
tmp1 = 0.0
for k = 0; k <= j-1; k++ {
tmp1 = tmp1 + G[i][k]*L[j][k]
}
G[i][j] = A[i][j] - tmp1
}
for j = 0; j <= i-1; j++ {
L[i][j] = G[i][j] / D[j]
}
//comput D[i]
tmp1 = 0.0
for k = 0; k <= i-1; k++ {
tmp1 = tmp1 + G[i][k]*L[i][k]
}
D[i] = A[i][i] - tmp1
}
//matOutput(L)
//set the diag elements
for i = 0; i < N; i++ {
L[i][i] = 1.0
}
return
}
func AddNeq(ATA [][]float64, ATb []float64, H []float64, oc float64) {
/*------------------------------------------------------
Created : Song Yezhi 2022-5-21 14:15
add a new observation to normal equations
(ATA+ H'H) x = ATb + H'oc
ATA = ATA + H'H
ATb = ATb + H'oc
--------------------------------------------------------
Input Parameters :
ATA
ATb
H
oc
Output Parameters :
ATA --- updated
ATb --- updated
--------------------------------------------------------
Email : song.yz@foxmail.com
Copyrigt (C) : Chinese Academy of Sciences
All rights reserved, 2022
-------------------------------------------------------*/
N := len(ATA)
var i, j int
for i = 0; i < N; i++ {
for j = 0; j < N; j++ {
ATA[i][j] = ATA[i][j] + H[i]*H[j]
}
ATb[i] = ATb[i] + H[i]*oc
}
}
func LeastSquareEquation(ATA [][]float64, ATb []float64) (X []float64) {
/*------------------------------------------------------
Created : Song Yezhi 2022-5-21 20:04
solve Least Square Equation by LDL'
--------------------------------------------------------
Input Parameters :
Output Parameters :
--------------------------------------------------------
Email : song.yz@foxmail.com
Copyrigt (C) : Chinese Academy of Sciences
All rights reserved, 2022
-------------------------------------------------------*/
N := len(ATA)
L := Mat(N, N)
D := Vec(N)
L, D = LDL(ATA)
X = Vec(N)
var tmp1 float64
var i, k int
y := Vec(N)
y[0] = ATb[0]
for i = 1; i < N; i++ {
tmp1 = 0.0
for k = 0; k <= i-1; k++ {
tmp1 = tmp1 + L[i][k]*y[k]
}
y[i] = ATb[i] - tmp1
}
X[N-1] = y[N-1] / D[N-1]
//-------------------------
for i=N-1; i>=0 ;i--{
tmp1 = 0.0
for k = i + 1; k < N; k++ {
tmp1 = tmp1 + L[k][i]*X[k]
}
X[i] = y[i]/D[i] - tmp1
}
return
}
func main() {
fin, _ := os.Open("obs.txt")
defer fin.Close()
ATA := Mat(3, 3)
ATb := Vec(3)
H := Vec(3)
br := bufio.NewReader(fin)
var word []string
var x, y, oc float64
for {
var line string
line, ioerr := br.ReadString('\n')
if ioerr == io.EOF {
break
}
if strings.TrimSpace(line) == "" {
continue
}
if line[0] == '#' {
continue
}
word = strings.Fields(line)
x, _ = strconv.ParseFloat(word[0], 64)
y, _ = strconv.ParseFloat(word[1], 64)
H[0] = 2.0 * x
H[1] = 2.0 * y
H[2] = 1.0
oc = x*x + y*y
AddNeq(ATA, ATb, H, oc)
}
X := LeastSquareEquation(ATA, ATb)
fmt.Printf("the center of circle is %16.6f %16.6f \n", X[0], X[1])
fmt.Printf("the radius of circle is %16.6f \n", math.Sqrt(X[2]+X[0]*X[0]+X[1]*X[1]))
}
测试数据为obs.txt,内容如下
187.6604 29.391 24.9298 57.083 6.8860 47.580 -49.0342 -124.730 120.7845 -213.135 215.0836 -136.364输出结果为
the center of circle is 85.794936 -73.195089 the radius of circle is 144.186642
fortran代码为
subroutine add_neq(ATA,ATb,oc,H,N)
!---------------------------------subroutine comment
! Purpose : 2018-09-05 13:33 (Created)
! add a new observation to normal equations
! (ATA+ H'H) x = ATb + H'oc
! Changes :
! *.
!-----------------------------------------------------
! Input Parameters :
! ATA ---- initial normal matrix
! Atb ---- initial right vector
! oc ----- o-c for liner equation
! H ------ obs vector
! N ------- the dimention of the matrix
! Output Parameters :
! ATA ------ updated normal matrix
! ATb ------- updated right vector
!-----------------------------------------------------
! Author : Song Yezhi
! Copyrigt (C) : Shanghai Astronomical Observatory,CAS
! (All rights reserved, 2018)
!----------------------------------------------------
implicit real*8(a-h,o-z)
real*8:: ATA(N,N),ATb(N) ,H(N)
do i = 1,N
if (H(i)==0d0) cycle
do j =1 ,N
ATA(i,j) = ATA(i,j)+ H(i)*H(j)
end do
ATb(i)= ATb(i)+H(i)*oc
end do
end subroutine add_neq
subroutine chol_eq(A,b,x,P,N)
!---------------------------------subroutine comment
! Version : V1.0
! Coded by : song.yz
! Date : 2019.04.15
!-----------------------------------------------------
! Purpose : 对称正定方程解算(如法方程)
!
! Post Script :
! 1. 修正的不开平方Cholesky分解法
! 2.
! 3.
!-----------------------------------------------------
! Input parameters :
! 1. A(N,N)对称正定系数矩阵
! 2. b(N)右向量
! 3. N--方程维数
! 4.
! Output parameters :
! 1. x-计算结果
! 2. P ----协方差 (由于A已经为对称正定矩阵<法矩阵>,因此P为A的逆)
! 3.
! 4.
!-----------------------------------------------------
! Copyrigt :
! Center for Astro-geodynamics
! Shanghai Astronomical Observatory
! Chinese Academy of Sciences
!----------------------------------------------------
implicit real*8(a-z)
integer::N
real*8::A(N,N),b(n),x(n),P(N,N)
!---------------------------------subroutine variable
real*8::L(N,N),d(n)
integer::i,k
real*8::y(N)
call chol_rf(A,L,d,P,n)
!以上已经得到Cholesky分解
y(1)=b(1)
do i=2,n
tmp1=0d0
do k=1,i-1
tmp1=tmp1+L(i,k)*y(k)
end do
y(i)=b(i)-tmp1
end do
x(n)=y(n)/d(n)
do i=n-1,1,-1
tmp1=0d0
do k=i+1,n
tmp1=tmp1+L(k,i)*x(k)
end do
x(i)=y(i)/d(i)-tmp1
end do
end subroutine chol_eq
subroutine chol_rf(A,L,d,P,N)
!---------------------------------subroutine comment
! Version : V1.0
! Coded by : syz
! Date : 2019.04.15
!-----------------------------------------------------
! Purpose :
!
! Post Script :
! 1. 不用开平方的Cholesky分解
! 2.
! 3.
!-----------------------------------------------------
! Input parameters :
! 1. A(N,N)--输入矩阵
! 2. N----矩阵维书
! 3.
! 4.
! Output parameters :
! 1. L矩阵
! 2. d---对角矩阵(用向量存储)
! 3.
! 4. P ---协方差矩阵(由于A已经为对称正定矩阵<法矩阵>,因此P为A的逆)
!-----------------------------------------------------
! Copyrigt :
! Center for Astro-geodynamics
! Shanghai Astronomical Observatory
! Chinese Academy of Sciences
!----------------------------------------------------
implicit real*8(a-z)
integer::N
real*8::A(n,n),L(n,n),d(n),P(N,N)
!---------------------------------subroutine variable
integer::i,j
real*8:: g(n,n)
!设置初值
L=0d0
d(1)=a(1,1)
do i=2,n
do j=1,i-1
!此层循环算g(i,j)
tmp1=0d0
do k=1,j-1
tmp1=tmp1+g(i,k)*L(j,k)
end do
g(i,j)=a(i,j)-tmp1
end do
do j=1,i-1
!此层循环算L(i,j)
L(i,j)=g(i,j)/d(j)
end do
!以下算d(i)
tmp1=0d0
do k=1,i-1
tmp1=tmp1+g(i,k)*L(i,k)
end do
d(i)=a(i,i)-tmp1
end do
!设置对角线元素
do i=1,N
L(i,i)=1d0
end do
call covariance(L,D,P,N)
end subroutine chol_rf
subroutine covariance(L,D,P,N)
!-----------------------------------------------------
! Purpose : 2019-04-15 13:08 (Created)
! 根据LDL'分解计算协方差矩阵
!
! Changes :
!
!-----------------------------------------------------
! Input Parameters :
! L -----
! D ---- 对角线元素
! N ---- 矩阵维数
! Output Parameters :
!
!
!-----------------------------------------------------
! Author : Song Yezhi
! Copyrigt (C) : Shanghai Astronomical Observatory,CAS
! (All rights reserved, 2019)
!----------------------------------------------------
implicit none
integer :: N
real*8 :: L(N,N),D(N),P(N,N)
!-----------------------------------------------------
integer :: i
real*8 :: invL(N,N),invLT(N,N)
!-----------------------------------------------------
call inv_dtri(L,invL,N)
invLT = transpose(invL)
do i =1,N
invLT(:,i)=invLT(:,i)/D(i)
end do
P = matmul(invLT,invL)
end subroutine covariance
subroutine inv_dtri(L,invL,N)
!-----------------------------------------------------
! Purpose : 2019-04-15 10:45 (Created)
! 计算下三角矩阵的逆矩阵,回带过程,无开平方运算
!
! Changes :
!
!-----------------------------------------------------
! Input Parameters :
! L ----- 下三角矩阵
! N ----- 矩阵维数
!
! Output Parameters :
! invL ------ 下三角的逆矩阵
!
!-----------------------------------------------------
! Author : Song Yezhi
! Copyrigt (C) : Shanghai Astronomical Observatory,CAS
! (All rights reserved, 2019)
!----------------------------------------------------
implicit none
integer :: N
real*8 :: L(N,N),invL(N,N)
!----------------------------------------------------
integer :: i , j ,k
real*8 :: xsum
!-----------------------------------------------------
invL = 0d0
invL(1,1) = 1/L(1,1)
do i = 2, N
invL(i,i)= 1/L(i,i)
do j = 1,i-1
xsum = 0d0
do k = 1,i
xsum = xsum + L(i,k)*invL(k,j)
end do
invL(i,j)= - xsum / L(i,i)
end do
end do
end subroutine inv_dtri
program main
!-----------------------------------------------------
! Created : Song Yezhi 2022-5-21 21:34
! Purpose :
!
! Changes :
!
! Paramters :
!
!-----------------------------------------------------
! Email : song.yz@foxmail.com
! Copyrigt (C) : Chinese Academy of Sciences
! All rights reserved, 2022
!-----------------------------------------------------
implicit none
integer i,j ,ioerr
integer,parameter :: N =3
real*8 :: ATA(N,N) ,ATb(N) ,XX(N) ,H(N) ,oc ,P(N,N)
real*8 :: x,y
open(unit=11,file ='obs.txt')
ATA =0D0
ATb =0d0
do
read(11,*,iostat=ioerr) x,y
if(ioerr/=0) exit
H(1) = 2d0*x
H(2) = 2d0*y
H(3) = 1d0
oc = x*x + y* y
call add_neq(ATA,ATb,oc,H,N)
end do
call chol_eq(ATA,ATb,XX,P,N)
write(*,*) XX
end program main