当前位置: 首页 > article >正文

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1

        本教程使用零知标准板(STM32F103RBT6)通过I2C驱动ICM20948九轴传感器,实现姿态解算,并通过串口将数据实时发送至VOFA+上位机进行3D可视化。代码基于开源库修改优化,适合嵌入式及物联网开发者。在基础驱动上新增滤波参数优化,重点解决yaw值漂移问题,提供完整的参数调优方案和效果对比。

目录

STM32F1

一、硬件准备

二、软件环境搭建

三、核心代码实现

四、VOFA+上位机配置

五、滤波参数优化与动态效果对比

六、效果演示

七、 效果验证与结论


一、硬件准备

1.硬件清单

  • 零知标准板(主控STM32F103RBT6)

  • ICM20948九轴传感器模块

  • USB转串口模块(用于调试和数据传输)

  • 杜邦线若干

2.接线方式

ICM20948引脚零知开发板引脚
VCC3.3V
GNDGND
SDAA4
SCLA5
硬件连接图

连接实物图 

注意:确保I2C引脚正确,避免接反导致芯片损坏。

二、软件环境搭建

开发环境

  • 零知 IDE + 零知开发板支持包

  • 所需库文件:

      AHRSAlgorithms.cpp(姿态解算库)

      ICM20948.cpp(传感器驱动库)

库文件关键功能

  • AHRSAlgorithms.cpp

                Madgwick/Mahony滤波算法

                四元数实时输出 getQ()

                参数可调:KpKibeta

  • ICM20948.cpp

                I2C通信底层驱动

                自动量程配置(加速度计±2/4/8/16g,陀螺仪±250/500/1000/2000dps)

                磁力计初始化 initAK09916()

                校准函数 calibrateICM20948()

三、核心代码实现

主程序框架 

(ICM20948_VOFA.ino)

/* ICM20948完整优化代码 */
#include "AHRSAlgorithms.h"
#include "ICM20948.h"#define AHRS true
#define SerialDebug true int myLed = LED_BUILTIN;  
ICM20948 myIMU;void setup() {pinMode(myLed, OUTPUT);digitalWrite(myLed, HIGH);Serial.begin(115200);Wire.begin();// 初始化与自检if(myIMU.begin()) {Serial.println("ICM20948初始化成功");// 执行两级校准myIMU.calibrateICM20948(myIMU.gyroBias, myIMU.accelBias);float magBias[3], magScale[3];myIMU.magCalICM20948(magBias, magScale);// 设置优化分辨率myIMU.getAres(); myIMU.getGres();myIMU.getMres();} else {Serial.println("传感器初始化失败!");while(1);}
}void loop() {// 数据读取if (myIMU.readByte(ICM20948_ADDRESS, INT_STATUS_1) & 0x01) {myIMU.readAccelData(myIMU.accelCount);myIMU.readGyroData(myIMU.gyroCount);myIMU.readMagData(myIMU.magCount);// 单位转换myIMU.ax = (float)myIMU.accelCount[0] * myIMU.aRes;myIMU.ay = (float)myIMU.accelCount[1] * myIMU.aRes;myIMU.az = (float)myIMU.accelCount[2] * myIMU.aRes;myIMU.gx = (float)myIMU.gyroCount[0] * myIMU.gRes;myIMU.gy = (float)myIMU.gyroCount[1] * myIMU.gRes;myIMU.gz = (float)myIMU.gyroCount[2] * myIMU.gRes;myIMU.mx = (float)myIMU.magCount[0] * myIMU.mRes - myIMU.magBias[0];myIMU.my = (float)myIMU.magCount[1] * myIMU.mRes - myIMU.magBias[1];myIMU.mz = (float)myIMU.magCount[2] * myIMU.mRes - myIMU.magBias[2];}// 更新时间基准myIMU.updateTime();// 姿态解算(使用优化参数)MahonyQuaternionUpdate(myIMU.ax, myIMU.ay, myIMU.az,myIMU.gx * DEG_TO_RAD, myIMU.gy * DEG_TO_RAD,myIMU.gz * DEG_TO_RAD,myIMU.my, myIMU.mx, myIMU.mz, // 轴序修正myIMU.deltat);// 转换为欧拉角const float* q = getQ();myIMU.yaw   = atan2(2.0f*(q[1]*q[2] + q[0]*q[3]), q[0]*q[0] + q[1]*q[1] - q[2]*q[2] - q[3]*q[3]) * RAD_TO_DEG;myIMU.pitch = -asin(2.0f*(q[1]*q[3] - q[0]*q[2])) * RAD_TO_DEG;myIMU.roll  = atan2(2.0f*(q[0]*q[1] + q[2]*q[3]),q[0]*q[0] - q[1]*q[1] - q[2]*q[2] + q[3]*q[3]) * RAD_TO_DEG;// 发送到VOFA+Serial.print(myIMU.yaw, 1);   // yawSerial.print(",");Serial.print(myIMU.pitch, 1); // pitchSerial.print(",");Serial.println(myIMU.roll, 1);// rolldelay(10); // 100Hz输出
}

 关键配置修改

 在 ICM20948.cpp 中调整量程(根据应用需求):

