☰
C#实现单像空间后方交会:用照片反推相机位置与姿态
2026/10/14 11:49:46 网站建设 项目流程

简介:面向摄影测量、计算机视觉与遥感测绘学习者的C#单向空间后方交会实现,解决了从单幅影像配合已知地面控制点反算三维空间坐标的问题,涉及摄像机内参外参建模、像素坐标与世界坐标转换、最小二乘误差优化等关键环节。资源包内含31个文件,压缩后仅63KB,以10个.cs源码文件为核心,配合Visual Studio解决方案与项目工程文件、界面与资源配置文件、可直接运行的exe程序,以及用于实验的txt坐标数据文件,便于直接编译运行或二次修改。源码将核心算法、矩阵运算与界面逻辑分模块组织,结构清晰易读,可作为摄影测量课程设计或算法入门的参考实现;通过示例数据能够直观对比输入控制点与计算结果的对应关系,帮助理解空间后方交会的完整流程。目前已有243人学习使用,对该领域初学者和需要快速搭建实验环境的开发者均有实用价值。

1. C#单向空间后方交会:用一张照片反推相机位置与姿态

C#单向空间后方交是这个标题的完整称呼,但它在摄影测量里就是最基础的那道题:手里只有一张影像和少量地面控制点,反算出拍摄瞬间相机在物方坐标系里的位置和姿态,也就是六个外方位元素。做GIS工具链、测绘软件或者近景摄影测量的人迟早要撞上它,因为后续的正射纠正、影像定向全都从这一步开始。适合谁读?如果你正为存量照片找不到定向参数、或者想把单张影像落到某个坐标系里,这篇就是为你写的。用C#实现而不是脚本,多半是要把解算模块塞进Windows桌面工具或.NET数据管线里,下面这套做法不用第三方矩阵库,直接让代码能跑。

2. 共线方程到最小二乘:单向空间后方交会的数学模型

2.1 共线方程与六个外方位元素

空间后方交会要恢复的六个外方位元素分两类:三个线元素Xs、Ys、Zs,描述摄影中心在物方坐标系中的位置;三个角元素φ、ω、κ,描述影像姿态。这里的物方坐标系一般用地方空间直角坐标系,单位是米,Z轴对应高程。角元素的定义在不同教材里有差异,常见的是以Y轴为主轴的φ-ω-κ转角系统,我后续代码也按这个约定写,换坐标系时矩阵元素要跟着变。

物点、像点、摄影中心三点共线,这是共线方程成立的前提。把物方点(X, Y, Z)通过旋转矩阵R转到以摄影中心为原点的像空间辅助坐标系,再除以深度分量投影到像平面,就能得到理论像点坐标:

x - x0 = -f * [a1(X-Xs) + b1(Y-Ys) + c1(Z-Zs)] / [a3(X-Xs) + b3(Y-Ys) + c3(Z-Zs)] y - y0 = -f * [a2(X-Xs) + b2(Y-Ys) + c2(Z-Zs)] / [a3(X-Xs) + b3(Y-Ys) + c3(Z-Zs)]

其中a1、b1、c1等是旋转矩阵R的元素,下标1、2、3对应三行;x0、y0是像主点坐标,f是主距。这个公式看起来长,结构上就是“旋转后的坐标差除以深度,再乘负焦距”。负号来自成像过程里像点与物点在主点两侧的几何关系。实际使用注意单位:物方坐标是米,像方坐标如果是毫米,f也要是毫米;如果是像素,f就得换算成像素,混用会让解算结果直接出错。

把已知控制点的物方坐标代入共线方程,在六个外方位元素未知的情况下,这个方程是非线性的,没法直接解。方向和思路很明确:把它线性化,再用最小二乘迭代逼近。线性化工具是泰勒展开,这也是后方交会程序里最关键的一步。

2.2 误差方程线性化:泰勒展开与偏导数矩阵

对于第i个控制点,量测像点坐标(x_i, y_i)和按近似外方位元素算出的理论像点坐标(x_calc, y_calc)之间存在差异。对外方位元素取一阶泰勒展开,得到误差方程:

v_x = ∂x/∂Xs·ΔXs + ∂x/∂Ys·ΔYs + ∂x/∂Zs·ΔZs + ∂x/∂φ·Δφ + ∂x/∂ω·Δω + ∂x/∂κ·Δκ - l_x v_y = ∂y/∂Xs·ΔXs + ∂y/∂Ys·ΔYs + ∂y/∂Zs·ΔZs + ∂y/∂φ·Δφ + ∂y/∂ω·Δω + ∂y/∂κ·Δκ - l_y

其中l_x = x_meas - x_calc,l_y = y_meas - y_calc,是“实测减计算”。一个控制点贡献两行误差方程,六个未知数,所以至少三个控制点才能满足方程数不少于未知数。所有控制点合起来写成矩阵形式:

V = A·Δ - l

A是设计矩阵,行数等于2n(n为控制点数),列数固定为6;每一行是一个偏导数,描述外方位元素微小变化对像点坐标的影响。

偏导数怎么求,这里有条分岔路。解析法是把共线方程对外方位元素逐项求偏导,写出来很长,还要注意旋转矩阵元素本身又是角元素的函数,链式法则容易漏项。数值法用中心差分,对某个参数加一个小步长求函数值变化量,代码短、不容易错,在控制点几十个、迭代几十次这个规模下性能完全够用。我一般建议教学和验证代码用数值微分,跑通后再决定要不要换成解析式,后者适合最终要大规模批量运行的生产代码。

偏导数的步长有个细节:线元素Xs、Ys、Zs的量纲是米,步长取1e-3量级;角元素量纲是弧度,步长取1e-8。两者量级差很多,如果共用同一个步长,差分结果会被舍入误差吃掉。

2.3 最小二乘平差:法方程与迭代收敛

误差方程建立后,按最小二乘原理,目标是让残差加权平方和最小:

V^T·P·V = min

P是权矩阵。对V = AΔ - l求极值,得到法方程:

(A^T·P·A)·Δ = A^T·P·l

把N = A^T·P·A、g = A^T·P·l代进去,变成N·Δ = g,剩下的就是解一个6阶线性方程组。高斯消元在这个规模下是标准解法,不需要求逆矩阵,因为求逆既慢也不稳。

迭代流程固定:给外方位元素一个初值,用当前值算A和l,解法方程得到改正数Δ,更新X = X + Δ,重复直到收敛。收敛条件一般看改正数的绝对值:线元素小于1e-3米,角元素小于1e-6弧度,同时残差平方和不再明显下降。需要注意角元素更新时,如果改正角大于0.5度,直接加在前值上会引入旋转合成误差,稳妥做法是小角度近似仅在收敛后期成立,初值太差时要先把角度初值磨好,或者改用旋转矩阵增量更新。

3. C#实现:单像空间后方交会的完整代码流程

3.1 数据准备:控制点结构与像点坐标预处理

控制点结构很简单,一个类装物方坐标和像点量测坐标。物方坐标来自实测或者已有成果,像点坐标来自影像上人工刺点或者自动匹配。量测坐标通常从影像上直接点的像素坐标开始,必须先把它归算到以像主点为原点的像平面坐标系,这个步骤漏掉的话,解算出来的主距和姿态全是错的。

// 控制点:物方坐标(米) + 像点量测坐标(像素) public class ControlPoint { public double X { get; set; } // 物方坐标 X,单位米 public double Y { get; set; } // 物方坐标 Y,单位米 public double Z { get; set; } // 物方坐标 Z(高程),单位米 public double x { get; set; } // 像点坐标 x,单位像素 public double y { get; set; } // 像点坐标 y,单位像素 } // 像素坐标转以主点为原点的像平面坐标 // 像素原点在左上角,y轴向下;像平面坐标原点在主点,y轴向上 public static void ToPrincipalPoint( double pixelX, double pixelY, double cx, double cy, out double x, out double y) { x = pixelX - cx; y = cy - pixelY; }

这个转换的坑集中在y方向:影像像素坐标的y是向下增长的,而摄影测量像平面坐标的y向上增长,忘记翻转的话,解出来的κ角符号相反,整个姿态都会偏。实际操作中还要看影像的扫描方向约定,有的系统用了不同的行序约定,转换时先做一步验证,别默认都一致。

控制点的物方坐标和像点坐标准备好后,还要统一单位。最稳妥的做法是物方一律用米,像方先转成毫米(像素尺寸乘以像元大小),这样f就用毫米。也有人习惯全程用像素,那物方坐标也要做相应的缩放,但不推荐,因为单位混进法方程后,N矩阵的条件数会变得很差。

3.2 旋转矩阵与共线方程计算:两个核心方法

旋转矩阵是姿态参数的核心载体。以Y轴为主轴的φ-ω-κ转角系统,构建矩阵的C#代码如下,三维坐标旋转后的行向量按a1、b1、c1到c3的顺序存放:

// 构建旋转矩阵 R,按 φ-ω-κ 转角系统(Y 轴为主轴) static double[,] BuildRotation(double phi, double omega, double kappa) { double sp = Math.Sin(phi), cp = Math.Cos(phi); double so = Math.Sin(omega), co = Math.Cos(omega); double sk = Math.Sin(kappa), ck = Math.Cos(kappa); // R[0,*] 是旋转后 X 方向基向量 // R[1,*] 是旋转后 Y 方向基向量 // R[2,*] 是旋转后 Z 方向基向量 double[,] R = new double[3, 3]; R[0, 0] = cp * ck; R[0, 1] = -cp * sk; R[0, 2] = sp; R[1, 0] = co * sk + sp * so * ck; R[1, 1] = co * ck - sp * so * sk; R[1, 2] = -so * cp; R[2, 0] = so * sk - sp * co * ck; R[2, 1] = so * ck + sp * co * sk; R[2, 2] = cp * co; return R; } // 一个物方点按当前外方位元素投影到像点 static double[] Project( double X, double Y, double Z, double[] eop, double f, double x0, double y0, double[,] R) { double dX = X - eop[0]; // 物方点到摄影中心的向量 double dY = Y - eop[1]; double dZ = Z - eop[2]; double r1 = R[0, 0] * dX + R[0, 1] * dY + R[0, 2] * dZ; double r2 = R[1, 0] * dX + R[1, 1] * dY + R[1, 2] * dZ; double r3 = R[2, 0] * dX + R[2, 1] * dY + R[2, 2] * dZ; double xc = x0 - f * r1 / r3; double yc = y0 - f * r2 / r3; return new double[] { xc, yc }; }

参数说明:eop是长度为6的数组,索引0到2是Xs、Ys、Zs,索引3到5是φ、ω、κ;f、x0、y0是内方位元素,由相机检校得到;R可以在每次迭代开头根据当前角元素重新构建,也可以在迭代中保持不变直到角元素更新。Project方法返回的是“按当前外方位元素计算的理论像点坐标”,它同时用于两个地方:算残差l和数值微分。

这个旋转矩阵的公式必须和教材的转角系统对上。有的教材用X轴为主轴的φ-ω-κ,矩阵元素排列完全不同,直接抄代码而没对上定义,后面怎么迭代都收敛不到正确解。换转角系统时,验证方法很简单:取一组φ、ω、κ,把已知物方点投影到像平面,和已知像点比对,方向对不上就调整旋转矩阵的符号或元素顺序。

3.3 主循环:误差方程、法方程与高斯消元

建立误差方程我用数值微分,对每个外方位参数分别加步长再调Project,得到偏导数。这个做法的好处是代码可读性强,参数增减直接在数组下标上操作,不容易把解析公式写串行。步长分开设置,线元素用1e-3,角元素用1e-8。

// 数值微分:对第 idx 个外方位元素做中心差分 static double Diff( Func<double[], double> func, double[] pars, int idx, double step) { double[] plus = (double[])pars.Clone(); plus[idx] += step; double[] minus = (double[])pars.Clone(); minus[idx] -= step; return (func(plus) - func(minus)) / (2.0 * step); } // 解线性方程组:高斯消元,N 为 n 阶方阵,g 为右端向量 static double[] SolveLinear(double[,] N, double[] g) { int n = g.Length; double[,] aug = new double[n, n + 1]; for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) aug[i, j] = N[i, j]; for (int i = 0; i < n; i++) aug[i, n] = g[i]; // 列主元高斯消去,避免对角线小元素放大误差 for (int col = 0; col < n; col++) { int pivot = col; for (int row = col + 1; row < n; row++) if (Math.Abs(aug[row, col]) > Math.Abs(aug[pivot, col])) pivot = row; for (int j = 0; j <= n; j++) { double tmp = aug[col, j]; aug[col, j] = aug[pivot, j]; aug[pivot, j] = tmp; } for (int row = col + 1; row < n; row++) { double factor = aug[row, col] / aug[col, col]; for (int j = col; j <= n; j++) aug[row, j] -= factor * aug[col, j]; } } double[] x = new double[n]; for (int row = n - 1; row >= 0; row--) { double sum = aug[row, n]; for (int j = row + 1; j < n; j++) sum -= aug[row, j] * x[j]; x[row] = sum / aug[row, row]; } return x; }

主迭代逻辑写在一个循环里,依次做四件事:构建旋转矩阵R、遍历控制点组装A矩阵和l向量、解法方程、更新外方位元素、检查收敛。核心代码片段:

// A 为 2n x 6 矩阵,l 为 2n 向量 double[,] A = new double[2 * n, 6]; double[] l = new double[2 * n]; double[,] R = BuildRotation(eop[3], eop[4], eop[5]); for (int i = 0; i < n; i++) { var pt = points[i]; double[] calc = Project(pt.X, pt.Y, pt.Z, eop, f, x0, y0, R); l[2 * i] = pt.x - calc[0]; // 实测减计算 l[2 * i + 1] = pt.y - calc[1]; // 对 x 方程的六个偏导数 Func<double[], double> fx = p => Project(pt.X, pt.Y, pt.Z, p, f, x0, y0, BuildRotation(p[3], p[4], p[5]))[0]; Func<double[], double> fy = p => Project(pt.X, pt.Y, pt.Z, p, f, x0, y0, BuildRotation(p[3], p[4], p[5]))[1]; A[2 * i, 0] = Diff(fx, eop, 0, 1e-3); // ∂x/∂Xs A[2 * i, 1] = Diff(fx, eop, 1, 1e-3); // ∂x/∂Ys A[2 * i, 2] = Diff(fx, eop, 2, 1e-3); // ∂x/∂Zs A[2 * i, 3] = Diff(fx, eop, 3, 1e-8); // ∂x/∂φ A[2 * i, 4] = Diff(fx, eop, 4, 1e-8); // ∂x/∂ω A[2 * i, 5] = Diff(fx, eop, 5, 1e-8); // ∂x/∂κ // y 方程同理填到 A[2*i+1, 0..5] } double[,] N = new double[6, 6]; double[] g = new double[6]; // N = A^T A, g = A^T l(等权时 P 为单位阵) for (int i = 0; i < A.GetLength(0); i++) { for (int c = 0; c < 6; c++) { g[c] += A[i, c] * l[i]; for (int c2 = 0; c2 < 6; c2++) N[c, c2] += A[i, c] * A[i, c2]; } } double[] delta = SolveLinear(N, g); for (int k = 0; k < 6; k++) eop[k] += delta[k];

这个框架里有两个地方值得多讲。首先是N矩阵是6×6,即使控制点数几百个,最后要解的也只是6阶线性方程组,所以后方交会的计算量不在法方程求解,而在每一轮迭代里组装A矩阵。用数值微分每轮要对每个控制点调用Project至少12次(6个参数×中心差分2次),控制点数多时这个成本要心里有数,生产环境建议换解析偏导。

其次是初值的选取。线元素初值通常取控制点物方坐标的均值,Zs加一个估计航高(比如正射影像飞行高度);三个角元素一般设0,前提是影像近似垂直摄影。大倾角影像直接设0很容易让迭代卡在局部极小,这时候先用线性方法解一个初值再进入最小二乘迭代,是成熟做法。

4. 控制点布设与精度评估:后方交会的可靠性不由点数单一决定

4.1 最少三个控制点,为什么实践中不这么用

解算六个未知数,每个控制点提供两个方程,数学上三个点恰好够。但三个点没有任何多余观测,误差方程的解是唯一的,却无法检核,任何一个点的量测误差都会直接摊进六个外方位元素里。更关键的是三个点的几何结构太脆弱:物方三点共线、像方三点挨得很近、或者三个点高程基本一致,法方程会接近秩亏,迭代时改正数剧烈跳动。

实际项目里我一般至少用五个控制点,常见的是六到八个,分布上让点尽量铺满影像四角和中间区域,同时保证Z方向上有明显的高程差异。控制点在物方不能近似落在同一平面内,像平面内也不能密集堆在一个角落。这背后的原因是旋转角和外方位元素之间的相关性要靠点的空间分布去解耦,点位分布越均匀,N矩阵的条件数越好,解算越稳。

