这个项目实现了一个基于51单片机的双路超声波测距系统,核心功能包括:
我在实际项目中多次使用这种方案,发现它特别适合需要同时监测两个方向距离的场景,比如智能小车避障、仓库货架间距监测等。相比单路方案,双路设计可以获取更全面的空间信息,而温度补偿则显著提升了测量精度(实测误差可控制在±1cm以内)。
超声波测距的基本原理是时差法:发射超声波脉冲并接收回波,通过测量时间差Δt计算距离。基本公式为:
code复制距离 = (声速 × Δt) / 2
其中除以2是因为超声波经历了往返路径。
关键点在于声速的温度补偿:声速在空气中并非恒定,而是随温度变化:
code复制v = 331.5 + 0.607×T (单位:m/s,T为摄氏温度)
例如:
实际测试发现,在10-30℃范围内,无温度补偿的测距误差可达3-5%。加入DS18B20的温度补偿后,误差可降至1%以内。
DS18B20的三个显著优势使其成为本项目的理想选择:
其温度转换时间与分辨率相关:
采用8位并行接口模式,主要控制信号:
显示布局设计示例:
code复制Distance1: 25.3cm
Distance2: 18.7cm
Temp: 26.5C
| 元件 | 型号 | 关键参数 |
|---|---|---|
| 单片机 | STC89C52 | 11.0592MHz晶振 |
| 超声波模块 | HC-SR04 | 工作电压5V,探测范围2-400cm |
| 温度传感器 | DS18B20 | 防水型TO-92封装 |
| 液晶屏 | LCD1602 | 5V供电,带背光 |
| 单片机引脚 | 连接目标 | 备注 |
|---|---|---|
| P1.0 | 超声波1 Trig | 输出 |
| P1.1 | 超声波1 Echo | 输入 |
| P1.2 | 超声波2 Trig | 输出 |
| P1.3 | 超声波2 Echo | 输入 |
| P1.4 | DS18B20 DQ | 开漏输出,需4.7K上拉 |
| P2.0-P2.7 | LCD1602 D0-D7 | 8位数据总线 |
| P3.4 | LCD1602 RS | |
| P3.5 | LCD1602 E | |
| P3.6 | LCD1602 RW | 接地(只写模式) |
超声波模块供电:
DS18B20布线:
LCD对比度调节:
c复制void system_init() {
TMOD = 0x01; // 定时器0模式1(16位)
TH0 = TL0 = 0; // 定时器初值清零
ET0 = 0; // 禁用定时器中断(采用查询方式)
TR0 = 0; // 先停止定时器
LCD_Init(); // LCD初始化
LCD_Clear(); // 清屏
// 显示初始信息
LCD_Write_String(0, 0, "Distance1: ");
LCD_Write_String(0, 1, "Distance2: ");
LCD_Write_String(11, 1, "Temp:");
}
c复制float read_temperature() {
unsigned char tempL, tempH;
int temp;
float result;
DS18B20_Reset(); // 复位
DS18B20_Write(0xCC); // 跳过ROM
DS18B20_Write(0x44); // 启动转换
delay_ms(750); // 等待转换完成(12位分辨率)
DS18B20_Reset();
DS18B20_Write(0xCC);
DS18B20_Write(0xBE); // 读暂存器
tempL = DS18B20_Read();
tempH = DS18B20_Read();
temp = (tempH << 8) | tempL;
if(temp & 0x8000) { // 负温度处理
temp = ~temp + 1;
result = -0.0625 * temp;
} else {
result = 0.0625 * temp;
}
return result;
}
实测技巧:在读取温度值前加入3-5ms的延时,可提高单总线通信稳定性。
c复制unsigned int measure_distance(sbit trig, sbit echo, float temp) {
unsigned long time;
float velocity;
// 计算当前温度下的声速(m/s)
velocity = 331.5 + 0.607 * temp;
// 发送10us触发脉冲
trig = 1;
delay_us(10);
trig = 0;
// 等待回波上升沿
while(!echo);
TR0 = 1; // 启动定时器
TH0 = TL0 = 0; // 定时器清零
// 等待回波下降沿
while(echo);
TR0 = 0; // 停止定时器
// 计算时间(us)
time = (TH0 << 8) | TL0;
time *= (12 / 11.0592); // 晶振误差补偿
// 计算距离(cm)
return (unsigned int)((velocity * time) / 20000);
}
关键改进点:
c复制void main() {
float temp;
unsigned int dist1, dist2;
system_init();
while(1) {
temp = read_temperature();
dist1 = measure_distance(Trig1, Echo1, temp);
dist2 = measure_distance(Trig2, Echo2, temp);
// 显示更新
display_value(10, 0, dist1, "cm");
display_value(10, 1, dist2, "cm");
display_float(16, 1, temp, 1, "C");
// 抗干扰处理
if(dist1 > 400) dist1 = 400;
if(dist2 > 400) dist2 = 400;
delay_ms(200); // 适当延时降低刷新率
}
}
| 参数 | 设置值 | 说明 |
|---|---|---|
| 晶振频率 | 11.0592MHz | 必须与代码一致 |
| 仿真速度 | 1x | 实时仿真确保时序准确 |
| SRF04距离 | 可调 | 测试不同距离下的响应 |
超声波无响应:
DS18B20读取失败:
LCD显示乱码:
多次测量取平均:
c复制#define SAMPLE_TIMES 5
unsigned int get_avg_distance(/*...*/) {
unsigned int sum = 0;
for(int i=0; i<SAMPLE_TIMES; i++) {
sum += measure_distance(/*...*/);
delay_ms(50);
}
return sum / SAMPLE_TIMES;
}
动态温度采样策略:
回波信号调理:
软件滤波算法:
定时器校准:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 测量值固定为0 | Echo信号未连接 | 检查硬件连接 |
| 测量值波动大 | 电源干扰 | 加强电源滤波 |
| 温度显示85℃ | DS18B20通信失败 | 检查上拉电阻 |
| LCD仅显示白块 | 初始化失败 | 增加初始化延时 |
| 单路工作正常双路异常 | 电源电流不足 | 增加电源容量 |
三路测距系统:
无线数据传输:
报警功能:
数据记录:
经过多次实测验证,以下几个优化措施能显著提升系统性能:
中断驱动设计:
低功耗模式:
自动量程切换:
机械结构优化:
校准参数存储:
在最近的一个仓储监控项目中,我们采用这种双路方案实现了货架间距监测。通过上述优化措施,系统实现了:
对于需要更高精度的场合,建议考虑以下升级方案: