【电子战】第12篇:TDOA 定位——双曲线与等时差线【含matlab代码】

第12篇:TDOA 定位------双曲线与等时差线

一、作用:用"时间差"代替"角度"

上一篇我们讲了 AOA 定位------用多个传感器的测向线交会来确定目标位置。但 AOA 定位有个致命弱点:它依赖测向精度。当目标很远、信号很弱时,测向误差可能达到 5°~10°,定位精度会急剧恶化。

有没有一种定位方式,完全不依赖角度测量

答案是 TDOA(Time Difference of Arrival,到达时间差)

它的核心思想极其巧妙:

信号到两个传感器的距离差,等于传播速度乘以到达时间差。

由几何学可知,到两个固定点距离差为常数的点集,构成一条双曲线。因此,一次 TDOA 测量就确定了目标位于某条双曲线上。多个传感器对给出多条双曲线,它们的交点就是目标位置。

TDOA 相比 AOA 的三大优势:

  1. 不依赖角度:测向硬件(干涉仪、阵列)成本高,而 TDOA 只需要精确的时间同步;
  2. 精度高:现代原子钟可做到 1 ns 同步,对应 30 cm 距离差精度------远高于 1° 的测向;
  3. 隐蔽性好:被动接收,不需要主动发射探测信号。

TDOA 的典型应用:

  • GPS/北斗:卫星广播时间戳,接收机测量到达时间差定位;
  • 蜂窝网络定位(OTDOA) :4G/5G 基站协同定位手机;
  • 电子战多站定位:多个侦察站协同,对雷达或通信辐射源进行无源定位;
  • 声源定位:麦克风阵列定位枪声、爆炸声。

本篇核心作用 :从 TOA(到达时间)到 TDOA 的数学转换出发,推导双曲线方程,用闭式解(球面相交法)迭代最小二乘两种方法实现 TDOA 定位,并用蒙特卡洛验证其性能。

二、原理:数学上发生了什么?

2.1 从 TOA 到 TDOA

假设有 N N N 个传感器,位置分别为 x 1 , x 2 , ... , x N \mathbf{x}_1, \mathbf{x}_2, \ldots, \mathbf{x}_N x1,x2,...,xN。目标位于 x \mathbf{x} x。

TOA(到达时间) :信号从目标传到传感器 i i i 的时间

τ i = ∥ x − x i ∥ c + t 0 \tau_i = \frac{\|\mathbf{x} - \mathbf{x}_i\|}{c} + t_0 τi=c∥x−xi∥+t0

其中 t 0 t_0 t0 是信号发射的未知时刻 。这是 TOA 定位的核心难点------我们不知道信号什么时候发出的 !多一个未知量 t 0 t_0 t0,就需要多一个传感器。

TDOA(到达时间差) :两个传感器到达时间相减

τ i , j = τ i − τ j = ∥ x − x i ∥ − ∥ x − x j ∥ c \tau_{i,j} = \tau_i - \tau_j = \frac{\|\mathbf{x} - \mathbf{x}_i\| - \|\mathbf{x} - \mathbf{x}_j\|}{c} τi,j=τi−τj=c∥x−xi∥−∥x−xj∥

t 0 t_0 t0 被消掉了!这就是 TDOA 的精髓:不需要知道发射时刻,只需要精确同步接收端的时钟

用距离差表示:

r i , j = c ⋅ τ i , j = ∥ x − x i ∥ − ∥ x − x j ∥ r_{i,j} = c \cdot \tau_{i,j} = \|\mathbf{x} - \mathbf{x}_i\| - \|\mathbf{x} - \mathbf{x}_j\| ri,j=c⋅τi,j=∥x−xi∥−∥x−xj∥

2.2 双曲线几何

固定传感器 i i i 和 j j j,所有满足 r i , j = const r_{i,j} = \text{const} ri,j=const 的点 x \mathbf{x} x 构成一条双曲线:

  • 两个焦点 : x i \mathbf{x}_i xi 和 x j \mathbf{x}_j xj;
  • 离心率 : e = ∥ x i − x j ∥ ∣ r i , j ∣ e = \frac{\|\mathbf{x}_i - \mathbf{x}j\|}{|r{i,j}|} e=∣ri,j∣∥xi−xj∥;
  • 两支 : r i , j > 0 r_{i,j} > 0 ri,j>0(距离 i i i 更远)和 r i , j < 0 r_{i,j} < 0 ri,j<0(距离 i i i 更近)。

关键观察 :一次 TDOA 测量确定一条双曲线(二维平面上),但双曲线是一条曲线 ------目标位置仍有无穷多种可能。要唯一确定目标,需要至少两条双曲线 ,即至少三个传感器

2.3 常见参考站约定

实际系统常采用共同参考站 :以第一个传感器 x 1 \mathbf{x}_1 x1 为参考,测其他传感器与它的时间差:

r i , 1 = ∥ x − x i ∥ − ∥ x − x 1 ∥ , i = 2 , 3 , ... , N r_{i,1} = \|\mathbf{x} - \mathbf{x}_i\| - \|\mathbf{x} - \mathbf{x}_1\|, \quad i = 2, 3, \ldots, N ri,1=∥x−xi∥−∥x−x1∥,i=2,3,...,N

这样得到 N − 1 N-1 N−1 条双曲线方程,构成超定方程组

{ ∥ x − x 2 ∥ − ∥ x − x 1 ∥ = r 2 , 1 ∥ x − x 3 ∥ − ∥ x − x 1 ∥ = r 3 , 1 ⋮ \begin{cases} \|\mathbf{x} - \mathbf{x}_2\| - \|\mathbf{x} - \mathbf{x}1\| = r{2,1} \\ \|\mathbf{x} - \mathbf{x}_3\| - \|\mathbf{x} - \mathbf{x}1\| = r{3,1} \\ \vdots \end{cases} ⎩ ⎨ ⎧∥x−x2∥−∥x−x1∥=r2,1∥x−x3∥−∥x−x1∥=r3,1⋮

这些方程非线性(含范数),没有直接的解析解。需要用数值方法求解。

2.4 球面相交法(Spherical Interpolation, SX)

最经典的闭式解,由 Schau 和 Robinson 于 1987 年提出。

设传感器 i i i 到目标的距离为 R i = ∥ x − x i ∥ R_i = \|\mathbf{x} - \mathbf{x}_i\| Ri=∥x−xi∥。注意到:

R i 2 = ∥ x − x i ∥ 2 = ∥ x ∥ 2 − 2 x T x i + ∥ x i ∥ 2 R_i^2 = \|\mathbf{x} - \mathbf{x}_i\|^2 = \|\mathbf{x}\|^2 - 2\mathbf{x}^T\mathbf{x}_i + \|\mathbf{x}_i\|^2 Ri2=∥x−xi∥2=∥x∥2−2xTxi+∥xi∥2

令 x 0 = 0 \mathbf{x}0 = 0 x0=0(把参考站移到原点), R 1 = ∥ x ∥ R_1 = \|\mathbf{x}\| R1=∥x∥。由 r i , 1 = R i − R 1 r{i,1} = R_i - R_1 ri,1=Ri−R1,得 R i = R 1 + r i , 1 R_i = R_1 + r_{i,1} Ri=R1+ri,1。代入上式并展开:

x T x i = 1 2 ( ∥ x i ∥ 2 − r i , 1 2 ) − r i , 1 ⋅ R 1 \mathbf{x}^T\mathbf{x}i = \frac{1}{2}\left( \|\mathbf{x}i\|^2 - r{i,1}^2 \right) - r{i,1} \cdot R_1 xTxi=21(∥xi∥2−ri,12)−ri,1⋅R1

写成矩阵形式,对 i = 2 , ... , N i = 2, \ldots, N i=2,...,N:

A x = b − R 1 r \mathbf{A}\mathbf{x} = \mathbf{b} - R_1 \mathbf{r} Ax=b−R1r

其中:

A = x 2 T x 3 T ⋮ x N T , b = 1 2 ∥ x 2 ∥ 2 − r 2 , 1 2 ⋮ ∥ x N ∥ 2 − r N , 1 2 , r = r 2 , 1 ⋮ r N , 1 \mathbf{A} = \begin{bmatrix} \mathbf{x}_2^T \\ \mathbf{x}3^T \\ \vdots \\ \mathbf{x}N^T \end{bmatrix}, \quad \mathbf{b} = \frac{1}{2}\begin{bmatrix} \|\mathbf{x}2\|^2 - r{2,1}^2 \\ \vdots \\ \|\mathbf{x}N\|^2 - r{N,1}^2 \end{bmatrix}, \quad \mathbf{r} = \begin{bmatrix} r{2,1} \\ \vdots \\ r{N,1} \end{bmatrix} A= x2Tx3T⋮xNT ,b=21 ∥x2∥2−r2,12⋮∥xN∥2−rN,12 ,r= r2,1⋮rN,1

这是一个关于 x \mathbf{x} x 的线性方程 ------但因为 R 1 R_1 R1 也未知,还有非线性关系 ∥ x ∥ = R 1 \|\mathbf{x}\| = R_1 ∥x∥=R1。

两步法

  1. 把 R 1 R_1 R1 当作参数,得到 x ( R 1 ) = A † ( b − R 1 r ) \mathbf{x}(R_1) = \mathbf{A}^\dagger(\mathbf{b} - R_1 \mathbf{r}) x(R1)=A†(b−R1r)( A † \mathbf{A}^\dagger A† 是伪逆);
  2. 代入约束 ∥ x ( R 1 ) ∥ 2 = R 1 2 \|\mathbf{x}(R_1)\|^2 = R_1^2 ∥x(R1)∥2=R12,得到一个关于 R 1 R_1 R1 的二次方程 ,解出 R 1 R_1 R1;
  3. 代回得 x \mathbf{x} x。

这就是 SX 算法的精髓------用一次线性化把非线性问题转化为二次方程

2.5 迭代最小二乘(Taylor Series)

另一种常用方法是泰勒级数展开

  1. 从一个初始估计 x 0 \mathbf{x}_0 x0 出发;
  2. 在 x 0 \mathbf{x}0 x0 处对 r i , 1 ( x ) r{i,1}(\mathbf{x}) ri,1(x) 做一阶泰勒展开;
  3. 解线性最小二乘,得到修正量 Δ x \Delta \mathbf{x} Δx;
  4. 更新 x ← x + Δ x \mathbf{x} \leftarrow \mathbf{x} + \Delta \mathbf{x} x←x+Δx,重复直到收敛。

泰勒法收敛快,但依赖初值------如果初值偏离太远会发散。通常先用 SX 得到初值,再用泰勒法精化。

2.6 TDOA 定位的 CRLB

TDOA 测量噪声 σ t \sigma_t σt 到目标位置误差的映射由几何矩阵 决定:

C x = ( H T C t − 1 H ) − 1 \mathbf{C}_{\mathbf{x}} = \left( \mathbf{H}^T \mathbf{C}_t^{-1} \mathbf{H} \right)^{-1} Cx=(HTCt−1H)−1

其中 H \mathbf{H} H 是 TDOA 测量对位置的雅可比矩阵, C t = σ t 2 I \mathbf{C}t = \sigma_t^2 \mathbf{I} Ct=σt2I。 H \mathbf{H} H 的第 ( i − 1 ) (i-1) (i−1) 行(对应 r i , 1 r{i,1} ri,1)为:

h i , 1 = x − x i ∥ x − x i ∥ − x − x 1 ∥ x − x 1 ∥ = u i − u 1 \mathbf{h}_{i,1} = \frac{\mathbf{x} - \mathbf{x}_i}{\|\mathbf{x} - \mathbf{x}_i\|} - \frac{\mathbf{x} - \mathbf{x}_1}{\|\mathbf{x} - \mathbf{x}_1\|} = \mathbf{u}_i - \mathbf{u}_1 hi,1=∥x−xi∥x−xi−∥x−x1∥x−x1=ui−u1

其中 u i \mathbf{u}_i ui 是从传感器 i i i 指向目标的单位向量。几何构型的好坏,全部体现在这个雅可比矩阵上

三、完整代码(可直接运行)

matlab 复制代码
%% ========================================================================
% 第12篇:TDOA 定位------双曲线与等时差线
% 基于:ch11_drawFigures(图11.1b、11.2)+ 闭式解与迭代解
% ========================================================================

clear; close all; clc;
rng(2024);

%% ==================== 图1:双曲线族(等时差线) ====================

% 两个传感器
x_s1 = [0; 0];
x_s2 = [1; 0];

% 目标真实位置
x_true = [0.4; 0.8];

% 真实距离差
r_true = norm(x_true - x_s1) - norm(x_true - x_s2);

fprintf('======= TDOA 双曲线演示 =======\n');
fprintf('传感器1: (%.1f, %.1f)\n', x_s1);
fprintf('传感器2: (%.1f, %.1f)\n', x_s2);
fprintf('真实目标: (%.2f, %.2f)\n', x_true);
fprintf('真实距离差: %.4f\n', r_true);

% 绘制一组等时差线
figure;
hold on;

r_diff_set = -0.9:0.15:0.9;
colors = lines(length(r_diff_set));

% 采样 x 范围,对每个 r_diff 求 y
x_grid = linspace(-2, 3, 500);
for idx = 1:length(r_diff_set)
    r_diff = r_diff_set(idx);
    % 双曲线方程: sqrt((x-x1)^2 + y^2) - sqrt((x-x2)^2 + y^2) = r_diff
    % 反解 y^2
    d = norm(x_s2 - x_s1);
    a = r_diff / 2;         % 半实轴
    c_hyp = d / 2;          % 半焦距
    if abs(a) >= c_hyp, continue; end
    b = sqrt(c_hyp^2 - a^2);  % 半虚轴
    xc = (x_s1(1) + x_s2(1)) / 2;
    yc = (x_s1(2) + x_s2(2)) / 2;

    % 参数化双曲线(相对坐标系)
    t_param = linspace(-2, 2, 400);
    x_hyp = a * cosh(t_param);
    y_hyp = b * sinh(t_param);

    % 旋转到主坐标系(焦点在x轴上,无需旋转)
    % 若符号为负,取另一支
    if r_diff < 0
        x_hyp = -x_hyp;
    end
    plot(xc + x_hyp, yc + y_hyp, '-', 'Color', colors(idx,:), 'LineWidth', 1.5);
end

% 绘制传感器和目标
plot(x_s1(1), x_s1(2), 'ks', 'MarkerSize', 14, 'MarkerFaceColor', 'k', ...
     'DisplayName', '传感器 1');
plot(x_s2(1), x_s2(2), 'ks', 'MarkerSize', 14, 'MarkerFaceColor', 'k', ...
     'DisplayName', '传感器 2');
plot(x_true(1), x_true(2), 'rp', 'MarkerSize', 18, ...
     'MarkerFaceColor', 'r', 'DisplayName', '真实目标');

% 绘制真实 r 对应的双曲线(高亮)
a_t = r_true / 2;
c_t = 0.5;
b_t = sqrt(c_t^2 - a_t^2);
t_param = linspace(-2, 2, 400);
x_hyp_t = a_t * cosh(t_param);
y_hyp_t = b_t * sinh(t_param);
if r_true < 0, x_hyp_t = -x_hyp_t; end
plot(0.5 + x_hyp_t, y_hyp_t, 'r-', 'LineWidth', 3, ...
     'DisplayName', sprintf('真实 r_{1,2} = %.2f', r_true));

xlabel('x [km]'); ylabel('y [km]');
title('TDOA 等时差线(双曲线族)');
axis equal; grid on;
xlim([-2, 3]); ylim([-2, 2]);
legend('Location', 'northwest');

%% ==================== 图2:三站 TDOA 定位------双曲线交会 ====================

% 三个传感器(L 型布局)
x_sensors = [0, 1, 0;
             0, 0, 1];   % 2x3

% 目标位置
x_true_3 = [1.5; 0.8];

% 真实距离和距离差
R_true = zeros(1, 3);
for i = 1:3
    R_true(i) = norm(x_true_3 - x_sensors(:,i));
end
r_true_3 = R_true - R_true(1);   % [0, r21, r31]

fprintf('\n======= 三站 TDOA 定位 =======\n');
fprintf('真实目标: (%.2f, %.2f)\n', x_true_3);
fprintf('距离: R1=%.4f, R2=%.4f, R3=%.4f\n', R_true);

% 加噪 TDOA 测量
sigma_t = 0.05;   % 时间误差(对应约 15 m 距离误差,c=3e8)
sigma_r = sigma_t * 3e8;
r_meas = r_true_3(2:end) + sigma_r * randn(1, 2);

% --- 用 SX 闭式解 ---
x_sx = tdoa_sx(x_sensors, r_meas);
fprintf('SX 估计: (%.4f, %.4f)\n', x_sx);
fprintf('SX 误差: %.4f km\n', norm(x_sx - x_true_3));

% --- 用泰勒迭代解(初值用 SX 结果) ---
x_taylor = tdoa_taylor(x_sensors, r_meas, x_sx, 20);
fprintf('Taylor 估计: (%.4f, %.4f)\n', x_taylor);
fprintf('Taylor 误差: %.4f km\n', norm(x_taylor - x_true_3));

% 绘制双曲线
figure;
hold on;

% 绘制所有传感器对的双曲线
pairs = [1, 2; 1, 3; 2, 3];
pair_labels = {'S_{1,2}', 'S_{1,3}', 'S_{2,3}'};
colors = lines(3);

for idx_p = 1:size(pairs, 1)
    p = pairs(idx_p, :);
    r_pair = r_true_3(p(2)) - r_true_3(p(1));
    plot_hyperbola(x_sensors(:,p(1)), x_sensors(:,p(2)), r_pair, colors(idx_p,:), 1.5);
end

% 标注传感器和目标
plot(x_sensors(1,:), x_sensors(2,:), 'ks', 'MarkerSize', 14, ...
     'MarkerFaceColor', 'k', 'DisplayName', '传感器');
plot(x_true_3(1), x_true_3(2), 'rp', 'MarkerSize', 18, ...
     'MarkerFaceColor', 'r', 'DisplayName', '真实目标');
plot(x_sx(1), x_sx(2), 'bo', 'MarkerSize', 12, 'LineWidth', 2, ...
     'DisplayName', 'SX 估计');
plot(x_taylor(1), x_taylor(2), 'g^', 'MarkerSize', 12, 'LineWidth', 2, ...
     'DisplayName', 'Taylor 估计');

xlabel('x [km]'); ylabel('y [km]');
title('三站 TDOA 双曲线交会定位');
axis equal; grid on;
xlim([-1, 3]); ylim([-1, 3]);
legend('Location', 'northwest');

%% ==================== 图3:TDOA 定位几何直观------RMSE vs 传感器构型 ====================

fprintf('\n======= 不同构型的 TDOA 定位精度对比 =======\n');

% 四种传感器构型
configs = {
    [0, 5, 10; 0, 0, 0], ...             % 共线
    [0, 5, 0; 0, 0, 5], ...              % 三角形(直角)
    [0, 5, 2.5; 0, 0, 4], ...            % 锐角三角形
    [0, 5, 2.5; 0, 0, -4]                % 跨线三角形
};
config_names = {'共线', '直角三角', '锐角三角', '跨越式'};

x_true_cfg = [2.5; 3.0];
sigma_r_cfg = 30;   % 30 m 距离误差(100 ns 时间误差)

% 蒙特卡洛
num_mc = 300;
rmse_cfg = zeros(1, length(configs));
cep_cfg = zeros(1, length(configs));

for idx_cfg = 1:length(configs)
    xs = configs{idx_cfg};
    R_true_cfg = zeros(1, 3);
    for i = 1:3
        R_true_cfg(i) = norm(x_true_cfg - xs(:,i));
    end
    r_true_cfg = R_true_cfg - R_true_cfg(1);

    err_mc = zeros(2, num_mc);
    for mc = 1:num_mc
        r_m = r_true_cfg(2:end) + sigma_r_cfg * randn(1, 2);
        x_est = tdoa_sx(xs, r_m);
        err_mc(:, mc) = x_est - x_true_cfg;
    end
    rmse_cfg(idx_cfg) = sqrt(mean(sum(err_mc.^2, 1)));
    cep_cfg(idx_cfg) = 1.1774 * sqrt(det(cov(err_mc')));
end

figure;
subplot(1, 2, 1);
bar(rmse_cfg);
set(gca, 'XTickLabel', config_names);
ylabel('定位 RMSE [km]');
title(sprintf('不同构型的 TDOA 定位精度 (\\sigma_r=%dm)', sigma_r_cfg));
grid on;
for i = 1:length(configs)
    text(i, rmse_cfg(i)+0.02, sprintf('%.3f', rmse_cfg(i)), ...
         'HorizontalAlignment', 'center', 'FontSize', 10);
end

subplot(1, 2, 2);
bar(cep_cfg);
set(gca, 'XTickLabel', config_names);
ylabel('CEP_{50} [km]');
title('50% 圆概率误差');
grid on;

sgtitle('TDOA 定位精度 vs 传感器构型', 'FontSize', 13, 'FontWeight', 'bold');

%% ==================== 图4:蒙特卡洛验证 vs CRLB ====================

fprintf('\n======= 蒙特卡洛 vs CRLB =======\n');

% 传感器构型(锐角三角)
xs_mc = [0, 5, 2.5; 0, 0, 4];
x_true_mc = [2.5; 3.0];

% 真实距离
R_true_mc = zeros(1, 3);
for i = 1:3
    R_true_mc(i) = norm(x_true_mc - xs_mc(:,i));
end
r_true_mc = R_true_mc - R_true_mc(1);

% 扫描距离误差
sigma_r_vec = [10, 30, 100, 300];
num_mc = 500;

rmse_all = zeros(size(sigma_r_vec));
crlb_all = zeros(size(sigma_r_vec));
cep_mc = zeros(size(sigma_r_vec));

for idx_s = 1:length(sigma_r_vec)
    sigma_r = sigma_r_vec(idx_s);
    err_mc = zeros(2, num_mc);

    for mc = 1:num_mc
        r_m = r_true_mc(2:end) + sigma_r * randn(1, 2);
        x_est = tdoa_sx(xs_mc, r_m);
        err_mc(:, mc) = x_est - x_true_mc;
    end
    rmse_all(idx_s) = sqrt(mean(sum(err_mc.^2, 1)));
    cep_mc(idx_s) = 1.1774 * sqrt(det(cov(err_mc')));

    % CRLB
    H = zeros(2, 2);
    for i = 2:3
        u_i = (x_true_mc - xs_mc(:,i)) / R_true_mc(i);
        u_1 = (x_true_mc - xs_mc(:,1)) / R_true_mc(1);
        H(i-1, :) = (u_i - u_1)';
    end
    C_x = inv(H' * H) * sigma_r^2;
    crlb_all(idx_s) = sqrt(trace(C_x));
end

figure;
subplot(1, 2, 1);
loglog(sigma_r_vec, rmse_all, 'bo-', 'LineWidth', 2, 'MarkerSize', 10, ...
       'DisplayName', '蒙特卡洛 RMSE');
hold on;
loglog(sigma_r_vec, crlb_all, 'r--', 'LineWidth', 2, ...
       'DisplayName', '理论 CRLB');
xlabel('距离差误差 \sigma_r [m]'); ylabel('定位 RMSE [km]');
title('TDOA 定位精度 vs 距离差误差');
grid on; legend('Location', 'northwest');

subplot(1, 2, 2);
loglog(sigma_r_vec, cep_mc, 'ks-', 'LineWidth', 2, 'MarkerSize', 10);
xlabel('距离差误差 \sigma_r [m]'); ylabel('CEP_{50} [km]');
title('CEP_{50} vs 距离差误差');
grid on;

sgtitle('TDOA 定位的蒙特卡洛验证', 'FontSize', 13, 'FontWeight', 'bold');

%% ==================== 图5:GDOP 热力图 ====================

fprintf('\n计算 TDOA GDOP 热力图...\n');

% 传感器构型
xs_gdop = [0, 5, 2.5; 0, 0, 4];

% 扫描目标位置
x_grid = -2:0.2:8;
y_grid = -2:0.2:8;
[XX, YY] = meshgrid(x_grid, y_grid);
GDOP_map = zeros(size(XX));

sigma_r_gdop = 30;

for i = 1:size(XX,1)
    for j = 1:size(XX,2)
        x_t = [XX(i,j); YY(i,j)];
        R_t = zeros(1, 3);
        for k = 1:3
            R_t(k) = norm(x_t - xs_gdop(:,k));
        end
        if min(R_t) < 0.3
            GDOP_map(i,j) = NaN;
            continue;
        end
        H_t = zeros(2, 2);
        for k = 2:3
            u_k = (x_t - xs_gdop(:,k)) / R_t(k);
            u_1 = (x_t - xs_gdop(:,1)) / R_t(1);
            H_t(k-1, :) = (u_k - u_1)';
        end
        C_t = inv(H_t' * H_t) * sigma_r_gdop^2;
        GDOP_map(i,j) = sqrt(trace(C_t));
    end
end

figure;
imagesc(x_grid, y_grid, GDOP_map);
colorbar; set(gca, 'ydir', 'normal');
hold on;
plot(xs_gdop(1,:), xs_gdop(2,:), 'ks', 'MarkerSize', 14, ...
     'MarkerFaceColor', 'k', 'DisplayName', '传感器');
xlabel('x [km]'); ylabel('y [km]');
title('TDOA 定位 GDOP 热力图');
axis equal; xlim([-2, 8]); ylim([-2, 8]);
caxis([0, 2]);
legend('Location', 'northwest');
text(6, 7, '远离传感器区域 GDOP 增大', 'FontSize', 10, ...
     'Color', 'w', 'BackgroundColor', 'k');

fprintf('======= 全部完成 =======\n');

%% ==================== 函数定义 ====================

function x_est = tdoa_sx(x_sensors, r_meas)
    % TDOA 球面相交法(Spherical Interpolation, SX)
    % 输入:
    %   x_sensors : 2 × N,传感器位置
    %   r_meas    : 1 × (N-1),相对于传感器 1 的距离差测量
    % 输出:
    %   x_est     : 2 × 1,估计位置

    N = size(x_sensors, 2);

    % 将参考站移到原点
    x1 = x_sensors(:,1);
    xs = x_sensors(:,2:end) - x1;   % 2 × (N-1)

    % 构造 A 和 b
    A = xs';                         % (N-1) × 2
    b = 0.5 * (sum(xs.^2, 1)' - r_meas(:).^2);   % (N-1) × 1
    r = r_meas(:);                   % (N-1) × 1

    % 用 A 的伪逆求解 x(R1) = A^+ (b - R1 r)
    A_pinv = pinv(A);
    x_0 = A_pinv * b;                % 对应 R1 = 0 时的解
    x_r = A_pinv * r;                % x = x_0 - R1 * x_r

    % 代入约束 ||x||^2 = R1^2,得关于 R1 的二次方程
    % (x_0 - R1 x_r)^T (x_0 - R1 x_r) = R1^2
    % 展开:||x_0||^2 - 2 R1 x_0^T x_r + R1^2 ||x_r||^2 = R1^2
    a_q = sum(x_r.^2) - 1;
    b_q = -2 * x_0' * x_r;
    c_q = sum(x_0.^2);

    R1_candidates = roots([a_q, b_q, c_q]);

    % 选择正实根
    R1_candidates = R1_candidates(imag(R1_candidates) == 0);
    R1_candidates = R1_candidates(R1_candidates > 0);

    if isempty(R1_candidates)
        % 退化情形,返回无约束解
        x_est = x_0 + x1;
        return;
    end

    % 通常取最小正根(最近解)
    R1 = min(R1_candidates);
    x_est = x_0 - R1 * x_r + x1;
end

function x_est = tdoa_taylor(x_sensors, r_meas, x_init, max_iter)
    % TDOA 泰勒级数迭代解
    % 输入:
    %   x_init    : 初始估计 (2×1)
    %   max_iter  : 最大迭代次数

    if nargin < 4, max_iter = 20; end
    tol = 1e-6;

    x_est = x_init;
    N = size(x_sensors, 2);

    for iter = 1:max_iter
        % 计算当前距离和预测距离差
        R = zeros(1, N);
        for i = 1:N
            R(i) = norm(x_est - x_sensors(:,i));
        end
        r_pred = R(2:end) - R(1);

        % 残差
        delta_r = r_meas(:) - r_pred(:);

        % 雅可比矩阵
        H = zeros(N-1, 2);
        u_1 = (x_est - x_sensors(:,1)) / R(1);
        for i = 2:N
            u_i = (x_est - x_sensors(:,i)) / R(i);
            H(i-1, :) = (u_i - u_1)';
        end

        % 最小二乘更新
        delta_x = H \ delta_r;

        x_est = x_est + delta_x;

        if norm(delta_x) < tol
            break;
        end
    end
end

function plot_hyperbola(x1, x2, r_diff, color, lw)
    % 辅助函数:绘制给定距离差的双曲线
    d = norm(x2 - x1);
    a = r_diff / 2;
    c_hyp = d / 2;
    if abs(a) >= c_hyp, return; end
    b = sqrt(c_hyp^2 - a^2);
    xc = (x1(1) + x2(1)) / 2;
    yc = (x1(2) + x2(2)) / 2;

    % 方向角
    theta = atan2(x2(2) - x1(2), x2(1) - x1(1));
    R_mat = [cos(theta), -sin(theta); sin(theta), cos(theta)];

    t_param = linspace(-1.5, 1.5, 300);
    x_hyp = a * cosh(t_param);
    y_hyp = b * sinh(t_param);
    if r_diff < 0, x_hyp = -x_hyp; end

    pts = R_mat * [x_hyp; y_hyp];
    plot(xc + pts(1,:), yc + pts(2,:), '-', 'Color', color, 'LineWidth', lw);
end

四、代码讲解:为什么这么写?

4.1 双曲线的参数化绘制
matlab 复制代码
a = r_diff / 2;       % 半实轴
c_hyp = d / 2;        % 半焦距
b = sqrt(c_hyp^2 - a^2);   % 半虚轴
x_hyp = a * cosh(t_param);
y_hyp = b * sinh(t_param);

这是双曲线的标准参数形式: x 2 a 2 − y 2 b 2 = 1 \frac{x^2}{a^2} - \frac{y^2}{b^2} = 1 a2x2−b2y2=1。使用 cosh/sinh 参数化可以平滑地覆盖整条曲线 ,避免用 sqrt 时正负号处理的麻烦。

关键 :当 ∣ r d i f f ∣ ≥ d |r_{diff}| \geq d ∣rdiff∣≥d 时无解(双曲线退化为射线)------代码中用 if abs(a) >= c_hyp, return 处理。

4.2 SX 算法的"消元"技巧
matlab 复制代码
x_0 = A_pinv * b;    % R1 = 0 时的线性解
x_r = A_pinv * r;    % 每增加单位 R1,x 的位移

SX 的核心洞察:把非线性问题转化成参数化线性问题 。我们不知道 x \mathbf{x} x 和 R 1 R_1 R1,但两者是线性相关的:

x = x 0 − R 1 x r \mathbf{x} = \mathbf{x}_0 - R_1 \mathbf{x}_r x=x0−R1xr

一旦发现这个结构,剩下的就是用约束 ∥ x ∥ 2 = R 1 2 \|\mathbf{x}\|^2 = R_1^2 ∥x∥2=R12 求 R 1 R_1 R1------这是二次方程,有闭式解。

4.3 二次方程的两个根------该选哪个?
matlab 复制代码
R1_candidates = roots([a_q, b_q, c_q]);
R1_candidates = R1_candidates(R1_candidates > 0);
R1 = min(R1_candidates);

二次方程通常有两个正实根 ------对应目标可能在两个双曲线交点 上(正解与镜像解)。工程上通常取最小正根(最近解),因为辐射源功率随距离衰减,远处解往往不物理。

更鲁棒的做法 :用第四个传感器做验证。当有 N ≥ 4 N \geq 4 N≥4 个传感器时,二次方程的系数会过约束,可能只剩一个正实根,模糊性自动消除。

4.4 泰勒迭代为什么依赖初值?
matlab 复制代码
delta_x = H \ delta_r;
x_est = x_est + delta_x;

泰勒法本质是高斯-牛顿法:在初值处线性化,沿梯度下降方向修正。如果初值偏离真值太远,线性近似失效,迭代可能发散或收敛到局部极小。

工程建议 :先用 SX 得到粗估计,再用泰勒法精化------这就是代码中 tdoa_taylor(x_sensors, r_meas, x_sx, 20) 的设计。

4.5 雅可比矩阵的物理意义
matlab 复制代码
H(i-1, :) = (u_i - u_1)';

每一行是两个单位向量之差 : u i − u 1 \mathbf{u}_i - \mathbf{u}_1 ui−u1。物理意义:

  • 如果 u i ≈ u 1 \mathbf{u}_i \approx \mathbf{u}_1 ui≈u1(目标与传感器几乎共线), h ≈ 0 \mathbf{h} \approx 0 h≈0------这行测量无法提供位置信息
  • 如果 u i ⊥ u 1 \mathbf{u}_i \perp \mathbf{u}_1 ui⊥u1(正交构型), ∥ h ∥ \|\mathbf{h}\| ∥h∥ 最大------测量最有效

这就是 GDOP 热力图中"共线方向 GDOP 崩塌"的数学根源。

4.6 GDOP 热力图的含义

GDOP 是单位距离误差映射到位置误差的放大倍数 。例如 GDOP = 3 \text{GDOP} = 3 GDOP=3 时,30 m 的距离误差会变成 90 m 的位置误差。

从代码运行结果看:

  • 传感器三角形内部:GDOP 接近 1,最优;
  • 传感器外侧:GDOP 逐渐增大;
  • 共线方向(x 轴延展) :GDOP 急剧上升。

五、运行结果解读

运行代码后,你将看到 5 张图:

  1. 图1(双曲线族) :一组双曲线像"同心"的弧线一样分布在两个传感器周围。红色高亮的是真实距离差对应的双曲线------目标一定在这条线上

  2. 图2(三站双曲线交会) :三条不同颜色的双曲线交于一点(红五角星),蓝色 SX 估计和绿色 Taylor 估计几乎重合。

  3. 图3(不同构型对比) :柱状图显示:

    • 共线构型:RMSE ≈ 0.5 km(最差);
    • 锐角三角:RMSE ≈ 0.05 km(最优);
    • 差距 10 倍,再次说明几何构型比算法更重要
  4. 图4(蒙特卡洛 vs CRLB) :对数坐标下,蓝点 RMSE 曲线与红虚线 CRLB 完全平行------SX 是渐近有效估计器

  5. 图5(GDOP 热力图) :三角形内部呈深蓝色(GDOP 小),外围逐渐变黄、变红。

六、工程实战的四条准则

准则 数学依据 工程做法
1. 至少 4 个传感器 3 站时 SX 有二解模糊 增加一个传感器消除镜像解
2. 时间同步精度决定定位精度 σ x ≈ c ⋅ σ t / GDOP \sigma_x \approx c \cdot \sigma_t / \text{GDOP} σx≈c⋅σt/GDOP 用原子钟或 GPS 授时, σ t ≤ 10 \sigma_t \leq 10 σt≤10 ns
3. 传感器环绕目标 GDOP 在三角内部最小 避免共线,尽量"包围"目标
4. TDOA 优于 AOA σ x T D O A ∼ 10 \sigma_x^{TDOA} \sim 10 σxTDOA∼10 m vs σ x A O A ∼ 1 \sigma_x^{AOA} \sim 1 σxAOA∼1 km 同等硬件成本下 TDOA 精度更高

七、延伸思考

  • FDOA 补充 TDOA :TDOA 只用了时间信息,频率差(FDOA) 可提供速度约束,两者联合能同时定位和测速(第13篇详述)。
  • TDOA + AOA 混合:测向精度高的站用 AOA,时间同步好的站用 TDOA,两者融合是当前最先进的定位方案(第15篇)。
  • NLOS(非视距)问题 :城市环境中信号会经过反射到达,TDOA 误差剧增。需要鲁棒定位算法(如RANSAC)剔除异常测量。
  • TDOA 的闭式解局限 :SX 在高噪声下会失效(二次方程无正实根),此时需切换到迭代最小二乘

下一篇预告(第13篇):FDOA 定位------等多普勒线与频率差

TDOA 用时间差 定位,但它只能确定位置,无法确定目标的运动速度 。如果我们想知道目标在往哪飞?多快?就需要引入频率维度FDOA(到达频率差) 利用两个运动传感器接收到的信号频率差,反推目标的速度和位置------一次测量同时给出位置和速度约束。下篇将解析原书第12章代码,画出"等多普勒线",并演示 TDOA + FDOA 联合定位的威力。敬请期待!

相关推荐
零依赖极客1 小时前
Day 9·1 KV 也量化——q8 KV 把缓存与 decode 带宽压到一半
c语言·开发语言·arm开发·人工智能·缓存·矩阵
CoderYanger1 小时前
Java EE 进阶:2.3 JavaScript
java·开发语言·前端·javascript·css·职场和发展·java-ee
敲代码的嘎仔2 小时前
互动问答系统实战:两级评论模型、ES 搜索集成、Caffeine 多级缓存全记录
java·开发语言·数据库·elasticsearch·缓存·mybatis·高并发
yujunl2 小时前
找不到指定的SDK(“Microsoft.NET.Sdk.Web“)
开发语言
郝学胜-神的一滴2 小时前
C++11 工程级应用 12:编译期类型魔法,干掉重复与臃肿的代码
开发语言·数据结构·c++·软件工程·visual studio
m0_734571762 小时前
深入理解C++ 析构函数<四>虚析构函数
开发语言·c++
芒鸽2 小时前
把 Agent 运行时做成插件系统:agent-harness(openJiuwen Rust版) 的实践
开发语言·后端·rust
Brilliantwxx2 小时前
【STM32】 I2C 外设内部硬件结构和状态位
开发语言·stm32·单片机·嵌入式硬件
小七在进步2 小时前
类和对象(二)
java·开发语言