一、前言
1.1 项目开发背景
(文末可以下载相关资料,软件)
随着电子测量技术、嵌入式控制技术以及微机电系统(MEMS)传感器技术的快速发展,姿态检测与水平测量技术已经广泛应用于工程施工、机械安装、设备调平、仪器校准以及日常生活等多个领域。传统水平仪主要依靠液体气泡进行水平状态判断,虽然结构简单、使用方便,但存在测量结果不够直观、无法量化角度变化、适应复杂环境能力较弱等问题。因此,利用电子传感器实现数字化、智能化水平检测逐渐成为一种重要的发展方向。
Arduino UNO作为典型的开源嵌入式开发平台,具有硬件资源丰富、开发门槛低、生态完善以及扩展能力强等特点,非常适合用于传感器数据采集与智能控制类项目开发。借助Arduino平台,可以快速完成硬件接口搭建、姿态算法实现以及显示交互设计,大幅降低系统开发复杂度,提高系统的可实现性和工程应用价值。
与此同时,MPU6050作为集成三轴加速度计和三轴陀螺仪的惯性测量单元(IMU),能够实时获取设备运动状态和空间姿态信息。通过对采集到的六轴数据进行计算处理,可以得到俯仰角(Pitch)与横滚角(Roll)等关键姿态参数,从而实现对设备水平状态的精准判断。相比传统机械式水平仪,该方案不仅能够显示具体角度值,还能够实时反映设备倾斜变化,提高测量的直观性与准确性。
为了进一步提升设备使用体验,本项目结合OLED显示技术,实现水平状态、倾斜状态以及姿态角度的实时可视化显示,使用户能够直观获取当前测量结果。同时引入蜂鸣器报警机制,当设备偏离水平状态时及时发出提示,提高检测效率和使用便利性。此外,系统设计了上电自动校准功能,在设备启动阶段自动完成传感器零点修正,降低环境误差和安装误差带来的影响,增强系统测量稳定性和可靠性。
基于以上背景,本项目设计了一套基于Arduino UNO的电子水平仪系统,通过融合姿态检测、状态显示、自动校准以及声光提示等功能,实现对设备水平状态的实时检测与反馈,为数字化测量设备设计提供一种低成本、易实现、具有良好扩展能力的解决方案。
1.2 设计实现的功能
(1)支持设备姿态检测功能
系统采用MPU6050六轴姿态传感器作为核心检测单元,实时采集设备当前的三轴加速度数据和三轴陀螺仪数据。通过对传感器输出数据进行计算处理,获得设备当前的俯仰角(Pitch)和横滚角(Roll)参数,用于判断设备是否处于水平状态。系统能够持续监测姿态变化,实现对设备倾斜情况的实时感知。
(2)支持水平状态判断与报警功能
系统根据计算得到的俯仰角(Pitch)和横滚角(Roll)结果进行状态判断。当检测到设备未处于水平状态时,自动触发报警机制,驱动高电平触发有源蜂鸣器发出声音提示,提醒用户当前设备发生倾斜;当设备恢复至水平状态后,蜂鸣器自动停止工作,实现实时状态反馈。
(3)支持OLED显示屏数据显示功能
系统配置0.96寸IIC接口OLED显示屏,用于实时显示姿态检测结果。显示界面能够输出当前设备的水平状态或倾斜状态,同时显示俯仰角(Pitch)和横滚角(Roll)的实时角度值,使用户能够直观了解当前设备的倾斜方向及角度变化情况。当Pitch与Roll数值接近0时,表示当前设备处于水平状态。
(4)支持上电自动校准功能
系统在设备上电启动后自动进入传感器校准流程,对MPU6050进行姿态初始化和零点校准,以减小安装误差及环境干扰对测量结果造成的影响。在校准过程中,OLED显示屏实时显示当前校准状态和校准进度信息,待校准完成后系统进入正常检测模式,提高整体测量精度与运行稳定性。
1.3 项目硬件模块组成
(1)Arduino UNO主控模块
系统以Arduino UNO开发板作为核心控制单元,负责完成整个系统的数据采集、运算处理、设备控制以及功能调度。主控模块通过IIC总线与MPU6050姿态传感器和OLED显示屏进行通信,同时控制蜂鸣器输出报警信号,实现系统各模块之间的数据交互与协同运行。Arduino UNO具有接口资源丰富、开发环境成熟以及程序调试方便等特点,满足本项目实时姿态检测与控制需求。
(2)MPU6050姿态检测模块
姿态检测部分采用MPU6050六轴惯性测量单元,用于采集设备当前运动状态信息。模块内部集成三轴加速度计与三轴陀螺仪,可输出三轴加速度数据及三轴角速度数据。系统通过对采集的数据进行处理与计算,得到俯仰角(Pitch)和横滚角(Roll)参数,用于判断设备是否处于水平状态,并作为报警和数据显示的依据。
(3)OLED显示模块
显示部分采用0.96寸IIC接口OLED显示屏,用于显示系统运行状态和检测结果。显示内容包括设备当前水平状态、倾斜状态、俯仰角(Pitch)以及横滚角(Roll)实时角度值。在系统启动阶段,显示屏同时承担校准状态显示功能,为用户提供直观的人机交互界面。
(4)蜂鸣器报警模块
报警部分采用高电平触发有源蜂鸣器实现声音提示功能。当系统检测到设备姿态偏离水平状态时,Arduino UNO输出控制信号驱动蜂鸣器工作,实现倾斜报警;当设备恢复水平状态后,自动关闭蜂鸣器,以达到实时提醒用户调整设备姿态的目的。
(5)电源供电模块
系统采用5V输出锂电池作为整体供电单元,为Arduino UNO、MPU6050、OLED显示屏以及蜂鸣器模块提供稳定电源支持。供电模块保证设备能够脱离固定电源独立运行,提高系统便携性和实际使用能力。
1.4 系统框架图
1.5 运行流程图
二、硬件选型
2.1 arduino uno r3 开发板
2.2 OLED显示屏(IIC协议4针)
2.3 MPU6050陀螺仪
三、Arduino uno代码设计
3.1 下载安装开发环境
官网下载地址:https://www.arduino.cc/en/software/
双击安装。
软件启动中:
在启动过程中,会弹出一堆安装驱动的说明。全部点击确定就行了。
软件启动成功。
设置中文界面。
重新启动,就是中文界面了。
3.2 选择开发板
根据自己的板子型号进行选择。
这是我的开发板。
将开发板连接电脑。
选择开发板的端口。
连接成功了。
将文件保存起来。编写代码,点击编译下载按钮就可以将代码下载到板子上就可以测试了。
下面代码是点亮2个LED灯。
void setup() {
pinMode(13, OUTPUT); //LED2
pinMode(12, OUTPUT); //LED1
}
void loop() {
digitalWrite(13, HIGH);
digitalWrite(12, HIGH);
delay(500);
digitalWrite(13, LOW);
digitalWrite(12, LOW);
delay(500);
}
3.3 开发板的引脚
开发板右边的013 就是数字口,D0D13.
开发板左边的A0~A5 是模拟口。
四、Arduino UNO 代码设计
4.1 硬件连线说明
1. MPU6050 陀螺仪(I2C接口)
•VCC →Arduino 3.3V
•GND →Arduino GND
•SCL →Arduino A5 (UNO硬件I2C时钟线)
•SDA →Arduino A4 (UNO硬件I2C数据线)
2. 0.96寸 OLED 显示屏(IIC/I2C接口)
•VCC →Arduino 5V
•GND →Arduino GND
•SCL →Arduino A5 (与MPU6050并联,共用I2C总线)
•SDA →Arduino A4 (与MPU6050并联,共用I2C总线)
3. 有源蜂鸣器(高电平触发)
•正极(+) →Arduino 数字引脚 D3
•负极(-) →Arduino GND
4. 供电系统(5V锂电池)
•电池正极(5V Out) →Arduino 5V Vin引脚
•电池负极(GND) →Arduino GND
4.2 项目完整代码设计
完整Arduino UNO代码。代码包含了MPU6050姿态解算、OLED显示、蜂鸣器报警和上电校准功能。
/*
* 项目:基于Arduino UNO的水平仪
* 功能:MPU6050姿态检测 + OLED显示 + 蜂鸣器报警 + 上电校准
* 硬件连接:
* MPU6050: VCC→3.3V, GND→GND, SCL→A5, SDA→A4
* OLED: VCC→5V, GND→GND, SCL→A5, SDA→A4
* 蜂鸣器: 正极→D3, 负极→GND
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <MPU6050.h>
// OLED显示屏配置
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3C // 大部分OLED地址为0x3C,如果是0x78请修改
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
// MPU6050对象
MPU6050 mpu;
// 姿态角度变量
float pitch = 0; // 俯仰角
float roll = 0; // 横滚角
// 角度计算相关变量
float accX, accY, accZ;
float gyroX, gyroY, gyroZ;
float gyroX_offset = 0, gyroY_offset = 0, gyroZ_offset = 0;
// 互补滤波参数
float anglePitch = 0, angleRoll = 0;
float gyroRatePitch = 0, gyroRateRoll = 0;
unsigned long lastTime = 0;
// 蜂鸣器引脚
#define BUZZER_PIN 3
// 水平状态判断阈值(角度小于此值视为水平)
#define LEVEL_THRESHOLD 2.0
// 校准状态
bool isCalibrated = false;
unsigned long calibrationStartTime = 0;
const unsigned long CALIBRATION_DURATION = 3000; // 校准持续3秒
void setup(){
Serial.begin(9600);
// 初始化蜂鸣器引脚
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(BUZZER_PIN, LOW);
// 初始化OLED
if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
// 显示校准提示
display.setCursor(20, 20);
display.println("Calibrating...");
display.setCursor(10, 40);
display.println("Please keep flat");
display.display();
// 初始化MPU6050
Serial.println("Initializing MPU6050...");
mpu.initialize();
// 检查MPU6050连接
if(mpu.testConnection()) {
Serial.println("MPU6050 connection successful");
} else {
Serial.println("MPU6050 connection failed");
display.clearDisplay();
display.setCursor(20, 20);
display.println("MPU6050 Error!");
display.display();
while(1);
}
// 上电校准 - 采集陀螺仪偏移值
calibrateMPU6050();
// 显示校准完成
display.clearDisplay();
display.setCursor(20, 20);
display.println("Calibration");
display.setCursor(30, 40);
display.println("Complete!");
display.display();
delay(1000);
// 清空显示屏准备显示数据
display.clearDisplay();
// 记录初始时间
lastTime = micros();
isCalibrated = true;
// 校准完成提示音
digitalWrite(BUZZER_PIN, HIGH);
delay(200);
digitalWrite(BUZZER_PIN, LOW);
}
void loop(){
if(!isCalibrated) return;
// 获取传感器数据
getSensorData();
// 计算角度
calculateAngles();
// 更新OLED显示
updateDisplay();
// 检查水平状态并控制蜂鸣器
checkLevelStatus();
// 延迟以控制刷新率
delay(50);
}
/*
* MPU6050校准函数
* 采集1000个样本取平均值作为偏移量
*/
void calibrateMPU6050(){
Serial.println("Starting calibration...");
int numSamples = 1000;
long gyroX_sum = 0, gyroY_sum = 0, gyroZ_sum = 0;
// 采集数据
for(int i = 0; i < numSamples; i++) {
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
gyroX_sum += gx;
gyroY_sum += gy;
gyroZ_sum += gz;
delay(2);
}
// 计算平均值作为偏移量
gyroX_offset = gyroX_sum / numSamples;
gyroY_offset = gyroY_sum / numSamples;
gyroZ_offset = gyroZ_sum / numSamples;
Serial.print("Gyro offsets: X=");
Serial.print(gyroX_offset);
Serial.print(", Y=");
Serial.print(gyroY_offset);
Serial.print(", Z=");
Serial.println(gyroZ_offset);
}
/*
* 获取传感器原始数据
*/
void getSensorData(){
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
// 将加速度转换为g单位
accX = ax / 16384.0;
accY = ay / 16384.0;
accZ = az / 16384.0;
// 将陀螺仪转换为度/秒,并减去偏移量
gyroX = (gx - gyroX_offset) / 131.0;
gyroY = (gy - gyroY_offset) / 131.0;
gyroZ = (gz - gyroZ_offset) / 131.0;
}
/*
* 使用互补滤波计算角度
*/
void calculateAngles(){
// 从加速度计计算角度
float accPitch = atan2(-accX, sqrt(accY * accY + accZ * accZ)) * 180.0 / PI;
float accRoll = atan2(accY, accZ) * 180.0 / PI;
// 计算时间差
unsigned long now = micros();
float dt = (now - lastTime) / 1000000.0;
lastTime = now;
// 防止时间差过大
if(dt > 0.1) dt = 0.01;
// 陀螺仪积分
gyroRatePitch = gyroX;
gyroRateRoll = gyroY;
// 互补滤波
float alpha = 0.96; // 滤波系数
anglePitch = alpha * (anglePitch + gyroRatePitch * dt) + (1 - alpha) * accPitch;
angleRoll = alpha * (angleRoll + gyroRateRoll * dt) + (1 - alpha) * accRoll;
// 赋值给全局变量
pitch = anglePitch;
roll = angleRoll;
}
/*
* 更新OLED显示屏
*/
void updateDisplay(){
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
// 显示标题
display.println("=== Level Meter ===");
// 显示角度值
display.print("Pitch: ");
display.print(pitch, 1);
display.println(" deg");
display.print("Roll: ");
display.print(roll, 1);
display.println(" deg");
// 显示水平状态
display.println("----------------");
display.print("Status: ");
if(abs(pitch) < LEVEL_THRESHOLD && abs(roll) < LEVEL_THRESHOLD) {
display.println("LEVEL");
} else {
display.println("TILTED");
}
// 显示简单的水平指示器(图形化显示)
drawLevelIndicator();
display.display();
}
/*
* 绘制水平指示器
* 在屏幕底部用一个点来模拟水平状态
*/
void drawLevelIndicator(){
// 计算指示器位置(在屏幕底部画一条线和一个点)
int centerX = SCREEN_WIDTH / 2;
int centerY = SCREEN_HEIGHT - 10;
int range = 50;
// 绘制基准线
display.drawLine(centerX - range, centerY, centerX + range, centerY, SSD1306_WHITE);
// 根据pitch和roll计算点的偏移位置(简化版)
int offsetX = constrain((roll / 10.0) * 20, -range + 5, range - 5);
int offsetY = constrain((pitch / 10.0) * 20, -range + 5, range - 5);
// 绘制指示点
display.fillCircle(centerX + offsetX, centerY - offsetY, 3, SSD1306_WHITE);
// 在中心位置画十字标记
display.drawLine(centerX - 3, centerY, centerX + 3, centerY, SSD1306_WHITE);
display.drawLine(centerX, centerY - 3, centerX, centerY + 3, SSD1306_WHITE);
}
/*
* 检查水平状态并控制蜂鸣器
*/
void checkLevelStatus(){
bool isLevel = (abs(pitch) < LEVEL_THRESHOLD && abs(roll) < LEVEL_THRESHOLD);
if(isLevel) {
// 水平状态 - 蜂鸣器关闭
digitalWrite(BUZZER_PIN, LOW);
} else {
// 倾斜状态 - 蜂鸣器发出警报(1Hz频率)
static unsigned long lastBuzzerTime = 0;
unsigned long currentTime = millis();
// 每秒触发两次蜂鸣(0.5秒间隔),产生间隔报警效果
if(currentTime - lastBuzzerTime >= 500) {
lastBuzzerTime = currentTime;
digitalWrite(BUZZER_PIN, !digitalRead(BUZZER_PIN)); // 翻转状态
}
}
}
代码说明
1. 核心功能模块
•校准函数calibrateMPU6050():上电采集1000个陀螺仪数据,计算平均偏移值•
数据采集getSensorData():读取原始加速度和角速度数据•
角度计算calculateAngles():使用互补滤波融合加速度计和陀螺仪数据•
显示更新updateDisplay():显示角度值和水平状态•
蜂鸣器控制checkLevelStatus():根据倾斜状态触发警报
2. 关键参数调整
| 参数 | 说明 | 建议值 |
|---|---|---|
LEVEL_THRESHOLD |
水平判断阈值(度) | 2.0 |
CALIBRATION_DURATION |
校准持续时间(毫秒) | 3000 |
alpha |
互补滤波系数(越大越平滑) | 0.96 |
| 蜂鸣器间隔 | 报警时的鸣响频率 | 500ms |
3. 需要安装的库
在Arduino IDE中,通过库管理器安装以下库:
Adafruit GFX Library
Adafruit SSD1306
MPU6050(或MPU6050_tockn)
4. 使用说明
1. 上电后,将设备水平放置在桌面上
2. OLED显示"Calibrating...",持续3秒自动完成校准
3. 校准完成后显示"Complete!",蜂鸣器短促鸣响一次
4. 正常工作时:
-
- • 屏幕显示Pitch/Roll角度和水平状态• 底部有图形化水平指示器(小球位置表示倾斜方向)• 倾斜时蜂鸣器间歇报警
五、总结
本项目完成了一套基于Arduino UNO设计的电子水平仪系统,实现了姿态检测、水平状态判断、报警提示、数据显示以及上电自动校准等功能。系统以Arduino UNO作为核心控制单元,结合MPU6050姿态传感器完成三轴加速度和三轴角速度数据采集,通过算法计算获得俯仰角(Pitch)和横滚角(Roll)参数,实现对设备当前水平状态的实时检测与分析。
在系统实现过程中,利用OLED显示屏完成姿态角度及水平状态的可视化显示,使用户能够直观获取设备运行状态;同时结合蜂鸣器报警机制,在检测到设备发生倾斜时及时进行声音提醒,提高设备的交互性与使用便利性。针对传感器存在的初始误差问题,系统设计了上电自动校准功能,增强了姿态测量结果的准确性与系统运行稳定性。
通过本项目的设计与实现,验证了基于MEMS姿态传感器进行水平检测方案的可行性,达到了预期功能目标。整个系统具有结构简单、成本较低、实时性较好、便于部署与扩展等特点,不仅能够满足基础水平检测需求,也为后续开展姿态测量、角度检测及嵌入式智能测量设备相关研究提供了实践基础和参考价值。
302