控制点数多余观测数能检验什么风险
30无检核能力粗差直接污染全部参数
42残差能看出个别粗差分布不佳时仍可能病态
66可以留2个检查点无明显风险
1014可以分区域交叉验证点位量测成本高

表中“多余观测数”等于2n减去6。多余观测不是越多越好,因为控制点的获取成本(实测坐标或刺点)很高,而且过多的低精度控制点会把误差平均化,掩盖局部问题。我通常的做法是解算用尽量少的点,把多的点留作检查点,这样能真实评估精度。

4.2 定权策略:什么情况用单位阵,什么情况加权

前方交会解算默认所有控制点像点量测精度一致,P取单位阵,代码里N = A^T A就是这个假设。如果一部分控制点是高精度实测得到的,另一部分是从旧图上抠出来的,精度差距很大,还按等权处理,低精度点会把解算结果拖偏。此时按先验精度定权:

// 第 i 个点的像点量测中误差为 sigma_i(单位像素) // 单位权中误差取 sigma0,比如 0.3 像素 double sigma0 = 0.3; for (int i = 0; i < n; i++) { double w = sigma0 * sigma0 / (sigma_i * sigma_i); // 权矩阵 P 为对角阵,组装到法方程时每行乘以 w }

定权后法方程从N = A^T A变成N = A^T P A,g = A^T P l。这个改动很小,但效果差别很大。需要注意sigma_i是像点坐标的量测中误差,不是物方坐标精度。如果你担心的是物方控制点本身的坐标误差,那就把它投影到像点量测精度等价的数值再定权,别把不同物理量的方差直接混在一起。

权矩阵设定后还要看一个指标:平差的验后单位权中误差。如果这个值和先验sigma0差得远,说明权比设得不合理,或者模型里有系统误差没被吸收,这时候先别急着改权,回去查原始观测数据。

4.3 检查点残差与中误差:判断解算结果能不能用

平差完成后,第一件事不是看外方位元素,而是看残差。单位权中误差按下式计算:

σ0 = sqrt( V^T·P·V / (2n - 6) )

V是每个控制点的残差向量,n是控制点数。σ0的单位和你像点坐标一致,像素或毫米。一般要求σ0接近像点量测精度,比如你刺点时能精确到0.3像素,σ0出来2个像素,说明观测数据里有粗差或者模型有错。

更可靠的验证是检查点法。解算前预留两个及以上控制点不参与平差,解算完成后把这些检查点的物方坐标代入共线方程,得到计算像点,与预留的量测像点坐标比对。以某模拟数据自测,8个解算点、2个检查点,检查点残差的RMS落在x方向约0.3像素、y方向约0.4像素,与刺点精度基本一致,这个结果就能放心往下做正射纠正。

注意检查点法的前提是检查点本身要布在解算点覆盖范围之内,如果检查点落在影像边缘外或远离控制点分布区域,残差会放大,这时不能简单归咎于解算质量,可能是外推区域的点位本身精度差。

5. 单向空间后方交会常见疑难与排查手段

5.1 弧度角度混用:迭代一步直接发散

现象:第一次迭代改正数就大得离谱,线元素改正几百米,角元素改正接近180度,二次迭代直接溢出。

原因:C#里Math.Sin、Math.Cos参数是弧度,控制点计算过程全按弧度走没问题;问题出在初值设定和结果输出。有人习惯把角元素初值写成度数(比如0、0、90),或者把迭代收敛后的结果转成度时搞错单位,导致下一轮迭代把度数当成弧度传进去。

解决:统一在一开始就明确“整个迭代内部全部使用弧度”,初值手工赋值时如果是度数就先乘Math.PI/180;结果输出时才转成度。建议在迭代入口加一个参数范围断言,角元素超过±3.15就抛异常,能第一时间暴露问题。

5.2 旋转矩阵顺序或角元素正负号与教材不一致

现象:迭代能收敛,但残差整体偏大,外方位元素和已知参考值对不上,尤其κ角符号相反。

原因:共线方程里的旋转矩阵R被定义成“物方到像空间辅助坐标系”的旋转,而不同教材的φ-ω-κ转角系统定义不同,矩阵元素排列和正负号随之不同。代码里抄了一个版本的R,却套用了另一个版本的共线方程。