// 加速度计量程 (AFS_2G/AFS_4G/AFS_8G/AFS_16G)
void ICM20948::getAres()
{switch (Ascale){// Possible accelerometer scales (and their register bit settings) are:// 2 Gs (00), 4 Gs (01), 8 Gs (10), and 16 Gs  (11).// Here's a bit of an algorith to calculate DPS/(ADC tick) based on that// 2-bit value:case AFS_2G:aRes = 2.0f / 32768.0f;break;case AFS_4G:aRes = 4.0f / 32768.0f;break;case AFS_8G:aRes = 8.0f / 32768.0f;break;case AFS_16G:aRes = 16.0f / 32768.0f;break;}
}// 陀螺仪量程 (GFS_250DPS/GFS_500DPS/GFS_1000DPS/GFS_2000DPS)
void ICM20948::getGres()
{switch (Gscale){// Possible gyro scales (and their register bit settings) are:// 250 DPS (00), 500 DPS (01), 1000 DPS (10), and 2000 DPS (11).// Here's a bit of an algorith to calculate DPS/(ADC tick) based on that// 2-bit value:case GFS_250DPS:gRes = 250.0f / 32768.0f;break;case GFS_500DPS:gRes = 500.0f / 32768.0f;break;case GFS_1000DPS:gRes = 1000.0f / 32768.0f;break;case GFS_2000DPS:gRes = 2000.0f / 32768.0f;break;}
}

四、VOFA+上位机配置

数据协议设置

  • 选择 FireWater 协议

  • 格式:q0, q1, q2(逗号分隔+换行符)

  • 波特率:115200

控件添加

  • 3D立方体:显示实时姿态、绑定四元数数据通道、设置模型缩放比例

  • 波形图:各轴角速度/加速度

  • 仪表盘:显示偏航角(Yaw)

界面效果

实时显示传感器3D姿态及运动波形

五、滤波参数优化与动态效果对比

1.传感器校准

float gyroBias[3], accelBias[3];
IMU.calibrateICM20948(gyroBias, accelBias); // 上电时执行一次

2.问题现象

使用默认参数(Kp=10.0, Ki=0.0)时,VOFA+显示yaw值持续漂移(约2-5°/s),动态运动时零漂明显

3.优化方案

 在AHRSAlgorithms.h中调整Mahony滤波参数:

// 原参数(漂移明显)
// #define Kp 2.0f * 5.0f
// #define Ki 0.0f// 优化参数(大幅改善漂移)
#define Kp 3.0f    // 降低比例增益,减少高频噪声响应
#define Ki 0.1f   // 降低积分增益,抑制累积误差

效果对比

参数状态Yaw漂移率VOFA+动态表现
默认(Kp=10f,Ki=0.0f)2-5°/s静止时缓慢旋转,运动后复位慢
优化(Kp=3.0f,Ki=0.1f)<0.5°/s静止稳定,运动后快速收敛

4.优化后效果

参数调整原理

Kp过高:对加速度计噪声敏感,导致高频抖动

Ki过高:积分累积误差引起零漂

黄金比例Kp/Ki ≈ 20-30 时平衡动态响应与稳定性

六、效果演示

静态测试

  • 传感器平放时,VOFA+显示俯仰角/横滚角接近0°

  • Z轴加速度≈9.8 m/s²

动态测试

  • 旋转开发板,3D模型同步跟随

  • 快速晃动时波形图显示各轴加速度变化

ICM20948九轴传感器动态测试

输出速率调优

  • ICM20948原始数据输出率约100Hz(10ms/次)

  • delt_t=60ms时,姿态解算循环(16.7Hz)与传感器更新周期不同步

  • 导致部分数据帧被重复使用或跳过

完整工程代码

百度网盘获取完整工程文件,链接如下:

零知标准板驱动ICM20948项目工程https://pan.baidu.com/s/11tr8XJvNrNernqwK1zA9Mw?pwd=pbxd 

七、 效果验证与结论

测试结果

指标优化前优化后
静态yaw漂移2-5°/s<0.5°/s
动态收敛时间>3s<1s
高温稳定性漂移增加300%漂移增加<50%

结论

  • 通过调整Kp/Ki比例可有效抑制yaw漂移
  • 磁力计轴序修正提升方位角精度
  • VOFA+可视化提供直观参数调优依据
  • 三阶段校准确保全温度范围稳定性

 ✔(●'◡'●)

有关本篇博客的任何问题、或者任何想法和建议等,欢迎您在底部评论区留言,一起交流~

零知开源是一个真正属于国人自己的开源软硬件平台,在开发效率上超越了Arduino平台并且更加容易上手,大大降低了开发难度。
零知开源在软件方面提供了完整的学习教程和丰富示例代码,让不懂程序的工程师也能非常轻而易举的搭建电路来创作产品,测试产品。快来动手试试吧!
www.lingzhilab.com

相关文章:

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

WPF八大法则:告别模态窗口卡顿

⚙️ 核心问题&#xff1a;阻塞式模态窗口的缺陷 原始代码中ShowDialog()会阻塞UI线程&#xff0c;导致后续逻辑无法执行&#xff1a; var result modalWindow.ShowDialog(); // 线程阻塞 ProcessResult(result); // 必须等待窗口关闭根本问题&#xff1a…...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...

【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验

Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...

【LeetCode】算法详解#6 ---除自身以外数组的乘积

1.题目介绍 给定一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法&#xff0c;且在 O…...

抽象类和接口(全)

一、抽象类 1.概念&#xff1a;如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象&#xff0c;这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法&#xff0c;包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中&#xff0c;⼀个类如果被 abs…...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...

Oracle11g安装包

Oracle 11g安装包 适用于windows系统&#xff0c;64位 下载路径 oracle 11g 安装包...

深入浅出Diffusion模型:从原理到实践的全方位教程

I. 引言&#xff1a;生成式AI的黎明 – Diffusion模型是什么&#xff1f; 近年来&#xff0c;生成式人工智能&#xff08;Generative AI&#xff09;领域取得了爆炸性的进展&#xff0c;模型能够根据简单的文本提示创作出逼真的图像、连贯的文本&#xff0c;乃至更多令人惊叹的…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

系统掌握PyTorch:图解张量、Autograd、DataLoader、nn.Module与实战模型

本文较长&#xff0c;建议点赞收藏&#xff0c;以免遗失。更多AI大模型应用开发学习视频及资料&#xff0c;尽在聚客AI学院。 本文通过代码驱动的方式&#xff0c;系统讲解PyTorch核心概念和实战技巧&#xff0c;涵盖张量操作、自动微分、数据加载、模型构建和训练全流程&#…...

【SpringBoot自动化部署】

SpringBoot自动化部署方法 使用Jenkins进行持续集成与部署 Jenkins是最常用的自动化部署工具之一&#xff0c;能够实现代码拉取、构建、测试和部署的全流程自动化。 配置Jenkins任务时&#xff0c;需要添加Git仓库地址和凭证&#xff0c;设置构建触发器&#xff08;如GitHub…...

go 里面的指针

指针 在 Go 中&#xff0c;指针&#xff08;pointer&#xff09;是一个变量的内存地址&#xff0c;就像 C 语言那样&#xff1a; a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10&#xff0c;通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...

nnUNet V2修改网络——暴力替换网络为UNet++

更换前,要用nnUNet V2跑通所用数据集,证明nnUNet V2、数据集、运行环境等没有问题 阅读nnU-Net V2 的 U-Net结构,初步了解要修改的网络,知己知彼,修改起来才能游刃有余。 U-Net存在两个局限,一是网络的最佳深度因应用场景而异,这取决于任务的难度和可用于训练的标注数…...

论文阅读:LLM4Drive: A Survey of Large Language Models for Autonomous Driving

地址&#xff1a;LLM4Drive: A Survey of Large Language Models for Autonomous Driving 摘要翻译 自动驾驶技术作为推动交通和城市出行变革的催化剂&#xff0c;正从基于规则的系统向数据驱动策略转变。传统的模块化系统受限于级联模块间的累积误差和缺乏灵活性的预设规则。…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

数学建模-滑翔伞伞翼面积的设计,运动状态计算和优化 !

我们考虑滑翔伞的伞翼面积设计问题以及运动状态描述。滑翔伞的性能主要取决于伞翼面积、气动特性以及飞行员的重量。我们的目标是建立数学模型来描述滑翔伞的运动状态,并优化伞翼面积的设计。 一、问题分析 滑翔伞在飞行过程中受到重力、升力和阻力的作用。升力和阻力与伞翼面…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...

MyBatis中关于缓存的理解

MyBatis缓存 MyBatis系统当中默认定义两级缓存&#xff1a;一级缓存、二级缓存 默认情况下&#xff0c;只有一级缓存开启&#xff08;sqlSession级别的缓存&#xff09;二级缓存需要手动开启配置&#xff0c;需要局域namespace级别的缓存 一级缓存&#xff08;本地缓存&#…...

学习一下用鸿蒙​​DevEco Studio HarmonyOS5实现百度地图

在鸿蒙&#xff08;HarmonyOS5&#xff09;中集成百度地图&#xff0c;可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API&#xff0c;可以构建跨设备的定位、导航和地图展示功能。 ​​1. 鸿蒙环境准备​​ ​​开发工具​​&#xff1a;下载安装 ​​De…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

通过MicroSip配置自己的freeswitch服务器进行调试记录

之前用docker安装的freeswitch的&#xff0c;启动是正常的&#xff0c; 但用下面的Microsip连接不上 主要原因有可能一下几个 1、通过下面命令可以看 [rootlocalhost default]# docker exec -it freeswitch fs_cli -x "sofia status profile internal"Name …...

【C++】纯虚函数类外可以写实现吗?

1. 答案 先说答案&#xff0c;可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...

uniapp 实现腾讯云IM群文件上传下载功能

UniApp 集成腾讯云IM实现群文件上传下载功能全攻略 一、功能背景与技术选型 在团队协作场景中&#xff0c;群文件共享是核心需求之一。本文将介绍如何基于腾讯云IMCOS&#xff0c;在uniapp中实现&#xff1a; 群内文件上传/下载文件元数据管理下载进度追踪跨平台文件预览 二…...

Linux部署私有文件管理系统MinIO

最近需要用到一个文件管理服务&#xff0c;但是又不想花钱&#xff0c;所以就想着自己搭建一个&#xff0c;刚好我们用的一个开源框架已经集成了MinIO&#xff0c;所以就选了这个 我这边对文件服务性能要求不是太高&#xff0c;单机版就可以 安装非常简单&#xff0c;几个命令就…...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...

认识CMake并使用CMake构建自己的第一个项目

1.CMake的作用和优势 跨平台支持&#xff1a;CMake支持多种操作系统和编译器&#xff0c;使用同一份构建配置可以在不同的环境中使用 简化配置&#xff1a;通过CMakeLists.txt文件&#xff0c;用户可以定义项目结构、依赖项、编译选项等&#xff0c;无需手动编写复杂的构建脚本…...