当前位置: 首页 > news >正文

摄影测量——单像空间后方交会

空间后方交会的求解是一个非线性问题,通常采用最小二乘法进行迭代解算。下面我将详细介绍具体的求解步骤:

1. 基本公式(共线条件方程)

共线条件方程是后方交会的基础:

复制

x - x₀ = -f * [m₁₁(X-Xₛ) + m₁₂(Y-Yₛ) + m₁₃(Z-Zₛ)] / [m₃₁(X-Xₛ) + m₃₂(Y-Yₛ) + m₃₃(Z-Zₛ)]
y - y₀ = -f * [m₂₁(X-Xₛ) + m₂₂(Y-Yₛ) + m₂₃(Z-Zₛ)] / [m₃₁(X-Xₛ) + m₃₂(Y-Yₛ) + m₃₃(Z-Zₛ)]

其中mᵢⱼ是旋转矩阵元素,由三个外方位角元素(φ,ω,κ)决定。

2. 求解步骤

步骤1:数据准备

  • 已知:至少3个地面控制点的地面坐标(X,Y,Z)和对应的像点坐标(x,y)

  • 已知:相机的内方位元素(x₀,y₀,f)

  • 需要:提供外方位元素的初始近似值(Xₛ⁰,Yₛ⁰,Zₛ⁰,φ⁰,ω⁰,κ⁰)

步骤2:线性化处理

将共线方程在近似值处进行泰勒展开,保留一阶项:

复制

vₓ = a₁₁ΔXₛ + a₁₂ΔYₛ + a₁₃ΔZₛ + a₁₄Δφ + a₁₅Δω + a₁₆Δκ - lₓ
vᵧ = a₂₁ΔXₛ + a₂₂ΔYₛ + a₂₃ΔZₛ + a₂₄Δφ + a₂₅Δω + a₂₆Δκ - lᵧ

其中:

  • vₓ,vᵧ为残差

  • aᵢⱼ为偏导数系数

  • lₓ,lᵧ为常数项(观测值与计算值之差)

步骤3:建立误差方程

对于n个控制点,可建立2n个误差方程,矩阵形式为:

复制

V = AΔ - L

其中:

  • V为残差向量

  • A为设计矩阵(偏导数矩阵)

  • Δ为外方位元素改正数向量

  • L为常数项向量

步骤4:组成法方程并求解

法方程为:

复制

AᵀPAΔ = AᵀPL

解为:

Δ = (AᵀPA)⁻¹AᵀPL

其中P为权矩阵,通常初始设为单位矩阵。

步骤5:更新参数

Xₛ¹ = Xₛ⁰ + ΔXₛ
Yₛ¹ = Yₛ⁰ + ΔYₛ
Zₛ¹ = Zₛ⁰ + ΔZₛ
φ¹ = φ⁰ + Δφ
ω¹ = ω⁰ + Δω
κ¹ = κ⁰ + Δκ

步骤6:迭代计算

重复步骤2-5,直到改正数Δ小于设定的阈值(如1e-6)。

3. 偏导数计算

设计矩阵A中的偏导数系数计算如下:

4. 精度评定

单位权中误差:

σ₀ = √(VᵀPV)/(2n-6)

参数协方差矩阵:

Dₓₓ = σ₀²(AᵀPA)⁻¹

5. C#代码

输入文件格式:

using System;
using System.Collections.Generic;
using System.IO;
using MathNet.Numerics.LinearAlgebra;// 定义 Points 结构体
public struct Points
{public double x0;public double y0;public double X;public double Y;public double Z;
}// 定义 OuterElements 结构体
public struct OuterElements
{public double f;public double Xs;public double Ys;public double Zs;public double phi;public double omega;public double kappa;
}// 定义 Accurracy 结构体
public struct Accurracy
{public double sigema;public double[] m;public Accurracy(){sigema = 0;m = new double[6];}
}class Program
{// 从文件中读取数据static int ReadFile(string filePath, out List<Points> p, out OuterElements o){p = new List<Points>();o = new OuterElements();try{string[] lines = File.ReadAllLines(filePath);int linenumber = 0;Points p0 = new Points();foreach (string line in lines){linenumber++;if (linenumber >= 2 && linenumber <= 5){string[] values = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);p0.x0 = double.Parse(values[0]);p0.y0 = double.Parse(values[1]);p0.X = double.Parse(values[2]);p0.Y = double.Parse(values[3]);p0.Z = double.Parse(values[4]);p.Add(p0);}else if (linenumber == 7){string[] values = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);o.f = double.Parse(values[0]);}else if (linenumber == 10){string[] values = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);o.Xs = double.Parse(values[0]);o.Ys = double.Parse(values[1]);o.Zs = double.Parse(values[2]);o.phi = double.Parse(values[3]);o.omega = double.Parse(values[4]);o.kappa = double.Parse(values[5]);}}return 1;}catch (Exception){Console.WriteLine("file open error");return 0;}}// 构建旋转矩阵static Matrix<double> BuildRotationMatrix(OuterElements eo){Matrix<double> R = Matrix<double>.Build.Dense(3, 3);double cos_phi = Math.Cos(eo.phi);double sin_phi = Math.Sin(eo.phi);double cos_omega = Math.Cos(eo.omega);double sin_omega = Math.Sin(eo.omega);double cos_kappa = Math.Cos(eo.kappa);double sin_kappa = Math.Sin(eo.kappa);double a1 = cos_phi * cos_kappa - sin_phi * sin_omega * sin_kappa;double a2 = -cos_phi * sin_kappa - sin_phi * sin_omega * cos_kappa;double a3 = -sin_phi * cos_omega;double b1 = cos_omega * sin_kappa;double b2 = cos_omega * cos_kappa;double b3 = -sin_omega;double c1 = sin_phi * cos_kappa + cos_phi * sin_omega * sin_kappa;double c2 = -sin_phi * sin_kappa + cos_phi * sin_omega * cos_kappa;double c3 = cos_phi * cos_omega;R[0, 0] = a1; R[0, 1] = b1; R[0, 2] = c1;R[1, 0] = a2; R[1, 1] = b2; R[1, 2] = c2;R[2, 0] = a3; R[2, 1] = b3; R[2, 2] = c3;return R;}// 构建系数矩阵static void BuildCoefficientsMatrix(List<Points> p, OuterElements eo, Matrix<double> R, out Matrix<double> A, out Matrix<double> L){int n = p.Count;double f = eo.f;double a1, a2, a3, b1, b2, b3, c1, c2, c3;double a11, a12, a13, a14, a15, a16;double a21, a22, a23, a24, a25, a26;A = Matrix<double>.Build.Dense(2 * n, 6);L = Matrix<double>.Build.Dense(2 * n, 1);for (int i = 0; i < p.Count; i++){Vector<double> V = Vector<double>.Build.Dense(3);V[0] = p[i].X - eo.Xs;V[1] = p[i].Y - eo.Ys;V[2] = p[i].Z - eo.Zs;Vector<double> Xmean = R * V;double x_approx = -f * Xmean[0] / Xmean[2];double y_approx = -f * Xmean[1] / Xmean[2];double obs_x = p[i].x0 / 1000.0;double obs_y = p[i].y0 / 1000.0;L[2 * i, 0] = obs_x - x_approx;L[2 * i + 1, 0] = obs_y - y_approx;a1 = R[0, 0]; b1 = R[0, 1]; c1 = R[0, 2];a2 = R[1, 0]; b2 = R[1, 1]; c2 = R[1, 2];a3 = R[2, 0]; b3 = R[2, 1]; c3 = R[2, 2];double denom = Xmean[2];double x = x_approx;double y = y_approx;a11 = (a1 * f + a3 * x) / denom;a12 = (b1 * f + b3 * x) / denom;a13 = (c1 * f + c3 * x) / denom;a21 = (a2 * f + a3 * y) / denom;a22 = (b2 * f + b3 * y) / denom;a23 = (c2 * f + c3 * y) / denom;double term1_x = y * Math.Sin(eo.omega);double term2_x = (x / f * (x * Math.Cos(eo.kappa) - y * Math.Sin(eo.kappa)) + f * Math.Cos(eo.kappa)) * Math.Cos(eo.omega);a14 = term1_x - term2_x;a15 = -f * Math.Sin(eo.kappa) - (x / f) * (x * Math.Sin(eo.kappa) + y * Math.Cos(eo.kappa));a16 = y;double term1_y = -x * Math.Sin(eo.omega);double term2_y = (y / f * (x * Math.Cos(eo.kappa) - y * Math.Sin(eo.kappa)) - f * Math.Sin(eo.kappa)) * Math.Cos(eo.omega);a24 = term1_y - term2_y;a25 = -f * Math.Cos(eo.kappa) - (y / f) * (x * Math.Sin(eo.kappa) + y * Math.Cos(eo.kappa));a26 = -x;A[2 * i, 0] = a11; A[2 * i, 1] = a12; A[2 * i, 2] = a13;A[2 * i, 3] = a14; A[2 * i, 4] = a15; A[2 * i, 5] = a16;A[2 * i + 1, 0] = a21; A[2 * i + 1, 1] = a22; A[2 * i + 1, 2] = a23;A[2 * i + 1, 3] = a24; A[2 * i + 1, 4] = a25; A[2 * i + 1, 5] = a26;}}// 最小二乘法static int LeastSquares(List<Points> p, ref OuterElements o, ref Accurracy acc, string outFilePath){double threshold = 1e-6;int n = p.Count;int NumberofIteration = 0;Vector<double> x = Vector<double>.Build.Dense(6, 1);Matrix<double> R = Matrix<double>.Build.Dense(3, 3);Matrix<double> A = Matrix<double>.Build.Dense(2 * n, 6);Matrix<double> L = Matrix<double>.Build.Dense(2 * n, 1);Matrix<double> V = Matrix<double>.Build.Dense(2 * n, 1);Matrix<double> Q = Matrix<double>.Build.Dense(6, 6);while (!x.All(d => d < threshold)){R = BuildRotationMatrix(o);BuildCoefficientsMatrix(p, o, R, out A, out L);Q = (A.Transpose() * A).Inverse();x = Q * (A.Transpose() * L);V = A * x - L;o.Xs += x[0];o.Ys += x[1];o.Zs += x[2];o.phi += x[3];o.omega += x[4];o.kappa += x[5];Accuracy_assessment(V, Q, ref acc);NumberofIteration++;if (NumberofIteration > 10){Console.WriteLine("Not converging!");return 0;}Save_Adjustment_report(outFilePath, o, acc, NumberofIteration);}return 1;}// 精度评估static void Accuracy_assessment(Matrix<double> V, Matrix<double> Q, ref Accurracy acc){int n = V.RowCount / 2;double m0 = 0.0;acc.sigema = Math.Sqrt((V.Transpose() * V)[0, 0] / (2 * n - 6));for (int i = 0; i < 6; i++){m0 = acc.sigema * Math.Sqrt(Q[i, i]);acc.m[i] = m0;}}// 初始化外方位元素static void Initialize_Outerelements(List<Points> p, ref OuterElements o){double X_all = 0.0;double Y_all = 0.0;int n = p.Count;for (int i = 0; i < n; i++){X_all += p[i].X;Y_all += p[i].Y;}o.Xs = X_all / n;o.Ys = Y_all / n;o.Zs = 50000 * o.f;o.phi = 0.0;o.omega = 0.0;o.kappa = 0.0;}// 保存调整报告static void Save_Adjustment_report(string outFilePath, OuterElements o, Accurracy acc, int NumberofIteration){using (StreamWriter writer = new StreamWriter(outFilePath, true)){writer.WriteLine($"Iteration: {NumberofIteration}");writer.WriteLine($"Xs: {o.Xs}, Ys: {o.Ys}, Zs: {o.Zs}, phi: {o.phi}, omega: {o.omega}, kappa: {o.kappa}");writer.WriteLine($"sigema: {acc.sigema}");for (int i = 0; i < 6; i++){writer.WriteLine($"m[{i}]: {acc.m[i]}");}}}static void Main(){List<Points> p;OuterElements o = new OuterElements();Accurracy acc = new Accurracy();string filename = "input.txt";string outfilename = "result.txt";if (ReadFile(filename, out p, out o) == 1){o.f = o.f / 1000.0;LeastSquares(p, ref o, ref acc, outfilename);}Console.WriteLine("The calculation is complete!");}
}

相关文章:

  • AI知识补全(十六):A2A - 谷歌开源的agent通信协议是什么?
  • 集成学习介绍
  • 【YOLOv8改进 - 特征融合】EFC: 基于增强层间特征关联的轻量级即插即用融合策略,即插即用适,用于小目标检测
  • AIP-233 批量方法:Create
  • 中和农信的“三农”服务密码:科技+标准化助力乡村振兴
  • React中 点击事件写法 的注意(this、箭头函数)
  • 【React】基本语法
  • 浙江大学DeepSeek系列专题线上公开课第二季第五期即将上线!deepseek音乐创作最强玩法来了!
  • python: audioFlux XXCC 提取梅尔频率倒谱系数 MFCC
  • day30 第八章 贪心算法 part04
  • 阳台光伏 “WiFi” 电表:开启智慧能源新纪元/出海欧标认证!
  • 2025高频面试算法总结篇【动态规划】
  • Spark-SQL核心编程:DataFrame、DataSet与RDD深度解析
  • leetcode:1351. 统计有序矩阵中的负数(python3解法)
  • SQL学习笔记-聚合查询
  • 16:00开始面试,16:08就出来了,问的问题有点变态。。。
  • 大数据学习栈记——MongoDB编程
  • 【Web三十一】K8S的常用命令
  • 设计模式-模板模式
  • Node.js 模块包的管理和使用是
  • 山西省委常委李金科添新职
  • 为证明我爸是我爸,我将奶奶告上法庭
  • 阶跃星辰CEO姜大昕:追求智能上限仍是最重要的事,多模态的“GPT-4时刻”尚未到来
  • 航行警告:渤海海峡黄海北部执行军事任务,禁止驶入
  • 古埃及展进入百天倒计时,闭幕前168小时不闭馆
  • 长三角地区中华老字号品牌景气指数发布,哪些牌子是你熟悉的?