792 lines
49 KiB
Markdown
792 lines
49 KiB
Markdown
# 航迹起始(Track Init)详细逻辑说明
|
||
|
||
> 适用范围:`data_process_class_dll/track_init.cpp` / `track_init.h`(类 `Track_Init`),TWS 通道逻辑法航迹起始。
|
||
> 关联文件:`kalman.cpp`(滤波与统计距离)、`coor_trans.cpp`(坐标转换)、`track_index_mangement.cpp`(航迹号分配)、`data_process.cpp`(调用与参数)。
|
||
> 本文按**当前源码实际行为**梳理,行号均指当前版本文件行号;与 `README.md` 第 8.3 / 9.3 节存在差异之处,见第 12 节。
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [模块定位与调用时机](#1-模块定位与调用时机)
|
||
2. [数据结构与状态容器](#2-数据结构与状态容器)
|
||
3. [处理流程总览](#3-处理流程总览)
|
||
4. [阶段一:输入装填与圈数计数](#4-阶段一输入装填与圈数计数)
|
||
5. [阶段二:点迹与临时航迹关联](#5-阶段二点迹与临时航迹关联)
|
||
6. [阶段三:点迹与航迹头关联](#6-阶段三点迹与航迹头关联)
|
||
7. [阶段四:点迹消费与航迹头生成](#7-阶段四点迹消费与航迹头生成)
|
||
8. [阶段五:临时航迹转可靠航迹(起批)](#8-阶段五临时航迹转可靠航迹起批)
|
||
9. [阶段六:临时航迹消亡](#9-阶段六临时航迹消亡)
|
||
10. [起始条件汇总表](#10-起始条件汇总表)
|
||
11. [关键算法细节](#11-关键算法细节)
|
||
12. [与其他文档/实现的差异](#12-与其他文档实现的差异)
|
||
13. [参数与调参](#13-参数与调参)
|
||
14. [输出字段映射](#14-输出字段映射)
|
||
15. [已知问题与风险点](#15-已知问题与风险点)
|
||
16. [代码索引](#16-代码索引)
|
||
|
||
---
|
||
|
||
## 1. 模块定位与调用时机
|
||
|
||
### 1.1 在整体流程中的位置
|
||
|
||
航迹起始处于 TWS 处理链的**末端**:只有「当圈未被任何可靠航迹关联上」的剩余点迹才会进入起始流程。
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
A["data_preprocess<br/>点迹入 Data_buffer"] --> B["dot_coh(model=2)<br/>两帧滑窗凝聚"]
|
||
B --> C["track_asso(model=1)<br/>IMM + EKF 点航关联<br/>已用点从 point_recv 删除"]
|
||
C --> D["track_die<br/>可靠航迹消亡"]
|
||
D --> E["<b>track_init</b><br/>逻辑法航迹起始"]
|
||
E --> F["Trust_Track_Output<br/>(起批航迹 + 历史点)"]
|
||
```
|
||
|
||
调用点(`data_process.cpp`):
|
||
|
||
```cpp
|
||
// track_process()(data_process.cpp L167 起),仅 model==1(TWS 满一圈)时执行
|
||
track_asso.track_asso_process(&point_recv, &trust_track, ...); // 入口删除 Use_Flag==1 的点
|
||
track_die.track_die_process(&trust_track, ...);
|
||
track_init.track_init_process_logic(&point_recv, &trust_track, &temp_track,
|
||
Trust_Track_Output, Trust_track_num_Output);
|
||
```
|
||
|
||
> 工作参数为**全局对象 `rdp_parameter`**(`struct.h` 中 `extern` 声明、`data_process.cpp` 定义并零初始化),由公开接口 `parameters_reload(配置文件路径)` 从 TOML 配置文件加载;各模块直接以 `rdp_parameter.<field>` 访问,不再作为函数形参逐层传递(参数来源见 README 第 10 节;改造前为宿主下发 `RadarPara` + `parameters.h` 宏,更早则由 `const RDP_parameter&` 形参传入)。
|
||
|
||
### 1.2 调用频率 = 扫描圈
|
||
|
||
`data_preprocess` 判为一圈完成时返回 1(`data_process.cpp`),宿主据此调用 `track_process(model=1)`。判据由配置项 `[radar_params] scan_mode` **运行时**选择(0 相扫 / 1 机扫;替代原编译期 `PHASE_SCANNING`/`MECHANICAL_SCANNING` 分支):**scan_mode=0** 比较前后两帧方位波位号(当前 ≤ 上一帧即满圈,`last_beam_num` 参与判断,清空后/首帧即满圈);**scan_mode=1** 判 `Data_Input[0].Beam_index_aiz == 0`(详见 README 7.2 与 `docs/data_process_flow_report.md` 2.2 节)。
|
||
因此 **`track_init_process_logic()` 每次调用对应 TWS 一圈**,`Temp_track::buff_round`(已缓存圈数)正是以「调用次数」为单位累加的。
|
||
|
||
### 1.3 类接口
|
||
|
||
| 成员 | 可见性 | 作用 |
|
||
|---|---|---|
|
||
| `track_init_process_logic(...)` | public | 起始主流程(唯一入口) |
|
||
| `reset()` | public | 清空内部点迹缓存 + 复位航迹号分配器 |
|
||
| `point_temp_track_asso(...)` | private | 点迹 ↔ ≥2 点临时航迹关联 |
|
||
| `point_track_head_asso(...)` | private | 点迹 ↔ 1 点航迹头关联 |
|
||
| `tmp_track_to_trust_track(...)` | private | 收集 → 去重 → 起批 → 输出 |
|
||
| `tmp_track_die(...)` | private | 临时航迹消亡 |
|
||
| `alpha_cal_track_init(...)` | private | 三点折角(度) |
|
||
| `track_init_prohibit(...)` | private | 起批屏蔽区判断 |
|
||
| `track_index_mangement` | private | 航迹号(1~`track_index_max`,默认 500)分配器 |
|
||
|
||
> `temp_track`(`std::vector<std::vector<Temp_track>>`)**不是** `Track_Init` 的成员,而是 `Data_Process::temp_track`,由调用方传入并跨圈保持;`track_clear_all()` 中清空(`data_process.cpp` L233),`Track_Init::reset()` 只清自己的 `point_process`。
|
||
|
||
---
|
||
|
||
## 2. 数据结构与状态容器
|
||
|
||
### 2.1 Temp_track(临时航迹点,`struct.h`)
|
||
|
||
| 字段 | 含义 | 起始流程中的作用 |
|
||
|---|---|---|
|
||
| `X[4]` | 4 维状态 **[x, vx, y, vy]** | EKF 一步预测的状态;由两点卡尔曼初始化填充 |
|
||
| `P[4][4]` | 状态协方差 | 同上的协方差 |
|
||
| `r / azi / vr / height / SNR / RCS / amplitude` | 该点的极坐标测量 | 初始化、输出、条件判据(`azi` 为**弧度**) |
|
||
| `CPI_time` / `GNSS_time` | CPI 时间 / GNSS 时间(ms) | ΔT 计算(除 1000 转秒) |
|
||
| `d` | 该点关联时的马氏统计距离 | 去重阶段用于「保留更优者」 |
|
||
| `buff_round` | **已缓存圈数** | 生命周期与关联时机控制(见 4.2) |
|
||
| `asso_flag` | 0 未关联 / 1 已关联(待删除)/ 2 去重淘汰 | 临时状态标记 |
|
||
| `pitch_num`、`speed_dim[129]`、`range_dim[8]` | 俯仰波位、十字星数据 | 纯透传 |
|
||
| `temp_track_id` / `point_id` / `circle_id` | 临时航迹 ID / 末关联点迹 ID / 处理圈 ID(c3c7e66 新增) | 数据记录链路标识;起批时透传给 `Trust_Track` |
|
||
| `quality` | **航迹质量(0~1,V1.5.8 新增)** | 每延长一点即重算(见 8.6);起批时取末点值作为初始质量,并参与起批质量门限判断 |
|
||
| `temp_track_section_idx` | 临时航迹区号 | 相关赋值已被注释停用,当前不参与逻辑 |
|
||
|
||
> 字段更名提示(c3c7e66):`snr`→`SNR`、`Amp`→`amplitude`、`T`→`CPI_time`、`Temp_track_section_idx`→`temp_track_section_idx`。阅读早期提交或旧文档时注意对应关系。
|
||
>
|
||
> ⚠ **下标陷阱**:`Temp_track::X` 是 4 维 `[x, vx, y, vy]`(y 在 index 2),而 `Trust_Track::X` 是 6 维 `[x, vx, ax, y, vy, ay]`(y 在 index 3)。两套结构混用下标必错。
|
||
|
||
### 2.2 容器语义
|
||
|
||
| 容器 | 语义 |
|
||
|---|---|
|
||
| `temp_track`(外层 vector) | 「临时航迹表」:外层每一项 = 一条临时航迹,**下标是不稳定的位置号**(擦除后会重排);内层 vector = 该航迹按时间正序的点序列 |
|
||
| `point_process`(`Track_Init` 成员) | 本次调用待处理的点迹工作副本 |
|
||
| `point_recv`(`Data_Process` 成员) | TWS 点迹文件:凝聚输出 → 被 `track_asso` 消费 → 剩余点进入 `track_init` → 被清空 |
|
||
|
||
### 2.3 输入与输出
|
||
|
||
| 参数 | 方向 | 说明 |
|
||
|---|---|---|
|
||
| `point_recv` | 输入/输出 | 输入:当圈未被可靠航迹关联的点迹;**输出:被清空**(`track_init.cpp` L118) |
|
||
| `trust_track` | 输入/输出 | 可靠航迹表;起批成功的临时航迹 `push_back` 到此 |
|
||
| `temp_track` | 输入/输出 | 临时航迹表(跨圈状态) |
|
||
| `Trust_Track_Output[MAX_TRACK_NUM][10]` | 输出 | 与 `track_asso` **共用同一数组与同一计数**;起始流程在 `track_asso` 已写入的行之后追加新航迹的历史点 |
|
||
| `Trust_track_num_Output` | 输出 | 累计行数(track_asso 输出 N 行后,track_init 在其后继续追加) |
|
||
| 全局 `rdp_parameter` | 读取 | 工作参数(TOML 配置加载的全局对象,`struct.h` 声明、`data_process.cpp` 定义);**不再经函数形参传入**,模块直接访问 |
|
||
|
||
> **输出行数核算**:`track_asso` 先为当圈**有更新**的可靠航迹写 N 行(输出循环判 `Track_Update_Flag==1` 并清零,即只含关联到实点或成功外推的航迹);`track_die` 随后删除 D 条(已写出的行与计数不变);起始流程每次起批前判 `trust_track->size() < track_index_max`(配置项,默认 500),故最多再追加 `499 − N + D` 行,**合计最多 499 + D 行**。正常情况下 D 很小,远小于 500;但理论上当 D ≥ 2 且当圈起批数量恰好占满配额(需数百条同时起批)时,行号会超过第一维容量 500,存在越界写入风险(极端场景,见 15.14)。第二维 `[j]`(j < L ≤ 10)始终安全。代价是可靠航迹数达 `track_index_max` 时不再允许新起批。
|
||
|
||
---
|
||
|
||
## 3. 处理流程总览
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
S["track_init_process_logic 入口"] --> A1["① 取出 point_recv → point_process"]
|
||
A1 --> A2["② temp_track 全部点的 buff_round += 1"]
|
||
A2 --> Q{"point_process 空?"}
|
||
Q -- 是 --> DIE["⑥ tmp_track_die"]
|
||
Q -- 否 --> B["③ point_temp_track_asso<br/>点 ×(≥2 点临时航迹)"]
|
||
B --> C["④ point_track_head_asso<br/>点 ×(1 点航迹头)"]
|
||
C --> E["⑤ 删除 Use_Flag==1 的点<br/>剩余点 → 新建航迹头(buff_round=1)"]
|
||
E --> F["⑦ tmp_track_to_trust_track<br/>收集 → 去重 → 起批 → 输出"]
|
||
F --> G["清空 point_process / point_recv"]
|
||
G --> DIE
|
||
DIE --> R["return 0"]
|
||
```
|
||
|
||
要点顺序(不可颠倒):
|
||
|
||
1. **buff_round 先自增,再做关联** —— 这正是「同一圈内不重复关联同一条临时航迹」的实现方式(见 4.2)。
|
||
2. **先关联 ≥2 点临时航迹,再关联 1 点航迹头** —— 前者优先,但两者**不互斥**:同一圈可同时被两边关联(点迹可以既延续长航迹,又延续航迹头)。
|
||
3. **关联结果统一用 `Use_Flag` 标记,最后一次性物理删除** —— 只有 `Use_Flag==0` 的剩余点才会变成新航迹头。
|
||
4. **转可靠航迹在航迹头生成之后** —— 因此当圈新建的航迹头(1 点)不参与当圈起批。
|
||
5. **消亡在最后执行** —— 且即使当圈无剩余点迹(跳过 ③~⑦)也照常执行。
|
||
|
||
---
|
||
|
||
## 4. 阶段一:输入装填与圈数计数
|
||
|
||
### 4.1 装填(`track_init.cpp` L36-38)
|
||
|
||
```cpp
|
||
for (size_t i=0;i<(*point_recv).size();i++)
|
||
point_process.push_back((*point_recv)[i]); // 全量拷贝,不检查 Use_Flag
|
||
```
|
||
|
||
- 上游 `track_asso` 已把关联上的点从 `point_recv` 中删除,所以这里进来的都是「未关联点」;函数内部**不再**用 `Use_Flag` 过滤输入(`Use_Flag` 只用于本次调用内部两阶段关联的标记)。
|
||
- 点迹的 `Range` 已在凝聚阶段做过量程过滤(下限/上限为配置项 `point_cohesion_range_min` / `point_cohesion_range_max`,默认 100 m / 100000 m)。
|
||
|
||
### 4.2 buff_round 自增(L40-47)
|
||
|
||
```cpp
|
||
for (每一条临时航迹 j) (*temp_track)[i][j].buff_round += 1;
|
||
```
|
||
|
||
含义与后果(关键机制):
|
||
|
||
| 动作 | 效果 |
|
||
|---|---|
|
||
| 新建点(航迹头 / 新关联点) | `buff_round = 1` |
|
||
| 每经过一次 `track_init` 调用(一圈) | 该航迹内**所有**点的 `buff_round` +1;关联判据只看**末点** |
|
||
| 关联判据要求 `末点.buff_round >= 2` | 等价于:**上一圈加入的点才能被本圈延续** ⇒ 同一条临时航迹每圈最多吸收一个点(除分裂,见 5.5) |
|
||
| 消亡判据 `末点.buff_round > 2` | 等价于:**连续 2 圈没有新点**就消亡(允许中间空 1 圈,最大关联间隔 2 圈) |
|
||
|
||
> 时间戳只在关联时用 `ΔT = (T_point − T_末点)/1000` 秒表示,**不参与生命周期判断**;生命周期完全由「圈数」驱动。这也意味着临时航迹的存活时间与扫描周期成正比,与航迹的绝对时间无关。
|
||
|
||
**生命周期时间轴(以 scan k 为起点)**
|
||
|
||
```text
|
||
scan k : 新点加入 → 末点 buff_round = 1 (本圈不能再关联)
|
||
scan k+1 : 自增 → 2 → 允许关联;若关联成功 → 新点 1;若失败 → 2 > 2 不成立,存活
|
||
scan k+2 : 自增 → 3 → 仍然允许关联;若成功 → 新点 1;若失败 → 3 > 2 成立,消亡
|
||
```
|
||
|
||
---
|
||
|
||
## 5. 阶段二:点迹与临时航迹关联
|
||
|
||
函数:`point_temp_track_asso()`(L145-304)。双重循环:外层点迹、内层临时航迹,**无一对一竞争约束**。
|
||
|
||
### 5.1 参与资格(L164)
|
||
|
||
```cpp
|
||
if( (*temp_track)[j].size() > 1 && (*temp_track)[j][L-1].buff_round >= 2 )
|
||
```
|
||
|
||
- 只处理 **≥2 点** 的临时航迹(1 点的是航迹头,留给阶段三);
|
||
- 末点必须来自历史圈次。
|
||
|
||
### 5.2 关联判据(L167-221)
|
||
|
||
设点迹量测(`Z[3]`):
|
||
|
||
$$
|
||
Z=\begin{bmatrix} r_p \\ \theta_p \\ v_{r,p}\end{bmatrix},\qquad
|
||
\mathbf{X}_{k-1}=\text{末点状态}[x,v_x,y,v_y],\qquad
|
||
\Delta T=\frac{T_p-T_{\text{末点}}}{1000}\ \text{(s)}
|
||
$$
|
||
|
||
**前置硬门限(L191)**:`|r_p − r_末点| >= 5` —— 距离差小于 5 m 的点直接不参与(连 d 都不算)。
|
||
|
||
**统计距离(L194-195)**:调用 `kalman::d_cal_track_init_EKF(Z, X, P, ΔT, prt, freq_ind)` 得到 3 维量测的马氏距离平方 `d`(详见 11.2)。
|
||
|
||
**折角(L199-205)**:取末点前一点 → 末点 → 新点,计算折角 `α`(度,详见 11.3)。
|
||
|
||
**三条件同时成立(L221)**:
|
||
|
||
$$
|
||
\boxed{\;d^2 \le \text{TRACK\_START\_THRESHOLD}^2=64
|
||
\quad\wedge\quad
|
||
\alpha < \text{ALPHA\_START}=60^\circ
|
||
\quad\wedge\quad
|
||
v_{r,p}\cdot v_{r,\text{末点}} > 0\;}
|
||
$$
|
||
|
||
> ⚠ 由于 `d_cal_track_init_EKF` 返回的 `d` **本身就是马氏距离平方**,而判据又写作 `d*d <= 64`(即 `d <= 8`),实际生效的门限是「马氏距离平方 ≤ 8」。3 自由度卡方分布 95% 分位为 7.815,因此该写法恰好给出约 95.4% 的置信门限(详见 11.2 与 15.6)。
|
||
|
||
### 5.3 记录关联(L223-231)
|
||
|
||
命中后向 `asso_info` 记录 `(point_idx=i+1, track_idx=j+1, d)`,同时:
|
||
|
||
- `末点.asso_flag = 1` → 标记**原航迹**待删除;
|
||
- `point_process[i].Use_Flag = 1` → 标记该点已被消费。
|
||
|
||
索引在整个扫描循环中保持稳定(循环期间不修改两个容器),因此后面的 1-based 索引回查是安全的。
|
||
|
||
### 5.4 生成新临时航迹(L237-281)
|
||
|
||
对每一条关联记录:**复制原航迹的全部 L 个点**(并把各点 `asso_flag` 清 0)、再**追加关联上的新点**,作为一条**新的临时航迹** push 到 `temp_track` 末尾。
|
||
|
||
新点字段:极坐标测量、幅度、SNR、RCS、时间、俯仰波位、十字星数据全套透传;`asso_flag=0`、`buff_round=1`、`d = asso_info[i].d`;
|
||
状态与协方差由 **两点卡尔曼初始化** 得到(L268-279):
|
||
|
||
```cpp
|
||
Z0 = (末点.r·cos(末点.azi), 末点.r·sin(末点.azi)); // 原航迹末点
|
||
Z1 = (点.Range·cos(点.Azimuth), 点.Range·sin(点.Azimuth));
|
||
T = (点.CPI_Time − 末点.T)/1000.0;
|
||
Kalman.kalman_filter_init_2dots(Z0, Z1, T, X, P); // 4 维 CV 初始化
|
||
```
|
||
|
||
### 5.5 航迹分裂(重要特性)
|
||
|
||
关联循环**不做一对一分配**:
|
||
|
||
- 一个点若满足多条临时航迹的判据,会为**每一条**各自复制出一条新航迹 —— 一条母航迹可以派生多条子航迹;
|
||
- 一条临时航迹若被多个点同时命中,也会**各自复制**成多条子航迹(每个子航迹只带一个点);
|
||
- 因此同一圈内可能产生大量同源临时航迹,靠第 8.3 节的**去重**收敛。
|
||
|
||
### 5.6 删除被关联的原航迹(L283-303)
|
||
|
||
遍历 `temp_track`,凡 `size()>=2` 且 **末点 `asso_flag==1`** 者整体 `erase`(每次擦除后 `Iter = begin()` 重扫,O(n²) 但结果正确;1 点航迹不在此处理)。新生成的子航迹末点 `asso_flag==0`,不会被误删。
|
||
|
||
---
|
||
|
||
## 6. 阶段三:点迹与航迹头关联
|
||
|
||
函数:`point_track_head_asso()`(L306-445)。这是**两点起始**:航迹头 + 1 个点 → 2 点临时航迹。
|
||
|
||
### 6.1 参与资格(L325)
|
||
|
||
```cpp
|
||
if((*temp_track)[j].size()==1 && (*temp_track)[j][0].buff_round >= 2)
|
||
```
|
||
|
||
只处理 **1 点**且该点来自历史圈次的航迹头。
|
||
|
||
### 6.2 关联判据(L328-372)
|
||
|
||
点迹与航迹头都转直角坐标后取**平面距离**:
|
||
|
||
$$
|
||
x_p = r_p\cos\theta_p,\quad y_p=r_p\sin\theta_p;\qquad
|
||
x_h=\text{头.X}[0],\quad y_h=\text{头.X}[2]
|
||
$$
|
||
|
||
$$
|
||
dis=\sqrt{(x_p-x_h)^2+(y_p-y_h)^2},\qquad \Delta T=\frac{T_p-T_h}{1000}\ \text{(s)}
|
||
$$
|
||
|
||
**当前生效的判据(L368-372)**:
|
||
|
||
$$
|
||
\boxed{\;
|
||
dis \le V_{max}\Delta T
|
||
\;\wedge\;
|
||
dis \ge V_{min}\Delta T
|
||
\;\wedge\;
|
||
v_{r,p}\cdot v_{r,h} > 0
|
||
\;\wedge\;
|
||
dis \ge |v_{r,p}|\,\Delta T
|
||
\;\wedge\;
|
||
dis \ge |v_{r,h}|\,\Delta T\;}
|
||
$$
|
||
|
||
各条件的物理含义:
|
||
|
||
| 条件 | 含义 |
|
||
|---|---|
|
||
| `dis ≤ V_max·ΔT` | 平均速度不超过最大速度(`V_max = rdp_parameter.track_init_velocity_max`,配置项) |
|
||
| `dis ≥ V_min·ΔT` | 平均速度不低于最小速度(滤除静止/慢速点) |
|
||
| `v_r,p · v_r,h > 0` | 两点多普勒径向速度**同号**(同接近/同远离) |
|
||
| `dis ≥ abs(v_r,p)·ΔT` | 位置变化不小于**点迹自身多普勒**在这段时间内的径向位移(多普勒-几何一致性) |
|
||
| `dis ≥ abs(v_r,h)·ΔT` | 同上,用航迹头的多普勒约束 |
|
||
|
||
**近/远程分支(L348-356)当前是死分支**:`work_mode==0` 与 `else` 都取 `vmax = rdp_parameter.track_init_velocity_max`(注释"近程模式最大速度减小一点"未生效;原按 `V_MAX/5` 收紧的写法仅保留在未启用的 `track_init_direct_tracking.cpp` 注释里)。
|
||
|
||
**ΔT 语义**:判据隐含要求 `ΔT > 0`(若 `ΔT < 0`,则 `dis ≤ 负数` 必假)。`ΔT == 0` 时仅在 `dis == 0`(同时间同位置的点)才可能成立,属退化情形。
|
||
|
||
**与文档差异**:源码中被注释停用的旧判据按距离分段收紧门限(`r<1000` 时半速、中远程 `r<2000` 时 1/2~1/3 速),见第 12 节。
|
||
|
||
### 6.3 命中后的处理(L374-427)
|
||
|
||
- 记录 `(point_idx, track_idx)`,标记 `头.asso_flag=1`、`point_process[i].Use_Flag=1`;
|
||
- 逐条生成 2 点临时航迹:**第 1 点 = 原航迹头**(`asso_flag` 清 0),**第 2 点 = 新点**(全套测量透传,`buff_round=1`,`asso_flag=0`,**`d` 未赋值,保持 0**);
|
||
- 同样调用 `kalman_filter_init_2dots` 得到 4 维状态/协方差(`Z0` = 头点、`Z1` = 新点,`T = (点.CPI_Time − 头.T)/1000`)。
|
||
|
||
### 6.4 删除被关联的航迹头(L429-443)
|
||
|
||
凡 `[0].asso_flag==1` 的 1 点航迹头整条 `erase`(同样每次重扫)。
|
||
注意:本阶段遍历的是**全部** `temp_track`,其中由阶段二新建的 ≥2 点航迹因 `size()!=1` 不会被误处理。
|
||
|
||
---
|
||
|
||
## 7. 阶段四:点迹消费与航迹头生成
|
||
|
||
### 7.1 删除已消费点迹(L57-70)
|
||
|
||
```cpp
|
||
if((*Iter).Use_Flag==1) { point_process.erase(Iter); Iter = point_process.begin(); }
|
||
```
|
||
|
||
物理删除所有被阶段二或阶段三消费的点(两阶段共用同一 `Use_Flag`)。
|
||
|
||
> 结果:一个点迹**要么**参与延续已有临时航迹/航迹头(可同时延续两者),**要么**成为新航迹头,二者互斥。这保证不会「用同一个点既延续又起新头」。
|
||
|
||
### 7.2 剩余点回写(L72-75,等效空操作)
|
||
|
||
```cpp
|
||
std::vector<PointRecv>().swap((*point_recv));
|
||
for (...) (*point_recv).push_back(point_process[i]); // 回写未被使用的点
|
||
```
|
||
|
||
紧接着 L117-118 又把 `point_process` 与 `point_recv` 双双清空,因此这段回写**不产生任何对外效果**(`point_recv` 最终必然为空),属历史残留代码。
|
||
|
||
### 7.3 剩余点转航迹头(L77-114)
|
||
|
||
对每个剩余点构造 1 点 `Temp_track`:
|
||
|
||
| 字段 | 取值 |
|
||
|---|---|
|
||
| `r / azi / height / amplitude / vr / SNR / RCS` | 点迹对应量(`azi` 为弧度) |
|
||
| `pitch_num / speed_dim / range_dim` | 透传 |
|
||
| `X[0] = Range·cos(Azimuth)`、`X[1]=0`、`X[2] = Range·sin(Azimuth)`、`X[3]=0` | 位置赋值,速度置 0(**注意 4 维布局**) |
|
||
| `CPI_time`、`GNSS_time` | 点迹时间 |
|
||
| `temp_track_id = temp_track_id++`、`point_id`、`circle_id` | 取自该点迹(记录链路标识,c3c7e66 新增) |
|
||
| `asso_flag = 0`、`buff_round = 1` | 生命周期起点 |
|
||
| `d` | 未赋值 → 0 |
|
||
|
||
---
|
||
|
||
## 8. 阶段五:临时航迹转可靠航迹(起批)
|
||
|
||
函数:`tmp_track_to_trust_track()`(L458-671),四步。
|
||
|
||
### 8.1 步骤 1:收集候选(L467-499)
|
||
|
||
```cpp
|
||
int L = (*Iter).size();
|
||
double range = (*Iter)[L-1].r;
|
||
double azi = (*Iter)[L-1].azi/PI*180; // 转度,供屏蔽区比较
|
||
double quality = (*Iter)[L-1].quality; // V1.5.8:末点航迹质量
|
||
|
||
if( L <= 10 && L >= rdp_parameter.track_init_point_num_min
|
||
&& track_init_prohibit(range, azi) == 0
|
||
&& quality >= 按长度取的质量门限 ) // V1.5.8 新增
|
||
{ 复制到 Track_to_start; 从 temp_track 中 erase; }
|
||
else
|
||
Iter++;
|
||
```
|
||
|
||
**入选条件**:
|
||
|
||
1. **长度**:`track_init_point_num_min ≤ L ≤ 10`(配置项 `[track_init] track_init_point_num_min`,由 TOML 配置文件提供,加载器校验范围 **3~9**;改造前由宿主经 `track_process_parameters_initial` 传入);
|
||
2. **不在起批屏蔽区**内(用**末点**的距离/方位判断,见 8.5);
|
||
3. **航迹质量达标(V1.5.8,d9a7bd0)**:按长度取门限 `track_init_quality_L3~L7`(配置项)——L=3 →0.7、L=4 →0.6、L=5 →0.5、L=6 →0.4、L≥7 →0.3;比较对象是**末点** `quality`,不达标则本圈不起批、留在 `temp_track` 继续积累。
|
||
|
||
命中即从 `temp_track` 摘出(先复制后删除,`Track_to_start` 成为候选表)。
|
||
|
||
> 结果:**一旦长度达到阈值、不落在屏蔽区且末点质量达标,当圈即起批**(没有"连续 M 圈满足才起批"的确认期)。若因屏蔽区被拦住,航迹会继续在 `temp_track` 中增长,直到离开屏蔽区(此时 `L > track_init_point_num_min`,见 8.3 的副作用)或达到 `L=10` 被判消亡;若因质量门限被拦住,则继续积累直到质量回升或临时航迹消亡。
|
||
|
||
### 8.2 步骤 2:同源去重(L504-550)
|
||
|
||
```cpp
|
||
int L = rdp_parameter.track_init_point_num_min; // 注意:此 L 是"起批点数",不是航迹长度
|
||
for (i: 0 .. size-2) for (j: i+1 .. size-1) {
|
||
if (两者都未被标记 asso_flag==2) {
|
||
if (某条长度 < 3) continue; // 防御性判断(实际不会触发)
|
||
if (前三点中存在一点 "T 与 r 完全相同") { // 硬编码比较点 0/1/2
|
||
if (Track_to_start[i][L-1].d < Track_to_start[j][L-1].d) j 标记 asso_flag=2;
|
||
else i 标记 asso_flag=2;
|
||
}
|
||
}
|
||
}
|
||
// 然后统一删除 [0].asso_flag==2 的航迹
|
||
```
|
||
|
||
- **同源判定**:两条候选航迹的前三个点中,只要有一个点的 `T` 与 `r` 完全相等(同一点迹被复制而来),就视为同源重复;
|
||
- **择优准则**:比较 `[L-1].d`(第 `track_init_point_num_min` 个点的关联统计距离),**保留 d 更小者**;
|
||
- **标记位置**:只写 `[0].asso_flag = 2`,最后统一擦除;
|
||
- 去重仅看**前三点**,且无论航迹实际多长都用固定下标 0/1/2。
|
||
|
||
### 8.3 步骤 3:起批与构造可靠航迹(L552-667)
|
||
|
||
```cpp
|
||
if (Track_to_start[i].size() < 3) continue; // 防御
|
||
if (trust_track->size() < rdp_parameter.track_index_max) { // 默认 500 条上限
|
||
int index = track_index_mangement.track_ind_get(trust_track);
|
||
if (index > 0) { ... }
|
||
}
|
||
```
|
||
|
||
**三点卡尔曼初始化**(L563-571,注意使用的是 `L-3, L-2, L-1`,即**最后三个点**):
|
||
|
||
```cpp
|
||
Z0 = 极坐标→直角(点[L-3]); Z1 = (点[L-2]); Z2 = (点[L-1]);
|
||
T1 = (点[L-2].T − 点[L-3].T)/1000.0; T2 = (点[L-1].T − 点[L-2].T)/1000.0;
|
||
Kalman.kalman_filter_init_3dots(Z0, Z1, Z2, T1, T2, X, P); // 6 维 [x,vx,ax,y,vy,ay]
|
||
```
|
||
|
||
> ⚠ **不一致点**:初始化用「最后三点」,而输出历史点(8.4)只写「前 `L` 个点」。当航迹长度 = `track_init_point_num_min` 时两者一致;当因屏蔽区滞留使长度 > 阈值时,输出的历史点里**不包含**用于初始化的那三个点。
|
||
|
||
**Trust_Track 字段构造(L573-608)**:
|
||
|
||
| 字段 | 赋值 |
|
||
|---|---|
|
||
| `X[6] / P[6][6]` | 三点初始化结果 |
|
||
| `X1/X2/X3 = X`、`P1/P2/P3 = P` | 三个 IMM 子模型同状态、同协方差初始化 |
|
||
| `u[0..2] = 0.3333` | 模型概率均分 |
|
||
| `Track_Index` | `track_ind_get()` 分配的航迹号 |
|
||
| `T_track` / `GNSS_time` / `Height` | 取**末点**的对应值(`CPI_time` 字段) |
|
||
| `Amplitude / snr_point / RCS / pitch_num / speed_dim / range_dim` | 末点透传 |
|
||
| `track_id / point_id / circle_id` | 取末点的 `temp_track_id / point_id / circle_id`(记录链路标识,b227ae1 新增) |
|
||
| `point_flag = 1` | 实点 |
|
||
| `Track_Mode = 0` | TWS |
|
||
| `Target_Type = 0` | 未确认目标(`UNCONF_TARGET` 宏已删除,直接写 0) |
|
||
| `Extrapolate_round = 0` | 外推计数清零 |
|
||
| `approach_flag = -1` | 接近标志复位 |
|
||
| `manual_delete_flag / manual_tracking_flag / associate_point_number / point_type / primary_target_flag = 0` | 各类标志复位(`Trust_Track trust_track_tmp = {}` 零初始化,故新航迹的重点目标标记为 0) |
|
||
|
||
> L606-608 有一处**死代码**:`coor_trans::cart2polar(X[0], X[3], &r, &azi)`(把 `X[3]=vy` 当 y 用),计算结果 `r/azi` 从未被使用。
|
||
|
||
**航迹号分配**(`track_index_mangement.cpp`):空表返回 1;否则从 `lastest_index+1` 向上找第一个未被占用的号(含 `track_index_max`,默认 500),找不到再从 1 回绕;全满返回 0(**不起批**,该候选被静默丢弃)。
|
||
|
||
### 8.4 输出更新航迹(L612-664)
|
||
|
||
- `*Trust_track_num_Output += 1`,在 `track_asso` 已写的行之后追加一行;
|
||
- 对 `j = 0 .. L-1`(`L = rdp_parameter.track_init_point_num_min`)写入该航迹的**历史点**:`Track_Index`、`Point_Sum = L`,以及每个点的距离/方位/俯仰、速度、幅度/SNR/RCS、俯仰波位、十字星数据、航向角、时间、直角坐标、原始点信息等(完整映射见第 14 节);
|
||
- 之后该航迹的后续更新由 `track_asso`(每次 `Point_Sum=1`)负责。
|
||
|
||
### 8.5 起批屏蔽区(L699-712)
|
||
|
||
```cpp
|
||
for (i = 0; i < rdp_parameter.track_init_prohibite_area_num; i++)
|
||
if (r < rdp_parameter.track_init_prohibite_area_range_max[i] && r > rdp_parameter.track_init_prohibite_area_range_min[i] &&
|
||
azi < rdp_parameter.track_init_prohibite_area_azimuth_max[i] && azi > rdp_parameter.track_init_prohibite_area_azimuth_min[i])
|
||
return 1; // 位于屏蔽区,禁止起批
|
||
return 0;
|
||
```
|
||
|
||
- 判据是**距离-方位矩形区域**(`azi` 已转成度),最多 30 个(`RDP_parameter` 定长数组长度;配置入口由加载器校验 `*_num ∈ [0,30]` 且 4 个数组长度等于 `*_num`,非法配置在 `parameters_reload` 阶段即失败);
|
||
- 采用**严格不等号**:恰好落在边界上的点不算屏蔽;
|
||
- 判断用**末点**极坐标,不使用高度。
|
||
|
||
### 8.6 临时航迹质量评估与起批门限(V1.5.8,1ae0cf2 / d9a7bd0)
|
||
|
||
在阶段二(点-临时航迹关联)每成功延长一个点后,`point_temp_track_asso()` 立即重算该临时航迹末点的质量:
|
||
|
||
```cpp
|
||
L = temp_track_ref.size();
|
||
d_tmp = exp(-temp_track_ref[L-1].d); // 末点统计距离项(d 已是马氏距离平方)
|
||
// L == 3:取三个点 SNR 的平均;L > 3:只取末点 SNR
|
||
snr_tmp = 1 / (1 + exp(-(SNR - 16) / 1.2)); // 以 16 dB 为界的 Sigmoid
|
||
// 权重为配置项 track_quality_d_weight(0.5) / track_quality_snr_weight(0.3)
|
||
quality_tmp = (w_d * d_tmp + w_snr * snr_tmp) / (w_d + w_snr); // 只用距离 + SNR 两项(不含长度项)
|
||
|
||
if (L == 3) 三点全部写 quality_tmp; // 质量起点
|
||
else 末点 quality = 前一点 quality * c + quality_tmp * (1 - c); // 指数平滑,c = track_quality_iter_coeff(0.8)
|
||
```
|
||
|
||
- `Temp_track::quality` 是 **0~1** 的归一化值;起批时 `Trust_Track.Quality = 末点 quality`,`Point_sum = Valid_point_sum = L`;
|
||
- 起批门限按**临时航迹长度 L** 取(比较对象是**末点**质量):L=3 → 0.7、L=4 → 0.6、L=5 → 0.5、L=6 → 0.4、L≥7 → 0.3;
|
||
- 质量不达标时该临时航迹**不进入 `Track_to_start`**,继续留在 `temp_track` 中参与下一圈关联(受 `buff_round`/长度 10 的消亡约束)。
|
||
|
||
> 注意:质量只在「点-临时航迹关联」路径更新,阶段三(点 × 1 点航迹头 → 2 点临时航迹)不写 `quality`;2 点航迹要到下一圈被延长成 3 点时才会首次获得质量值,这与其本来也不能起批(长度不足)是一致的。
|
||
|
||
---
|
||
|
||
## 9. 阶段六:临时航迹消亡
|
||
|
||
函数:`tmp_track_die()`(L673-697):
|
||
|
||
```cpp
|
||
if (n <= 0) erase; // 空航迹
|
||
if ((*Iter)[n-1].buff_round > 2 || n >= 10) erase;
|
||
```
|
||
|
||
| 消亡条件 | 含义 |
|
||
|---|---|
|
||
| 序列为空 | 防御性清理 |
|
||
| 末点 `buff_round > 2` | 连续 2 圈没有新点(允许空 1 圈) |
|
||
| 长度 `n >= 10` | 达到上限仍未完成起批(例如长期滞留屏蔽区) |
|
||
|
||
消亡**不输出任何信息**(与可靠航迹消亡不同,起批失败的临时航迹对宿主不可见)。
|
||
|
||
---
|
||
|
||
## 10. 起始条件汇总表
|
||
|
||
### 10.1 条件链(一条 3 点起批航迹的完整判据)
|
||
|
||
| 阶段 | 判据(源码位置) |
|
||
|---|---|
|
||
| **0. 输入资格** | 该点未被 `track_asso` 关联(已从 `point_recv` 删除);凝聚后 `100 m ≤ r ≤ 100000 m` |
|
||
| **1. 生成航迹头** | 未被阶段二/三消费(`Use_Flag==0`);创建时 `buff_round=1`(L77-114) |
|
||
| **2. 航迹头 → 2 点**(L325, L368-372) | 航迹头 `size==1` 且 `buff_round ≥ 2`;`dis ≤ track_init_velocity_max·ΔT`;`dis ≥ track_init_velocity_min·ΔT`;`dis ≥ abs(vr_p)·ΔT`;`dis ≥ abs(vr_h)·ΔT`;`vr_p·vr_h > 0`(隐含 `ΔT > 0`) |
|
||
| **3. 2 点 → 3 点及以上**(L164, L191, L221) | 临时航迹 `size > 1` 且末点 `buff_round ≥ 2`;`abs(r_p − r_末点) ≥ 5 m`;`d ≤ 8`(代码写 `d² ≤ 64`,`track_init_threshold=8`);`α < 60°`(`track_init_angle`);`vr_p·vr_末点 > 0` |
|
||
| **4. 转可靠航迹**(L484, L556) | `track_init_point_num_min ≤ L ≤ 10`;末点不在起批屏蔽区;**末点质量 ≥ 按 L 取的门限(V1.5.8)**;同源去重后保留;`trust_track.size() < track_index_max` 且成功分配航迹号 |
|
||
| **5. 消亡**(L686) | `末点.buff_round > 2`(连续 2 圈无新点)或 `长度 ≥ 10` |
|
||
|
||
### 10.2 门限汇总
|
||
|
||
| 判据 | 数值 | 来源 | 可调 |
|
||
|---|---|---|---|
|
||
| 最大速度 `vmax` | 50 m/s(样例) | 配置项 `track_init.track_init_velocity_max` | ✅(改配置文件) |
|
||
| 最小速度 `vmin` | 1 m/s(样例) | 配置项 `track_init.track_init_velocity_min` | ✅(改配置文件) |
|
||
| 距离差下限 `abs(Δr)` | **5 m(硬编码)** | `track_init.cpp` L191 | ❌ |
|
||
| 起批波门 `d ≤ 8` | `track_init_threshold=8` | 配置项 `track_init.track_init_threshold` | ✅(改配置文件) |
|
||
| 起批折角 | `track_init_angle=60°` | 配置项 `track_init.track_init_angle` | ✅(改配置文件) |
|
||
| 起批点数 | 3~9(典型 3~4) | 配置项 `track_init.track_init_point_num_min` | ✅(加载器校验 3~9) |
|
||
| 临时航迹长度上限 | 10(硬编码) | L484 / L686 | ❌ |
|
||
| 允许丢圈数 | 1 圈(`buff_round > 2` 消亡) | L686 | ❌ |
|
||
| 航迹头上限等待 | 2 圈 | L686(1 点航迹同判据) | ❌ |
|
||
| 起批屏蔽区 | ≤30 个矩形 | 配置段 `[track_init_prohibite_area]`(数组长度 = `*_num`) | ✅(加载器校验 0~30) |
|
||
| 起批最低质量(V1.5.8) | L3→0.7 / L4→0.6 / L5→0.5 / L6→0.4 / L≥7→0.3 | 配置项 `track_init.track_init_quality_L3~L7` | ✅(改配置文件) |
|
||
| 质量权重(V1.5.8) | 距离 0.5 / SNR 0.3 / 长度 0.2 | 配置段 `[track_quality]` | ✅(改配置文件) |
|
||
| 航迹数上限 | 500(默认) | 配置项 `track_index.track_index_max` | ✅(加载器校验 1~500) |
|
||
|
||
---
|
||
|
||
## 11. 关键算法细节
|
||
|
||
### 11.1 两点卡尔曼初始化(`kalman.cpp` L18-58)
|
||
|
||
输入两个直角坐标量测 `Z0, Z1` 与时间差 `T`(秒):
|
||
|
||
$$
|
||
X=\begin{bmatrix}x_1\\ (x_1-x_0)/T\\ y_1\\ (y_1-y_0)/T\end{bmatrix},
|
||
\qquad
|
||
P_{axis}=\begin{bmatrix}R & R/T\\ R/T & 2R/T^2\end{bmatrix}
|
||
$$
|
||
|
||
其中 `R` 取 `Z1` 处的**转换量测协方差**(极坐标→直角坐标,见 `README.md` 9.1 节),x/y 两轴独立填充 `P[4][4]`。
|
||
|
||
> 风险:`T → 0` 时速度与协方差同时发散,函数无除零保护(对应 BUG-18);临时航迹的两点初始化 `T` 来自相邻两圈,实际由时间戳决定,若宿主给出重复时间戳会异常。
|
||
|
||
### 11.2 EKF 统计距离(`kalman.cpp` L615-693)
|
||
|
||
对 4 维状态做 CV 一步预测(`F = [[1,T,0,0],[0,1,0,0],[0,0,1,T],[0,0,0,1]]`,过程噪声 `Q = 0.03²·I`,`G=[T²/2, T]ᵀ`):
|
||
|
||
$$
|
||
\hat X_{k|k-1}=F X,\qquad P_{k|k-1}=FPF^{T}+GQG^{T}
|
||
$$
|
||
|
||
非线性量测函数(距离-方位-径向速度):
|
||
|
||
$$
|
||
h(\hat X)=\begin{bmatrix}\sqrt{x^2+y^2}\\ \operatorname{atan2}(y,x)\bmod 2\pi\\ -\dfrac{x v_x+y v_y}{\sqrt{x^2+y^2}}\end{bmatrix},
|
||
\qquad H=\left.\frac{\partial h}{\partial X}\right|_{\hat X_{k|k-1}}
|
||
$$
|
||
|
||
量测噪声 `R = diag(σ_r², σ_a², σ_v²) = diag(10², 0.02², 2²)`,新息协方差 `S = H P Hᵀ + R`,残差做两项修正:
|
||
|
||
- **方位环绕**:`ν_θ = wrapToPi(θ_p − θ_pred)`,归一化到 `[−π, π)`;
|
||
- **多普勒盲速折叠**:`ν_v ← ν_v − round(ν_v / v_bind)·v_bind`,其中
|
||
|
||
$$
|
||
v_{bind}=\frac{150000}{f_{GHz}\cdot PRI_{\mu s}}\ \text{(m/s)},
|
||
\qquad f_{GHz}=9.2+0.02\cdot f_{ind}
|
||
$$
|
||
|
||
(`PRI ≤ 0` 或 `f ≤ 0` 时返回安全值 1.0;调用方约定 PRI 单位 µs,不允许为 0。)
|
||
|
||
返回马氏距离平方:
|
||
|
||
$$
|
||
d=\nu^{T}S^{-1}\nu,\qquad \nu=Z-h(\hat X_{k|k-1})
|
||
$$
|
||
|
||
**调用参数来源**:`prt = point.PRF_index`(实际存的是 `DataRev.PRI`,单位 µs);`freq_ind = point.Freq_index`。
|
||
|
||
### 11.3 折角计算(L448-456)
|
||
|
||
$$
|
||
\alpha=\arccos\!\left(\frac{\vec D_{12}\cdot\vec D_{23}}{\|\vec D_{12}\|\,\|\vec D_{23}\|}\right)\cdot\frac{180}{\pi}
|
||
$$
|
||
|
||
其中 `D12 = (末点 − 前一点)`、`D23 = (新点 − 末点)`,三点均取 **Temp_track 的直角坐标**(`X[0]` 为 x、`X[2]` 为 y)。`α` 越小表示越接近直线;`track_init_angle`(配置项,样例 60°)表示允许的航向偏折。
|
||
|
||
> 无 `acos` 定义域钳位:当两点重合(分母为 0)或浮点误差使比值略超 ±1 时结果为 `NaN`,`NaN < 60` 恒为 false → 表现为「该点无法关联」,不崩溃。
|
||
|
||
---
|
||
|
||
## 12. 与其他文档/实现的差异
|
||
|
||
### 12.1 与 `README.md` 第 8.3 / 9.3 节的差异(README 已同步本节结论)
|
||
|
||
> 说明:下表为本文档成文时发现的差异,当时 README 尚未同步 7d5a56f 的改动;随后已按本节结论更新 README 第 2、7.2、8.1、8.3、9.3、9.6、10.2 等节,「README 描述」列对应的是更新前的旧表述,保留以说明改动缘由。
|
||
|
||
| 项 | README 描述(更新前) | 当前源码 |
|
||
|---|---|---|
|
||
| 点-航迹头判据 | 按距离分段:近程 `r≥1000` 全速 / `r<1000` 半速;中远程 `r≥2000` 全速 / `r<2000` 1/2~1/3 速 | 该分支已**整体注释停用**,改为统一 `track_init_velocity_min·ΔT ≤ dis ≤ track_init_velocity_max·ΔT` **外加两条多普勒一致性条件** `dis ≥ abs(vr_p)·ΔT`、`dis ≥ abs(vr_h)·ΔT`(L361-372) |
|
||
| 起批触发长度 | 长度满足 `track_init_point_num_min ≤ L ≤ 10` | 一致 |
|
||
| 初始化用点 | 最近三点 | 不一致:取下标 `track_init_point_num_min-3 ~ -1` 的三个点,航迹长度大于起批点数时**不是**航迹真正的末三点(详见第 15 节第 2 条) |
|
||
| 输出历史点 | "将临时航迹的全部 L 个点写入" | 实际只写**前 `track_init_point_num_min` 个点**(L614 的 `L` 是起批点数,不是航迹长度) |
|
||
| use_flag 过滤 | "track_init 跳过 Use_Flag==1 的点" | 上游 `track_asso_process()` 已删除 `Use_Flag==1` 的点迹,`track_init` 入口(L59-70)也会再剔除一次,两道处理叠加,描述成立 |
|
||
|
||
### 12.2 与 `track_init_direct_tracking.cpp`(引导跟踪版,未启用)的差异
|
||
|
||
| 项 | `track_init.cpp`(TWS,启用) | `track_init_direct_tracking.cpp`(未启用) |
|
||
|---|---|---|
|
||
| 入口 | 完整实现 | `track_init_process_logic` 空实现(L11-23) |
|
||
| 点-航迹头判据 | 统一门限 + 两条多普勒一致性条件 | 仍为分距离段门限(原 `V_MAX/5` 亦在其注释中) |
|
||
| 起批长度 | `L ≥ track_init_point_num_min`(长度可大于阈值) | `L == track_init_point_num_min`(严格相等) |
|
||
| 去重 | 有同源去重 | 无 |
|
||
| 屏蔽区 | 有 | 无 |
|
||
| 航迹号 | `track_ind_get()` 分配 | 外部传入 `track_ID` |
|
||
| 消亡阈值 | `buff_round > 2` | `buff_round > 1`(更严格) |
|
||
| 输出模式 | `Track_Mode = 0`(TWS) | `Track_Mode = 1`(TAS) |
|
||
|
||
---
|
||
|
||
## 13. 参数与调参
|
||
|
||
> **参数配置方式改造后**:与起始流程相关的全部可调参数都在 TOML 配置文件里(样例 `config/config_tmp.toml`),由公开接口 `parameters_reload(配置文件路径)` 加载到 `RDP_parameter`;`parameters.h` 不再有这些宏,宿主也不再下发 `RadarPara`。改参数 = 改配置文件后重新调用 `parameters_reload()`(加载失败时参数保持不变)。完整参数表与严格校验规则见 README 第 10 节。
|
||
|
||
### 13.1 配置项(样例值,均为「配置段.键」)
|
||
|
||
| 配置项 | 样例值 | 含义 | 调大效果 |
|
||
|---|---|---|---|
|
||
| `track_init.track_init_threshold` | 8 | 起批波门,实际生效为 `d ≤ 8` | **更容易起批**(虚警增多) |
|
||
| `track_init.track_init_angle` | 60 | 起批折角(度) | 更容易起批(允许更急的转向) |
|
||
| `track_associate.sigma_range/azimuth/pitch/velocity` | 10 / 0.02 / 0.2 / 2.0 | EKF 量测噪声 | 影响 `S` 与 `d`,进而影响关联难度 |
|
||
| `track_index.track_index_max` | 500 | 航迹号/航迹数上限(1~500) | — |
|
||
| `cohesion.point_cohesion_range_min/max` | 100 / 100000 | 有效量程(凝聚阶段过滤) | — |
|
||
| `track_init.track_init_point_num_min` | 3 | 起批点数(加载器校验 3~9):调大 → 起批更慢、更抗虚警,但机动目标捕获更迟 | |
|
||
| `track_init.track_init_velocity_max/min` | 50 / 1 | 头-点关联的速度区间;`min` 调大可滤除静止/慢速点 | |
|
||
| `radar_params.radar_work_mode` | 0 | 当前对起始流程**无实际影响**(近/远程分支取值相同) | |
|
||
| `track_init_prohibite_area.*_num` + 4 个数组 | 0 + 空数组 | 起批屏蔽区(距离 m / 方位 度;≤30 组,数组长度 = `*_num`) | |
|
||
| `track_init.track_init_quality_L3~L7` | 0.7 / 0.6 / 0.5 / 0.4 / 0.3 | 按临时航迹长度的起批最低质量(V1.5.8) | 降低 → 更容易起批 |
|
||
| `track_quality.*` | 0.5 / 0.3 / 0.2 / 0.8 | 质量权重、平滑系数与衰减系数(V1.5.8) | 影响质量评估与消亡速度 |
|
||
|
||
> 旧 `RadarPara` 中的 `track_start_threshold` 字段原本就未被实现读取;新结构体只保留实现真正使用/保留的参数(见 README 10.3 对照表)。
|
||
|
||
### 13.2 调参直觉
|
||
|
||
- **虚警多、起批太容易**:先降 `track_init_threshold`(8 → 5 左右),再降 `track_init_angle`;必要时提高 `track_init_point_num_min`。
|
||
- **真实目标起批慢**:提高 `track_init_threshold` / `track_init_angle`,或降低 `track_init_point_num_min`(最小 3)。
|
||
- **静止/低速杂波起批**:提高 `track_init_velocity_min`。
|
||
- **某方位扇区/距离段禁止起批**(如地物、干扰区):配置起批屏蔽区,比调门限更精准。
|
||
- **注意**:`config/config_tmp.toml` 是相扫取值(`scan_mode = 0`);机扫部署时需配 `scan_mode = 1`,且 `extrapolate_max_tas`、`height_smooth_win`、`track_die_round_tas`、`tas_track_num_max` 按机扫取值配置(见 README 4.2、10.1)。
|
||
|
||
---
|
||
|
||
## 14. 输出字段映射
|
||
|
||
对每条新起批航迹的每个历史点 `j = 0 .. track_init_point_num_min-1`(L614-664):
|
||
|
||
| 输出字段 | 来源 | 备注 |
|
||
|---|---|---|
|
||
| `Track_Index` | 新分配的航迹号 | 同一行内所有点相同 |
|
||
| `Point_Sum` | `= track_init_point_num_min` | 与 `track_asso` 输出的 `Point_Sum=1` 可作区分 |
|
||
| `Range` / `Azimuth` | 点的 `r` / `azi`(→度) | 极坐标 |
|
||
| `Elevation` | `asin(h/r)`(度,钳位 ±1) | BUG-27 修复后的定义域钳位 |
|
||
| `z` | 点的 `height` | |
|
||
| `Range_V` | `sqrt(X[1]² + X[3]²)` = `sqrt(vx²+vy²)` | 速度**模值**(非径向速度) |
|
||
| `Direction_Angle` | `atan2(vy, vx)`(→度, `[0,360)`) | 航向角 |
|
||
| `x / v_x / y / v_y` | 点的 `X[0..3]` | 注意 4 维布局 |
|
||
| `range_point / azi_point / elev_point / vr_point` | 点的原始测量 | `point_type=0` |
|
||
| `Amplitude / track_snr / track_rcs` | 点的 `amplitude / SNR / RCS` | |
|
||
| `pitch_num / speed_dim / range_dim` | 点透传 | |
|
||
| `track_time` | `CPI_time/1000`(秒) | |
|
||
| `GNSS_time` | 点透传 | |
|
||
| `Flag_Point` | 恒为 **1**(实点) | |
|
||
| `Track_Mode` | 恒为 **0**(TWS) | |
|
||
| `Quality` | **该历史点的临时航迹质量 × 100**(V1.5.8) | 内部 `quality` 为 0~1,输出折算为 0~100;同一条航迹的各个历史点可能取值不同(L=3 时三点相同,之后只有末点更新) |
|
||
| **未赋值** | `Flag_Direction`、`x_Predict/y_Predict/z_Predict`、`Range_Predict/Azimuth_Predict/Elevation_Predict`、`Target_Type`、`prf_point`、`v_z`、`amp_asv_point`、`prn_fftn_point` | DLL 不清零该数组,宿主读到的是其自身缓存的旧值/未初始化值。注意 `Flag_Direction` 只在关联输出路径赋值(`track_asso.cpp` / `track_asso_tas.cpp`),起批输出不写该字段。该字段的判据已由 f249fdc 的 `vr_point > 0 ? 1 : 0`(实测径向速度,受多普勒速度模糊影响符号可能取反)改为 fc49707 + 42b598e 的**航向-位置夹角法**:`Direction_Angle = atan2(X[4], X[1])`(弧度转度后同时输出到 `Direction_Angle`),`x`、`y` 为经配置项 `radar_system_delay` 补偿后的位置、`r` 为其模,`acos((-x·cos(Direction_Angle) - y·sin(Direction_Angle))/r) < π/2` 判为靠近(1)、否则远离(0),即滤波速度方向与"雷达→目标"位置方向夹角超 90° 判为接近。另 `amp_asv_point` / `prn_fftn_point` 属 e8c56f9 新增,`data_preprocess()` **现已**把 `DataRev.amp_asv / prn_fftn` 搬入 `PointRecv`(TWS 与 TAS 分支均赋值,并随 `RdpRecPoint` 落库),因此 TWS 关联/输出路径的该字段已有有效来源;TAS 关联与输出路径(`track_asso_tas.cpp`)仍未涉及这两个字段,TAS 输出行中它们保持宿主原值 |
|
||
|
||
---
|
||
|
||
## 15. 已知问题与风险点
|
||
|
||
> 以下为阅读源码得到的观察,**不代表已确认的缺陷**,部分为有意保留的历史行为;修改前请结合实测数据评估。
|
||
|
||
1. **硬编码 5 m 距离差门限**(L191):`|r_p − r_末点| < 5` 的点永不参与关联。对切向飞行、远距离慢速目标可能造成漏关联(起批失败)。
|
||
2. **初始化用点不是航迹真正的末三点**(L505、L563-565 vs L614):起批流程中 `L = track_init_point_num_min`(L505 覆盖了前面循环里的航迹长度变量),Z0/Z1/Z2 取下标 `L-3 / L-2 / L-1`,输出循环也只写到 `j = L-1`。因此当航迹长度 N 大于起批点数时,参与初始化的只是**前 L 个点中的最后三点**,而不是航迹真正的末三点;第 L 个点之后的尾部点既未参与初始化也不出现在输出中(这些点通常来自滞留在起批屏蔽区期间的积累)。
|
||
3. **去重只比较前三个点**(L517-519 硬编码 0/1/2),且优度指标取 `[track_init_point_num_min-1].d`(长度大于阈值时非末点);若 `track_init_point_num_min > 3`,共享前三点但后续分叉的两条航迹会被误判为同源。
|
||
4. **无一对一关联约束**:一个点可同时为多条临时航迹续点(阶段二/三),产生航迹分裂,虚警场景下临时航迹表可能快速膨胀(靠去重与消亡收敛)。
|
||
5. **ΔT ≤ 0 无显式校验**(BUG-33 遗留):头-点关联靠 `dis ≤ track_init_velocity_max·ΔT` 隐式排除负 ΔT;EKF 关联路径 `d_cal_track_init_EKF(ΔT<0)` 会得到无物理意义的结果,只是通常会被 `d ≤ 8` 挡住。
|
||
6. **判据对马氏距离平方二次平方**(L221):`track_init_threshold=8` 时实际门限为 `d ≤ 8`(3 自由度约 95.4%)。若按字面 `d² ≤ 64` 理解会误判为 `d ≤ 64`(极宽松)。修改 `track_init_threshold` 时务必记得这一层。
|
||
7. **两点/三点初始化无 `T > 0` 保护**(BUG-18):`T` 极小(重复/乱序时间戳)时速度、加速度与协方差会爆炸性放大。
|
||
8. **死代码**:L72-75 的 `point_recv` 回写随即被 L117-118 清空;L606-608 的 `cart2polar` 结果未使用;L348-356 的近/远程分支取值相同。
|
||
9. **`acos` 无定义域钳位**(L452):浮点越界产生 `NaN`,表现为条件恒假(静默丢弃该关联),不报错也不崩溃。
|
||
10. **航迹数饱和静默丢弃**:`trust_track.size() ≥ track_index_max`(配置项,默认 500)或 `track_ind_get()` 返回 0 时,候选被直接跳过且该临时航迹已从 `temp_track` 摘除 → 起批机会永久丢失。
|
||
11. **未赋值输出字段**(第 14 节):宿主若直接使用 `x_Predict`、`Flag_Direction`、`Target_Type` 等字段,会读到非本次写入的数据。
|
||
12. **`PI` 为 float 常量**(3.1415926f)参与双精度运算,角度换算存在约 1e-7 量级偏差,属既有实现风格。
|
||
13. **精度/量纲提醒**:`Temp_track::vr` 直接取点的 `Velocity`(多普勒)作为径向速度,EKF 关联时用于残差折叠(盲速 `v_bind`)与符号判据;若宿主的 `Velocity` 存在模糊,符号判据 `vr_p·vr_末点 > 0` 可能失效。
|
||
14. **输出数组行号可能越界(极端场景)**:容量判据用 `trust_track->size() < track_index_max`,而 `Trust_Track_Output` 的行计数已在 `track_asso` 阶段按「消亡前」的航迹数推进,中间的 `track_die` 删除 D 条后腾出的配额会让起始流程多写 D 行;当 D ≥ 2 且当圈起批数占满配额时,写入行号可超过 `MAX_TRACK_NUM=500`(详见 2.3)。建议把判据改为同时校验 `*Trust_track_num_Output < MAX_TRACK_NUM` 与 `trust_track->size() < track_index_max`。
|
||
15. **起批质量门限比较的是「末点」质量而非整条航迹质量(V1.5.8)**:`tmp_track_to_trust_track()` 取 `(*Iter)[L-1].quality` 与按长度取的门限比较,而该质量在 L>3 时只是末点的指数平滑值(L=3 时三点同值),并不代表整条临时航迹的平均质量;长度越大门限越低,实际是「用更宽松的门限校验最新一点」。
|
||
16. **起批质量来源依赖阶段二**:`quality` 只在「点-临时航迹关联」成功后更新,阶段三产生的 2 点临时航迹不带质量值;长度 3 的临时航迹在第三次延长时写入三点同值,因此其起批质量有定义。若将来把起批点数放宽到 3 以下(当前加载器校验 3~9),则会出现质量恒为 0、永远无法通过质量门限的情况。
|
||
|
||
---
|
||
|
||
## 16. 代码索引
|
||
|
||
### 16.1 `track_init.cpp`
|
||
|
||
| 行号 | 内容 |
|
||
|---|---|
|
||
| L11-143 | `track_init_process_logic` 主流程 |
|
||
| L36-38 | 装填点迹 |
|
||
| L40-47 | `buff_round` 自增 |
|
||
| L49 | `if(point_process.size()>0)` 主分支 |
|
||
| L52 / L55 | 两阶段关联调用 |
|
||
| L57-70 | 删除已消费点迹 |
|
||
| L72-75 | 点迹回写(等效空操作) |
|
||
| L77-114 | 剩余点 → 航迹头 |
|
||
| L117-118 | 清空 `point_process` / `point_recv` |
|
||
| L121 | 转可靠航迹调用 |
|
||
| L126 | 临时航迹消亡调用 |
|
||
| L145-304 | `point_temp_track_asso` |
|
||
| L164 | ≥2 点 + `buff_round≥2` 资格 |
|
||
| L191 | `abs(Δr) ≥ 5 m` 门限 |
|
||
| L194-195 | EKF 统计距离 `d` |
|
||
| L199-205 | 折角 `α` |
|
||
| L221 | 三条件判据 |
|
||
| L237-281 | 生成新临时航迹(两点初始化) |
|
||
| L283-303 | 删除被关联的原航迹 |
|
||
| L306-445 | `point_track_head_asso` |
|
||
| L325 | 1 点 + `buff_round≥2` 资格 |
|
||
| L361-372 | 门限判据(含停用的旧分支注释) |
|
||
| L388-427 | 生成 2 点临时航迹 |
|
||
| L429-443 | 删除被关联的航迹头 |
|
||
| L448-456 | `alpha_cal_track_init` |
|
||
| L458-671 | `tmp_track_to_trust_track` |
|
||
| L484 | 起批长度 + 屏蔽区条件 |
|
||
| L504-550 | 同源去重 |
|
||
| L552-667 | 起批与输出 |
|
||
| L563-571 | 三点卡尔曼初始化 |
|
||
| L573-604 | `Trust_Track` 构造 |
|
||
| L612-664 | 输出历史点 |
|
||
| L673-697 | `tmp_track_die` |
|
||
| L699-712 | `track_init_prohibit` |
|
||
| L714-723 | 构造 / `reset()` |
|
||
|
||
### 16.2 依赖
|
||
|
||
| 文件 | 函数 | 用途 |
|
||
|---|---|---|
|
||
| `kalman.cpp` L18-58 | `kalman_filter_init_2dots` | 两点(4 维)初始化 |
|
||
| `kalman.cpp` L125-194 | `kalman_filter_init_3dots` | 三点(6 维)初始化 |
|
||
| `kalman.cpp` L615-693 | `d_cal_track_init_EKF` | 起始关联统计距离 |
|
||
| `kalman.cpp` L780-789 | `Bind_speed` | 盲速计算 |
|
||
| `coor_trans.cpp` L8-20 | `polar2cart` / `cart2polar` | 坐标转换 |
|
||
| `track_index_mangement.cpp` L14-77 | `track_ind_get` | 航迹号分配 |
|
||
| `data_process.cpp` L22 | `data_preprocess` | 一圈完成 → `model=1` |
|
||
| `data_process.cpp` L167 起 | `track_process` | 起始流程调用点(L187-201 调用 `track_asso`/`track_die`/`track_init`) |
|
||
| `data_process.cpp` L246-258 | 参数加载 | `parameters_reload()`:TOML 配置 → 全局对象 `rdp_parameter`(原「参数初始化/修改」已删除) |
|