
简介基于JY901与STM32的波浪测量系统是一份面向单片机、传感器融合与海洋测绘方向的毕业设计级完整资料。资源围绕九轴加速度计JY901和STM32构建下位机采集波浪起伏加速度并存储于SD卡再由上位机与Matlab完成频域积分最终统计分析波高与周期适合自动化、电子、海洋工程等专业学生及嵌入式开发者参考。整套项目共347个文件压缩包约13.43MB。其中c/h源文件对应STM32底层驱动、JY901解析与FATFS文件系统uvprojx等为Keil工程配置m文件用于位移曲线绘制和数据处理docx/txt则为毕业论文、八篇实习日记、实习日志、实习报告及配置说明png图片辅助展示电路与波形效果。已有477人学习这套资源包内容从硬件接线、传感器初始化到上位机可视化覆盖完整项目链条既可直接编译运行也可作为毕业设计开题、论文撰写与答辩的参考资料。 刚拿到这个项目的时候我第一反应是JY901这种几十块钱的九轴姿态传感器配上STM32能测出什么有意义的波浪数据但真正做完浮标样机、拿去水池里跑了几轮之后我得说这个组合其实被严重低估了。JY901内置了姿态解算直接输出欧拉角和线性加速度省去了自己写AHRS融合算法的痛苦STM32F103这种入门级MCU也足以完成数据采集、FFT频谱分析和特征提取整套系统成本压到一百多块非常适合做海洋浮标、近岸波浪观测、船舶横摇测量甚至是毕业设计级别的科研演示。这篇文章我不打算讲虚的直接拆开这个系统从JY901的协议解析开始到STM32怎么采数据、怎么把这堆加速度和角度变成有物理意义的波高、波周期再到实际操作中那些文档里根本不会写的问题。无论你是想做海洋工程的课题还是只想在实验室里复现一套波浪监测装置这份完整的问题排查记录都能让你少走不少弯路。1. 方案选型与整体设计思路1.1 为什么是JY901而不是自己拼传感器做波浪测量本质上是测量浮体随波面运动的姿态和加速度。实现方式从来不缺直接用MEMS加速度计比如MPU6050/ADXL345自己采原始数据再用卡尔曼滤波或互补滤波解算出姿态这当然可行但问题在于——滤波参数调起来是真的折磨人。船摇、涌浪、风浪混在一起噪声大、频带宽调一个参数静水实验看着挺好拿到波浪环境里马上就飘。JY901的核心优势在于它在模组内部就把MPU9250加速度计陀螺仪磁力计的原始数据做完了九轴融合直接输出欧拉角Roll/Pitch/Yaw、三轴角速度和三轴加速度。也就是说姿态解算这个最麻烦的环节厂家已经在出厂前帮你搞定了。实测下来JY901的静态角度漂移很小动态响应也能到100Hz左右对波周期通常在3到10秒的海浪来说精度和带宽都完全够用。价格方面也很有吸引力。一颗JY901零售价在五十到八十块而同样带姿态解算的工业级AHRS模块动辄上千。对原型验证、教学实验、中小型浮标项目来说JY901的性价比几乎无解。1.2 系统整体架构整套系统可以画成四段链路感知层JY901姿态传感器测量浮体三轴角度、角速度、加速度输出串口数据帧主控层STM32F103C8T6或者F103RCT6负责接收数据帧、协议解析、数据预处理、FFT频谱分析、特征量提取输出层OLED显示屏本地显示串口发送至上位机也可以用4G模块或LoRa远程回传供电层3.7V锂电池升压至5V或者直接用USB供电海上应用需要加防水舱比较关键的一点是STM32在这里不只是做一个透明传输——它承担了核心的频谱分析任务。如果只用单片机转发数据到上位机处理整个系统就退化成了一套数据采集器价值大打折扣。所以设计目标是STM32本地完成从原始数据到波浪参数的完整计算链路。1.3 选型时最容易忽略的坑硬件选型看起来简单但有几个坑我一开始没注意导致后面返工一是JY901的量程设置。默认加速度量程是±16g这在绝大多数波浪场景下都够用。但如果你要测的是极端海况下的小型浮标瞬时加速度可能超过这个范围数据会直接削顶FFT结果出现大量高频虚假峰。建议上电后通过串口指令把量程配置到适合当前场景的范围。二是传感器的安装位置。JY901必须尽量安装在浮体的几何中心或者重心附近离重心越远角速度信号里混入的线加速度分量越大后期补偿非常麻烦。我第一版样机把传感器装在浮标顶部支架上结果同样的波浪条件下测出来的角度幅值比参考值偏大了快两成。三是磁力计要不要开。JY901默认融合了磁力计做航向参考但如果系统附近有电机或者铁磁性物体磁力计数据就是灾难Yaw角会不停跳变甚至污染Roll/Pitch解算。水面浮标场景一般没有强磁场干扰但如果你在实验室水池里调试边上就是水泵电机、钢制支架一定要先把磁场校准做了或者干脆关闭磁力计融合只依靠陀螺仪和加速度计。2. JY901数据协议解析与STM32串口采集2.1 JY901数据帧结构JY901最常用的输出方式是串口默认波特率96008位数据位、1位停止位、无校验。它每次输出一包数据协议帧以0x55开头第二个字节是数据类型标识后面跟4个数据段每个数据段2字节小端序最后是校验和。常用的帧类型有这么几种帧类型标识含义数据内容0x51加速度三轴加速度单位g需除以32768再乘以量程0x52角速度三轴角速度单位°/s需除以32768再乘以量程0x53角度三轴欧拉角单位°需除以32768再乘以1800x59四元数四元数q0~q3无需换算范围-1到1每个数据段都是16位有符号整数用int16接收后做换算。换算公式和量程相关比如角度帧原始值除以32768再乘以180就是当前角度值。2.2 串口配置的实操细节STM32端我用的是USART1 空闲中断 DMA接收这样可以不用一字节一字节地进中断CPU占用率低很多。9600波特率下数据量不大即便用轮询接收也扛得住但我建议直接上DMA为后面扩展更高波特率留余地。串口初始化的几个关键参数直接在代码里贴出来看一下void JY901_UART_Init(void) { // 使能串口1和GPIOA时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin GPIO_Pin_9; // TX GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin GPIO_Pin_10; // RX GPIO_InitStructure.GPIO_Mode GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, GPIO_InitStructure); USART_InitTypeDef USART_InitStructure; USART_InitStructure.USART_BaudRate 9600; USART_InitStructure.USART_WordLength USART_WordLength_8b; USART_InitStructure.USART_StopBits USART_StopBits_1; USART_InitStructure.USART_Parity USART_Parity_No; USART_InitStructure.USART_Mode USART_Mode_RX | USART_Mode_TX; USART_Init(USART1, USART_InitStructure); // DMA配置接收缓冲 DMA_InitTypeDef DMA_InitStructure; DMA_InitStructure.DMA_PeripheralBaseAddr (uint32_t)(USART1-DR); DMA_InitStructure.DMA_MemoryBaseAddr (uint32_t)JY901_RxBuf; DMA_InitStructure.DMA_DIR DMA_DIR_PeripheralSRC; DMA_InitStructure.DMA_BufferSize JY901_RX_BUF_SIZE; DMA_InitStructure.DMA_PeripheralInc DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize DMA_PeripheralDataSize_Byte; DMA_InitStructure.DMA_MemoryDataSize DMA_MemoryDataSize_Byte; DMA_InitStructure.DMA_Mode DMA_Mode_Circular; DMA_InitStructure.DMA_Priority DMA_Priority_High; DMA_InitStructure.DMA_M2M DMA_M2M_Disable; DMA_Init(DMA1_Channel5, DMA_InitStructure); USART_DMACmd(USART1, USART_DMAReq_RX, ENABLE); DMA_Cmd(DMA1_Channel5, ENABLE); USART_Cmd(USART1, ENABLE); }留意一个细节DMA接收用的是循环模式缓冲区会不断被覆盖所以解析逻辑必须做指针快照判断当前DMA写到哪个位置再和上次处理的位置做差找到这次新到的数据。2.3 协议解析的完整思路解析JY901帧建议用状态机不要搞那种每收到一字节就进一次中断处理大逻辑的写法。uint8_t JY901_ProtocolParse(uint8_t byte) { static uint8_t state 0; static uint8_t frameType 0; static uint8_t dataCnt 0; static uint8_t frameData[12]; static uint8_t checksum 0; switch (state) { case 0: if (byte 0x55) { state 1; checksum byte; } break; case 1: frameType byte; checksum byte; dataCnt 0; state 2; break; case 2: frameData[dataCnt] byte; checksum byte; if (dataCnt 8) state 3; break; case 3: if (byte checksum) HandleJY901Frame(frameType, frameData); state 0; break; default: state 0; break; } return 0; }这里拆包成功后把frameData里的8个字节按小端序拼成4个int16再做量程换算。我建议把换算后的float数据直接放进全局结构体供上层滤波、FFT使用。3. 波浪信息提取算法与参数计算3.1 波浪测量原理波浪测量的底层逻辑并不复杂把一个浮体扔到水面上浮体会随着波浪做升沉和摇摆运动。JY901测到的Z轴加速度包含重力加速度分量和波浪驱动的升沉加速度而Roll/Pitch角则直接反映了波面的倾斜角。对于单点浮标测量最常用的是频域分析法。把时域上的加速度/角度信号通过FFT变换到频域找到频谱峰值对应的频率就是波浪的主频率频谱的矩可以用来计算有效波高。这种方法在工程上非常成熟近岸波浪观测站大多也是这么干的。那为什么不能直接在时域里看最大值算波高因为波浪是多频率叠加的随机过程时域幅值受噪声和短时冲击影响很大直接取峰值误差不可控。频域分析相当于把信号按频率“梳”了一遍是统计意义上的稳定结果。3.2 数据预处理FFT之前的关键步骤直接拿原始数据做FFT是新手最容易犯的错误。浮标在波浪上运动时信号里除了波浪成分还有低频趋势项比如海流把浮标慢慢带偏、安装支架的机械谐振高频噪声。如果不处理FFT频谱会非常难看主峰完全被趋势项的能量淹没。我的处理流程是三步去均值把整段数据的平均值减掉消除直流分量高通滤波用简单的一阶高通截止频率0.05Hz左右滤掉超低频趋势或者直接用移动平均法去趋势——把窗口设成信号周期5倍以上用原始信号减掉滑动平均值效果非常直观加窗FFT前乘汉宁窗减少频谱泄漏汉宁窗的代码实现很简单for (uint16_t i 0; i FFT_SIZE; i) { window[i] 0.5f * (1.0f - cosf(2.0f * PI * i / (FFT_SIZE - 1))); fftInput[i] rawData[i] * window[i]; }3.3 FFT实现与关键参数计算STM32F103主频72MHz跑1024点浮点FFT大概需要几十毫秒完全够用。建议直接用官方DSP库不要自己造轮子。ST官方的arm_cfft_f32函数性能有保障而且调用简单。#include arm_math.h #include arm_common_tables.h #define FFT_SIZE 1024 float32_t fftInput[FFT_SIZE * 2]; float32_t fftOutput[FFT_SIZE]; uint32_t fftIndex 0; // FFT计算流程 void WaveFFT_Calc(void) { // 加窗 for (uint16_t i 0; i FFT_SIZE; i) { fftInput[2 * i] waveData[i] * hanningWindow[i]; fftInput[2 * i 1] 0; } // FFT注意arm_cfft_f32要求输入为复数格式实部虚部交替排列 arm_cfft_f32(arm_cfft_sR_f32_len1024, fftInput, 0, 1); // 计算幅值谱取前N/2个有效频率点 arm_cmplx_mag_f32(fftInput, fftOutput, FFT_SIZE / 2); // 找峰值频率 uint32_t peakIdx 0; float32_t peakMag 0; for (uint32_t i 1; i FFT_SIZE / 2; i) { if (fftOutput[i] peakMag) { peakMag fftOutput[i]; peakIdx i; } } // 主波周期 float samplingRate 20.0f; // 比如采样率20Hz float peakFreq (float)peakIdx * samplingRate / FFT_SIZE; float mainPeriod 1.0f / peakFreq; }关键参数的物理意义得说清楚有效波高Hs工程上通常用4倍零阶谱矩的平方根来估算Hs 4 * sqrt(m0)m0是频谱所有频率成分能量的总和。这个公式来源于海浪谱理论虽然看着简单但在实际浮标数据上统计效果相当稳定主波周期Tp频谱峰值对应的周期反映的是当前海况下能量最集中的波浪周期采样率选择波浪信号的频率集中在0.03到0.5Hz之间20Hz采样率已经绰绰有余。千万不要学别人做姿态采集就傻乎乎地用100Hz数据量大了不说FFT窗口时长反而会缩短我实测一组水池数据静水状态下Z轴加速度标准差在0.01g左右加一个大约5cm波高的人造波浪后主频非常清晰地出现在0.33Hz附近算出的周期3.03秒有效波高4.6cm与人工量测值误差不到10%。4. 实操过程与核心环节实现4.1 硬件连接与供电方案硬件接线非常简单JY901的TX接STM32的PA10USART1_RXJY901的RX接PA9USART1_TX两者的GND必须共地这是很多新手忽略的重点。JY901供电范围是3.3V到5V推荐直接接5V模组内部的稳压电路会处理好电压转换但STM32的IO是3.3V逻辑JY901从5V供电时TX输出已经是3.3V电平所以不用加电平转换芯片实测通信稳定。供电方面我的经验是STM32用AMS1117-3.3稳压JY901和STM32共用一个电源输入。如果电源纹波很大FFT频谱上会出现明显的50Hz及其倍频干扰峰可以在电源输入端并一个100uF电解电容和0.1uF陶瓷电容滤波。接线确认无误后先用USB转TTL模块把JY901单独接电脑打开串口助手看有没有数据滚动输出——这一步能在设计STM32程序之前就确认传感器本身工作正常。4.2 主控程序的任务划分整个STM32程序我不建议全部堆在主循环里按功能拆成几个模块逻辑清晰也方便后期维护数据接收任务DMA空闲中断触发后快速把数据拷贝到解析缓冲区顺便用状态机把JY901帧解析成结构体变量数据缓存任务每次解析到完整数据帧时把加速度/角度写入后续FFT用的环形缓冲区同时更新数据时间戳周期性FFT任务每个采样窗口比如128秒完成后触发一次FFT计算算完波高、周期把结果更新到全局变量输出任务OLED每秒钟刷新一次实时角度和加速度计算完成后通过串口2把结果发出去关键点在FFT任务的触发上。我一开始直接在解析到数据帧时做FFT结果FFT计算期间串口DMA缓冲区被覆盖数据丢得一塌糊涂。后来改成主循环里检查“数据攒满一个FFT窗口”的标志位再触发计算计算期间中断全部关闭也没问题因为DMA接收不会丢数据。4.3 上位机联调与标定系统单独跑起来以后下一步就是和上位机联调验证数据准确性。JY901官方有上位机可以直接观察实时姿态但我建议把STM32解析后的数据用串口发到Matlab或者Python里做一次离线分析对照STM32本地算出的FFT结果既能验证算法移植正确性又能顺便观察原始数据的质量。Matlab里分析流程大概是串口接收数据 → 去掉前10秒的启动过渡段 → 去均值、加窗、FFT → 找峰值频率 → 计算有效波高。如果Matlab算出来的频谱和STM32本地算的基本重合那说明固件算法是可靠的。标定这一步很多人直接跳过但实验室环境和水池环境的零点偏移相差很大。建议静水状态下采集30秒数据把三轴角度和加速度的均值记录下来作为零点偏置后续所有数据都先减去这个偏置再做分析。我实测不同温度下JY901的零点偏移会变化所以如果条件允许每次实验前都做一次快速静水标定。5. 常见问题与排查技巧实录5.1 JY901串口无数据这是入手后遇到概率最高的问题排查顺序一定是接线 → 供电 → 波特率 → 串口配置。接线检查TX/RX有没有接反这是失误率最高的一件事JY901的TX应该接STM32的RX交叉连接共地问题两个模块的GND必须共地只接了信号线不接地大概率收不到稳定数据波特率确认JY901默认9600但有些商家会改成115200发货用官方上位机读一下当前配置或者发送寄存器读取指令确认STM32串口初始化确认USART外设时钟、GPIO复用模式都配置正确用逻辑分析仪看波形是最省事的方法5.2 数据乱码或周期性跳变数据乱码通常是电气问题而非协议问题。最常见的是供电不足——JY901峰值电流虽然只有几十毫安但如果和舵机、电机共用一个电源电压跌落会导致模组内部复位输出乱码。分开供电基本能解决。另一个隐蔽问题是数据解析中int16符号处理错误。JY901输出负角度时原始数据在UART上体现为大于0x8000的数值如果解析时用了uint16而不是int16负数会变成一个巨大的正数换算出来的角度会在0度和360度之间反复横跳。解决办法很简单强制类型转换为int16再参与计算。5.3 STM32连接调试器报错调试阶段如果Keil报错“no STM32 target found”先别急着换芯片。排查顺序是确认ST-Link和STM32的接线SWDIO/SWCLK/GND三根线必不可少3.3V供电线建议也接上确认STM32的BOOT0引脚是低电平如果BOOT0被拉高芯片会进入Bootloader模式核心程序不运行调试器也连不上重点检查是不是在程序里禁用了JTAG/SWD引脚。很多人照着网上的教程把PA15、PB3、PB4配置成了普通IO美其名曰“释放引脚”结果SWD调试口也被禁用了——程序一旦烧进去调试器就再也连不上只能通过设置BOOT0为高电平用串口ISP擦除Flash来救我自己的习惯是只要不是引脚占用特别紧张的场合SWD口绝不禁用。真到量产阶段要用这些引脚也一定会做一个跳线开关调试时断开。5.4 虚拟串口设备感叹号用USB转TTL模块在电脑上识别出COM口但带黄色感叹号十有八九是驱动问题。CH340的驱动在Win10/Win11下一般会自动安装但有些精简版系统会漏掉。解决办法就是去芯片官网手动装对应版本的驱动装完重启电脑。顺手把USB线换成高质量带磁环的线劣质USB线在波特率较高时也容易出现丢包。5.5 FFT频谱异常主峰不明显如果FFT做出来是一片噪声或者主峰被淹没按照这个顺序排查先看原始数据把一段原始波形打印出来肉眼看有没有明显的周期起伏。如果还是高频抖动问题在数据采集端如果是缓慢漂移问题在预处理端检查去趋势忘记去趋势是主峰不明显的最常见原因低频漂移的能量会填满整个低频段检查加窗不加窗会导致频谱泄漏谱峰变宽、变矮确认频率分辨率FFT分辨率和采样率、窗口长度直接相关。比如20Hz采样率1024点FFT频率分辨率约为0.02Hz这个精度对应波周期误差约为0.5秒基本够用。如果用了256点FFT分辨率降到0.08Hz波周期峰值可能完全对不上6. 后续还能怎么扩展整套系统跑通以后扩展空间其实很大。给浮标加上GPS定位和4G通信模块就能变成一套低成本的近岸波浪监测浮标定时把波高、波周期、位置信息回传到岸基站。把单浮标变成三点阵列用互相关分析还能推算出波向——这个就涉及多通道时间延迟估计了不过原理上依然是FFT那一套底子。如果对实时性有更高要求可以把数据采集和FFT处理拆到双线程里跑RTOS比如FreeRTOS的任务划分思路和我上面说的模块划分完全一致只是把主循环轮询换成了信号量和消息队列本质没变。如果用的是STM32F4系列带FPU之后FFT计算时间还能再缩短一个数量级对更高采样率的需求也能轻松满足。按照我个人的使用体会这套系统最值得打磨的地方其实是数据质量判据。海上实测不像水池实验那么干净浮标自由漂移时会有低频振荡、碎浪冲击时会有高频尖峰做一两个判据把这些坏数据自动剔除掉系统才算真正从实验室走向了实际部署。这里面最有意思的部分是波浪周期和波高在不同频段上的能量分配——研究时间久了你会发现自己看海的方式都会变得不一样。本文还有配套的精品资源点击获取