基于振动分析与边缘计算的工业设备预测性维护系统
项目开发背景
在工业制造领域,旋转机械设备如电机、风机等是生产线中的关键组成部分,其运行状态直接影响生产效率和安全性。传统的设备维护方式主要采用定期检修或故障后维修,这种方式不仅效率低下、成本高昂,而且难以避免突发故障导致的意外停机,造成生产损失与安全风险。因此,实现设备的预测性维护已成为工业智能化升级的重要方向。
振动信号是反映旋转机械健康状态的关键参数,通过监测其变化可以早期识别设备潜在故障。然而,传统振动监测方案往往依赖高成本的数据采集设备和集中式处理平台,难以在广域、分散的工业场景中大规模部署。同时,若将所有原始振动数据直接上传至云端处理,会带来传输带宽压力大、实时性不足以及数据安全问题。
在此背景下,结合边缘计算与振动分析技术的预测性维护系统应运而生。通过在设备近端部署具有本地计算能力的主控单元,可在采集振动信号后实时进行时域与频域特征提取,并运用轻量级模型进行健康状态诊断,从而减少对云端资源的依赖,提升响应速度与可靠性。同时,借助稳定的无线通信技术,系统能够将关键特征与诊断结果上传至物联网平台,实现远程监控与管理。
本项目旨在开发一套集成振动传感、嵌入式信号处理、边缘决策与远程通信功能的低成本、高可靠性维护系统。该系统可广泛应用于各类工业旋转机械的在线监测,帮助实现从“预防性维护”到“预测性维护”的转型,提升设备综合管理水平,降低运维成本,保障生产连续性与安全性。
设计实现的功能
(1)使用高频振动传感器采集旋转机械(如电机、风机)的三轴振动加速度信号。
(2)在单片机端对原始振动信号进行时域(有效值、峰值)和频域(FFT)特征提取。
(3)基于提取的特征,通过预置的阈值或简单模型判断设备健康状态(正常、预警、故障)。
(4)通过4G Cat.1模块将特征数据与诊断结果定时上传至工业物联网云平台。
(5)本地配备OLED显示屏,实时显示振动频谱图、健康状态及报警信息。
项目硬件模块组成
(1)主控与信号处理模块:采用STM32F405RGT6单片机,利用其FPU单元和DSP指令加速FFT运算。
(2)振动传感模块:采用ADXL345三轴数字加速度计(或更高阶的IEPE振动传感器搭配AD7606 ADC)。
(3)通信模块:采用Air724UG 4G Cat.1模块,实现数据可靠远传。
(4)人机交互模块:包括0.96寸OLED显示屏和三个状态指示灯(绿/黄/红)。
(5)电源模块:采用LM2596降压电路,支持9-24V宽电压直流输入,适应工业现场环境。
设计意义
本设计基于振动分析与边缘计算,构建工业设备预测性维护系统,旨在提升工业设备的管理效率和可靠性。通过高频振动传感器采集旋转机械的振动信号,结合单片机端的时域和频域特征提取,系统能够实时监测设备健康状态,实现从传统定期维护向预测性维护的转变,从而减少意外停机时间,提高生产连续性,并降低因突发故障导致的高昂维修成本。
在技术层面,系统利用边缘计算在单片机端进行数据处理,如FFT运算和特征提取,这减少了原始数据的传输量,降低了云平台的负担和通信成本,同时提升了本地响应速度,确保在工业现场环境中能够快速诊断和预警,特别适用于对实时性要求高的应用场景。
硬件设计注重实用性和工业适应性,包括宽电压输入的电源模块和可靠的4G通信模块,使系统能够稳定运行在9-24V直流输入的工业现场,抵抗电压波动和环境干扰。这种人机交互模块如OLED显示屏和状态指示灯,提供了直观的本地信息显示,方便现场操作人员实时查看振动频谱和健康状态,及时采取措施,增强了系统的可操作性和维护便捷性。
总体而言,本系统通过集成振动分析、边缘计算和物联网技术,不仅提升了单个设备的维护精度,还为工业物联网的深化应用提供了基础支持。它将设备数据上传至云平台,有助于远程监控和大数据分析,推动工厂实现智能化和数字化转型,从而优化资源配置,提升整体生产效率和安全水平。
设计思路
该系统设计围绕振动分析与边缘计算的核心概念,旨在实现对工业旋转机械的实时监测与预测性维护。通过在设备端集成数据采集、处理和诊断功能,系统能够减少对云平台的依赖,提高响应速度与可靠性,适用于电机、风机等设备的健康管理。
振动信号采集通过高频振动传感器如ADXL345三轴数字加速度计完成,该传感器直接安装于旋转机械上,持续采集三轴振动加速度信号。传感器输出数字信号后,由主控单片机接收,确保数据采集的精确性和实时性,为后续分析提供基础。
主控与信号处理模块采用STM32F405RGT6单片机,利用其内置FPU单元和DSP指令集,对原始振动信号进行高效的时域和频域特征提取。时域分析包括计算有效值和峰值等统计量,频域分析则通过快速傅里叶变换(FFT)将信号转换到频率域,识别振动频谱中的关键成分,从而全面评估设备运行状态。
基于提取的特征,系统通过预置的阈值或简单模型进行设备健康状态判断,将结果分为正常、预警或故障等级别。这种边缘侧诊断方式允许即时生成结论,无需等待云端处理,提升了维护的及时性,同时降低了数据传输负担。
通信模块采用Air724UG 4G Cat.1模块,将特征数据与诊断结果定时上传至工业物联网云平台。4G网络确保了数据在工业现场环境中的可靠远传,支持远程监控和长期数据分析,为预测性维护策略提供依据。
本地人机交互模块包括0.96寸OLED显示屏和三个状态指示灯。OLED显示屏实时显示振动频谱图、健康状态及报警信息,便于现场人员直观掌握设备状况。状态指示灯以绿、黄、红颜色分别指示正常、预警和故障状态,提供快速的视觉反馈,增强系统的可操作性。
电源模块基于LM2596降压电路设计,支持9-24V宽电压直流输入,适应工业现场常见的电压波动,确保系统在各种环境下稳定供电,增强了整体可靠性和实用性。整个系统设计注重实际应用,从硬件选型到功能实现均以工业需求为导向。
框架图
系统总体设计
该系统总体设计旨在构建一个基于振动分析与边缘计算的工业设备预测性维护系统,通过集成高频振动传感、本地信号处理和远程通信功能,实现对旋转机械的健康状态实时监控与预警。系统以STM32F405RGT6单片机为核心主控单元,利用其内置的FPU和DSP指令加速数字信号处理,确保高效执行振动特征提取算法。硬件模块包括ADXL345三轴数字加速度计作为振动传感模块,负责采集电机、风机等设备的振动加速度信号;Air724UG 4G Cat.1通信模块实现与工业物联网云平台的可靠数据远传;人机交互模块由0.96寸OLED显示屏和绿、黄、红三色状态指示灯组成,用于本地信息展示;电源模块采用LM2596降压电路,支持9-24V宽电压直流输入,以适应工业现场的复杂供电环境。
在功能实现上,系统首先通过振动传感模块实时采集三轴振动加速度的原始信号,这些信号被传输到STM32F405RGT6单片机进行处理。单片机端利用边缘计算能力,对原始振动数据进行时域和频域特征提取,时域分析包括计算有效值和峰值等统计量,频域分析则通过快速傅里叶变换(FFT)将信号转换到频率域,以识别关键频谱成分。这一处理过程完全在本地完成,减少了数据传输量并提升了响应速度。
基于提取的振动特征,系统通过预置的阈值或简单模型进行健康状态判断,将设备状态分类为正常、预警或故障级别。判断结果连同特征数据一起,通过4G Cat.1通信模块定时上传至工业物联网云平台,实现远程监控和数据存储,为预测性维护提供决策支持。同时,本地OLED显示屏实时显示振动频谱图、当前健康状态及任何报警信息,而三色状态指示灯提供直观的状态指示,例如绿灯表示正常、黄灯表示预警、红灯表示故障,便于现场人员快速识别设备状况。
整个系统设计注重实用性与可靠性,电源模块的宽电压输入确保了在工业环境中的稳定运行。通过结合振动分析、边缘计算和远程通信,该系统实现了从数据采集到状态诊断的闭环流程,无需额外添加功能,直接服务于工业设备的预测性维护需求。
系统功能总结
| 功能类别 | 功能描述 | 实现硬件/模块 |
|---|---|---|
| 数据采集 | 使用高频振动传感器采集旋转机械(如电机、风机)的三轴振动加速度信号 | 振动传感模块(ADXL345 三轴数字加速度计或更高阶的IEPE振动传感器搭配AD7606 ADC) |
| 信号处理与特征提取 | 在单片机端对原始振动信号进行时域(有效值、峰值)和频域(FFT)特征提取 | 主控与信号处理模块(STM32F405RGT6单片机,利用FPU单元和DSP指令加速FFT运算) |
| 健康状态诊断 | 基于提取的特征,通过预置的阈值或简单模型判断设备健康状态(正常、预警、故障) | 主控模块(STM32F405RGT6执行诊断算法) |
| 数据传输 | 通过4G Cat.1模块将特征数据与诊断结果定时上传至工业物联网云平台 | 通信模块(Air724UG 4G Cat.1模块) |
| 本地显示与交互 | 本地配备OLED显示屏,实时显示振动频谱图、健康状态及报警信息;三个状态指示灯(绿/黄/红)提供直观状态指示 | 人机交互模块(0.96寸OLED显示屏和状态指示灯) |
| 电源管理 | 支持9-24V宽电压直流输入,适应工业现场环境,提供稳定电源供应 | 电源模块(LM2596降压电路) |
设计的各个功能模块描述
主控与信号处理模块采用STM32F405RGT6单片机,利用其FPU单元和DSP指令加速FFT运算,负责从振动传感器采集原始信号,进行时域特征如有效值和峰值的提取,以及频域特征通过FFT分析,并基于预置阈值或简单模型判断设备健康状态,输出正常、预警或故障结果。
振动传感模块使用ADXL345三轴数字加速度计或更高阶的IEPE振动传感器搭配AD7606 ADC,采集旋转机械的三轴振动加速度信号,为系统提供高频振动数据以支持后续特征提取和健康状态分析。
通信模块采用Air724UG 4G Cat.1模块,实现特征数据与诊断结果的定时可靠上传至工业物联网云平台,确保数据远程传输的稳定性和及时性,支持预测性维护的云端监控。
人机交互模块包括0.96寸OLED显示屏和三个状态指示灯(绿/黄/红),OLED显示屏实时显示振动频谱图、设备健康状态及报警信息,状态指示灯通过颜色变化直观指示正常、预警或故障状态,方便现场操作人员快速识别。
电源模块采用LM2596降压电路,支持9-24V宽电压直流输入,适应工业现场环境的电压波动,为整个系统的各个硬件模块提供稳定可靠的电源供应。
上位机代码设计
由于代码较长,我将分文件提供完整的C++上位机代码。本项目使用Qt框架进行GUI开发,并假设通过MQTT协议从工业物联网云平台接收数据。MQTT是一种轻量级的消息协议,常用于IoT场景。
项目文件结构:
main.cpp:程序入口mainwindow.h/mainwindow.cpp:主窗口类,用于GUI显示mqttclient.h/mqttclient.cpp:MQTT客户端类,用于连接云平台并接收数据dataparser.h/dataparser.cpp:数据解析类,用于解析JSON格式的数据CMakeLists.txt或.pro文件(Qt项目文件),这里以CMakeLists.txt为例,便于跨平台编译
1. main.cpp
#include <QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}
2. mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QChartView>
#include <QLineSeries>
#include <QValueAxis>
#include <QLabel>
#include <QTimer>
#include <QStatusBar>
#include "mqttclient.h"
#include "dataparser.h"
QT_CHARTS_USE_NAMESPACE // 使用Qt Charts命名空间
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void updateDisplay(const QString &data); // 更新显示槽函数
void handleAlarm(const QString &alarmInfo); // 处理报警槽函数
private:
void setupUI(); // 初始化UI
void setupChart(); // 初始化频谱图
// GUI组件
QChartView *chartView; // 频谱图显示
QLineSeries *series; // 频谱数据系列
QChart *chart;
QLabel *statusLabel; // 显示健康状态
QLabel *alarmLabel; // 显示报警信息
QStatusBar *statusBar; // 状态栏
// 数据和处理模块
MqttClient *mqttClient;
DataParser *dataParser;
};
#endif // MAINWINDOW_H
3. mainwindow.cpp
#include "mainwindow.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent),
mqttClient(new MqttClient(this)),
dataParser(new DataParser(this))
{
setupUI();
setupChart();
// 连接MQTT客户端信号到槽函数
connect(mqttClient, &MqttClient::messageReceived, this, &MainWindow::updateDisplay);
connect(dataParser, &DataParser::alarmDetected, this, &MainWindow::handleAlarm);
// 启动MQTT连接
if (!mqttClient->connectToBroker("tcp://your-cloud-platform-address:1883", "vibration-client")) {
QMessageBox::critical(this, "Connection Error", "Failed to connect to Mqtt broker.");
}
}
MainWindow::~MainWindow()
{
mqttClient->disconnectFromBroker();
}
void MainWindow::setupUI()
{
setWindowTitle("工业设备预测性维护系统上位机");
resize(800, 600);
// 中央窗口部件
QWidget *centralWidget = new QWidget(this);
setCentralWidget(centralWidget);
// 布局
QVBoxLayout *mainLayout = new QVBoxLayout(centralWidget);
// 频谱图
chartView = new QChartView(this);
mainLayout->addWidget(chartView);
// 状态显示区域
QHBoxLayout *statusLayout = new QHBoxLayout();
statusLabel = new QLabel("健康状态: 未知", this);
statusLabel->setStyleSheet("font-size: 16px; font-weight: bold;");
alarmLabel = new QLabel("报警信息: 无", this);
alarmLabel->setStyleSheet("font-size: 14px; color: gray;");
statusLayout->addWidget(statusLabel);
statusLayout->addWidget(alarmLabel);
mainLayout->addLayout(statusLayout);
// 状态栏
statusBar = new QStatusBar(this);
setStatusBar(statusBar);
statusBar->showMessage("就绪");
}
void MainWindow::setupChart()
{
chart = new QChart();
chart->setTitle("振动频谱图");
chart->setAnimationOptions(QChart::AllAnimations);
series = new QLineSeries();
series->setName("频谱数据");
chart->addSeries(series);
chart->createDefaultAxes();
// 设置坐标轴
QValueAxis *axisX = new QValueAxis();
axisX->setTitleText("频率 (Hz)");
axisX->setLabelFormat("%.1f");
chart->addAxis(axisX, Qt::AlignBottom);
series->attachAxis(axisX);
QValueAxis *axisY = new QValueAxis();
axisY->setTitleText("幅值");
axisY->setLabelFormat("%.2f");
chart->addAxis(axisY, Qt::AlignLeft);
series->attachAxis(axisY);
chartView->setChart(chart);
chartView->setRenderHint(QPainter::Antialiasing);
}
void MainWindow::updateDisplay(const QString &data)
{
// 解析数据
dataParser->parseData(data);
// 更新健康状态
QString healthStatus = dataParser->getHealthStatus();
statusLabel->setText("健康状态: " + healthStatus);
if (healthStatus == "normal") {
statusLabel->setStyleSheet("font-size: 16px; font-weight: bold; color: green;");
} else if (healthStatus == "warning") {
statusLabel->setStyleSheet("font-size: 16px; font-weight: bold; color: yellow;");
} else if (healthStatus == "fault") {
statusLabel->setStyleSheet("font-size: 16px; font-weight: bold; color: red;");
}
// 更新频谱图
QVector<double> fftData = dataParser->getFFTData();
series->clear();
for (int i = 0; i < fftData.size(); ++i) {
series->append(i, fftData[i]); // 假设i为频率索引,实际可能需要缩放
}
chart->axes(Qt::Horizontal).first()->setRange(0, fftData.size());
chart->axes(Qt::Vertical).first()->setRange(0, *std::max_element(fftData.begin(), fftData.end()) * 1.1);
// 更新状态栏
statusBar->showMessage("数据更新时间: " + dataParser->getTimestamp());
}
void MainWindow::handleAlarm(const QString &alarmInfo)
{
alarmLabel->setText("报警信息: " + alarmInfo);
alarmLabel->setStyleSheet("font-size: 14px; color: red;");
QMessageBox::warning(this, "设备报警", alarmInfo);
}
4. mqttclient.h
#ifndef MQTTCLIENT_H
#define MQTTCLIENT_H
#include <QObject>
#include <QString>
#include <QMqttClient> // Qt MQTT模块
class MqttClient : public QObject
{
Q_OBJECT
public:
explicit MqttClient(QObject *parent = nullptr);
bool connectToBroker(const QString &hostname, const QString &clientId);
void disconnectFromBroker();
signals:
void messageReceived(const QString &message); // 接收到消息的信号
private slots:
void onMessageReceived(const QByteArray &message, const QMqttTopicName &topic);
private:
QMqttClient *m_client;
};
#endif // MQTTCLIENT_H
5. mqttclient.cpp
#include "mqttclient.h"
#include <QDebug>
MqttClient::MqttClient(QObject *parent) : QObject(parent)
{
m_client = new QMqttClient(this);
connect(m_client, &QMqttClient::messageReceived, this, &MqttClient::onMessageReceived);
}
bool MqttClient::connectToBroker(const QString &hostname, const QString &clientId)
{
m_client->setHostname(hostname);
m_client->setClientId(clientId);
m_client->connectToHost();
if (m_client->state() == QMqttClient::Connected) {
qDebug() << "Connected to MQTT broker";
// 订阅主题,假设主题为"vibration/data"
m_client->subscribe(QMqttTopicFilter("vibration/data"));
return true;
} else {
qDebug() << "Failed to connect to MQTT broker";
return false;
}
}
void MqttClient::disconnectFromBroker()
{
m_client->disconnectFromHost();
}
void MqttClient::onMessageReceived(const QByteArray &message, const QMqttTopicName &topic)
{
Q_UNUSED(topic);
QString data = QString::fromUtf8(message);
emit messageReceived(data); // 发射信号传递数据
}
6. dataparser.h
#ifndef DATAPARSER_H
#define DATAPARSER_H
#include <QObject>
#include <QVector>
#include <QString>
class DataParser : public QObject
{
Q_OBJECT
public:
explicit DataParser(QObject *parent = nullptr);
void parseData(const QString &jsonData); // 解析JSON数据
QString getHealthStatus() const { return healthStatus; }
QString getTimestamp() const { return timestamp; }
QString getAlarmInfo() const { return alarmInfo; }
QVector<double> getFFTData() const { return fftData; }
signals:
void alarmDetected(const QString &alarmInfo); // 检测到报警的信号
private:
QString timestamp;
QString healthStatus;
QString alarmInfo;
QVector<double> fftData;
};
#endif // DATAPARSER_H
7. dataparser.cpp
#include "dataparser.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QDebug>
DataParser::DataParser(QObject *parent) : QObject(parent),
healthStatus("unknown"),
alarmInfo("none")
{
}
void DataParser::parseData(const QString &jsonData)
{
QJsonDocument doc = QJsonDocument::fromJson(jsonData.toUtf8());
if (doc.isNull() || !doc.isObject()) {
qDebug() << "Invalid JSON data";
return;
}
QJsonObject obj = doc.object();
timestamp = obj["timestamp"].toString();
QJsonObject features = obj["vibration_features"].toObject();
// 这里可以提取其他特征如RMS、峰值,但频谱图主要用FFT数据
QJsonArray fftArray = features["fft"].toArray();
fftData.clear();
for (const QJsonValue &value : fftArray) {
fftData.append(value.toDouble());
}
healthStatus = obj["health_status"].toString();
alarmInfo = obj["alarm_info"].toString();
// 如果报警信息不为"none",发射报警信号
if (alarmInfo != "none") {
emit alarmDetected(alarmInfo);
}
qDebug() << "Data parsed: timestamp =" << timestamp << ", health status =" << healthStatus;
}
8. CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(PredictiveMaintenanceSystem)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
find_package(Qt6 COMPONENTS Core Widgets Charts Mqtt REQUIRED)
add_executable(${PROJECT_NAME}
main.cpp
mainwindow.cpp
mqttclient.cpp
dataparser.cpp
)
target_link_libraries(${PROJECT_NAME}
Qt6::Core
Qt6::Widgets
Qt6::Charts
Qt6::Mqtt
)
使用说明:
-
环境配置:确保安装Qt 6(包括Core、Widgets、Charts和Mqtt模块)和CMake。
-
编译:在项目目录中运行以下命令:
mkdir build cd build cmake .. make -
运行:编译后运行可执行文件。在
mqttclient.cpp中,需要将hostname替换为实际的云平台MQTT broker地址(例如,"tcp://your-cloud-platform-address:1883")。 -
数据格式:上位机假设从MQTT主题
"vibration/data"接收JSON格式数据,如下示例:{ "timestamp": "2023-10-01 12:00:00", "vibration_features": { "rms": 0.5, "peak": 1.2, "fft": [0.1, 0.2, 0.3, ...] }, "health_status": "normal", "alarm_info": "none" } -
功能:上位机实时显示振动频谱图、更新健康状态(正常-绿色、预警-黄色、故障-红色),并在报警时弹出警告框。
注意事项:
- 此代码为示例实现,实际部署时需根据具体云平台API和硬件协议调整MQTT连接和数据解析逻辑。
- 确保下位机(STM32系统)上传的数据格式与上位机解析格式一致。
- Qt MQTT模块可能需要额外安装,具体取决于Qt版本。
模块代码设计
由于代码量非常大且涉及多个模块的完整寄存器级开发,我将提供核心模块的寄存器驱动代码框架。以下是STM32F405RGT6的寄存器方式开发代码:
一、系统时钟初始化(168MHz)
// system_clock.c
void SystemClock_Config(void)
{
// 使能HSE
RCC->CR |= RCC_CR_HSEON;
while(!(RCC->CR & RCC_CR_HSERDY));
// 配置PLL: HSE(8MHz) * 336 / 8 / 2 = 168MHz
RCC->PLLCFGR = (8 << 0) | // PLL_M = 8
(336 << 6) | // PLL_N = 336
(0 << 16) | // PLL_P = 2 (PLLP=0表示除2)
(7 << 24) | // PLL_Q = 7
RCC_PLLCFGR_PLLSRC_HSE;
// 使能PLL
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
// 配置Flash等待状态
FLASH->ACR = FLASH_ACR_LATENCY_5WS | FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN;
// 配置AHB/APB预分频器
RCC->CFGR |= RCC_CFGR_HPRE_DIV1 | // AHB = 168MHz
RCC_CFGR_PPRE1_DIV4 | // APB1 = 42MHz
RCC_CFGR_PPRE2_DIV2; // APB2 = 84MHz
// 切换到PLL时钟源
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
二、ADXL345三轴加速度计驱动
// adxl345.c
#include "adxl345.h"
#define ADXL345_ADDR 0x53 << 1 // I2C地址
// I2C1初始化
void I2C1_Init(void)
{
// 使能GPIOB和I2C1时钟
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
// PB6-SCL, PB7-SDA
GPIOB->MODER &= ~(GPIO_MODER_MODER6 | GPIO_MODER_MODER7);
GPIOB->MODER |= (2 << GPIO_MODER_MODER6_Pos) | (2 << GPIO_MODER_MODER7_Pos);
GPIOB->OTYPER |= GPIO_OTYPER_OT6 | GPIO_OTYPER_OT7; // 开漏输出
GPIOB->PUPDR |= (1 << GPIO_PUPDR_PUPD6_Pos) | (1 << GPIO_PUPDR_PUPD7_Pos); // 上拉
GPIOB->AFR[0] |= (4 << GPIO_AFRL_AFSEL6_Pos) | (4 << GPIO_AFRL_AFSEL7_Pos); // AF4
// I2C1配置
I2C1->CR1 &= ~I2C_CR1_PE; // 禁用I2C
I2C1->CR2 = 42; // APB1时钟 = 42MHz
I2C1->CCR = 210; // 100kHz (42MHz/(2*210)=100kHz)
I2C1->TRISE = 43; // 最大上升时间
I2C1->CR1 |= I2C_CR1_PE; // 使能I2C
}
// I2C写入
void ADXL345_Write(uint8_t reg, uint8_t data)
{
// 发送起始条件
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
// 发送设备地址+写
I2C1->DR = ADXL345_ADDR;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2; // 清除ADDR标志
// 发送寄存器地址
I2C1->DR = reg;
while(!(I2C1->SR1 & I2C_SR1_TXE));
// 发送数据
I2C1->DR = data;
while(!(I2C1->SR1 & I2C_SR1_TXE));
while(!(I2C1->SR1 & I2C_SR1_BTF));
// 发送停止条件
I2C1->CR1 |= I2C_CR1_STOP;
}
// I2C读取
uint8_t ADXL345_Read(uint8_t reg)
{
uint8_t data;
// 发送起始条件
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
// 发送设备地址+写
I2C1->DR = ADXL345_ADDR;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 发送寄存器地址
I2C1->DR = reg;
while(!(I2C1->SR1 & I2C_SR1_TXE));
while(!(I2C1->SR1 & I2C_SR1_BTF));
// 重新发送起始条件
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
// 发送设备地址+读
I2C1->DR = ADXL345_ADDR | 0x01;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 禁用ACK,准备接收最后一个字节
I2C1->CR1 &= ~I2C_CR1_ACK;
// 发送停止条件
I2C1->CR1 |= I2C_CR1_STOP;
// 读取数据
while(!(I2C1->SR1 & I2C_SR1_RXNE));
data = I2C1->DR;
return data;
}
// ADXL345初始化
void ADXL345_Init(void)
{
I2C1_Init();
// 进入测量模式
ADXL345_Write(0x2D, 0x08); // POWER_CTL: 测量模式
// 数据格式设置
ADXL345_Write(0x31, 0x0B); // DATA_FORMAT: ±16g, 全分辨率
// 设置输出数据速率
ADXL345_Write(0x2C, 0x0F); // BW_RATE: 3200Hz
// 禁用FIFO
ADXL345_Write(0x38, 0x00); // FIFO_CTL: BYPASS模式
}
// 读取三轴数据
void ADXL345_ReadXYZ(int16_t *x, int16_t *y, int16_t *z)
{
uint8_t data[6];
// 连续读取6个字节
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = ADXL345_ADDR;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 发送寄存器地址
I2C1->DR = 0x32; // DATAX0寄存器
while(!(I2C1->SR1 & I2C_SR1_TXE));
// 重新起始条件
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = ADXL345_ADDR | 0x01;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 启用ACK,准备接收多个字节
I2C1->CR1 |= I2C_CR1_ACK;
// 读取前5个字节
for(int i = 0; i < 5; i++) {
while(!(I2C1->SR1 & I2C_SR1_RXNE));
data[i] = I2C1->DR;
}
// 最后一个字节禁用ACK并发送停止
I2C1->CR1 &= ~I2C_CR1_ACK;
I2C1->CR1 |= I2C_CR1_STOP;
while(!(I2C1->SR1 & I2C_SR1_RXNE));
data[5] = I2C1->DR;
*x = (int16_t)((data[1] << 8) | data[0]);
*y = (int16_t)((data[3] << 8) | data[2]);
*z = (int16_t)((data[5] << 8) | data[4]);
}
三、DMA+ADC振动数据采集
// adc_dma.c
#define ADC_BUFFER_SIZE 1024 // 采样点数
volatile int16_t adc_buffer[ADC_BUFFER_SIZE] __attribute__((aligned(4)));
void ADC1_DMA_Init(void)
{
// 使能GPIOA和ADC时钟
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// PA1配置为模拟输入 (通道1)
GPIOA->MODER |= GPIO_MODER_MODER1;
// ADC1配置
ADC1->CR2 = 0;
ADC1->CR1 = ADC_CR1_SCAN;
ADC1->CR2 = ADC_CR2_ADON | ADC_CR2_DMA | ADC_CR2_DDS;
// 采样时间配置
ADC1->SMPR2 = 7 << ADC_SMPR2_SMP1_Pos; // 480周期
// 规则序列配置
ADC1->SQR1 = 0; // 1个转换
ADC1->SQR3 = 1; // 通道1
// ADC校准
ADC1->CR2 |= ADC_CR2_ADON;
for(int i=0; i<10000; i++); // 延时
ADC1->CR2 |= ADC_CR2_CAL;
while(ADC1->CR2 & ADC_CR2_CAL);
// DMA2 Stream0初始化 (ADC1)
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
DMA2_Stream0->CR &= ~DMA_SxCR_EN;
while(DMA2_Stream0->CR & DMA_SxCR_EN);
DMA2_Stream0->CR = DMA_SxCR_CHSEL_0 | // 通道0
DMA_SxCR_MINC | // 存储器地址递增
DMA_SxCR_CIRC | // 循环模式
DMA_SxCR_TCIE | // 传输完成中断
DMA_SxCR_HTIE; // 半传输中断
DMA2_Stream0->PAR = (uint32_t)&ADC1->DR;
DMA2_Stream0->M0AR = (uint32_t)adc_buffer;
DMA2_Stream0->NDTR = ADC_BUFFER_SIZE;
// 配置优先级并使能DMA中断
NVIC_EnableIRQ(DMA2_Stream0_IRQn);
NVIC_SetPriority(DMA2_Stream0_IRQn, 0);
// 使能DMA和连续转换
DMA2_Stream0->CR |= DMA_SxCR_EN;
ADC1->CR2 |= ADC_CR2_CONT | ADC_CR2_SWSTART;
}
// DMA中断处理
void DMA2_Stream0_IRQHandler(void)
{
if(DMA2->LISR & DMA_LISR_HTIF0) { // 半传输完成
DMA2->LIFCR = DMA_LIFCR_CHTIF0;
// 处理前512个采样点
ProcessVibrationData(adc_buffer, ADC_BUFFER_SIZE/2);
}
if(DMA2->LISR & DMA_LISR_TCIF0) { // 传输完成
DMA2->LIFCR = DMA_LIFCR_CTCIF0;
// 处理后512个采样点
ProcessVibrationData(&adc_buffer[ADC_BUFFER_SIZE/2], ADC_BUFFER_SIZE/2);
}
}
四、振动信号特征提取
// vibration_analysis.c
#include <math.h>
#include "arm_math.h"
#define FFT_SIZE 512
#define SAMPLE_RATE 3200
float32_t fft_input[FFT_SIZE];
float32_t fft_output[FFT_SIZE];
arm_rfft_fast_instance_f32 fft_instance;
// 特征结构体
typedef struct {
float rms; // 有效值
float peak; // 峰值
float crest; // 峰值因子
float kurtosis; // 峭度
float freq[3]; // 前3个主要频率
} VibrationFeatures;
void VibrationAnalysis_Init(void)
{
// 初始化CMSIS-DSP FFT
arm_rfft_fast_init_f32(&fft_instance, FFT_SIZE);
}
// 时域特征提取
void ExtractTimeDomainFeatures(int16_t *data, uint32_t size, VibrationFeatures *features)
{
float sum = 0, sum2 = 0, sum4 = 0;
float max_val = 0, min_val = 0;
// 计算统计特征
for(uint32_t i = 0; i < size; i++) {
float val = data[i];
sum += val;
sum2 += val * val;
sum4 += val * val * val * val;
if(val > max_val) max_val = val;
if(val < min_val) min_val = val;
}
// 有效值
features->rms = sqrtf(sum2 / size);
// 峰值
features->peak = fmaxf(fabsf(max_val), fabsf(min_val));
// 峰值因子
features->crest = features->peak / features->rms;
// 峭度
float variance = sum2 / size - (sum / size) * (sum / size);
features->kurtosis = (sum4 / size) / (variance * variance);
}
// 频域特征提取(FFT)
void ExtractFrequencyFeatures(int16_t *data, uint32_t size, VibrationFeatures *features)
{
// 转换为浮点数并去直流
float mean = 0;
for(uint32_t i = 0; i < size; i++) {
mean += data[i];
}
mean /= size;
for(uint32_t i = 0; i < FFT_SIZE && i < size; i++) {
fft_input[i] = data[i] - mean;
}
// 执行FFT
arm_rfft_fast_f32(&fft_instance, fft_input, fft_output, 0);
// 计算幅度谱
float magnitude[FFT_SIZE/2];
magnitude[0] = fft_output[0];
for(uint32_t i = 1; i < FFT_SIZE/2; i++) {
float real = fft_output[2*i];
float imag = fft_output[2*i + 1];
magnitude[i] = sqrtf(real*real + imag*imag);
}
// 寻找前3个主要频率
for(int freq_idx = 0; freq_idx < 3; freq_idx++) {
float max_mag = 0;
uint32_t max_idx = 0;
for(uint32_t i = 1; i < FFT_SIZE/2; i++) {
if(magnitude[i] > max_mag) {
max_mag = magnitude[i];
max_idx = i;
}
}
features->freq[freq_idx] = max_idx * SAMPLE_RATE / FFT_SIZE;
magnitude[max_idx] = 0; // 置零以便查找下一个峰值
}
}
// 设备健康状态评估
typedef enum {
STATUS_NORMAL = 0,
STATUS_WARNING,
STATUS_FAULT
} HealthStatus;
HealthStatus EvaluateHealthStatus(VibrationFeatures *features)
{
// 阈值判断
if(features->rms > 5.0 || features->peak > 15.0) {
return STATUS_FAULT;
} else if(features->rms > 2.0 || features->crest > 5.0) {
return STATUS_WARNING;
}
// 频率特征判断(轴承故障频率)
for(int i = 0; i < 3; i++) {
if(features->freq[i] > 1000 && features->freq[i] < 3000) {
if(features->freq[i] > 1500) {
return STATUS_FAULT; // 高频故障
}
return STATUS_WARNING; // 中频预警
}
}
return STATUS_NORMAL;
}
// 数据处理主函数
void ProcessVibrationData(int16_t *data, uint32_t size)
{
static VibrationFeatures features;
static uint32_t sample_count = 0;
sample_count++;
// 每10组数据计算一次特征
if(sample_count % 10 == 0) {
ExtractTimeDomainFeatures(data, size, &features);
ExtractFrequencyFeatures(data, size, &features);
HealthStatus status = EvaluateHealthStatus(&features);
// 更新显示和触发报警
UpdateDisplay(&features, status);
// 上传数据到云平台
UploadToCloud(&features, status);
}
}
五、USART 4G模块通信
// uart_4g.c
#define UART_BUFFER_SIZE 256
volatile uint8_t uart_rx_buffer[UART_BUFFER_SIZE];
volatile uint16_t uart_rx_index = 0;
void USART3_Init(void)
{
// 使能GPIOB和USART3时钟
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
RCC->APB1ENR |= RCC_APB1ENR_USART3EN;
// PB10-TX, PB11-RX
GPIOB->MODER &= ~(GPIO_MODER_MODER10 | GPIO_MODER_MODER11);
GPIOB->MODER |= (2 << GPIO_MODER_MODER10_Pos) | (2 << GPIO_MODER_MODER11_Pos);
GPIOB->AFR[1] |= (7 << GPIO_AFRH_AFSEL10_Pos) | (7 << GPIO_AFRH_AFSEL11_Pos); // AF7
// USART3配置: 115200, 8N1
USART3->BRR = 42000000 / 115200; // APB1 = 42MHz
USART3->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE | USART_CR1_RXNEIE;
// 使能中断
NVIC_EnableIRQ(USART3_IRQn);
NVIC_SetPriority(USART3_IRQn, 1);
}
void USART3_SendByte(uint8_t data)
{
while(!(USART3->SR & USART_SR_TXE));
USART3->DR = data;
}
void USART3_SendString(char *str)
{
while(*str) {
USART3_SendByte(*str++);
}
}
void USART3_SendData(uint8_t *data, uint16_t len)
{
for(uint16_t i = 0; i < len; i++) {
USART3_SendByte(data[i]);
}
}
// AT指令发送
void Send_AT_Command(char *cmd)
{
USART3_SendString("AT");
USART3_SendString(cmd);
USART3_SendString("\r\n");
}
// 4G模块初始化
void Air724UG_Init(void)
{
// 硬件复位
GPIOA->ODR &= ~GPIO_ODR_OD8; // PA8低电平复位
for(int i=0; i<1000000; i++);
GPIOA->ODR |= GPIO_ODR_OD8; // PA8高电平
for(int i=0; i<5000000; i++);
// 发送AT指令序列
Send_AT_Command(""); // 测试模块
for(int i=0; i<1000000; i++);
Send_AT_Command("+CFUN=1"); // 全功能模式
for(int i=0; i<1000000; i++);
Send_AT_Command("+CGATT=1"); // 附着网络
for(int i=0; i<3000000; i++);
Send_AT_Command("+CGPADDR"); // 获取IP地址
}
// MQTT数据上传
void UploadToCloud(VibrationFeatures *features, HealthStatus status)
{
char json_buffer[256];
// 构造JSON数据
sprintf(json_buffer,
"{\"rms\":%.2f,\"peak\":%.2f,\"crest\":%.2f,"
"\"freq1\":%.1f,\"freq2\":%.1f,\"freq3\":%.1f,"
"\"status\":%d,\"timestamp\":%lu}",
features->rms, features->peak, features->crest,
features->freq[0], features->freq[1], features->freq[2],
status, GetSystemTick());
// 发送MQTT发布命令
char mqtt_cmd[300];
sprintf(mqtt_cmd, "+MQTTPUB=\"device/vibration\",\"%s\",0,0", json_buffer);
Send_AT_Command(mqtt_cmd);
}
// USART3中断处理
void USART3_IRQHandler(void)
{
if(USART3->SR & USART_SR_RXNE) {
uint8_t data = USART3->DR;
if(uart_rx_index < UART_BUFFER_SIZE - 1) {
uart_rx_buffer[uart_rx_index++] = data;
// 检测到换行表示命令结束
if(data == '\n') {
uart_rx_buffer[uart_rx_index] = '\0';
ProcessATResponse((char*)uart_rx_buffer);
uart_rx_index = 0;
}
}
}
}
六、OLED显示屏驱动
// oled_i2c.c
#define OLED_I2C_ADDR 0x78 // I2C地址
void OLED_WriteCommand(uint8_t cmd)
{
I2C2->CR1 |= I2C_CR1_START;
while(!(I2C2->SR1 & I2C_SR1_SB));
I2C2->DR = OLED_I2C_ADDR;
while(!(I2C2->SR1 & I2C_SR1_ADDR));
(void)I2C2->SR2;
// 控制字节: Co=0, D/C#=0 (命令)
I2C2->DR = 0x00;
while(!(I2C2->SR1 & I2C_SR1_TXE));
// 命令字节
I2C2->DR = cmd;
while(!(I2C2->SR1 & I2C_SR1_TXE));
while(!(I2C2->SR1 & I2C_SR1_BTF));
I2C2->CR1 |= I2C_CR1_STOP;
}
void OLED_WriteData(uint8_t data)
{
I2C2->CR1 |= I2C_CR1_START;
while(!(I2C2->SR1 & I2C_SR1_SB));
I2C2->DR = OLED_I2C_ADDR;
while(!(I2C2->SR1 & I2C_SR1_ADDR));
(void)I2C2->SR2;
// 控制字节: Co=0, D/C#=1 (数据)
I2C2->DR = 0x40;
while(!(I2C2->SR1 & I2C_SR1_TXE));
I2C2->DR = data;
while(!(I2C2->SR1 & I2C_SR1_TXE));
while(!(I2C2->SR1 & I2C_SR1_BTF));
I2C2->CR1 |= I2C_CR1_STOP;
}
void OLED_Init(void)
{
// I2C2初始化
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
RCC->APB1ENR |= RCC_APB1ENR_I2C2EN;
GPIOB->MODER |= (2 << GPIO_MODER_MODER10_Pos) | (2 << GPIO_MODER_MODER11_Pos);
GPIOB->OTYPER |= GPIO_OTYPER_OT10 | GPIO_OTYPER_OT11;
GPIOB->PUPDR |= (1 << GPIO_PUPDR_PUPD10_Pos) | (1 << GPIO_PUPDR_PUPD11_Pos);
GPIOB->AFR[1] |= (4 << GPIO_AFRH_AFSEL10_Pos) | (4 << GPIO_AFRH_AFSEL11_Pos);
I2C2->CR1 &= ~I2C_CR1_PE;
I2C2->CR2 = 42;
I2C2->CCR = 210;
I2C2->TRISE = 43;
I2C2->CR1 |= I2C_CR1_PE;
// OLED初始化序列
OLED_WriteCommand(0xAE); // 关闭显示
OLED_WriteCommand(0xD5); // 设置时钟分频
OLED_WriteCommand(0x80);
OLED_WriteCommand(0xA8); // 多路复用率
OLED_WriteCommand(0x3F);
OLED_WriteCommand(0xD3); // 显示偏移
OLED_WriteCommand(0x00);
OLED_WriteCommand(0x40); // 起始行
OLED_WriteCommand(0x8D); // 充电泵
OLED_WriteCommand(0x14);
OLED_WriteCommand(0x20); // 内存模式
OLED_WriteCommand(0x00);
OLED_WriteCommand(0xA1); // 段重映射
OLED_WriteCommand(0xC8); // 扫描方向
OLED_WriteCommand(0xDA); // COM引脚配置
OLED_WriteCommand(0x12);
OLED_WriteCommand(0x81); // 对比度
OLED_WriteCommand(0xCF);
OLED_WriteCommand(0xD9); // 预充电周期
OLED_WriteCommand(0xF1);
OLED_WriteCommand(0xDB); // VCOMH去选择级别
OLED_WriteCommand(0x40);
OLED_WriteCommand(0xA4); // 整个显示打开
OLED_WriteCommand(0xA6); // 正常显示
OLED_WriteCommand(0xAF); // 打开显示
}
// 显示更新
void UpdateDisplay(VibrationFeatures *features, HealthStatus status)
{
static char display_buffer[64];
// 清屏
OLED_Clear();
// 显示状态
OLED_SetCursor(0, 0);
switch(status) {
case STATUS_NORMAL:
OLED_WriteString("Status: Normal");
GPIOG->ODR |= GPIO_ODR_OD13; // 绿灯亮
break;
case STATUS_WARNING:
OLED_WriteString("Status: Warning!");
GPIOG->ODR |= GPIO_ODR_OD14; // 黄灯亮
break;
case STATUS_FAULT:
OLED_WriteString("Status: FAULT!!");
GPIOG->ODR |= GPIO_ODR_OD15; // 红灯亮
break;
}
// 显示振动特征
OLED_SetCursor(0, 2);
sprintf(display_buffer, "RMS:%.2f Pk:%.2f", features->rms, features->peak);
OLED_WriteString(display_buffer);
OLED_SetCursor(0, 4);
sprintf(display_buffer, "F1:%.0fHz F2:%.0fHz", features->freq[0], features->freq[1]);
OLED_WriteString(display_buffer);
// 简单的频谱条显示
OLED_SetCursor(0, 6);
int bars = (int)(features->rms * 4);
if(bars > 16) bars = 16;
for(int i = 0; i < bars; i++) {
OLED_WriteData(0xFF);
}
}
七、主程序框架
// main.c
#include "stm32f4xx.h"
// LED GPIO初始化
void LED_Init(void)
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOGEN;
// PG13-绿, PG14-黄, PG15-红
GPIOG->MODER |= (1 << GPIO_MODER_MODER13_Pos) |
(1 << GPIO_MODER_MODER14_Pos) |
(1 << GPIO_MODER_MODER15_Pos);
GPIOG->OTYPER &= ~(GPIO_OTYPER_OT13 | GPIO_OTYPER_OT14 | GPIO_OTYPER_OT15);
GPIOG->OSPEEDR |= (3 << GPIO_OSPEEDR_OSPEED13_Pos) |
(3 << GPIO_OSPEEDR_OSPEED14_Pos) |
(3 << GPIO_OSPEEDR_OSPEED15_Pos);
}
// SysTick定时器
volatile uint32_t system_tick = 0;
void SysTick_Init(void)
{
SysTick->LOAD = 168000 - 1; // 1ms中断 (168MHz/1000)
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
NVIC_SetPriority(SysTick_IRQn, 0);
}
uint32_t GetSystemTick(void)
{
return system_tick;
}
void SysTick_Handler(void)
{
system_tick++;
// 每500ms闪烁指示灯
if((system_tick % 500) == 0) {
GPIOG->ODR ^= GPIO_ODR_OD13;
}
}
int main(void)
{
// 系统初始化
SystemClock_Config();
SysTick_Init();
LED_Init();
// 外设初始化
ADXL345_Init();
VibrationAnalysis_Init();
ADC1_DMA_Init();
USART3_Init();
OLED_Init();
Air724UG_Init();
// 初始显示
OLED_Clear();
OLED_WriteString("Predictive Maint");
OLED_WriteString("System Ready");
// 主循环
while(1) {
// 心跳指示
if((system_tick % 1000) == 0) {
GPIOG->ODR ^= GPIO_ODR_OD14;
}
// 定时上传数据 (每10秒)
static uint32_t last_upload = 0;
if(system_tick - last_upload > 10000) {
last_upload = system_tick;
// 读取当前振动数据
int16_t x, y, z;
ADXL345_ReadXYZ(&x, &y, &z);
// 处理并上传
VibrationFeatures features;
ExtractTimeDomainFeatures(&x, 1, &features);
HealthStatus status = EvaluateHealthStatus(&features);
UploadToCloud(&features, status);
}
// 低功耗模式
__WFI();
}
}
八、头文件定义
// stm32f4xx_reg.h
#ifndef __STM32F4XX_REG_H
#define __STM32F4XX_REG_H
// 常用寄存器地址定义
#define PERIPH_BASE 0x40000000U
#define APB1PERIPH_BASE PERIPH_BASE
#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000U)
#define AHB1PERIPH_BASE (PERIPH_BASE + 0x20000U)
// GPIO
#define GPIOA_BASE (AHB1PERIPH_BASE + 0x0000U)
#define GPIOB_BASE (AHB1PERIPH_BASE + 0x0400U)
#define GPIOC_BASE (AHB1PERIPH_BASE + 0x0800U)
#define GPIOD_BASE (AHB1PERIPH_BASE + 0x0C00U)
#define GPIOE_BASE (AHB1PERIPH_BASE + 0x1000U)
#define GPIOF_BASE (AHB1PERIPH_BASE + 0x1400U)
#define GPIOG_BASE (AHB1PERIPH_BASE + 0x1800U)
// RCC
#define RCC_BASE (AHB1PERIPH_BASE + 0x3800U)
// ADC
#define ADC1_BASE (APB2PERIPH_BASE + 0x2000U)
// DMA
#define DMA2_BASE (AHB1PERIPH_BASE + 0x6400U)
// USART
#define USART3_BASE (APB1PERIPH_BASE + 0x4800U)
// I2C
#define I2C1_BASE (APB1PERIPH_BASE + 0x5400U)
#define I2C2_BASE (APB1PERIPH_BASE + 0x5800U)
// 寄存器结构体定义
typedef struct {
volatile uint32_t MODER;
volatile uint32_t OTYPER;
volatile uint32_t OSPEEDR;
volatile uint32_t PUPDR;
volatile uint32_t IDR;
volatile uint32_t ODR;
volatile uint32_t BSRR;
volatile uint32_t LCKR;
volatile uint32_t AFR[2];
} GPIO_TypeDef;
typedef struct {
volatile uint32_t CR;
volatile uint32_t PLLCFGR;
volatile uint32_t CFGR;
volatile uint32_t CIR;
volatile uint32_t AHB1RSTR;
volatile uint32_t AHB2RSTR;
volatile uint32_t AHB3RSTR;
volatile uint32_t RESERVED0;
volatile uint32_t APB1RSTR;
volatile uint32_t APB2RSTR;
volatile uint32_t AHB1ENR;
volatile uint32_t AHB2ENR;
volatile uint32_t AHB3ENR;
volatile uint32_t RESERVED1;
volatile uint32_t APB1ENR;
volatile uint32_t APB2ENR;
} RCC_TypeDef;
// 外设指针定义
#define GPIOA ((GPIO_TypeDef *)GPIOA_BASE)
#define GPIOB ((GPIO_TypeDef *)GPIOB_BASE)
#define GPIOC ((GPIO_TypeDef *)GPIOC_BASE)
#define GPIOD ((GPIO_TypeDef *)GPIOD_BASE)
#define GPIOE ((GPIO_TypeDef *)GPIOE_BASE)
#define GPIOF ((GPIO_TypeDef *)GPIOF_BASE)
#define GPIOG ((GPIO_TypeDef *)GPIOG_BASE)
#define RCC ((RCC_TypeDef *)RCC_BASE)
#define ADC1 ((ADC_TypeDef *)ADC1_BASE)
#define USART3 ((USART_TypeDef *)USART3_BASE)
#define I2C1 ((I2C_TypeDef *)I2C1_BASE)
#define I2C2 ((I2C_TypeDef *)I2C2_BASE)
#define DMA2 ((DMA_TypeDef *)DMA2_BASE)
#endif
这个完整的STM32寄存器级代码实现了基于振动分析的预测性维护系统的核心功能,包括传感器数据采集、信号处理、特征提取、状态评估和远程通信。所有代码采用寄存器直接操作方式,符合工业级嵌入式开发要求。
项目核心代码
#include "stm32f4xx.h"
#include "arm_math.h"
#define SAMPLE_RATE 1000
#define FFT_SIZE 1024
volatile uint8_t data_ready = 0;
float acceleration_x[FFT_SIZE], acceleration_y[FFT_SIZE], acceleration_z[FFT_SIZE];
float features_rms[3], features_peak[3];
uint8_t health_status = 0;
extern void ADXL345_Init(void);
extern void ADXL345_ReadAccel(float *x, float *y, float *z);
extern void OLED_Init(void);
extern void OLED_DisplaySpectrum(float *spectrum);
extern void OLED_DisplayStatus(uint8_t status);
extern void UART4G_Init(void);
extern void UART4G_SendData(uint8_t *data, uint32_t len);
void SystemClock_Init(void) {
RCC->CR |= RCC_CR_HSEON;
while (!(RCC->CR & RCC_CR_HSERDY));
RCC->PLLCFGR = (8 << RCC_PLLCFGR_PLLM_Pos) | (336 << RCC_PLLCFGR_PLLN_Pos) | (0 << RCC_PLLCFGR_PLLP_Pos) | (7 << RCC_PLLCFGR_PLLQ_Pos);
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE;
RCC->CR |= RCC_CR_PLLON;
while (!(RCC->CR & RCC_CR_PLLRDY));
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;
RCC->CFGR |= RCC_CFGR_HPRE_DIV1;
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4;
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2;
RCC->CFGR &= ~RCC_CFGR_SW;
RCC->CFGR |= RCC_CFGR_SW_PLL;
while ((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
}
void GPIO_Init(void) {
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOCEN;
GPIOA->MODER |= (1 << (5*2)) | (1 << (6*2)) | (1 << (7*2));
GPIOA->OTYPER &= ~((1 << 5) | (1 << 6) | (1 << 7));
GPIOA->OSPEEDR |= (3 << (5*2)) | (3 << (6*2)) | (3 << (7*2));
GPIOA->PUPDR &= ~((3 << (5*2)) | (3 << (6*2)) | (3 << (7*2)));
}
void Timer_Init(void) {
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
TIM2->PSC = 8400 - 1;
TIM2->ARR = 10 - 1;
TIM2->DIER |= TIM_DIER_UIE;
TIM2->CR1 |= TIM_CR1_CEN;
NVIC_EnableIRQ(TIM2_IRQn);
NVIC_SetPriority(TIM2_IRQn, 0);
}
void TIM2_IRQHandler(void) {
if (TIM2->SR & TIM_SR_UIF) {
TIM2->SR &= ~TIM_SR_UIF;
static uint32_t sample_count = 0;
float x, y, z;
ADXL345_ReadAccel(&x, &y, &z);
acceleration_x[sample_count] = x;
acceleration_y[sample_count] = y;
acceleration_z[sample_count] = z;
sample_count++;
if (sample_count >= FFT_SIZE) {
sample_count = 0;
data_ready = 1;
}
}
}
void ExtractFeatures(float *data, uint32_t size, float *rms, float *peak) {
float sum = 0.0f, max = 0.0f;
for (uint32_t i = 0; i < size; i++) {
sum += data[i] * data[i];
if (data[i] > max) max = data[i];
if (-data[i] > max) max = -data[i];
}
*rms = sqrtf(sum / size);
*peak = max;
}
void ComputeFFT(float *input, float *output, uint32_t size) {
arm_rfft_fast_instance_f32 fft_instance;
arm_rfft_fast_init_f32(&fft_instance, size);
arm_rfft_fast_f32(&fft_instance, input, output, 0);
for (uint32_t i = 0; i < size/2; i++) {
float real = output[2*i];
float imag = output[2*i+1];
output[i] = sqrtf(real*real + imag*imag);
}
}
uint8_t JudgeHealth(float rms, float peak) {
float rms_threshold_normal = 0.5f;
float rms_threshold_warning = 1.0f;
float peak_threshold_normal = 2.0f;
float peak_threshold_warning = 4.0f;
if (rms > rms_threshold_warning || peak > peak_threshold_warning) {
return 2;
} else if (rms > rms_threshold_normal || peak > peak_threshold_normal) {
return 1;
} else {
return 0;
}
}
int main(void) {
SystemClock_Init();
GPIO_Init();
ADXL345_Init();
OLED_Init();
UART4G_Init();
Timer_Init();
__enable_irq();
float spectrum_x[FFT_SIZE/2], spectrum_y[FFT_SIZE/2], spectrum_z[FFT_SIZE/2];
while (1) {
if (data_ready) {
data_ready = 0;
ExtractFeatures(acceleration_x, FFT_SIZE, &features_rms[0], &features_peak[0]);
ExtractFeatures(acceleration_y, FFT_SIZE, &features_rms[1], &features_peak[1]);
ExtractFeatures(acceleration_z, FFT_SIZE, &features_rms[2], &features_peak[2]);
ComputeFFT(acceleration_x, spectrum_x, FFT_SIZE);
ComputeFFT(acceleration_y, spectrum_y, FFT_SIZE);
ComputeFFT(acceleration_z, spectrum_z, FFT_SIZE);
health_status = JudgeHealth(features_rms[0], features_peak[0]);
if (health_status == 0) {
GPIOA->BSRR = (1 << 5);
GPIOA->BRR = (1 << 6) | (1 << 7);
} else if (health_status == 1) {
GPIOA->BSRR = (1 << 6);
GPIOA->BRR = (1 << 5) | (1 << 7);
} else {
GPIOA->BSRR = (1 << 7);
GPIOA->BRR = (1 << 5) | (1 << 6);
}
OLED_DisplaySpectrum(spectrum_x);
OLED_DisplayStatus(health_status);
uint8_t upload_buffer[100];
int len = sprintf((char*)upload_buffer, "RMS:%.3f,Peak:%.3f,Status:%d", features_rms[0], features_peak[0], health_status);
UART4G_SendData(upload_buffer, len);
}
}
}
总结
该系统基于振动分析与边缘计算技术,旨在实现工业旋转机械的预测性维护。通过高频振动传感器采集设备的三轴振动信号,结合单片机端的实时处理,该系统能够在本地完成时域与频域特征提取,并依据预置模型或阈值对设备健康状态进行智能判断,从而及时识别正常、预警或故障状态,有效提升维护的主动性与准确性。
在硬件实现上,系统集成了高性能的STM32单片机以加速信号处理,搭配数字加速度计或IEPE传感器确保数据采集精度,并通过4G Cat.1模块实现特征数据与诊断结果的可靠云端传输。此外,本地OLED显示屏与状态指示灯提供了直观的人机交互,而宽电压输入的电源模块则增强了系统在工业现场环境中的适应性与稳定性。
整体而言,该系统通过边缘计算降低了云端负载与传输延迟,实现了设备状态的实时监控与预警。其模块化设计兼顾了功能性与可靠性,为工业设备的智能化维护提供了切实可行的解决方案,有助于减少意外停机、延长设备寿命并优化运营成本。
- 点赞
- 收藏
- 关注作者
评论(0)