解决:先固定转角系统,再固定一个验证用例。取一个物方点和一个已知外方位元素,手工在纸上算出理论像点坐标,跟Project函数输出比对。这个自检用例通过后再进行平差。我在写代码时会把R矩阵的前三行元素注释到代码里,标明是哪个转角系统,防止回头改代码时忘记。

5.3 像素坐标未翻转y轴:残差表现出系统性对称

现象:迭代正常收敛,但每个控制点的残差出现规律性分布——x残差都朝一个方向,y残差上下分量呈镜像对称;检查点残差远超预期。

原因:像点量测从像素坐标转像平面坐标时,只减了主点没翻转y轴。摄影测量像平面坐标y向上,像素坐标y向下,少了这个翻转,所有点的y方向像点坐标都取反,共线方程把姿态强行扭过来适应数据,导致外方位元素系统性错误。

解决:回到ToPrincipalPoint方法,检查是否有y = cy - pixelY这步。验证方法很直接:把解算出的外方位元素反投影回任意一个控制点,看残差方向。如果所有残差的y分量呈上下镜像对称,基本就是这个原因。把这行改掉,重新平差,残差会立刻降到正常水平。

5.4 控制点共面或共线导致法方程病态

现象:迭代不收敛或者收敛后外方位元素数值异常,比如Xs和Zs大范围浮动,单位权中误差很大。

原因:控制点的物方坐标全部落在同一个平面内(比如都在一个水平面上),或者三个以上的点近似在一条直线上,法方程N矩阵成为病态矩阵,线元素与角元素之间的相关性无法分离。

解决:重新选点,保证控制点高程有明显起伏,并让点位在像幅内分布开。实在受场地限制无法满足时,可以加虚拟观测或者先固定部分外方位元素,把病态的部分约束住。但要注意,加了约束的结果不能当成无条件精度来上报,它只是在数据不足时的一种权宜方案。

5.5 初值太差导致迭代卡在局部极小值

现象:从多个不同初值出发,迭代结果各不相同;残差没有降到预期水平;迭代次数超过10次还在缓慢变化。

原因:共线方程的误差方程是非线性的,泰勒展开只在一阶近似下成立。初值离真值太远时,一次项近似误差大,迭代容易滑向局部极小。角元素初值全设0,对于倾斜摄影影像特别危险。

解决:先用线性化的方法估一个初值,比如把共线方程改写成直接线性变换形式,用至少6个控制点解出11个参数,再从中分解出外方位元素,作为最小二乘迭代的出发点。这个做法在近景摄影测量里很常用,能稳定扛住大倾角情况。迭代过程中加一个判断:如果连续两次迭代残差平方和没有下降,就减少步长或者重新换初值,别硬跑完最大迭代次数。

6. 进阶:带权迭代与鲁棒估计,让解算结果更可信

6.1 残差驱动的选权迭代:用IGG权函数压掉粗差

如果在平差后发现一两个控制点的残差明显大于其余点,最简单的办法不是删点,而是用选权迭代。IGG权函数把残差分成三段:正常段权为1,可疑段权按比例下降,粗差段权直接归零。

// IGG 权函数示例,残差单位为像素,c0 取 1.5,c1 取 2.5 static double IggWeight(double residual, double sigma0, double c0, double c1) { double absV = Math.Abs(residual); if (absV <= c0 * sigma0) return 1.0; if (absV >= c1 * sigma0) return 0.0; double t = absV / sigma0; return (c1 - t) / (c1 - c0) * (c0 / t); }

每轮迭代结束后,用残差更新权矩阵,再重新组法方程解算,循环几次后粗差点的权会自然压到很低,不再干扰解算。

6.2 结果验证:把外方位元素送进正射纠正流程

解算出的外方位元素能不能用,单看残差还不够,最终验证要放到应用场景里。对于影像纠正来说,用解算结果把影像投影到物方平面生成正射影像,再叠加已有矢量或者参考影像检查边缘和道路的错位情况,错位在半米以内说明定向结果可靠。对于立体测图,把左右影像的外方位元素联立起来做前方交会,用已知长度的控制基线检验测距误差,这个误差才是你成果的真正精度。

我现在的习惯是解算完成后,除了打印外方位元素和残差,一定会把检查点残差的RMS和迭代收敛曲线一起输出,保存到文本文件里。这样后续某天成果出问题,能快速定位是平差环节还是后端处理环节。后来因为这个习惯,排查了不少初值设置和坐标转换的返工,省了大把时间。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询