18 KiB
18 KiB
机场碰撞预警系统设计文档
1. 系统概述
1.1 系统目标
实现机场内航空器与车辆的实时位置监控和碰撞预警。
1.2 主要功能
- 实时获取航空器位置数据
- 实时获取车辆位置数据
- 碰撞风险检测
- 预警信息输出
2. 系统架构
2.1 整体架构
- 数据采集层:负责从外部系统获取位置数据
- 数据处理层:进行坐标转换和数据规范化
- 碰撞检测层:执行碰撞风险分析
- 预警输出层:生成和发送预警信息
2.2 核心模块
- System:系统总控模块
- DataCollector:数据采集模块
- CollisionDetector:碰撞检测模块
- CoordinateConverter:坐标转换模块
3. 数据模型
3.1 基础数据类型
// 二维向量
struct Vector2D {
double x; // 东西方向(米)
double y; // 南北方向(米)
};
// 地理坐标
struct GeoPosition {
double latitude; // 纬度
double longitude; // 经度
};
// 基础移动物体
struct MovingObject {
std::string id; // 唯一标识
GeoPosition geo; // 地理坐标
Vector2D position; // 平面坐标
double heading; // 航向角(度)
uint64_t timestamp; // 时间戳
};
3.2 业务数据类型
// 航空器数据
struct Aircraft : MovingObject {
std::string flightNo; // 航班号
std::string trackNumber; // 航迹号
double altitude; // 高度(米)
};
// 车辆数据
struct Vehicle : MovingObject {
std::string vehicleNo; // 车牌号
double speed; // 速度
double direction; // 方向
bool controllable; // 是否可控
};
3.3 配置数据类型
// 可控车辆配置
struct ControllableVehicleConfig {
std::string vehicleNo; // 车牌号
std::string ip; // 车辆IP地址
int port; // 车辆端口号
};
4. 接口设计
4.1 数据源接口
class DataSource {
virtual std::vector<Aircraft> getAircraftData() = 0;
virtual std::vector<Vehicle> getVehicleData() = 0;
};
4.2 碰撞检测接口
class CollisionDetector {
bool detectAircraft(const Aircraft& a1, const Aircraft& a2);
bool detectAircraftVehicle(const Aircraft& aircraft, const Vehicle& vehicle);
};
5. 碰撞检测算法
5.1 基本原理
碰撞检测基于以下三个核心要素:
- 距离检测
- 相对运动分析
- 区域特定阈值
5.2 检测流程
5.2.1 距离检测
- 直接报警条件:
if (distance < threshold * 0.5) { // 距离小于阈值的一半
return true; // 直接报警
}
- 进一步检测条件:
if (distance < threshold) { // 距离在阈值范围内
// 进行相对运动分析
}
5.2.2 相对运动分析
- 速度分量计算:
// 考虑航向角,转换为数学坐标系
double vx = speed * std::cos((90 - heading) * M_PI / 180.0);
double vy = speed * std::sin((90 - heading) * M_PI / 180.0);
- 相对运动计算:
// 计算相对速度
double relativeVx = v1x - v2x;
double relativeVy = v1y - v2y;
// 计算相对运动
double relativeMotion = dx*relativeVx + dy*relativeVy;
- 碰撞判定:
if (relativeMotion <= 0) { // 物体正在接近或相对静止
return true; // 报警
}
5.3 区域特定阈值
不同区域采用不同的安全距离阈值:
| 区域 | 航空器地面阈值 | 车辆阈值 |
|---|---|---|
| 跑道 | 100米 | 50米 |
| 滑行道 | 50米 | 30米 |
| 停机位 | 40米 | 20米 |
| 服务区 | 30米 | 15米 |
5.4 空间优化
使用四叉树进行空间索引,优化查询性能:
- 四叉树构建:
QuadTree<Vehicle> vehicleTree_(bounds, 8); // 容量为8的四叉树
- 邻近查询:
auto nearbyVehicles = vehicleTree_.queryNearby(
position, // 中心点
threshold // 查询半径
);
5.5 性能考虑
-
空间复杂度:
- 四叉树:O(n),其中 n 为车辆数量
- 航空器列表:O(m),其中 m 为航空器数量
-
时间复杂度:
- 四叉树查询:O(log n)
- 总体碰撞检测:O(m * log n)
5.6 安全保障
-
距离冗余:
- 使用阈值的一半作为直接报警条件
- 为不同区域设置合适的安全距离
-
运动预测:
- 考虑物体的相对运动
- 提前发现潜在碰撞风险
-
降级处理:
- 当无法计算相对运动时,仅使用距离判断
- 保证基本的安全检测功能
5.7 碰撞检测主流程
// 加载可控车辆配置
std::vector<ControllableVehicleConfig> controllableVehicles = loadControllableVehicleConfig();
for (const auto& aircraft : aircrafts) {
for (const auto& vehicle : vehicles) {
if (detectCollision(aircraft, vehicle)) {
// 检查是否为可控车辆
auto iter = std::find_if(controllableVehicles.begin(), controllableVehicles.end(),
[&](const ControllableVehicleConfig& config) {
return config.vehicleNo == vehicle.vehicleNo;
});
if (iter != controllableVehicles.end()) {
// 生成控制指令
auto command = generateCommand(aircraft, vehicle);
// 发送控制指令
sendCommand(command, iter->ip, iter->port);
} else {
// 发送普通预警
sendWarning(aircraft, vehicle);
}
}
}
}
5.8 可控车辆配置加载
从配置文件加载可控车辆信息:
std::vector<ControllableVehicleConfig> loadControllableVehicleConfig() {
std::vector<ControllableVehicleConfig> configs;
// 读取配置文件
std::ifstream file("controllable_vehicles.json");
nlohmann::json jsonConfig;
file >> jsonConfig;
// 解析配置项
for (const auto& item : jsonConfig["vehicles"]) {
ControllableVehicleConfig config;
config.vehicleNo = item["vehicleNo"].get<std::string>();
config.ip = item["ip"].get<std::string>();
config.port = item["port"].get<int>();
configs.push_back(config);
}
return configs;
}
配置文件 controllable_vehicles.json 的格式如下:
{
"vehicles": [
{
"vehicleNo": "VEH001",
"ip": "192.168.1.101",
"port": 8080
},
{
"vehicleNo": "VEH002",
"ip": "192.168.1.102",
"port": 8080
},
{
"vehicleNo": "VEH003",
"ip": "192.168.1.103",
"port": 8080
}
]
}
6. 坐标转换
6.1 转换方法
- 使用等角投影方法
- 基于机场参考点(青岛胶东国际机场:北纬36°21'43.2",东经120°05'16.8")
- 考虑地球曲率影响
- WGS84椭球体参数
7. 数据更新策略
7.1 数据采集
- 定时轮询(默认1秒)
- 独立的数据采集线程
- 分别缓存航空器和车辆数据
7.2 碰撞检测
- 实时检测(默认100ms)
- 独立的检测线程
- 分类处理不同类型的碰撞风险
8. 开发规范
8.1 代码规范
- Google C++ Style
- 智能指针管理
- 异常安全
- RAII原则
8.2 性能规范
- 避免虚函数滥用
- 减少动态内存分配
- 合理使用模板
- 注意数据对齐
8.3 网络编程规范
8.3.1 数据读取策略
- 使用分块读取代替一次性读取
- 合理设置分块大小(如1KB)
- 正确处理部分读取的情况
- 验证数据完整性
8.3.2 错误处理
- 实现请求重试机制
- 设置合理的超时时间
- 正确处理连接断开
- 完善的错误日志记录
8.3.3 连接管理
- 及时关闭无用连接
- 正确处理连接状态
- 实现连接池(如需要)
- 处理并发连接
8.3.4 性能考虑
- 使用异步操作处理并发
- 实现数据缓存机制
- 免频繁建立连接
- 合理控制缓冲区大小
8.3.5 最佳实践
// 分块读取示例
const size_t chunk_size = 1024; // 1KB per chunk
while (total_read < content_length) {
size_t to_read = std::min(chunk_size, content_length - total_read);
// 读取数据块
// 处理错误
// 更新计数
}
// 重试机制示例
const int max_retries = 3;
for (int retry = 0; retry < max_retries; ++retry) {
try {
// 尝试操作
break; // 成功则退出
} catch (...) {
if (retry == max_retries - 1) throw; // 最后一次重试失败
// 等待后重试
}
}
8.4 HTTP 数据读取规范
8.4.1 问题描述
在使用 Boost.Asio 读取 HTTP 响应时,可能会遇到 "End of file" 错误。这个错误通常发生在:
- 读取响应体时
- 服务器已关闭连接
- 数据未完全读取
8.4.2 错误原因分析
- HTTP 响应头读取时可能已包含部分响应体数据
- 忽略缓冲区中的这部分数据会导致:
- 数据丢失
- 读取位置错误
- 连接提前关闭
8.4.3 解决方案
- 正确处理响应数据:
// 1. 读取响应头
size_t header_length = asio::read_until(*socket_, response_buf, "\r\n\r\n");
// 2. 处理缓冲区中的响应体数据
size_t body_part = response_buf.size() - header_length;
if (body_part > 0) {
// 提取已缓冲的响应体数据
body.append(
asio::buffers_begin(response_buf.data()) + header_length,
asio::buffers_end(response_buf.data())
);
total_read += body_part;
}
// 3. 继续读取剩余数据
while (total_read < content_length) {
// 读取剩余的响应体
}
8.4.4 最佳实践
-
数据读取原则:
- 使用简单直接的读取逻辑
- 准确跟踪已读取的数据量
- 正确处理缓冲区中的所有数据
- 避免复杂的重试机制
-
错误处理:
- 及时检查错误码
- 提供详细的错误日志
- 在错误发生时及时关闭连接
- 保持连接状态的一致性
-
数据完整性:
- 验证 Content-Length
- 确保读取完整的响应体
- 避免数据丢失或重复
- 正确处理分块数据
-
日志记录:
- 记录关键操作步骤
- 包含详细的错误信息
- 记录数据读取进度
- 便于问题诊断和调试
9. 测试策略
9.1 单元测试
- 模块功能测试
- 边界条件测试
- 异常处理测试
- 内存泄漏测试
- 坐标转换精度测试
9.2 性能测试
- 延迟测试
- 并发测试
- 压力测试
- 内存使用测试
- 坐标转换性能测试
9. 测试场景
9.1 碰撞风险场景模拟
9.1.1 场景描述
模拟一个典型的地面碰撞风险场景:
- 航空器在跑道上滑行
- 地面车辆垂直接近跑道
- 两者轨迹存在交叉点
- 如不采取措施将发生碰撞
9.1.2 场景参数
-
航空器参数:
- 初始位置:跑道西端 (AIRPORT_LON - 0.002, AIRPORT_LAT)
- 运动方向:向东滑行
- 运动速度:0.0002经度/秒
- 高度:5米(地面滑行高度)
-
车辆参数:
- 初始位置:跑道南侧 (AIRPORT_LON, AIRPORT_LAT - 0.002)
- 运动方向:向北行驶
- 运动速度:0.0002纬度/秒
- 与航空器轨迹垂直相交
-
时间参数:
- 更新频率:每秒一次
- 场景周期:10秒
- 碰撞风险时间:约5秒(场景中点)
9.1.3 数据特点
-
位置数据:
- 只提供实时位置信息
- 不包含速度信息
- 不包含方向信息
- 位置精度:小数点后6位
-
数据格式:
// 航空器数据
{
"flightNo": "CES2501",
"longitude": 120.088003,
"latitude": 36.361999,
"time": 1700123456,
}
// 车辆数据
{
"vehicleNo": "VEH001",
"longitude": 120.088003,
"latitude": 36.361999,
"time": 1700123456
}
9.1.4 预期结果
-
碰撞检测:
- 在交叉点附近应触发预警
- 预警时间:碰撞前约5秒
- 持续时间:直到危险解除
-
风险等级:
- 距离大于100米:低风险
- 距离50-100米:中等风险
- 距离小于50米:高风险
-
系统响应:
- 实时计算相对距离
- 及时发出预警信息
- 记录完整的碰撞风险过程
10. 风险控制
10.1 技术风险
- 实时性保证
- 内存管理
- 多线程同步
- 系统稳定性
- 坐标转换精度
10.2 解决方案
- 性能优化
- 资源监控
- 故障恢复
- 降级处理
- 坐标转换验证
11. 后续优化
- 引入GPU加速计算
- 优化空间索引算法
- 改进故障检测机制
- 增加自适应负载均衡
- 完善监控告警系统
- 优化坐标转换算法
- 支持多种投影方式
使用纯C++实现可以获得:
- 最佳的实时性能
- 直接的硬件控制
- 精确的内存管理
- 最小的系统开销
- 可预测的行为表现
10. 单元测试
10.1 测试框架
- 使用 Google Test 框架
- 支持 EXPECT 和 ASSERT 断言
- 支持 Mock 对象
- 支持测试夹具(Test Fixtures)
10.2 测试用例组织
10.2.1 基础类型测试 (BasicTypesTest)
-
Vector2D 测试
- 测试向量大小计算
- 测试方向角计算
-
MovingObject 测试
- 测试速度计算
- 测试航向角计算
- 验证位置历史记录
-
数据有效性测试
- 测试航空器数据
- 测试车辆数据
- 验证速度和位置限制
10.2.2 碰撞检测测试 (CollisionDetectorTest)
-
安全距离测试
- 测试水平安全距离
- 测试垂直安全距离
-
边界条件测试
- 测试临界距离
- 测试高度边界
-
特殊情况测试
- 测试零距离情况
- 测试对角线距离
10.2.3 数据采集测试 (DataCollectorTest)
-
速度计算测试
// 测试数据生成 double lon = 120.08 + (LON_CHANGE_PER_SEC * i); // 每秒经度变化 uint64_t timestamp = baseTime + (i * 1); // 每秒一个点 // 验证速度计算 EXPECT_NEAR(aircraft.speed, 55.0, 5.0); // 允许5m/s误差 -
Mock 数据源
class MockDataSource : public DataSource { MOCK_METHOD(bool, connect, (), (override)); MOCK_METHOD(bool, fetchAircraftData, (std::vector<Aircraft>&), (override)); MOCK_METHOD(bool, fetchVehicleData, (std::vector<Vehicle>&), (override)); };
10.3 测试数据生成
10.3.1 位置数据
-
航空器数据
- 基于青岛胶东机场坐标
- 模拟<EFBFBD><EFBFBD>面滑行场景
- 生成连续位置点
-
车辆数据
- 模拟地面车辆运动
- 生成可能的碰撞场景
10.3.2 时间序列
-
时间戳生成
- 使用 Unix 时间戳
- 每秒更新一次
- 保证时间连性
-
数据更新频率
- 模拟实际系统的更新频率
- 确保数据连续性
- 避免时间戳重复
10.4 测试覆盖率
-
基础功能测试
- 坐标转换
- 速度计算
- 航向计算
-
错误处理测试
- 无效数据处理
- 连接错误处理
- 数据解析错误
-
边界条件测试
- 最大/最小速度
- 位置跳变限制
- 时间戳异常
10.5 测试最佳实践
-
测试数据准备
- 使用实际的地理坐标
- 模拟真实的运动场景
- 考虑数据精度
-
Mock 对象使用
- 模拟外部依赖
- 控制测试环境
- 验证交互逻辑
-
日志记录
- 记录测试过程
- 输出中间结果
- 便于问题诊断
11. 可控车辆通信
11.1 通信方式
使用 HTTP 协议与可控车辆通信:
- 碰撞检测系统作为 HTTP 客户端,向可控车辆发送 POST 请求
- 请求地址格式为:
http://<vehicle_ip>:<port>/command - 请求 Body 中包含控制指令的 JSON 表示
- 可控车辆作为 HTTP 服务器,接收并处理控制指令
11.2 指令格式
使用 JSON 格式表示控制指令:
POST /command HTTP/1.1
Host: <vehicle_ip>:<port>
Content-Type: application/json
{
"command": "stop",
"timestamp": 1700123456
}
或者:
POST /command HTTP/1.1
Host: <vehicle_ip>:<port>
Content-Type: application/json
{
"command": "change_route",
"route": [
{"longitude": 120.08, "latitude": 36.36},
{"longitude": 120.09, "latitude": 36.37},
{"longitude": 120.10, "latitude": 36.38}
],
"timestamp": 1700123456
}
11.3 可控车辆响应
可控车辆收到指令后,需要及时响应:
- 立即执行停车、减速等指令
- 如果是改变路线,则规划新的路径并切换
- 将执行结果以 JSON 格式返回:
HTTP/1.1 200 OK
Content-Type: application/json
{
"status": "ok",
"message": "Command executed successfully",
"timestamp": 1700123456
}
或者:
HTTP/1.1 500 Internal Server Error
Content-Type: application/json
{
"status": "error",
"message": "Failed to execute command",
"error": "No route found",
"timestamp": 1700123456
}
11.4 安全考虑
由于 HTTP 是明文传输协议,存在安全隐患。需要采取以下措施:
- 使用 HTTPS 代替 HTTP,实现加密通信
- 对可控车辆的访问进行身份验证,防止非法控制
- 对控制指令进行数字签名,防止指令被篡改
- 对敏感数据(如路径点坐标)进行加密,防止泄露
11.5 可用性考虑
为保证可控车辆的控制链路高可用,需要:
- 监控可控车辆的 HTTP 服务可用性,发现异常及时告警
- 对控制指令的发送进行重试和超时处理,避免单次请求失败导致控制中断
- 考虑引入备用的控制方式(如 MQTT),作为 HTTP 不可用时的降级方案
11.6 指令发送
根据可控车辆的IP地址和端口号,发送控制指令:
void sendCommand(const ControlCommand& command, const std::string& ip, int port) {
// 创建HTTP客户端
HttpClient client(ip, port);
// 构造请求
HttpRequest request;
request.setMethod(HttpRequest::POST);
request.setPath("/command");
request.setHeader("Content-Type", "application/json");
request.setBody(command.toJson());
// 发送请求
HttpResponse response = client.send(request);
// 处理响应
if (response.getStatus() == HttpResponse::OK) {
// 指令发送成功
logger.info("Command sent successfully");
} else {
// 指令发送失败
logger.error("Failed to send command, status code: {}", response.getStatus());
}
}
使用 HTTP 客户端库,向可控车辆发送 POST 请求。请求路径为 /command,请求体为控制指令的 JSON 表示。根据响应状态码判断指令是否发送成功。