ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于STM32与JY901的低成本波浪监测系统设计与实现

基于STM32与JY901的低成本波浪监测系统设计与实现 简介一份面向STM32与JY901的波浪测量系统毕业设计资源完整覆盖论文文档、8篇实习日记、实习日志与实习报告并附带项目源码。这套系统以JY901九轴加速度计采集波浪起伏加速度STM32作为下位机读取并存储至SD卡随后通过Matlab对加速度数据做频域积分得到波浪位移曲线再经统计分析计算波高与周期构成从传感器数据采集、本地存储到上位机离线分析的完整链路。适合正在做单片机、嵌入式或海洋监测类课题的高校学生参考压缩包共347个文件约13.43MB以C源码、H头文件、Keil工程文件uvprojx、Matlab的m脚本以及docx文档为主其中C源码与头文件组成STM32下位机固件工程m脚本用于Matlab频域积分与绘图docx文档包含论文和实习材料另有txt说明、png图表和编译中间文件目录结构便于按文档、源码、报告分类检索。目前已有477人学习下载。借助这套资源可以快速理解下位机采集、SD卡存储、上位机Matlab处理与频域积分算法的一体化流程并复用论文框架和代码完成自己的设计。 先说结论这个项目不是简单的“拿STM32读个IMU数据”而是典型的“低精度惯性器件 频域信号分析”组成的低成本波浪监测方案。它的实质是用JY901姿态传感器捕获浮标/浮子的加速度与姿态角通过STM32做数据采集、帧解析和转发然后在PC端用频谱分析反演波浪周期与有效波高。适合做海洋工程、物联网监测方向的毕业设计也适合想用嵌入式手段接触信号处理的开发者。如果你手头有一个JY901和一块STM32最小系统板完全可以在宿舍里把整套链路跑通——包括传感器读取、串口协议解析、FFT频谱分析和波高估算。我最初拿到这个项目时第一反应是测波浪为什么要用IMU测波浪不是该用压力传感器或超声波吗但后来发现用惯性传感器测波浪在工程上其实是个常规操作——浮标随波浪上下起伏时JY901测到的垂直加速度本身就携带了波面的运动信息。问题的关键不在于“怎么测”而在于“怎么从加速度里把波浪信息还原出来”。下面我把整套系统的设计思路、通信协议、核心算法和踩坑记录一次性讲清楚。1. 方案选型为什么是JY901加STM32这个组合1.1 JY901在波浪测量里的定位JY901是维特智能出品的一款九轴姿态传感器内部集成了MPU9250加速度计、陀螺仪、磁力计和一颗负责姿态解算的MCU。它最大的特点是出厂就内置了姿态融合算法直接通过串口输出欧拉角、角速度和加速度不需要你自己在单片机上跑卡尔曼滤波或四元数解算。这一点对波浪测量项目非常关键。如果你用裸的MPU9250或MPU6050你需要自己处理传感器噪声、陀螺仪漂移、姿态更新和坐标系转换这一套下来少说两三周。而JY901把“从原始惯性数据到姿态”这件事做完了你拿到手的已经是稳定的三轴加速度和三轴角度。对于波浪测量这种“需要长时间连续采集”的场景JY901的硬件姿态解算能省去大量调参时间。当然它也有局限输出的是经过内部滤波后的数据对高频振动有一定抑制但波浪本身的频率集中在0.05Hz到0.5Hz之间JY901的量程和带宽足够覆盖不会丢信息。1.2 STM32在这里承担什么角色很多人会问JY901自己就能输出数据为什么还要STM32直接USB转TTL接电脑不就行了吗单看采集确实可以但在实际工程中STM32承担了三个无法绕开的任务第一数据格式转换与缓冲。JY901输出的是二进制帧且带校验如果直接丢给PC处理也不是不行但嵌入式系统里更常见的做法是让MCU做协议解析、校验、打包成干净的CSV或CAN帧再上送。这样即使后续换成无线数传模块也只需要改MCU端的转发逻辑。第二多通道同步采集的扩展。波浪测量往往不只有一个传感器点。你可能需要同时采集浮标顶部的IMU和底部的压力传感器或者多个浮标组成阵列。STM32的多个串口外设天然适合做这种多传感器汇聚节点而JY901又是串口输出接起来几乎零成本。第三低功耗与离线存储。海上的浮标不可能一直有线连着电脑。STM32可以把解析后的数据写到SD卡或通过LoRa/4G模块上报这套架构在无人值守场景下是标配。所以即便你只是做原型验证用STM32做中间层后续扩展会非常方便。1.3 方案对比JY901 vs 专业测波浮标专业测波浮标如波浪骑士Datawell Waverider用的是高精度加速度计加GPS价格动辄几十万精度确实高。而JY901这类消费级IMU的方案精度上差一截但它用不到十分之一的成本实现了可用的波浪周期和波高估算对于教学演示、近岸环境监测、渔排养殖气象站这类场景性价比已经足够了。从我实测的情况来看JY901方案的误差主要来自两个方面一是传感器本身的零偏和噪声二是积分算法带来的漂移。前者可以通过安装后的静态校准来减小后者则完全依赖频域处理——这也是波浪测量和普通惯性导航最大的不同点。2. 波浪测量原理从加速度到波高的完整链条2.1 为什么波浪频率集中在0.05Hz到0.5Hz海面上的波浪按周期可以分成风浪几秒到十几秒和涌浪十几秒到几十秒对应的频率大约在0.05Hz到0.5Hz。这个频段比起机械振动、船体高频噪声要低得多所以在信号处理上有很清晰的优势我们可以用低通滤波器把高频分量全干掉只留下波浪频段。每个波浪周期内波面近似做简谐运动。理论上如果浮标完全随波运动那浮标垂直方向的位移就是波面的位移浮标的垂直加速度就是波面位移的二阶导数。所以从加速度反推波面是可行的。2.2 为什么不能直接在时域积分求波高这是很多新手第一个掉进去的坑。先用数学公式举个例子假设你测到的垂直加速度是 a(t) A·sin(2πft)A是加速度幅值那么位移应该是 x(t) -A/(2πf)²·sin(2πft)。也就是说位移的幅值是加速度幅值除以角频率的平方。对于0.1Hz的波浪加速度幅值如果是0.1g约0.98m/s²算出来的位移幅值大约就是 0.98/(0.628)² ≈ 2.5米。这个反算关系看着简单但问题在于时域积分会把加速度计的低频零偏积分成巨大的漂移项。假设加速度计有5mg约0.049m/s²的零偏一次积分之后每秒产生约0.049m/s的速度误差二次积分之后每秒产生约0.0245m的位移误差。一次波浪测量持续十几分钟的话漂移量会达到几十米直接淹没真实位移信号。所以工程上绝对不要在时域直接做二重积分。正确做法是走频域FFT之后在频域除以(2πf)²或者干脆不算位移直接在频域算谱矩。2.3 有效波高Hs与谱矩的关系在海洋工程里最常用的波高指标是有效波高Significant Wave Height, Hs它定义为波浪记录中前三分之一最大波的波高平均值。但实际计算时我们不会去数波而是用谱矩法把加速度数据FFT得到功率谱密度PSD(f)然后对波浪频段积分得到零阶矩m0m0 ∫ PSD(f) df有效波高 Hs 4√m0平均跨零周期 Tz √(m0 / m2)其中 m2 ∫ f²·PSD(f) df谱峰周期 Tp 1 / fpfp为PSD峰值对应的频率这套公式看起来简单但有两个前提一是你必须确保PSD算得准二是滤波边界要设置合理。如果低频段没滤干净m0会被温漂撑得很大算出来的Hs会虚高。3. 硬件接线与JY901通信协议解析3.1 接线与安装注意事项JY901的接口是标准UART串口一般引出VCC、GND、TXD、RXD四根线。与STM32连接时要注意交叉连接JY901的TXD接STM32的USART1_RXPA10JY901的RXD接STM32的USART1_TXPA9GND共地VCC接3.3V或5VJY901兼容3.3V~5V供电。这里面最容易被忽略的是共地。如果JY901和STM32各自用独立的电源适配器供电但GND没有连在一起串口电平就没有参考点收到的数据全是乱码。我一开始用两个USB转TTL模块供电时踩过这个坑后来统一用一个电池组给整个系统供电才解决。安装位置要注意JY901应该固定在浮标或浮子的几何中心附近且尽量保持Z轴朝上。固定方式用尼龙柱不要用金属螺丝直接硬拧避免安装应力造成壳体变形影响内部加速度计的零偏。如果装到防水舱里舱体要尽量密封但注意不要为追求绝对防水把传感器泡在高温环境里操作JY901工作温度上限一般在85℃左右焊接或热熔胶固定时要留出散热距离。3.2 JY901的数据帧格式详解JY901的串口输出是一种带帧头的数据包协议结构如下字节位置内容说明00x55帧头1数据类型0x51角度 / 0x52角速度 / 0x53加速度 / 0x54磁场2-3数据1低字节在前如AxL, AxH4-5数据2如AyL, AyH6-7数据3如AzL, AzH8-9温度/校验前数据各帧类型略有不同10校验和前10字节之和的低8位以常用的0x53加速度包为例一帧完整数据是55 53 AxL AxH AyL AyH AzL AzH TL TH SUM其中Ax、Ay、Az是16位有符号整数加速度真值 原始值 / 32768 × 量程。比如说量程是±16gAz原始值是8192那么Az 8192 / 32768 × 16 4g。我在项目里把JY901配置为加速度量程±4g波特率115200输出内容只保留加速度包这样STM32的解析压力小很多。3.3 JY901的配置指令JY901的寄存器配置也是通过串口发指令。配置寄存器前必须发送解锁指令FF AA 69 88 B5解锁后可以设置波特率、量程和回传内容。例如设置波特率为115200的指令是FF AA 01 01 00设置加速度量程为±4g的指令对应寄存器0x06需要查对应数据手册不同版本设置值不同建议直接用维特智能官方的上位机软件先配置好再通过“保存配置”指令写入模块内部Flash。这样每次上电就直接按你的配置工作不需要STM32每次开机都发一遍配置命令。但这里有个细节如果你计划用STM32给JY901上电配置上电后必须延时至少200ms再发指令因为JY901内部MCU启动需要时间指令发早了会被丢弃。4. STM32端数据采集与解析实现4.1 串口接收的两种方案STM32读取JY901数据最常规的方案是串口中断逐字节接收。每收到一个字节就进入状态机判断当前处于帧头、类型、数据还是校验阶段。这种方案的优点是实时性好、内存占用小缺点是代码逻辑写起来稍繁琐。还有一种方案是用串口空闲中断加DMAJY901的数据帧是连续收发的每一帧之间有时间间隔利用空闲中断可以判断一帧数据接收完毕然后从DMA缓冲区取出整帧数据解析。这个方案更高效但对DMA配置有一定要求适合已经熟悉HAL库DMA开发的读者。我实际用的是串口中断加状态机代码量不大逻辑清晰调试也方便。4.2 串口中断状态机解析代码核心解析思路是三个状态找帧头、收数据、校验。// 伪代码核心逻辑示意 uint8_t rx_buf[11]; uint8_t rx_cnt 0; uint8_t rx_state 0; void USART1_IRQHandler(void) { if (USART_GetITStatus(USART1, USART_IT_RXNE)) { uint8_t data USART_ReceiveData(USART1); switch (rx_state) { case 0: // 等待帧头 if (data 0x55) { rx_buf[0] data; rx_cnt 1; rx_state 1; } break; case 1: // 等待类型字节 rx_buf[1] data; if (data 0x53) { // 只处理加速度包 rx_cnt 2; rx_state 2; } else { rx_state 0; } break; case 2: // 接收其余数据 rx_buf[rx_cnt] data; if (rx_cnt 11) { // 校验 uint8_t sum 0; for (int i 0; i 10; i) sum rx_buf[i]; if (sum rx_buf[10]) { process_acc_frame(rx_buf); } rx_state 0; } break; } } }这段代码有个关键点当收到一个0x55帧头但下一个字节不是0x53时需要把状态机重置回等待帧头状态而不是继续在当前状态下累积。否则一旦数据不同步后面所有的帧都会解析错位而且因为校验和偶尔碰巧通过你很难发现数据是错的。我在这里吃过亏当时采集的波形图看着正常但算出来的波高误差离谱排查了很久才发现是状态机没有彻底复位导致的帧错位。4.3 数据打包与时间戳STM32解析完一帧加速度数据后不能直接发原始字节给PC。为了方便上位机处理我把它打包成文本格式通过另一个串口发送每行数据格式如下0.00, 0.12, 1.02分别是时间戳秒、X轴加速度g、Y轴加速度g和Z轴加速度g。时间戳用STM32的SysTick或者TIM定时器累加每次收到一帧数据就取当前时间。这里有个容易被忽略的问题JY901的输出频率通常设定为100Hz或50Hz如果STM32的处理不及时时间戳会丢帧。建议把JY901的输出频率设为20Hz或50Hz就够用了——波浪信号本身频率很低50Hz采样绰绰有余还能减少数据量。STM32通过USART2把打包好的文本数据发送到USB-TTL模块给PCUSART1负责接收JY901。两个串口独立工作互不干扰。5. 上位机频谱分析与波高反演5.1 Python端数据接收与预处理上位机我用Python实现pyserial库接收STM32发来的文本行numpy和scipy做FFT和谱分析。整个过程很简单import serial import numpy as np ser serial.Serial(COM3, 115200, timeout1) samples [] duration 300 # 采集300秒 fs 50 # 假设JY901输出频率50Hz samples_max duration * fs while len(samples) samples_max: line ser.readline() if line.startswith(b0.): parts line.decode().strip().split(,) ax, ay, az map(float, parts[1:4]) samples.append(az) # 只需要垂直方向加速度 samples np.array(samples) # 去掉均值消除重力分量和零偏 samples samples - np.mean(samples)需要说明的是如果JY901安装不完全水平Z轴加速度里会混入一部分水平加速度的投影这时候就不能简单去均值了。严谨的做法是用JY901输出的姿态角0x51包把加速度从机体坐标系转换到导航坐标系再取Z轴分量减去1g。但在浮标近似水平浮动、姿态角小于10度的情况下直接使用Z轴原始值减去均值造成的误差在可接受范围内。5.2 FFT功率谱与谱矩计算接下来做FFT并计算功率谱密度注意加窗函数以减少频谱泄漏from numpy.fft import rfft, rfftfreq N len(samples) window np.hanning(N) win_norm np.sum(window) / N # 归一化系数 spec rfft(samples * window) * 2 / (N * win_norm) freqs rfftfreq(N, 1/fs) psd (np.abs(spec)**2) / fs # 单边功率谱密度 # 限定波浪频段0.03Hz~0.5Hz band (freqs 0.03) (freqs 0.5) freqs_b freqs[band] psd_b psd[band] m0 np.trapz(psd_b, freqs_b) m2 np.trapz(freqs_b**2 * psd_b, freqs_b) hs 4 * np.sqrt(m0) tz np.sqrt(m0 / m2) tp 1.0 / freqs_b[np.argmax(psd_b)] print(fHs{hs:.3f} m, Tz{tz:.2f} s, Tp{tp:.2f} s)这里有两个细节要特别注意。第一fft结果要乘以2/N的系数不然幅值不对。第二功率谱密度PSD和功率谱是有区别的如果用np.abs(spec)**2直接当PSD用而不除以频率分辨率fs/N谱矩积分结果会差多个数量级。我在最初几次计算时Hs算出来是几十米就是这个系数没理清楚。5.3 频域带通滤波的必要性很多人问为什么不能直接对时域信号做数字带通滤波器然后再算波高当然也可以但频域处理有个额外好处你可以在同一个结果里同时得到PSD曲线、谱峰周期和波高还能直接观察数据质量。我用的是频域直接截断法在PSD计算完成后只保留0.03Hz到0.5Hz之间的谱值参与积分。等效于一个带通滤波但更直观。需要注意的是如果原始信号里存在明显的高频噪声比如船上电机振动、舱体共振会导致PSD在高频段出现尖峰这时应该先对时域数据做一次低通滤波截止频率1Hz再做FFT效果会好很多。5.4 标定方法与精度验证系统搭完之后必须做标定验证。我是这样做的把JY901固定在一个可以上下往复运动的平台上用电机驱动凸轮机构实现近似正弦运动记录平台的实际位移幅值用刻度尺或编码器测量然后对比算法算出来的位移。测试结果是当激励频率为0.2Hz、位移幅值为10cm时系统算出的Hs约为11cm误差在10%以内。这个量级的误差对于低成本波浪测量系统来说是可以接受的特别是当你要测的是2米以上的大浪时10%的误差在工程上完全够用。6. 常见问题与排查技巧实录6.1 JY901数据收不到或乱码这是我遇到最多的问题原因通常是三类第一串口接线接反。JY901的TXD要接STM32的RXRXD接STM32的TX很多新手习惯性地同名相连结果就是完全没数据。第二波特率不匹配。JY901老版本默认9600新版本默认115200要看清楚模块上的丝印或先接USB转TTL用官方上位机确认。第三共地问题前面提过两个模块供电的GND必须连通。排查顺序建议是先接USB转TTL到电脑直接看JY901原始数据——如果能出数据说明传感器OK再接STM32用串口调试助手看STM32转发的数据——如果乱码重点查波特率和接线如果完全无数据查中断配置和状态机。6.2 加速度数据有漂移JY901在刚上电的前几十秒加速度输出会有明显的温漂和零偏变化。我建议在正式采集前让系统上电预热至少2分钟然后做一次静态校准——把传感器水平静置采集1000组数据求平均作为零偏扣除。另外JY901可以执行加计校准具体指令是FF AA 67 00 00校准过程要把传感器分别六个面朝上静止放置各保持2秒以上官方上位机里有引导流程。如果跳过这步长期使用后的传感器零偏会比较大直接影响波浪积分精度。6.3 STM32死机或跑飞刚性浮标在海上颠簸时JY901的电源可能瞬时跌落STM32的HSE晶振质量如果不好也会在震动中失锁导致死机。解决办法是给整个系统加一个低压差LDO稳压如AMS1117-3.3并在STM32的VDD和GND之间加100uF电解电容和104陶瓷电容。如果条件允许程序里加独立看门狗IWDG每100ms喂一次狗彻底杜绝死机不重启的情况。另外STM32芯片包安装、Keil5兼容C51和STM32这类问题属于开发环境配置的基础问题也卡了很多人。建议直接用Keil MDK 5.36以上版本在Pack Installer里安装对应芯片的Device Family Pack不要手动去网上找乱七八糟的包版本不匹配会导致编译报警告甚至死机。6.4 算出来的波高数据明显偏大或偏小波高偏大的原因90%是低频噪声没过滤干净。可以把PSD曲线打出来看如果0.01Hz附近有一个巨大的能量峰那基本是传感器零偏或温度漂移不是真实波浪。第二种可能是样本长度不够导致频谱分辨率太低。要分辨0.05Hz的信号FFT的最低分辨率要低于0.01Hz即N/fs 100秒采样时间越长频域分辨率越高。我做实验时一般采10分钟数据保证分辨率在0.0017Hz左右。波高偏小的原因通常是JY901被固定得太刚性浮标的运动没有完全传递给传感器或者是传感器量程设置过小导致削顶。解决思路是检查安装方式和量程配置。6.5 应对恶劣天气或浪况的经验如果要在真实海况下使用建议把整个系统放到防水舱体里舱体用环氧树脂灌封但要注意JY901不能完全被导热胶包裹太厚否则散热不畅会影响精度。舱体引出线用防水航空插头接头处加涂硅胶密封。数据链路尽量用RS485长线传输不要用普通TTL电平走长线否则受到电机电磁干扰后误码率会很高。另外浮体设计不能太轻否则会被波浪打翻也不能太重否则惯性大会滤掉短周期波浪。初代原型我用的是直径30cm的泡沫浮球加配重块实测下来效果可以接受。7. 项目扩展方向这个项目的核心是“低成本惯性测波”基于这个架构可以很自然地扩展出三个方向第一是无线化。把STM32的USB-TTL换成ESP8266或LoRa模块数据通过Wi-Fi或LoRa发送到岸基基站配合物联网平台做实时波浪监测。如果换成4G模块还能实现近海远程遥测。第二是多点组网。用多个JY901传感器分布在不同的浮标上同时采集每个点的波动数据通过互相关分析可以估算波浪方向和传播速度。这算是从“单点波高测量”升级到“波浪方向谱测量”的雏形。第三是边缘计算。STM32端其实也可以跑FFTCortex-M4内核带FPU做1024点FFT只需要几毫秒。如果不想依赖PC可以把FFT和谱矩计算直接下放到STM32上输出结果就是波高和周期适合做嵌入式设备直接上报。不过说实话上位机用Python调起来确实方便想快速出成果的话先不折腾MCU端的复杂数学库更高效。我个人在实际做这个项目时最深的体会是这类系统调试起来硬件接线反而比算法更能耽误时间。串口解析、FFT算法这些都有成熟的框架可以参考但JY901的供电稳定性、安装方式、浮体的机械结构这些“非代码”因素才是决定最终数据质量的关键。如果你正在做类似的系统建议先拿一个小水槽或者干脆用一个上下晃动的台架验证算法再考虑往真实海况上扩展。先把链路跑通、把误差理解了后续任何扩展方向都会顺手很多。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进