实例教程:BBDB为AHRS算法开发提供完善的支撑环境(上)
1. 概述
本教程将结合程序代码及CSS控制站工程,讲述如何基于PH47代码框架的BBDB固件版本,为开发自己的AHRS姿态解算算法提供完善支撑环境,以及数据分析手段。
BBDB固件已内置了一套姿态解算算法。对于需要进行AHRS算法开发研究的开发者,可在BBDB已有AHRS算法基础上另行增加自己的算法进行性能比对,或使用自己的算法模块替换预置AHRS解算模块。
本节教程就在PH47代码框架的应用层实现AHRS算法开发环境,以及如何使用CSS控制站工程显示、分析、记录相关算法数据进行讲述。关于在算法层实现对AHRS算法的支撑,以及如何使用自己的AHRS算法替换BBDB预置的算法模块,将在下节教程讲述。
2. BBDB基础上为算法实现提供支撑方式一:应用层实现
2.1. 代码初始化与解算代码的实现位置
通常我们的算法都以模块化方式实现,既方便程序设计,又利于将来代码复用。算法模块能够以一个或多个C++函数的方式进行组织,也能够通过封装为C++类的方式进行实现。
基于PH47代码框架BBDB固件版本,用户的AHRS算法模块既可以简便的在的应用层(Application)实现;又可以进行更好的封装后在算法层(Algorithms)中实现。本节就首先对在应用层的实现方式进行讲解。
此方式下,用户的AHRS代码可在BBDB的应用层实现类CAppBBDB(\DevStudio\Application\App_BBDB.cpp/.h)中实现或调用。具体如下:
| 函数名称 | AHRS代码实现 |
| CAppBBDB. Init() | PH47框架初始化函数。 用于AHRS算法初始化功能实现。本函数在PH47代码框架初始化过程调用,可安全使用PH47的各项调试或其他功能函数。 注意: 在本函数调用之前快速线程函数如FastThread_1000Hz(), NormalThread_250Hz() 已经开始运行,所以位于上述函数中的代码应对初始化完成情况进行判断(借助标志变量core.blSysInitCompleted或全局状态变量S_INIT_COMPLETE) |
| CAppBBDB. FastThread_1000Hz() | PH47框架快速线程函数。 可用于AHRS解算代码的具体实现。根据控制板mcu不同型号,调用频率在400-500hz间波动。可通过相关函数获取两次调用间的时间间隔(后面详述)。 注意: AHRS解算时间必须小于函数调用周期(2-2.5ms),事实上,由于在该线程中还有框架自身的函数运行,故实际解算时间要更短。 关于本线程全部的运行耗时,可通过在调试串口输入”freemem;”命令进行查询。 |
| CAppBBDB. NormalThread_250Hz() | PH47框架普通线程函数。 可用于AHRS解算代码的具体实现。调用频率固定为250hz。 关于对AHRS运行时间的要求与FastThread_1000Hz()函数相同。 |
2.2. AHRS相关的输入数据
AHRS算法部分以模块化方式实现,那么模块的输入输出数据交换就是必要的一个环节。PH47代码框架以数据总线、全局状态变量、机载控制参数3种不同的形式将相关的数据提供给各功能模块使用。
除了框架提供的数据之外,用户还可以根据自己的需要,向数据总线、全局状态变量、机载控制参数中加入自己定制的数据。
与AHRS解算模块的相关输入数据如下(如无特殊说明,数据单位均为国际标准单位,以下均同):
| IMU相关数据 | 数据类型 | 数据说明 |
| IMU.GyrRaw | Vector3f | 陀螺原始角速度(x,y.z三轴,下同) |
| IMU.GyrFilted | Vector3f | 陀螺滤波后角速度 |
| IMU.AccRaw | Vector3f | 加速度计原始加速度 |
| IMU.AccFilted | Vector3f | 加速度计滤波后加速度 |
| IMU.MagRaw | Vector3f | 磁强计原始磁强(无单位) |
| IMU.MagAvg | Vector3f | 磁强计平均磁强(无单位) |
| IMU.Temp | IMU_TEMPERATURE | IMU温度数据(Gyro温度x3,A储存温度x1) |
| GPS相关数据 | 数据类型 | 数据说明 |
| sGps.fix_type | uint8_t_ | gps锁定类型(无单位) |
| sGps.sat_num | uint8_t_ | 卫星数量(无单位) |
| sGps.loc | Location | 当前定位数据, Location.lng,Location.lat单位为1e-7degree, Location.alt单位为mm |
| sGps.vel | Vector3f | 当前gps 3维速度矢量 |
| sGps.eph | float | 垂直定位精度 |
| sGps.epv | float | 水平定位精度 |
| sGps.groundspeed | float | 地速 |
| sGps.speed_acc | float | 速度精度 |
| sGps.p_dop | float | 位置定位精度 |
| sGps.cog | float | gps航迹角 |
| sGps.time_ms | uint32_t | |
| sGps. locked_time_ms | uint32_t | |
| sGps.locked_dt | float |
| 全局状态变量 | 数据说明 |
| S_IMU_ENABLE | IMU初始化工作完成标志,true表示初始化成功 |
| S_MAG_ENBALE | 磁强计初始化标志,true表示初始化完成 |
| S_COMPASS_BE_USED | 磁强计数据有效标志 true表示根据磁强数据计算的航向角误差在允许范围内 |
2.3. AHRS解算后输出数据
经AHRS解算后的数据同样可以返回到数据总线、全局状态变量、机载控制参数当中。框架中与AHRS输出相关的预置数据如下:
| AHRS输出数据 | 数据类型 | 数据说明 |
| AngleRate | Vector3f | 机体平台三轴转动角速率 |
| AccelBody | Vector3f | 机体坐标系三轴加速度 |
| AccelNed | Vector3f | North-East-Down坐标系三轴加速度 |
| sAhrs.Roll | float | 机体滚转角 |
| sAhrs.Pitch | float | 机体俯仰角 |
| sAhrs.Yaw | float | 机体航向角 |
| sAhrs.SinRoll | float | 滚转角正弦值 |
| sAhrs.SinPitch | float | 俯仰角正弦值 |
| sAhrs.SinYaw | float | 航向角正弦值 |
| sAhrs.CosRoll | float | 滚转角余弦值 |
| sAhrs.CosPitch | float | 俯仰角余弦值 |
| sAhrs.CosYaw | float | 航向角余弦值 |
| sAhrs.dcm | float | 方向余弦矩阵 |
2.4. 解算频率,及对数据时间间隔的使用
AHRS解算循环当中的时间间隔数据对算法实现非常重要,如前文所述,CAppBBDB.FastThread_1000Hz()函数运行频率是一个在1000-1500hz之间波动的一个数值(波动范围根据控制板MCU不同而不同),但实际上,在基于PH47框架的程序开发中:
强烈不推荐使用循环函数的设计运行频率来计算循环时间间隔。
建议使用实际测量的循环时间间隔,或者使用总线数据设置时间间隔来作为算法设计所需要的时间间隔。框架提供了一个简单的类CLoopDt用于循环时间间隔的测量,示例代码如下:
// Step1:在.h文件中定义CloopDt类对象 _QuatDt
CloopDt _QuatDt;
// Step2 在.cpp文件的循环代码中获取当前时刻距离上一次调用本函数时刻的
// 时间差,以ms为单位,精确到小数点后三位
// 如果过获取的dt为0,则返回 false
if( _QuatDt.GetLocalDt_ms(_fDt_Sec) )_fDt_Sec /= 1000.0f; // 将获取时间间隔从ms转换为sec
通过上述代码即可精确的获取循环运行时间间隔。通过此方式获取的时间间隔,一般称为本地时间间隔。与之相对应的是原始时间间隔。
原始时间间隔是指通过总线数据的时间戳计算获取的时间间隔,因为该时间戳是总线数据被设置时刻记录的时间(精确到us),故总线数据两次被设置之间的间隔就称为原始时间间隔。通过简单函数调用就可获得总线数据的原始时间间隔:
// 获取陀螺仪原始角速度的设置时间间隔
float fRawGyrDt_ms = bus.sImu.GyrRaw.GetDt2Prev_us()/1000.0f;
在AHRS算法实现过程中,应当仔细和小心的确定在什么时候使用本地时间间隔,在什么时候该使用原始时间间隔。
2.5. 应用层代码框架示例
若我们在应用层进行AHRS算法模块的开发,那么该模块的初始化函数为InitMyAHRS(),在CAppBBDB.Init() 函数中被调用。
姿态解算函数为UpdateMyAHRS(),在CAppBBDB.FastThread_1000Hz(),或是在CAppBBDB.NormalThread_250Hz()函数中被调用。
在App_BBDB.h文件中对函数进行声明。当然,AHRS解算必须的一些成员变量也可以在.h文件中进行声明。
// AHRS 应用层框架代码
void InitMyAHRS();
bool UpdateMyAHRS();// AHRS解算所需的成员变量声明…
// ...
在App_BBDB.cpp文件中实现函数InitMyAHRS()及UpdateMyAHRS():
通过上述代码框架框架实现,用户即可在上述框架中实现自己的姿态解算算法,并将于姿态相关的各种数据,通过如下数据帧下行发送至CSS进行显示、记录、回放,以及后续数据分析使用:
| 数据帧 (mavlink message) | 帧id (msg id) | 包含字段 (Field of mavlink message) |
| GPS_RAW_INT | 0x18 | lat, Lon, alt eph, epv, vel, cog, fix_type, satellites_visi |
| RAW_IMU | 0x1b | xacc, yacc, zacc, xgyro, ygyro, zgyro, xmag, ymag, zmag |
| ATTITUDE | 0x1e | roll, pitch, yaw, rollspeed, pitchspeed, yawspeed |
| PILOT_DBG_1 | 0xf7 | fvalue_0 – fvalue_9 |
| PILOT_DBG_2 | 0xf8 | fvalue_0 – fvalue_9 |
| 备注:
| ||
void CAppBBDB::InitMyAHRS()
{/******************************************************AHRS 算法初始化函数本函数在系统初始化过程中调用函数调用顺序: CThreadCtrl_BBDB.InitAfterThreadStart() ==> CAppBBDB.Init()==> CAppBBDB.InitMyAHRS()******************************************************//******************************************************* AHRS 初始化过程实现 ...******************************************************/
}bool CAppBBDB::UpdateMyAHRS()
{/******************************************************AHRS 算法实现函数本函数在 CAppBBDB.FastThread_1000Hz() 或 CAppBBDB.NormalThread_250Hz() 函数中被调用******************************************************/// 若系统初始化(即包含了 InitMyAHRS() 初始化)还未完成,则暂不进行 AHRS 解算过程if(core.blSysInitCompleted == false)return false;// 获取陀螺仪原始角速度的设置时间间隔// imu 当中的 AccRaw, GyrRaw 数据在 CThreadCtrl_BBDB.IdleLoop() 函数中被近乎同时的进行设置(< 1us)// 故可用 AccRaw, GyrRaw 当中的任一原始时间间隔作为这两个数据被设置的时间间隔 float fDt_ms_RawGyr = bus.sImu.GyrRaw.GetDt2Prev_us() / 1000.0f;Vector3f vGyrRaw = bus.sImu.GyrRaw.Get(); // 获取陀螺仪原始角速度Vector3f vAccRaw = bus.sImu.AccRaw.Get(); // 获取加速度计原始数据Vector3f vMagAvg = bus.sImu.MagAvg.Get(); // 获取磁强计测量的平均磁强float fCog = bus.sGps.cog.Get(); // 获取GPS计算的飞行航迹角// 其他 AHRS 解算所需数据 ...bool blImuInit = gGetStatus(S_IMU_ENABLE); // 获取 imu 初始化完成标志// 其他全局状态变量获取 ...float fGyrLPF = core.para.Get(P_SENSOR_GYR_LPF_Hz); // 获取陀螺仪低通滤波器截止频率// 其他机载控制参数获取 .../******************************************************* ...* AHRS 解算过程实现 ...* ...******************************************************//******************************************************* AHRS 输出数据设置 ...bus.sAhrs.dcm.Set(_dcm);bus.sAhrs.Roll.Set(_euler.x);...******************************************************//******************************************************* 如有必要, 对调试数据赋值 ...bus.arDbg_1[0].Set(_fDbg_x);bus.arDbg_1[6].Set(_fDbg_y);bus.arDbg_2[1].Set(_fDbg_z);******************************************************//******************************************************* 如有必要, 根据 AHRS 解算结果设置全局变量 ...gSetStatus(S_MY_STATUS);******************************************************/return true;
}
3. 使用CSS的BBDB控制站工程提进行观测分析
在BBDB固件的支持下,AHRS算法设计中的相关数据通过前述预设数据帧下行发送到地面控制站(CSS),并通过对应的控制站工程CssDemo_BBDB进行解析、显示、记录,以及后续的分析。关于CSS的使用方法,可参见《Control Station Studio控制站开发平台概述》。以下仅简单讲述:
3.1. 接收并记录BBP控制板下行数据
选择“自动记录”模式后点击“GCS启动”启动CSS,开始接收、解析、显示、记录来自于BBP控制板的下行数据。

