数据起飞了?当无人机仿真遇上 KaiwuDB

0 阅读39分钟

本篇内容出自「码」上数据库——KaiwuDB 社区第二届征文大赛优秀作品:《KaiwuDB 社区版在 PX4-ROS2 无人机飞行仿真中的落地实践,加速仿真时序数据的高效存取与智能分析》,作者:富贵儿 998

一、前言

无人机作为智能移动机器人的重要分支,已广泛应用于巡检、测绘、物流、应急救援等众多领域。在无人机系统的研发与测试中,飞行控制仿真是不可或缺的关键环节。通过仿真,开发者可以在安全、低成本的环境中验证算法、调试逻辑并评估性能,大幅缩短从设计到实飞的周期。

当前,开源飞控软件 PX4 与机器人操作系统 ROS2 已成为无人机仿真与二次开发的主流技术栈。PX4 提供专业的飞控核心算法(如姿态解算、导航滤波、混控输出),而 ROS2 则为其带来了模块化、分布式通信和丰富的生态工具。两者通过 PX4‑Gazebo‑ROS2 框架紧密耦合,构成了一个完整的软件在环(SITL)仿真环境。在该框架中,PX4 通过 RTPS(Real‑Time Publish‑Subscribe)协议将飞控内部状态、传感器数据、控制指令等以 Topic 的形式发布到 ROS2 网络,同时接收来自 ROS2 节点的外部控制命令(Off‑Board Control)。

仿真过程中产生的数据本质上是高并发、强时序的流式数据。这些数据主要包括:

• 传感器原始数据:IMU 测量值、GPS 位置、气压计高度等,通常以数百赫兹的频率输出。

• 状态估计数据:EKF 滤波后的姿态、位置、速度等状态量。

• 控制指令数据:油门、舵面指令、期望姿态/位置等。

• 系统状态数据:电池电压、模式切换、错误标志等。

在传统的仿真流程中,这些数据往往以 ULOG(PX4 专用二进制日志)或 CSV 文件的形式暂存于本地磁盘。随着仿真规模扩大(如多机编队、长时测试)、数据频率提升(如视觉/激光雷达数据接入)以及实时性要求加强(如硬件在环 HIL),传统文件存储方式在写入吞吐量、实时查询、长期归档与在线分析等方面面临显著挑战。如何高效地管理、存储和利用这些海量时序数据,已成为提升无人机仿真效能的关键问题。

在此背景下,面向 AIoT 场景的分布式多模数据库 KaiwuDB 社区版提供了新的解决方案。KaiwuDB 定位为“时序引擎、通用事务引擎、分析引擎、内存实时引擎等多引擎架构”的融合数据库,具备百万级数据秒级写入、毫秒级查询响应、高压缩比(5‑30 倍)以及云边端一体化部署等特性。其兼容 PostgreSQL 协议、提供 RESTful API 及多种编程语言接口的设计,使得与 ROS2 节点的集成尤为便捷。

本文将聚焦于 PX4 仿真场景下 KaiwuDB 与 ROS2 的结合应用。通过 ROS2 节点订阅 PX4 发布的各类 Topic,将实时流式数据持续写入  KaiwuDB 的时序表中;利用 KaiwuDB 的高性能写入与实时查询能力,实现仿真数据的在线持久化、秒级回溯与即时分析;进一步结合其跨模计算(时序+关系)特性,可支持复杂的业务查询与决策分析,为无人机仿真的数据驱动优化、智能算法训练与全生命周期测试验证提供坚实的数据底座。

二、时序数据增长下的业务痛点分析:MySQL 在 PX4-ROS2 无人机仿真中的瓶颈

在 PX4-ROS2 无人机飞行仿真的研发与测试流程中,海量、高频的时序数据是算法验证和系统分析的血液。传统上,团队可能选择使用MySQL 这类成熟的关系型数据库进行数据记录,因为它通用、稳定且生态完善。然而,随着仿真规模从单机向机群、从短时测试向全天候压力测试演进,数据的规模与复杂性呈指数级增长,MySQL 的架构特性在面对这种纯粹的时序数据写入与查询场景时,逐渐暴露出以下核心痛点:

2.1 高并发写入瓶颈与仿真中断风险

****• 场景:在仿真中,单个无人机节点可能同时发布数十个 Topic(如/fmu/vehicle_imu/data、/fmu/vehicle_gps_position/data 等),每个 Topic 的发布频率在 50Hz 至数百 Hz 不等。一个包含 10 架无人机的仿真集群,每秒产生的数据点轻松超过 10 万个。

****• MySQL痛点:MySQL 的 InnoDB 引擎基于 B+ 树索引,擅长处理复杂的 OLTP 事务,但面对每秒数十万次的简单 INSERT 操作时,会迅速成为瓶颈。频繁的磁盘 I/O、索引维护开销以及行级锁竞争,会导致数据写入队列阻塞。在实时仿真中,这直接表现为数据记录延迟,严重时甚至因写入超时导致 ROS2 节点阻塞,引发整个仿真链路的中断或丢帧,破坏了仿真的实时性与连续性。

2.2 查询性能低下,严重拖慢研发调试效率

•场景:工程师需要快速查询某架无人机在特定时间区间(如仿真第 120-130 秒)的俯仰角变化,或关联分析同一时刻 IMU 数据与 EKF 估计状态的差异。

• MySQL痛点

时间区间查询慢:即使对时间戳字段建立了索引,在亿级数据表中进行范围扫描,性能衰减依然明显。SELECT * FROM imu_data WHERE drone_id = 1 AND timestamp BETWEEN ‘2023-10-01 10:00:00’ AND ‘2023-10-01 10:00:10’ 这类查询会变得异常缓慢。

聚合分析吃力:对海量时序数据进行降采样(Downsampling)、统计每架无人机的最大转速或计算平均轨迹偏差等操作,需要执行全表扫描或复杂的联表查询,耗时可能长达数分钟甚至小时,使交互式分析变得几乎不可能,打断了工程师的思路流。

2.3 存储成本急剧膨胀,管理负担沉重

•场景:为进行长期趋势分析和算法回归测试,需要保留数月至数年的仿真历史数据。原始数据以每秒 10 万点的速度涌入。

•MySQL痛点

存储效率低:MySQL 按行存储,每条记录都包含完整的列头信息和索引开销。对于结构简单的传感器数据(时间戳、设备 ID、几个浮点数值),其有效数据占比(压缩前)可能低于 50%,造成存储空间的巨大浪费。

分区维护复杂:为缓解性能问题,通常需要对表按时间进行分区(Partitioning)。然而,MySQL 分区的创建、维护(如定期删除旧分区)和优化需要繁琐的 DBA 手工操作或脚本维护,自动化程度低,容易出错,且分区数量存在上限,不适合超长期存储。

2.4 数据模型与业务场景错配,开发复杂度高

•场景:仿真中除了数值型的传感器数据,还有事件型数据(如模式切换、指令下发)和半结构化数据(如飞控参数快照)。

•MySQL痛点

Schema僵化:增加一个新的传感器类型或 Topic 需要执行 ALTER TABLE 操作,在大表上这会引发锁表,导致服务中断。仿真系统的快速迭代特性与数据库 Schema 的刚性变更要求产生直接矛盾。

复杂查询代码冗长:实现时序数据常见的“按时间线对齐”、“最新值查询”等操作,需要编写复杂且低效的SQL语句,增加了业务代码的复杂度和维护成本。

2.5 总结:业务发展的核心制约

在 PX4-ROS2 无人机仿真这一追求高效率、高保真、高迭代速度的业务场景下,MySQL 的上述痛点已从技术问题上升为业务发展的制约:

• 拖慢研发周期:工程师将大量时间耗费在等待查询结果和数据导出上。

•限制仿真规模:为避免系统崩溃,被迫降低仿真频率、减少无人机数量或缩短测试时长,影响了测试的覆盖度和真实性。

• 推高运维与硬件成本:为支撑存储和性能,需要不断进行分库分表或升级硬件,总拥有成本(TCO)居高不下。

因此,仿真数据平台迫切需要一种能够原生适配时序数据特性,具备超高吞吐写入、低成本存储、毫秒级时序查询能力的数据基础设施。

面对海量时序数据带来的挑战,专业时序数据库(Time-Series Database, TSDB) 应运而生。它专为处理带时间戳的数据而设计,采用列式存储、高效压缩、时序索引等核心技术,天然适合无人机仿真这类高频率、强时序的场景。作为面向 AIoT 场景的代表,KaiwuDB 不仅能提供上述时序数据库的核心优势,更以其独特的“分布式多模”架构,为复杂仿真系统提供了更全面的数据管理方案。

2.6 超越单纯存储:赋能仿真智能化

除了解决存储与性能的燃眉之急,KaiwuDB 的“多模”与“AIoT”基因还为其在无人机仿真中扮演更智能的角色提供了可能:

• 跨模关联分析:不仅能存储传感器时序流,还能在同一平台上管理飞控参数表(关系型)、飞行事件日志(文档型),并通过跨模计算能力进行关联分析。例如,将某一时刻的异常震动数据(时序)与当时执行的飞行动作指令(关系)进行即时关联分析。

• 面向未来的智能集成:其多模架构为集成高级分析框架奠定了基础。这意味着,未来可以基于 KaiwuDB 中沉淀的高质量仿真数据,更方便地训练和集成 AI 模型,用于飞控算法的异常检测、自主飞行策略优化等,真正实现数据驱动的仿真智能化。

综上所述,KaiwuDB 凭借其高性能写入、低成本存储、毫秒级查询、多模融合以及云边端协同的核心优势,不仅能够直接解决 PX4-ROS2 无人机仿真中由海量时序数据引发的核心痛点,更能为仿真测试平台提供面向未来的、支持智能分析的数据底座。

三、实践过程

3.1 准备工作:

KaiwuDB 社区版支持用户根据自己的需求来选择安装的方式,分为单节点部署和集群部署两种方式,这里以个人常用的单节点裸机部署的方式来进行部署与使用(此处列举简单步骤,具体过程及其余方式可参考 KaiwuDB 官方文档及相关论坛):

3.1.1 安装 KaiwuDB 社区版

本实践使用的操作系统为 Ubuntu22.04,安装包下载链接:gitee.com/kwdb/kwdb/r… ,并使用脚本部署 KaiwuDB。

① 登录待部署节点,编辑安装包目录下的 deploy.cfg 配置文件,设置安全模式、管理用户、服务端口等信息。

[global]
# Whether to turn on secure mode
secure_mode=tls
# Management KaiwuDB user
management_user=kaiwudb
# KaiwuDB cluster http port
rest_port=8080
# KaiwuDB service port
kaiwudb_port=26257
# KaiwuDB brpc port
brpc_port=27257
# KaiwuDB data directory
data_root=/var/lib/kaiwudb
# CPU usage[0-1]
# cpu=1

[local]
# local node configuration
node_addr=127.0.0.1

# [cluster]
# remote node addr,split by ','
# node_addr=127.0.0.2
# ssh info
# ssh_port=22
# ssh_user=admin

# [additional]
# IPs=127.0.0.3,127.0.0.4

② 执行单机部署安装命令。

./deploy.sh install --single

③ 检查配置无误后输入 Y 或 y,如需返回修改 deploy.cfg 配置文件,输入 N 或 n。

================= KaiwuDB Basic Info =================
Deploy Mode: bare-metal
Start Mode: single
RESTful Port: 8080
KaiwuDB Port: 26257
BRPC Port: 27257
Data Root: /var/lib/kaiwudb
Secure Mode: tls
CPU Usage Limit: unlimited
Local Node Address: 127.0.0.1
======================================================
Please confirm the installation information above(Y/n):

执行成功后,控制台输出以下信息:

[INSTALL COMPLETED]:KaiwuDB has been installed successfully! ...

④ 启动 KaiwuDB 节点。

./deploy.sh start

⑤ 使用以下任一方式查看节点状态:

**•在当前目录使用部署脚本

./deploy.sh status

**• 在任一目录下使用 systemctl 命令

systemctl status kaiwudb

**• 在任一目录下使用便捷脚本(推荐)

kw-status

⑥ (可选)配置 KaiwuDB 开机自启动。

配置 KaiwuDB 开机自启动后,如果系统重启,则自动启动 KaiwuDB。

systemctl enable kaiwudb

⑦ 部署完成截图如下:

3.1.2 使用 KaiwuDB 开发者中心连接 KaiwuDB

① KaiwuDB 开发者中心下载链接:gitee.com/kwdb/kwdb/r…

② 合并解压缩安装包,文件目录如下:

③ 双击运行 KaiwuDB 开发者中心应用程序。

④ 连接 KaiwuDB 数据库。

首次建立连接。首次建立连接或软件中的所有连接都被删除后,软件启动后会自动弹出新建连接向导,引导用户建立连接。

以下步骤以首次建立连接为例,说明如何连接数据库。

• 在创建新连接窗口,选择 KaiwuDB 驱动,然后单击下一步

• 在常规页签,设置主机、端口、数据库。根据需要选择数据库认证方式(默认为数据库原生认证方式),然后完成对应的用户、密码(如果采用非安全模式,则无需设置密码)等设置。

•(可选)单击测试链接,检查连接是否成功。连接成功后,将显示以下信息:

• 单击确定。

数据库导航区将自动更新,显示用户具有权限的数据库。

其他连接方式。其他情况下,如需创建连接,可以选择以下任一操作:

• 单击工具栏或数据库导航区工具栏中的新建连接按钮。

• 在菜单栏中,单击数据库,然后从下拉菜单中选择新建连接。

⑤ 创建时序数据库。

前提条件

用户是 admin 角色的成员。默认情况下,root 用户属于 admin 角色。

步骤

• 在数据库导航区,右键单击时序数据库,选择新建时序数据库

• 在创建时序数据库窗口,填写数据库名称,单击确定。

创建成功后,新建数据库将自动显示在数据库导航区内,继承 KaiwuDB 数据库系统的角色和用户设置。

3.1.3 连接数据库

大部分仿真代码基于 C++ 语言开发,KaiwuDB 提供了 PostgreSQL ODBC 连接 KaiwuDB 数据库的方法。

开放数据库连接(Open Database Connectivity,ODBC)是一种应用程序编程接口(Application Programming Interface,API),为应用程序访问数据库存储的信息提供了一种标准。ODBC 为异构数据库访问提供统一接口,实现异构数据库间的数据共享。使用 ODBC API 的应用程序可以访问任何符合 ODBC 标准的数据库中的数据,通常无需修改应用程序代码。

① 安装 PostgreSQL ODBC 驱动。

前提条件

• 下载软件依赖包。

libgcc(9.4.0 及以上版本)

postgresql-devel(10.5 及以上版本)

unixODBC-devel(2.3.7 及以上版本)

② 安装 KaiwuDB 数据库、配置数据库认证方式、创建数据库。

postgresql-devel

yum install postgresql-devel

unixODBC-devel

yum install unixODBC-devel

③ 安装 PostgreSQL ODBC 驱动下载并安装 PostgreSQL ODBC 驱动。

wget https://update.cs2c.com.cn/NS/V10/V10SP3-2403/os/adv/lic/base/x86_64/Packages/postgresql-odbc-13.00.0000-1.ky10.x86_64.rpm
sudo rpm -ivh postgresql-odbc-13.00.0000-1.ky10.x86_64.rpm

验证是否成功安装 unixODBC。

odbcinst -j

验证是否成功安装 PostgreSQL ODBC 驱动。

odbcinst -q -d

④ 配置 ODBC 数据源。

• 打开 ODBC 配置文件(odbc.ini)

sudo gedit /etc/odbc.ini

• 参数配置示例:

[kwdb]
Description = PostgreSQL
Driver = PostgreSQL Unicode
Trace = No
TraceFile = /tmp/pgodbc.log
Database = px4_simulation
Servername = 10.195.165.24
UserName = yfl
Password = yfl123456..
Port = 26257
SSLMode = require

验证 unixODBC 是否识别配置的数据源。

odbcinst -q -s

执行成功后,控制台输出以下信息:

[kwdb]

3.2 实践过程

仿真环境基于 Ubuntu 22.04 操作系统构建,以 PX4 v1.14 自动驾驶软件为核心,通过 Micro XRCE-DDS Agent 实现与 ROS 2 (Humble) 的实时通信。仿真可视化与动力学模型由 Gazebo classic 提供,整个系统的监控、参数调试与任务规划则通过 QGroundControl (QGC) 地面站完成。此处仿真环境搭建不做演示,可根据个人兴趣及配置参考链接 blog.csdn.net/weixin_5594…

3.2.1 数据库连接

① 数据库连接架构设计:

ODBC 采用经典的三层句柄架构,确保资源的有序管理:

// 数据库连接变量定义 - 三层句柄设计
private:
    // 第一层:环境句柄 - 管理ODBC环境
    SQLHENV env_{nullptr};
    
    // 第二层:连接句柄 - 管理数据库连接  
    SQLHDBC dbc_{nullptr};
    
    // 第三层:语句句柄 - 执行SQL语句
    SQLHSTMT stmt_{nullptr};
    
    // 数据库连接参数
    std::string dsn_{"kwdb"};
    std::string user_{"yfl"};
    std::string password_{"yfl123456.."};

②  配置驱动的连接管理:

// 构造函数中的参数配置
ParamCollectorNode::ParamCollectorNode(const rclcpp::NodeOptions& options)
    : Node("px4_param_collector", options) {
    
    // 声明可配置的数据库参数
    this->declare_parameter<std::string>("dsn""kwdb");
    this->declare_parameter<std::string>("user""yfl");
    this->declare_parameter<std::string>("password""yfl123456..");
    this->declare_parameter<bool>("enable_database"true);
    
    // 获取运行时参数值
    dsn_ = this->get_parameter("dsn").as_string();
    user_ = this->get_parameter("user").as_string();
    password_ = this->get_parameter("password").as_string();
    enable_database_ = this->get_parameter("enable_database").as_bool();
    
    // 根据配置决定是否启用数据库
    if (enable_database_) {
        initializeDatabase();  // 初始化数据库连接
    } else {
        RCLCPP_WARN(this->get_logger(), "数据库功能已禁用");
    }
}

③ 错误处理机制,包含详细的错误诊断和异常处理:

// 连接数据库时的错误处理
ret = SQLConnect(dbc_, 
                (SQLCHAR*)dsn_.c_str(), SQL_NTS,
                (SQLCHAR*)user_.c_str(), SQL_NTS,
                (SQLCHAR*)password_.c_str(), SQL_NTS);
 
if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
    // 获取详细的ODBC错误信息
    SQLCHAR sqlState[6] = {0};  // SQL状态码(5字符+终止符)
    SQLCHAR message[SQL_MAX_MESSAGE_LENGTH] = {0};
    SQLSMALLINT length = 0;
    SQLINTEGER nativeError = 0;
    
    // 提取诊断信息
    SQLGetDiagRec(SQL_HANDLE_DBC, dbc_, 1, sqlState, &nativeError, 
                 message, sizeof(message), &length);
    
    // 记录详细的错误信息
    RCLCPP_ERROR(this->get_logger(), 
                "数据库连接失败: %s (SQL状态: %s, 原生错误码: %d)", 
                message, sqlState, nativeError);
    
    enable_database_false;  // 优雅降级:禁用数据库功能
    return;
}
 
// 异常处理包装
try {
    // 数据库操作代码...
} catch (const std::exception& e) {
    RCLCPP_ERROR(this->get_logger(), 
                "数据库操作异常: %s。已禁用数据库功能。", 
                e.what());
    enable_database_false;
}

④ 资源生命周期管理:

严格按照 RAII 原则管理 ODBC 资源:

// 析构函数中的资源清理
ParamCollectorNode::~ParamCollectorNode() {
    stopCollection();
    
    // 逆序释放ODBC资源(语句→连接→环境)
    if (stmt_) {
        SQLFreeHandle(SQL_HANDLE_STMT, stmt_);
        RCLCPP_DEBUG(this->get_logger(), "SQL语句句柄已释放");
    }
    if (dbc_) {
        SQLDisconnect(dbc_);      // 断开连接
        SQLFreeHandle(SQL_HANDLE_DBC, dbc_);  // 释放连接句柄
        RCLCPP_DEBUG(this->get_logger(), "数据库连接已断开");
    }
    if (env_) {
        SQLFreeHandle(SQL_HANDLE_ENV, env_);  // 释放环境句柄
        RCLCPP_DEBUG(this->get_logger(), "SQL环境句柄已释放");
    }
    
    RCLCPP_INFO(this->get_logger(), "参数采集器已关闭");
}

⑤ 连接优化措施:

void ParamCollectorNode::initializeDatabase() {
    // 设置ODBC 3.0版本
    ret = SQLSetEnvAttr(env_, 
                       SQL_ATTR_ODBC_VERSION, 
                       (void*)SQL_OV_ODBC3, 
                       0);
    
    // 设置5秒连接超时
    SQLSetConnectAttr(dbc_, 
                     SQL_LOGIN_TIMEOUT, 
                     (SQLPOINTER)5, 
                     0);
    
    RCLCPP_INFO(this->get_logger(), 
               "ODBC 3.0已启用,连接超时设置为5秒");
}

3.2.2 表格设计与创建

• 创建表格:

①  参数调优表 (parameter_tuning)

作用:记录飞行控制器参数的变化历史,用于参数调优和性能分析。

普通列:

ts - 时间戳(主键)
parameter_name - 参数名称(如:MC_PITCH_P, MC_ROLL_P)
parameter_value - 参数值
tuning_session_id - 调优会话 ID
commit_hash - Git 提交哈希
tuner_name - 调优者姓名
performance_score - 调优后的性能评分

TAGS列(元数据):

simulation_id - 仿真 ID(主TAG)
airframe_type - 机架类型(如:iris)
controller_type - 控制器类型(如:px4)
test_scenario - 测试场景(如:ros2_simulation)

② 模式切换表 (mode_transition)

作用:记录飞行模式切换事件,分析模式切换时的飞行状态。

普通列:

ts - 时间戳
transition_id - 切换事件 ID
from_mode - 源模式(如:MANUAL, POSCTL)
to_mode - 目标模式
altitude - 切换时高度
velocity_ned_x/y/z - NED 坐标系速度分量
roll/pitch/yaw - 切换时姿态角

TAGS列:

simulation_id - 仿真 ID
airframe_type - 机架类型
transition_type - 切换类型(manual/auto/emergency)

③  性能指标表 (performance_metrics)

作用:记录飞行性能指标,用于评估控制系统性能。

普通列:

ts - 时间戳
roll_error/pitch_error/yaw_error - 姿态角误差
pos_error/vel_error - 位置/速度误差
control_effort - 控制努力度
performance_score - 综合性能评分

TAGS列:

simulation_id - 仿真 ID
airframe_type - 机架类型
controller_type - 控制器类型
metric_type - 指标类型(如:periodic_metrics)

④  飞行性能基准测试表 (flight_performance_baseline)

作用:记录详细的飞行性能基准数据,用于系统性能评估和对比。

普通列:

ts - 时间戳
position_error_xy/z - XY 平面和垂直方向位置误差
attitude_error_roll/pitch/yaw - 姿态角误差
velocity_response_time - 速度响应时间
max_acceleration - 最大加速度
hover_stability_index - 悬停稳定性指数

TAGS列:

simulation_id - 仿真 ID
drone_type - 无人机类型
flight_mode - 飞行模式
payload_config - 载荷配置

⑤ 振动监测表 (vibration_monitoring)

作用:监测无人机振动水平,检测机械故障和异常。

普通列:

ts - 时间戳
accel_rms_x/y/z - 加速度计 XYZ 轴的 RMS 值
gyro_rms_x/y/z - 陀螺仪 XYZ 轴的 RMS 值
dominant_frequency_hz - 主振动频率
vibration_severity_index - 振动严重性指数

TAGS列:

simulation_id - 仿真 ID
component_id - 组件标识(如:motor_1)
component_type - 组件类型(如:motor)

⑥ 通信质量监测表 (communication_quality)

作用:监控无人机与地面站之间的通信质量。

普通列:

ts - 时间戳
rssi_dbm - 接收信号强度指示(dBm)
packet_loss_rate - 丢包率
latency_ms - 延迟(毫秒)
jitter_ms - 抖动(毫秒)
retransmission_count - 重传次数
link_margin_db - 链路余量(dB)

TAGS列:

simulation_id - 仿真 ID
link_type - 链路类型(如:UART, Radio)
transmitter_id - 发射器标识
receiver_id - 接收器标识
distance_m - 通信距离(米)

⑦ PID 参数调优历史表 (pid_tuning_history)

作用:记录 PID 参数调优的历史记录和调优效果。

普通列:

ts - 时间戳
parameter_group - 参数组(如:Attitude, Position)
parameter_name - 具体参数名
old_value/new_value - 调优前后的参数值
overshoot_percentage - 超调百分比
settling_time_ms - 调节时间(毫秒)
steady_state_error - 稳态误差
rmse_error - 均方根误差
stability_margin - 稳定裕度

TAGS列:

simulation_id - 仿真 ID
tuning_session_id - 调优会话 ID
tuning_method - 调优方法(如:manual, Ziegler-Nichols)
flight_condition - 飞行条件

⑧ 环境适应能力表 (environmental_adaptation)

作用:评估无人机在不同环境条件下的适应能力。

普通列:

ts - 时间戳
wind_speed_estimated - 估计风速
wind_direction_estimated - 估计风向
turbulence_intensity - 湍流强度
temperature/humidity - 温湿度
air_density - 空气密度
position_hold_error - 位置保持误差
attitude_correction_roll/pitch - 姿态修正量
power_consumption_factor - 功耗因子

TAGS列:

simulation_id - 仿真 ID
environment_type - 环境类型(如:simulation, outdoor)
altitude_range - 高度范围

• 参数调优表 (parameter_tuning)创建代码示例:

// ========== 创建表函数 ==========
void ParamCollectorNode::createTables() {
    try {
        RCLCPP_INFO(this->get_logger(), "正在创建数据库表...");
        
        if (!stmt_) {
            RCLCPP_ERROR(this->get_logger(), "数据库语句句柄无效");
            return;
        }
        
        // 先创建TS数据库(如果需要)
        const char* createDatabaseSQL"CREATE TS DATABASE IF NOT EXISTS px4_simulation";
        SQLRETURN ret = SQLExecDirect(stmt_, (SQLCHAR*)createDatabaseSQL, SQL_NTS);
        if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO) {
            // 数据库可能已存在,忽略错误
            SQLCHAR sqlState[6] = {0};
            SQLCHAR message[SQL_MAX_MESSAGE_LENGTH] = {0};
            SQLSMALLINT length = 0;
            SQLINTEGER nativeError = 0;
            
            if (SQLGetDiagRec(SQL_HANDLE_STMT, stmt_, 1, sqlState, &nativeError, 
                             message, sizeof(message), &length) == SQL_SUCCESS) {
                RCLCPP_DEBUG(this->get_logger(), "数据库创建消息: %s", message);
            }
        }
        
        // 在每个表创建之间重置语句句柄状态
        SQLFreeStmt(stmt_, SQL_CLOSE);
        
        // ========== 创建参数调优表 ==========
        const char* createParamTableSQL = R"(
            CREATE TABLE px4_simulation.parameter_tuning (
                ts TIMESTAMP NOT NULL,
                parameter_name VARCHAR(100),
                parameter_value DOUBLE PRECISION,
                tuning_session_id VARCHAR(50),
                commit_hash VARCHAR(40),
                tuner_name VARCHAR(50),
                performance_score DOUBLE PRECISION
            ) TAGS (
                simulation_id INTEGER NOT NULL,
                airframe_type VARCHAR(50),
                controller_type VARCHAR(50),
                test_scenario VARCHAR(100)
            ) PRIMARY TAGS (simulation_id)
        )";
        
        ret = SQLExecDirect(stmt_, (SQLCHAR*)createParamTableSQL, SQL_NTS);
        // ... 错误处理代码 ...
        
        SQLFreeStmt(stmt_, SQL_CLOSE);
        
        // ========== 创建其他表 ==========

        
        
        RCLCPP_INFO(this->get_logger(), "所有数据库表创建完成");
        
    } catch (const std::exception& e) {
        RCLCPP_ERROR(this->get_logger(), "创建表异常: %s", e.what());
    }
}

3.2.3 数据采集、插入、保存

① 数据采集回调函数:

这些函数由 ROS2 订阅器触发,实时采集各种飞行数据:

② 定时器处理函数:

这些函数由定时器定期触发,执行计算和分析任务:

③ 数据处理流程:

ROS2 消息订阅 → 回调函数采集 → 数据缓存/处理 → 定时器分析 → 数据库保存

  1. 实时数据采集:ROS2 订阅器接收 PX4 飞控的各类消息

  2. 数据预处理
    • 姿态四元数转换为欧拉角
    • NED 坐标系转换(z 轴取负得到高度)
    • 数据有效性检查

  3. 缓存管理
    • 传感器数据缓冲区(限制大小)
    • 性能计算数据缓存

  4. 定时分析
    • 性能指标计算(位置误差、姿态误差、响应时间等)
    • 振动分析(RMS 值、主频、严重性指数)
    • 电池健康分析(SOH 计算)
    • 组件寿命预测(磨损率计算)

  5. 数据库保存
    • 检查数据库连接状态
    • 准备 SQL 语句和参数绑定
    • 执行插入操作
    • 错误处理和日志记录

④ 代码示例:

// ========== 参数采集相关的回调函数 ==========
 
// 1. 参数更新回调函数
void ParamCollectorNode::onParameterUpdate(const px4_msgs::msg::ParameterUpdate::SharedPtr msg) {
    // parameter_update消息只是一个通知,表明参数有更新
    // 但PX4的ROS2消息中不包含具体的参数名和值
    
    RCLCPP_DEBUG(this->get_logger(), 
                "参数更新通知接收时间戳: %lu", 
                msg->timestamp);
    
    // 记录通知事件
    if (enable_file_log_ && log_file_.is_open()) {
        rclcpp::Time timestamp = px4TimeToRosTime(msg->timestamp);
        log_file_ << std::fixed << std::setprecision(6)
                 << timestamp.seconds() << ", "
                 << "PARAM_UPDATE_NOTIFICATION" << ", "
                 << "0" << ", "
                 << "uXRCE-DDS" << std::endl;
    }
}
 
// ========== 其他回调函数 ==========
 
 
 
// ========== 定时器触发的处理函数 ==========
 
// 10. 性能计算函数(定时器触发)
void ParamCollectorNode::calculatePerformanceMetrics() {
    std::lock_guard<std::mutex> lock(data_mutex_);
    
    // 检查是否有足够的数据进行计算
    if (!vehicle_local_position_ || !vehicle_attitude_) {
        static int missing_count = 0;
        missing_count++;
        if (missing_count % 50 == 0) {
            RCLCPP_WARN(this->get_logger(), 
                "数据不完整无法计算性能: loc_pos=%d, attitude=%d",
                vehicle_local_position_ != nullptr,
                vehicle_attitude_ != nullptr);
        }
        return;
    }
    
    // 如果没有设定点数据,创建一个默认的设定点(当前位置)
    std::shared_ptr<px4_msgs::msg::TrajectorySetpoint> setpoint;
    if (!trajectory_setpoint_) {
        // 创建基于当前位置的设定点
        setpoint = std::make_shared<px4_msgs::msg::TrajectorySetpoint>();
        setpoint->position[0] = vehicle_local_position_->x;
        setpoint->position[1] = vehicle_local_position_->y;
        setpoint->position[2] = vehicle_local_position_->z;
        setpoint->velocity[0] = 0;
        setpoint->velocity[1] = 0;
        setpoint->velocity[2] = 0;
        trajectory_setpoint_ = setpoint;
        has_setpoint_ = true// 标记已有设定点
        RCLCPP_INFO(this->get_logger(), "使用当前位置作为设定点: [%.2f, %.2f, %.2f]", 
                   setpoint->position[0], setpoint->position[1], setpoint->position[2]);
    } else {
        setpoint = trajectory_setpoint_;
    }
    
    // 检查是否有设定点数据
    if (!has_setpoint_) {
        static int missing_setpoint_count = 0;
        missing_setpoint_count++;
        if (missing_setpoint_count % 10 == 0) {
            RCLCPP_WARN(this->get_logger(), "没有设定点数据,无法计算性能");
        }
        return;
    }
    
    // 使用更安全的方式检查时间,避免使用时间减法
    // 获取当前时间戳
    auto now_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
        std::chrono::system_clock::now().time_since_epoch()).count();
    
    // 简单的频率控制:每2秒计算一次性能指标
    static int64_t last_calc_time = 0;
    int64_t current_time = now_ms;
    
    if (last_calc_time == 0) {
        last_calc_time = current_time;
    }
    
    // 如果距离上次计算不足2秒,跳过
    if (current_time - last_calc_time < 2000) {
        return;
    }
    
    last_calc_time = current_time;
    
    // 计算性能指标
    try {
        auto metrics = performance_calculator_->calculate(
            *vehicle_local_position_, 
            *vehicle_attitude_,
            *setpoint);
        
        // 更新性能评分
        current_legacy_performance_.performance_score = 100.0 - 
            (metrics.position_error_xy * 10.0 + 
             metrics.position_error_z * 5.0 + 
             metrics.attitude_error_roll + 
             metrics.attitude_error_pitch + 
             metrics.attitude_error_yaw);
        
        // 确保评分在合理范围内
        current_legacy_performance_.performance_score = 
            std::max(0.0, std::min(100.0, current_legacy_performance_.performance_score));
        
        // 检查是否有有效的数据需要保存
        if (std::isfinite(metrics.position_error_xy) && 
            std::isfinite(metrics.position_error_z) &&
            metrics.position_error_xy < 1000.0 &&  // 合理范围检查
            metrics.position_error_z < 1000.0) {
            
            // 保存到数据库
            insertFlightPerformance(metrics);
            
            // 更新当前性能评分
            current_performance_ = metrics;
            
            RCLCPP_INFO(this->get_logger(), 
                       "性能计算完成: 位置误差XY=%.3fm, 位置误差Z=%.3fm, 响应时间=%.3fs, 评分=%.1f", 
                       metrics.position_error_xy, metrics.position_error_z, 
                       metrics.velocity_response_time, current_legacy_performance_.performance_score);
        } else {
            RCLCPP_WARN(this->get_logger(), "无效的性能指标数据,跳过保存: pos_xy=%.3f, pos_z=%.3f",
                       metrics.position_error_xy, metrics.position_error_z);
        }
        
    } catch (const std::exception& e) {
        RCLCPP_ERROR(this->get_logger(), "性能计算异常: %s", e.what());
    }
}
// ========== 其他处理函数 ==========

3.2.4 查询与分析

完成上述工作后,在进行 PX4 仿真时运行数据采集节点,即可收集所需数据,对于采集数据采用以下代码进行查询与分析:

• 核心监控与概览:查询仿真活跃度(查询 1)、性能评分分布(查询 4)及关键飞行指标(查询 5),快速掌握系统整体运行状态。

• 异常与安全告警:实时筛查电池、振动、性能等方面的紧急异常(查询 6),并对各类告警进行统计分析(查询 3),确保仿真安全与问题可追溯。

• 深度诊断与分析:深入分析飞行失败的根本原因(查询 7)和异常问题的处理效率(查询 9),助力问题定位与流程优化。

• 运维与健康管理:评估电池健康(查询 2)、预测组件寿命并生成维护计划(查询 8),实现预防性维护。

• 多维度性能评估:从通信链路质量(查询 11)、环境适应能力(查询 12)、参数调优历史(查询 13)及时间趋势(查询 10)等多个维度,全面评估无人机系统性能。

-- 1. 查询最近24小时各仿真ID的飞行性能记录数量TOP10(保持不变)
SELECT 
    simulation_id,
    COUNT(*as flight_count,
    AVG(performance_score) as avg_performance,
    MIN(ts) as first_flight_time,
    MAX(ts) as last_flight_time
FROM px4_simulation.performance_metrics
WHERE ts >= NOW() - INTERVAL '24 hours'
GROUP BY simulation_id
ORDER BY flight_count DESC
LIMIT 10;
 
-- 2. 查询不同电池的健康状态统计(保持不变)
SELECT
    battery_id,
    battery_type,
    COUNT(*as record_count,
    ROUND(AVG(state_of_health)::numeric2as avg_soh,
    ROUND(AVG(remaining_capacity)::numeric2as avg_remaining,
    ROUND(MIN(remaining_capacity)::numeric2as min_remaining
FROM px4_simulation.battery_health_monitoring
WHERE ts >= NOW() - INTERVAL '24 hours'
GROUP BY battery_id, battery_type
ORDER BY avg_soh DESC
LIMIT 100;
 
-- 3. 查询各仿真ID的告警/异常统计
WITH alert_data AS (
    -- 电池异常
    SELECT simulation_id, 'battery_low' as alert_type, COUNT(*as alert_count
    FROM px4_simulation.battery_health_monitoring
    WHERE remaining_capacity < 0.2
    AND ts >= NOW() - INTERVAL '24 hours'
    GROUP BY simulation_id
    
    UNION ALL
    
    -- 振动异常
    SELECT simulation_id, 'vibration_high' as alert_type, COUNT(*as alert_count
    FROM px4_simulation.vibration_monitoring
    WHERE vibration_severity_index > 0.5
    AND ts >= NOW() - INTERVAL '24 hours'
    GROUP BY simulation_id
    
    UNION ALL
    
    -- 姿态误差过大
    SELECT simulation_id, 'attitude_error' as alert_type, COUNT(*as alert_count
    FROM px4_simulation.performance_metrics
    WHERE roll_error > 10.0 OR pitch_error > 10.0 OR yaw_error > 10.0
    AND ts >= NOW() - INTERVAL '24 hours'
    GROUP BY simulation_id
)
SELECT
    simulation_id,
    alert_type,
    SUM(alert_count) as total_alerts
FROM alert_data
GROUP BY simulation_id, alert_type
ORDER BY total_alerts DESC
LIMIT 100;
 
-- 4. 查询飞行性能分数分布(修正:移除ORDER BY中的CASE,使用别名)
SELECT
    score_range,
    COUNT(*as count,
    ROUND(MIN(performance_score)::numeric2as min_score,
    ROUND(MAX(performance_score)::numeric2as max_score,
    ROUND(AVG(performance_score)::numeric2as avg_score
FROM (
    SELECT
        performance_score,
        CASE 
            WHEN performance_score >= 0 AND performance_score < 10 THEN '0-9'
            WHEN performance_score >= 10 AND performance_score < 20 THEN '10-19'
            WHEN performance_score >= 20 AND performance_score < 30 THEN '20-29'
            WHEN performance_score >= 30 AND performance_score < 40 THEN '30-39'
            WHEN performance_score >= 40 AND performance_score < 50 THEN '40-49'
            WHEN performance_score >= 50 AND performance_score < 60 THEN '50-59'
            WHEN performance_score >= 60 AND performance_score < 70 THEN '60-69'
            WHEN performance_score >= 70 AND performance_score < 80 THEN '70-79'
            WHEN performance_score >= 80 AND performance_score < 90 THEN '80-89'
            WHEN performance_score >= 90 AND performance_score <= 100 THEN '90-100'
            ELSE '其他'
        END as score_range
    FROM px4_simulation.performance_metrics
    WHERE ts >= NOW() - INTERVAL '24 hours'as subquery
GROUP BY score_range
ORDER BY 
    CASE score_range
        WHEN '0-9' THEN 0
        WHEN '10-19' THEN 1
        WHEN '20-29' THEN 2
        WHEN '30-39' THEN 3
        WHEN '40-49' THEN 4
        WHEN '50-59' THEN 5
        WHEN '60-69' THEN 6
        WHEN '70-79' THEN 7
        WHEN '80-89' THEN 8
        WHEN '90-100' THEN 9
        ELSE 10
    END
LIMIT 100;
 
 
-- 5. 查询关键性能指标统计(保持不变)
SELECT
    simulation_id,
    COUNT(*as total_flights,
    ROUND(AVG(position_error_xy)::numeric4as avg_position_error_xy,
    ROUND(AVG(position_error_z)::numeric4as avg_position_error_z,
    ROUND(AVG(attitude_error_roll)::numeric4as avg_attitude_error_roll,
    ROUND(AVG(attitude_error_pitch)::numeric4as avg_attitude_error_pitch,
    ROUND(AVG(attitude_error_yaw)::numeric4as avg_attitude_error_yaw,
    ROUND(AVG(velocity_response_time)::numeric4as avg_response_time,
    ROUND(AVG(max_acceleration)::numeric4as avg_max_acceleration
FROM px4_simulation.flight_performance_baseline
WHERE ts >= NOW() - INTERVAL '24 hours'
GROUP BY simulation_id
ORDER BY avg_position_error_xy DESC
LIMIT 10;
 
 
 
-- 6.查询未处理的高优先级异常(修正版)
WITH battery_critical AS (
    SELECT 
        'battery_critical' as alert_type,
        '紧急' as alert_level,
        '电池电量严重不足' as alert_description,
        battery_id,
        remaining_capacity,
        ts
    FROM px4_simulation.battery_health_monitoring
    WHERE remaining_capacity < 0.15
    AND ts >= NOW() - INTERVAL '1 hour'
),
vibration_critical AS (
    SELECT 
        'vibration_critical' as alert_type,
        '紧急' as alert_level,
        '振动严重异常' as alert_description,
        component_id,
        vibration_severity_index,
        ts
    FROM px4_simulation.vibration_monitoring
    WHERE vibration_severity_index > 0.8
    AND ts >= NOW() - INTERVAL '1 hour'
),
performance_critical AS (
    SELECT 
        'performance_critical' as alert_type,
        '高' as alert_level,
        '飞行性能严重下降' as alert_description,
        CAST(simulation_id AS VARCHARas simulation_id_str,
        performance_score,
        ts
    FROM px4_simulation.performance_metrics
    WHERE performance_score < 60
    AND ts >= NOW() - INTERVAL '1 hour'
),
-- 注意:从performance_metrics表查询时,需要确保列名和数据类型与其他子查询一致
performance_critical_fixed AS (
    SELECT
        alert_type,
        alert_level,
        alert_description,
        -- 使用空字符串作为占位符,因为其他表有battery_id/component_id,但performance_metrics没有
        '' as component_id,
        performance_score as severity_value,  -- 重命名以统一
        ts
    FROM performance_critical
),
-- 统一所有子查询的列
battery_critical_fixed AS (
    SELECT
        alert_type,
        alert_level,
        alert_description,
        battery_id as component_id,
        remaining_capacity as severity_value,
        ts
    FROM battery_critical
),
vibration_critical_fixed AS (
    SELECT
        alert_type,
        alert_level,
        alert_description,
        component_id,
        vibration_severity_index as severity_value,
        ts
    FROM vibration_critical
)
-- 合并所有警报
SELECT * FROM battery_critical_fixed
UNION ALL
SELECT * FROM vibration_critical_fixed
UNION ALL
SELECT * FROM performance_critical_fixed
ORDER BY 
    CASE alert_type
        WHEN 'battery_critical' THEN 1
        WHEN 'vibration_critical' THEN 2
        WHEN 'performance_critical' THEN 3
        ELSE 4
    END,
    severity_value,  -- 按严重程度排序(值越小/越大表示越严重)
    ts DESC;
 
 
 
 
-- 7. 查询飞行失败原因分析(修正:使用正确的表和列)
SELECT
    failure_reason,
    SUM(failure_count) as total_failures,
    ROUND(AVG(avg_performance)::numeric2as avg_failure_score,
    COUNT(DISTINCT simulation_id) as affected_simulations
FROM (
    SELECT
        simulation_id,
        CASE 
            -- 使用performance_metrics表中的列
            WHEN pos_error > 1.0 THEN '位置控制误差过大'
            WHEN vel_error > 0.5 THEN '速度控制误差过大'
            WHEN roll_error > 15.0 THEN '滚转角不稳定'
            WHEN pitch_error > 15.0 THEN '俯仰角不稳定'
            WHEN yaw_error > 20.0 THEN '偏航角不稳定'
            WHEN control_effort > 2.0 THEN '控制努力度过大'
            ELSE '其他原因'
        END as failure_reason,
        COUNT(*as failure_count,
        AVG(performance_score) as avg_performance
    FROM px4_simulation.performance_metrics  -- 修改为performance_metrics表
    WHERE performance_score < 70
    AND ts >= NOW() - INTERVAL '24 hours'
    GROUP BY simulation_id, pos_error, vel_error, roll_error, 
             pitch_error, yaw_error, control_effort
) as subquery
GROUP BY failure_reason
ORDER BY total_failures DESC
LIMIT 100;
 
 
-- 8. 查询无人机组件维护计划(保持不变)
SELECT
    component_id,
    component_type,
    total_operating_hours,
    predicted_remaining_life_hours,
    ROUND((total_operating_hours /
          (total_operating_hours + predicted_remaining_life_hours))::numeric4as wear_percentage,
    maintenance_recommendation,
    CASE 
        WHEN predicted_remaining_life_hours < 24 THEN '紧急维护'
        WHEN predicted_remaining_life_hours < 72 THEN '近期维护'
        ELSE '计划维护'
    END as maintenance_priority,
    CASE 
        WHEN predicted_remaining_life_hours < 24 THEN NOW()
        WHEN predicted_remaining_life_hours < 72 THEN NOW() + INTERVAL '7 days'
        ELSE NOW() + INTERVAL '30 days'
    END as suggested_maintenance_time
FROM px4_simulation.component_life_prediction
WHERE ts >= NOW() - INTERVAL '1 hour'
ORDER BY maintenance_priority, predicted_remaining_life_hours
LIMIT 100;
 
-- 9. 查询异常处理效率分析(修正:使用正确的列名)
WITH battery_exceptions AS (
    SELECT 
        'battery' as exception_type,
        b1.battery_id as component,
        b1.ts as detection_time,
        MIN(b2.ts) as resolution_time
    FROM px4_simulation.battery_health_monitoring b1
    LEFT JOIN px4_simulation.battery_health_monitoring b2 
        ON b1.battery_id = b2.battery_id 
        AND b2.ts > b1.ts 
        AND b2.remaining_capacity > b1.remaining_capacity
    WHERE b1.remaining_capacity < 0.2
    AND b1.ts >= NOW() - INTERVAL '24 hours'
    GROUP BY b1.battery_id, b1.ts
),
vibration_exceptions AS (
    SELECT 
        'vibration' as exception_type,
        v1.component_id as component,
        v1.ts as detection_time,
        MIN(v2.ts) as resolution_time
    FROM px4_simulation.vibration_monitoring v1
    LEFT JOIN px4_simulation.vibration_monitoring v2 
        ON v1.component_id = v2.component_id 
        AND v2.ts > v1.ts 
        AND v2.vibration_severity_index < v1.vibration_severity_index  -- 修正:使用正确的列名
    WHERE v1.vibration_severity_index > 0.5  -- 修正:使用正确的列名
    AND v1.ts >= NOW() - INTERVAL '24 hours'
    GROUP BY v1.component_id, v1.ts
),
all_exceptions AS (
    SELECT exception_type, component, detection_time, resolution_time
    FROM battery_exceptions
    UNION ALL
    SELECT exception_type, component, detection_time, resolution_time
    FROM vibration_exceptions
)
SELECT
    exception_type,
    COUNT(*as total_exceptions,
    COUNT(resolution_time) as handled_exceptions,
    ROUND(AVG(
        CASE 
            WHEN resolution_time IS NOT NULL 
            THEN EXTRACT(EPOCH FROM (resolution_time - detection_time))
            ELSE NULL 
        END
    )::numeric2as avg_handling_time_seconds,
    ROUND(MIN(
        CASE 
            WHEN resolution_time IS NOT NULL 
            THEN EXTRACT(EPOCH FROM (resolution_time - detection_time))
            ELSE NULL 
        END
    )::numeric2as min_handling_time,
    ROUND(MAX(
        CASE 
            WHEN resolution_time IS NOT NULL 
            THEN EXTRACT(EPOCH FROM (resolution_time - detection_time))
            ELSE NULL 
        END
    )::numeric2as max_handling_time,
    ROUND((COUNT(resolution_time)::float / NULLIF(COUNT(*), 0* 100)::numeric2as handling_rate_percent
FROM all_exceptions
GROUP BY exception_type
ORDER BY avg_handling_time_seconds
LIMIT 100;
 
-- 10. 查询测试记录的时间分布和性能趋势(保持不变)
-- 查询测试记录的时间分布和性能趋势(修正版)
SELECT
    DATE_TRUNC('hour', ts) as hour_bucket,
    COUNT(*as test_count,
    ROUND(AVG(hover_stability_index)::numeric2as avg_stability,
    ROUND(AVG(position_error_xy)::numeric4as avg_position_error_xy,
    ROUND(AVG(position_error_z)::numeric4as avg_position_error_z,
    ROUND(AVG(attitude_error_roll)::numeric4as avg_attitude_error_roll,
    ROUND(AVG(attitude_error_pitch)::numeric4as avg_attitude_error_pitch,
    ROUND(AVG(attitude_error_yaw)::numeric4as avg_attitude_error_yaw,
    ROUND(AVG(velocity_response_time)::numeric4as avg_response_time
FROM px4_simulation.flight_performance_baseline
WHERE ts >= NOW() - INTERVAL '24 hours'
GROUP BY DATE_TRUNC('hour', ts)
ORDER BY hour_bucket DESC
LIMIT 100;
 
 
-- 11. 查询通信质量分析(保持不变)
SELECT
    simulation_id,
    link_type,
    COUNT(*as sample_count,
    ROUND(AVG(rssi_dbm)::numeric2as avg_rssi_dbm,
    ROUND(AVG(packet_loss_rate)::numeric4as avg_packet_loss,
    ROUND(AVG(latency_ms)::numeric2as avg_latency_ms,
    ROUND(AVG(jitter_ms)::numeric2as avg_jitter_ms,
    ROUND(MIN(link_margin_db)::numeric2as min_link_margin,
    ROUND(MAX(link_margin_db)::numeric2as max_link_margin,
    SUM(CASE WHEN packet_loss_rate > 0.1 THEN 1 ELSE 0 ENDas high_loss_samples
FROM px4_simulation.communication_quality
WHERE ts >= NOW() - INTERVAL '24 hours'
GROUP BY simulation_id, link_type
ORDER BY avg_packet_loss DESC
LIMIT 100;
 
-- 12. 查询环境适应能力分析(保持不变)
SELECT
    simulation_id,
    environment_type,
    COUNT(*as sample_count,
    ROUND(AVG(wind_speed_estimated)::numeric2as avg_wind_speed,
    ROUND(AVG(turbulence_intensity)::numeric4as avg_turbulence,
    ROUND(AVG(position_hold_error)::numeric4as avg_position_error,
    ROUND(AVG(power_consumption_factor)::numeric4as avg_power_factor,
    ROUND(AVG(attitude_correction_roll)::numeric4as avg_roll_correction,
    ROUND(AVG(attitude_correction_pitch)::numeric4as avg_pitch_correction
FROM px4_simulation.environmental_adaptation
WHERE ts >= NOW() - INTERVAL '24 hours'
GROUP BY simulation_id, environment_type
ORDER BY avg_position_error DESC
LIMIT 100;
 
-- 13. 查询参数调优历史分析(修正:添加GROUP BY中的performance_score或使用聚合函数)
SELECT
    simulation_id,
    parameter_name,
    COUNT(*as tuning_count,
    ROUND(MIN(parameter_value)::numeric4as min_value,
    ROUND(MAX(parameter_value)::numeric4as max_value,
    ROUND(AVG(parameter_value)::numeric4as avg_value,
    ROUND(AVG(performance_score)::numeric2as avg_performance,
    tuner_name,
    MAX(ts) as last_tuning_time
FROM px4_simulation.parameter_tuning
WHERE ts >= NOW() - INTERVAL '24 hours'
GROUP BY simulation_id, parameter_name, tuner_name
ORDER BY tuning_count DESC
LIMIT 100;
 

在 KaiwuDB 开发者中心中执行上述命令,结果如下,从执行日志中可以看出,每条查询语句都在毫秒级,查询任务总用时不超过 2 秒,相比于传统的关系数据库性能显著提升。

3.3 数据库监控

KaiwuDB 集成 Prometheus 和 Grafana 开源组件监控数据库状态。

3.3.1 部署 Prometheus

Prometheus 是一款开源的系统监控和告警平台,用于采集和存储 KaiwuDB 集群的监控和性能指标信息。Grafana 是一款开源的数据可视化工具,可以从多种数据源获取数据,并在数据面板中展示所有数据。Grafana 读取 KaiwuDB 集群的指标数据,以可视化方式展示数据库的集群节点状态、监控指标。

① 下载:Prometheus下载链接 prometheus.io/download/

以下示例解压缩 Prometheus v3.5.1 安装包。

tar -zxvf prometheus-3.5.1.linux-amd64.tar.gz

在 Prometheus-3.5.1.linux-amd64 目录下创建 rules 子目录。

Rules 文件下载链接:gitee.com/kwdb/kwdb/t…

② 规则文件配置:

下载 Prometheus 告警规则和聚合规则配置文件并将其放置在 rules 子目录。

KaiwuDB 在 monitoring/rules 目录下提供 alerts.rules.yml 和 aggregation.rules.yml 文件。有关告警规则配置项和聚合规则配置项的详细信息,参见 Prometheus 告警规则和 Prometheus 聚合规则。

• alerts.rules.yml:告警规则配置文件。

• aggregation.rules.yml:聚合规则配置文件

cd prometheus-2.53.0.linux-amd64 &&
sudo gedit prometheus.yml

以下是 3.5.1 配置文件示例。用户可以根据版本及实际部署情况,调整配置参数及取值。

global:
  scrape_interval: 10s
  evaluation_interval: 10s

rule_files:
  - "rules/alerts.rules.yml"
  - "rules/aggregation.rules.yml"

scrape_configs:
  - job_name: 'kaiwudb'
    metrics_path: '/_status/vars'
    
    # 根据实际情况选择http或https
    scheme: 'http'  # 如果KaiwuDB开启了TLS则改为https
    
    # 如果是https,需要配置TLS(可选)
    tls_config:
      insecure_skip_verify: true  # 自签名证书时设为true
    
    # 可添加认证信息(如果KaiwuDB需要认证)
    # basic_auth:
    #   username: 'prometheus'
    #   password: 'your-password'
    
    static_configs:
      - targets: 
          - '10.0.1.100:8080'  # KaiwuDB节点1
          - '10.0.1.101:8080'  # KaiwuDB节点2
          - '10.0.1.102:8080'  # KaiwuDB节点3
        labels:
          cluster: 'production-kaiwudb-cluster'
          environment: 'production'
          region: 'us-east-1'
    
    # 可以添加重试和超时设置
    scrape_timeout: 5s
    honor_labels: true

③ 启动 Prometheus 服务。

./prometheus --config.file=prometheus.yml

默认情况下,Prometheus 的启动端口是 9090。用户可以按需修改 Prometheus 的启动端口。

④ 登录 Prometheus。

默认情况下,Prometheus 的登录地址是 http://localhost:9090。启动 Prometheus 服务后,用户即可通过该地址访问 Prometheus。

3.3.2 部署 Grafana

① 下载 Grafana 安装包并解压缩到本地目录。

以下示例下载 Grafana v11.1.0 安装包。

wget https://dl.grafana.com/enterprise/release/grafana-enterprise-11.1.0.linux-amd64.tar.gz
tar -zxvf grafana-enterprise-11.1.0.linux-amd64.tar.gz

② 启动 Grafana 服务。

cd grafana-v11.1.0/bin
./grafana-server

3.3.3 配置 Grafana

添加 Prometheus 数据源。

① 登录 Grafana。

默认情况下,Grafana 的登录地址是 http://localhost:3000。用户可以使用默认的用户名和密码(均为 admin)登录 Grafana。

• 在 Grafana 左侧边栏,单击Connections > Data sources。

• 在 Data sources 窗口,单击 Add data source,然后选择 Prometheus。

• 配置 Prometheus 的相关信息。

    • Name:数据源的名称。

    • URL:Prometheus Server 的 IP 地址。

    • 按需配置其它字段。

• 单击 **Save & test**,保存 Prometheus 数据源。**

② 导入 Grafana 面板

默认情况下,KaiwuDB 在 monitoring/grafana-dashboards 目录下提供以下指标面板模板。用户将指标面板模板(.json 格式)导入 Grafana 后,即可监控 KaiwuDB 集群。

• 概览:展示集群和节点的关键指标。

• 硬件:展示硬件相关的监控指标。

• 运行时:展示运行时相关的监控指标。

• SQL:展示 SQL 相关的监控指标。

• 存储:展示存储相关的监控指标。

• 副本:展示副本相关的监控指标。

• *分布式:展示分布式相关的监控指标。

• 队列:展示队列相关的监控指标。

• 慢查询:展示慢查询相关的监控指标。

如需导入 KaiwuDB 指标面板,遵循以下步骤。

• 在 Grafana 左侧边栏,单击 Dashboards

• 在 Dashboard 窗口,单击 New,然后从下列菜单中选择 Import

• 上传目标面板文件,然后单击 Load

说明

默认情况下,KaiwuDB 在 monitoring/grafana-dashboards 目录下提供以下指标模板。KaiwuDB 各指标面板对应的文件名如下所示:

• 概览:1.KaiwuDB_Console_Overview.json

• 硬件:2.KaiwuDB_Console_Hardware.json

• 运行时:3.KaiwuDB_Console_Runtime.json

• SQL:4.KaiwuDB_Console_SQL.json

• 存储:5.KaiwuDB_Console_Storage.json

• 副本:6.KaiwuDB_Console_Replication.json

• 分布式:7.KaiwuDB_Console_Distribution.json

• 队列:8.KaiwuDB_Console_Queue.json

• 慢查询:9.KaiwuDB_Console_Slow_Query.json

(可选)在 Grafana 左侧边栏,单击 Dashboards,然后选择任一指标模板,即可查看监控指标数据。

③ 使用 Grafana 查看指标数据

Grafana 支持查看 KaiwuDB 集群及各个节点的监控指标,包括指标概览、硬件指标、运行指标、SQL 指标、存储指标、副本指标、分布式指标、队列指标和慢查询指标。

总结

基于上述在 PX4-ROS2 无人机仿真场景中的实践,可以清晰地看到,面对仿真产生的海量、高并发、强时序数据流,传统关系型数据库在写入性能、查询效率、存储成本和数据模型适配性上均存在显著瓶颈,已成为制约研发效率与仿真规模的关键因素。

而分布式多模数据库 KaiwuDB 则以其专为时序和 AIoT 场景设计的架构,提供了一整套精准、高效的解决方案,其核心优势与应用价值体现在:

卓越的性能表现,保障仿真实时性:凭借其时序引擎,KaiwuDB 轻松实现每秒百万级数据点的高性能写入与毫秒级查询响应。这确保了在多机、高频仿真中,数据能够被实时、不间断地持久化,同时支持工程师对任意历史片段进行即时回溯与分析,彻底告别了因数据堆积而导致的查询等待,极大提升了调试与验证效率。

极高的存储效率,降低长期运维成本:通过列式存储与高效压缩算法,KaiwuDB 为仿真时序数据提供了高达** 5-30 倍的压缩比。这意味着在存储相同规模历史数据时,可节省超过 90% 的存储空间**,显著降低了数据的长期归档与管理成本,使得保存全生命周期仿真数据以供回归测试和趋势分析变得经济可行。

多模融合能力,支撑复杂业务分析:KaiwuDB 超越了单一时序数据库的范畴。其多模架构允许在同一数据库内无缝管理时序数据(如传感器流)、关系型数据(如飞控参数表)和事件数据(如模式切换日志)。通过跨模计算,可以轻松实现如“将特定异常震动与当时飞控指令关联分析”的复杂查询,为系统级问题定位和性能优化提供了强大支持。

强大的生态兼容与云边端协同:兼容 PostgreSQL 协议使得现有基于 SQL 的工具链和开发经验可以平滑迁移,降低了学习与集成门槛。同时,其云边端一体化部署能力,为构建从仿真边缘节点(如运行 ROS2 的工控机)到中心云端的统一数据管道奠定了基础,支持数据的集中管理、分布式分析与协同处理。

面向未来的数据智能底座:KaiwuDB 的定位不仅是高性能存储,更是**“面向 AIoT 场景”**。它为仿真数据平台沉淀的高质量、结构化数据提供了理想的承载与分析环境。基于此,可以更便捷地集成机器学习框架,进行飞控算法异常检测、自主飞行策略优化或数字孪生模型训练,从而推动无人机仿真从“测试验证”走向“数据驱动研发”与“智能化分析”的新阶段。

总结而言,在 PX4-ROS2 无人机仿真体系中引入 KaiwuDB,并非简单的数据库替换,而是对仿真数据基础设施的一次全面升级。 它精准地解决了海量时序数据带来的核心痛点,并通过多模融合与高性能分析能力,将仿真数据从存档文件转变为可实时洞察、可深度挖掘、可驱动决策的核心资产,为无人机系统的快速迭代、算法优化与智能化演进构建了坚实、高效的数据底座。