3.2. 使用仪表控件对AHRS数据进行显示
控制站工程CssDemo_BBDB能够以数字、姿态显示仪表、波形图等多种方式显示与AHRS相关的各种数据。若现有控件不足以满足数据观测需求,可对已有控件的显示属性进行修改,或增加新的控件(如增加新的波形图控件叠加显示多路数据)。

3.3. 对AHRS数据进行初步分析
将CSS切换到“数据分析”页面,打开先前的数据记录文件,选择一个或多个需要进行分析的数据(数据帧中字段)绘制数据曲线供分析使用。在分析过程中,可通过设定曲线的原点、缩放比例等方式进行辅助。

3.4. 导出记录数供进一步分析
切换“数据分析”页面,点击“导出txt”按钮,打开对应的数据记录文件,选择需要包含导出数据的数据帧,选择确定后即可将控制板记录数据导出为txt格式供进一步分析使用。

更多内容见CSDN博客专栏:无人机飞控
https://blog.csdn.net/ss15/category_9690939.html?spm=1001.2014.3001.5482相关资源:PH47: PH47运动控制代码框架.
https://gitee.com/ss15/ph47
相关文章:
实例教程:BBDB为AHRS算法开发提供完善的支撑环境(上)
1. 概述 本教程将结合程序代码及CSS控制站工程,讲述如何基于PH47代码框架的BBDB固件版本,为开发自己的AHRS姿态解算算法提供完善支撑环境,以及数据分析手段。 BBDB固件已内置了一套姿态解算算法。对于需要进行AHRS算法开发研究的开发者&…...
k8s搭建1.23版本
文章目录 1、前期准备1、关闭防火墙和selinux2、关闭交换分区3、修改主机名和免密登录4、内核参数5、安装docker6、安装k8s源 2、安装1、安装k8s软件包2、初始化k8s3、安装calico网络插件4、检查 1、前期准备 以下操作所有主机都要运行的 1、关闭防火墙和selinux systemctl …...
【MYSQL】分库分表
一、什么是分库分表 分库分表就是指在一个数据库在存储数据过大,或者一个表存储数据过多的情况下,为了提高数据存储的可持续性,查询数据的性能而进行的将单一库或者表分成多个库,表使用。 二、为什么要分库分表 分库分表其实是两…...
CSS基础知识05(弹性盒子、布局详解,动画,3D转换,calc)
目录 0、弹性盒子、布局 0.1.弹性盒子的基本概念 0.2.弹性盒子的主轴和交叉轴 0.3.弹性盒子的属性 flex-direction row row-reverse column column-reverse flex-wrap nowrap wrap wrap-reverse flex-dirction和flex-wrap的组合简写模式 justify-content flex-s…...
常见error集合
Cannot use import statement outside a module 原因:在commonJS中用了es6的语法,import。分析: 一般我们的运行环境按照模块化标准来分,可以分为es6和commonJS两种,在es6中引入模块用import,在commonJS中…...
建筑施工特种作业人员安全生产知识试题
1.因生产安全事故受损害的从业人员,除依法享有工伤社会保验外。依照有关民事法律尚有获得赔偿的权利,有权向本单位提出(D)。 A.任何要求 B.保护要求 C.辞职要求 D.赔偿要求 2.为了加强建设工程安全监督管理&…...
2024140读书笔记|《作家榜名著:生如夏花·泰戈尔经典诗选》——你从世界的生命的溪流浮泛而下,终于停泊在我的心头
2024140读书笔记|《作家榜名著:生如夏花泰戈尔经典诗选》——你从世界的生命的溪流浮泛而下,终于停泊在我的心头 《作家榜名著:生如夏花泰戈尔经典诗选》[印]泰戈尔,郑振铎译,泰戈尔的诗有的清丽,有的童真&…...
JSP是如何被执行的?
JSP(JavaServer Pages)是一种用于构建动态Web页面的技术,它允许开发者在HTML、XML或其他文本格式的文档中嵌入Java代码。JSP页面在服务器端被编译成Servlet,然后由Servlet容器(如Tomcat)执行。以下是JSP页面…...
用python中的tkinter包实现进度条
python中的tkinter包是一种常见的设计程序的GUI界面用的包。本文主要介绍这里面的一个组件:进度条(Progressbar)。Tkinter Progressbar里面对进度条组件已经做了一定的介绍,但比较抽象。本文以另一种方式介绍这个组件及其常用用法…...
oneplus3t-lineageos-16.1编译-android9
本仓库开发已经完毕,使用请直接从4.2开始 ** LineageOS16_oneplus3t仓库群克隆 、 编译 、 刷机** 编译过程若ctrlc强制终止,请重新从头编译 LineageOS16_oneplus3t仓库群克隆过程 LineageOS16_oneplus3t仓库群克隆过程描述: LineageOS16_oneplus3t_gitRepoLs_c…...
VSCode+ESP-IDF开发ESP32-S3-DevKitC-1(1)开发环境搭建
VSCodeESP-IDF开发ESP32-S3-DevKitC-1(1)开发环境搭建 1.开发环境搭建(安装ESP-IDF)2.开发环境搭建(安装VS Code)3.开发环境搭建(VSCode中安装ESP-IDF插件及配置) 1.开发环境搭建&am…...
LeetCode --- 143周赛
题目列表 3345. 最小可整除数位乘积 I 3346. 执行操作后元素的最高频率 I 3347. 执行操作后元素的最高频率 II 3348. 最小可整除数位乘积 II 一、最小可整除数位成绩I 由于本题的数据范围比较小,我们直接暴力枚举即可,代码如下 class Solution { p…...
[AI] 【提高认知】自动翻译技术的演变:从规则系统到深度学习的崛起
机器自动翻译 (MT) 是人工智能历史上最早的应用之一,尤其是在英语和俄语之间的翻译应用。自诞生以来,自动翻译技术从符号系统逐步演化到依赖大数据和深度学习的先进模型。本文将深入探讨机器翻译的早期方法、统计方法和现代神经网络方法的演变过程,帮助大家了解自动翻译技术…...
python机器人Agent编程——多Agent框架的底层逻辑(上)
目录 一、前言二、两个核心概念2.1 Routines(1)清晰的Prompt(2)工具调用json schema自动生成(3)解析模型的toolcall指令(4)单Agent的循环决策与输出 PS.扩展阅读ps1.六自由度机器人相…...
渑池县中药材产业党委莅临河南广宇企业管理集团有限公司参观交流
11月14日,渑池县人大副主任、工商联主席杨航率县中药材产业党委代表团一行13人,莅临河南广宇集团参观交流。河南广宇集团总经理王峰、副总经理王培等领导热情接待并陪同参观、座谈。 代表团一行首先参观了集团旗下郑州美信中医院(庚贤堂中医药…...
学习日志011--模块,迭代器与生成器,正则表达式
一、python模块 在之前学习c语言时,我们学了分文件编辑,那么在python中是否存在类似的编写方式?答案是肯定的。python中同样可以实现分文件编辑。甚至还有更多的好处: 提高代码的可维护性:当代码被分成多个文件时…...
ChatGPT 搜索 vs Google 搜索
原文:Amanda Caswell - 2024.11.01 随着 OpenAI 推出的实时搜索功能,ChatGPT 正在逐步成为像 Google 这样的传统搜索引擎的竞争对手。ChatGPT 以其对话式的回答方式而闻名,它能够在没有广告干扰的情况下提供实时的上下文信息。 我迫不及待地…...
一文简单了解Android中的input流程
在 Android 中,输入事件(例如触摸、按键)从硬件传递到应用程序并最终由应用层消费。整个过程涉及多个系统层次,包括硬件层、Linux 内核、Native 层、Framework 层和应用层。我们将深入解析这一流程,并结合代码逐步了解…...
【MySQL】SQL语言
【MySQL】SQL语言 文章目录 【MySQL】SQL语言前言一、SQL的通用语法二、SQL的分类三、SQLDDLDMLDQLDCL 总结 前言 本篇文章将讲到SQL语言,包括SQL的通用语法,SQL的分类,以及SQL语言的DDL,DML,DQL,DCL。 一、SQL的通用语法 在学习具体的SQL语句之前,先来…...
5.4.2-1 编写Java程序在HDFS上创建文件
本次实战涉及使用Java操作Hadoop HDFS,包括创建文件、判断文件存在性及异常处理。通过手动添加依赖、启动HDFS服务,成功在HDFS上创建和检查文件。进一步探索了文件操作的最佳实践,如检查文件存在性以避免重复创建,以及处理HDFS安全…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
K8S认证|CKS题库+答案| 11. AppArmor
目录 11. AppArmor 免费获取并激活 CKA_v1.31_模拟系统 题目 开始操作: 1)、切换集群 2)、切换节点 3)、切换到 apparmor 的目录 4)、执行 apparmor 策略模块 5)、修改 pod 文件 6)、…...
Unity3D中Gfx.WaitForPresent优化方案
前言 在Unity中,Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染(即CPU被阻塞),这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案: 对惹,这里有一个游戏开发交流小组&…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
vue3 字体颜色设置的多种方式
在Vue 3中设置字体颜色可以通过多种方式实现,这取决于你是想在组件内部直接设置,还是在CSS/SCSS/LESS等样式文件中定义。以下是几种常见的方法: 1. 内联样式 你可以直接在模板中使用style绑定来设置字体颜色。 <template><div :s…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
拉力测试cuda pytorch 把 4070显卡拉满
import torch import timedef stress_test_gpu(matrix_size16384, duration300):"""对GPU进行压力测试,通过持续的矩阵乘法来最大化GPU利用率参数:matrix_size: 矩阵维度大小,增大可提高计算复杂度duration: 测试持续时间(秒&…...
k8s业务程序联调工具-KtConnect
概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...
