本教程将指导你制作一个基于51单片机的智能避障小车,它能够自主避开障碍物,跟随光线移动,并可以通过蓝牙遥控。通过本项目,你将学习:
安装提示:
注意事项:
// L298N模块连接
IN1 - P1.0
IN2 - P1.1
IN3 - P1.2
IN4 - P1.3
ENA - P1.4 (PWM)
ENB - P1.5 (PWM)
// 电机接线
M1 - 左前轮
M2 - 右前轮
M3 - 左后轮
M4 - 右后轮
// 超声波模块
TRIG - P2.0
ECHO - P2.1
// 红外避障模块
LEFT_IR - P2.2
RIGHT_IR - P2.3
// 光敏模块
LEFT_LIGHT - P2.4
RIGHT_LIGHT - P2.5
// 蓝牙模块
TXD - P3.0
RXD - P3.1
#include <reg52.h>
#include <intrins.h>
// 引脚定义
sbit IN1 = P1^0;
sbit IN2 = P1^1;
sbit IN3 = P1^2;
sbit IN4 = P1^3;
sbit ENA = P1^4;
sbit ENB = P1^5;
sbit TRIG = P2^0;
sbit ECHO = P2^1;
sbit LEFT_IR = P2^2;
sbit RIGHT_IR = P2^3;
sbit LEFT_LIGHT = P2^4;
sbit RIGHT_LIGHT = P2^5;
// 全局变量
unsigned int distance;
unsigned char mode = 0; // 0:蓝牙控制 1:避障 2:寻光
// 函数声明
void delay_ms(unsigned int ms);
void motor_init(void);
void uart_init(void);
void timer_init(void);
void get_distance(void);
void motor_control(unsigned char direction, unsigned char speed);
void obstacle_avoidance(void);
void light_following(void);
void bluetooth_control(void);
// 主函数
void main(void) {
// 初始化
motor_init();
uart_init();
timer_init();
while(1) {
switch(mode) {
case 0:
bluetooth_control();
break;
case 1:
obstacle_avoidance();
break;
case 2:
light_following();
break;
}
}
}
// 电机初始化
void motor_init(void) {
IN1 = IN2 = IN3 = IN4 = 0;
ENA = ENB = 0;
}
// 电机控制
void motor_control(unsigned char direction, unsigned char speed) {
switch(direction) {
case 0: // 停止
IN1 = IN2 = IN3 = IN4 = 0;
break;
case 1: // 前进
IN1 = 1; IN2 = 0;
IN3 = 1; IN4 = 0;
break;
case 2: // 后退
IN1 = 0; IN2 = 1;
IN3 = 0; IN4 = 1;
break;
case 3: // 左转
IN1 = 0; IN2 = 0;
IN3 = 1; IN4 = 0;
break;
case 4: // 右转
IN1 = 1; IN2 = 0;
IN3 = 0; IN4 = 0;
break;
}
// PWM控制速度
ENA = ENB = speed;
}
// 获取距离
void get_distance(void) {
unsigned int time = 0;
// 发送触发信号
TRIG = 1;
delay_us(20);
TRIG = 0;
// 等待回响信号
while(!ECHO);
TR0 = 1; // 启动定时器
while(ECHO);
TR0 = 0; // 停止定时器
// 计算距离 (cm)
time = TH0 * 256 + TL0;
distance = (time * 1.7) / 100; // 声速340m/s
// 重置定时器
TH0 = 0;
TL0 = 0;
}
// 避障模式
void obstacle_avoidance(void) {
get_distance();
if(distance < 20) { // 前方障碍物
motor_control(0, 0); // 停止
delay_ms(500);
// 检测左右红外
if(!LEFT_IR && RIGHT_IR) { // 右侧有障碍
motor_control(3, 80); // 左转
}
else if(LEFT_IR && !RIGHT_IR) { // 左侧有障碍
motor_control(4, 80); // 右转
}
else { // 两侧都有障碍
motor_control(2, 80); // 后退
delay_ms(1000);
motor_control(3, 80); // 左转
delay_ms(800);
}
}
else { // 无障碍
motor_control(1, 100); // 前进
}
}
// 寻光模式
void light_following(void) {
unsigned char left_val, right_val;
// 读取光敏值
left_val = LEFT_LIGHT;
right_val = RIGHT_LIGHT;
if(!left_val && !right_val) { // 两侧都有光
motor_control(1, 100); // 前进
}
else if(!left_val && right_val) { // 左侧有光
motor_control(3, 80); // 左转
}
else if(left_val && !right_val) { // 右侧有光
motor_control(4, 80); // 右转
}
else { // 没有检测到光
motor_control(0, 0); // 停止
}
}
// 串口初始化
void uart_init(void) {
TMOD |= 0x20; // 设置定时器1为模式2
SCON = 0x50; // 8位数据,可变波特率
PCON |= 0x80; // 波特率加倍
TH1 = 0xF3; // 设置波特率9600
TL1 = 0xF3;
TR1 = 1; // 启动定时器1
ES = 1; // 使能串口中断
EA = 1; // 使能总中断
}
// 串口中断服务程序
void uart_isr() interrupt 4 {
unsigned char cmd;
if(RI) {
RI = 0;
cmd = SBUF;
switch(cmd) {
case 'F': motor_control(1, 100); break; // 前进
case 'B': motor_control(2, 100); break; // 后退
case 'L': motor_control(3, 80); break; // 左转
case 'R': motor_control(4, 80); break; // 右转
case 'S': motor_control(0, 0); break; // 停止
case '1': mode = 0; break; // 蓝牙控制模式
case '2': mode = 1; break; // 避障模式
case '3': mode = 2; break; // 寻光模式
}
}
}
HC-SR04超声波模块是通过发射超声波并接收回波来测量距离的传感器。其工作原理如下:
距离计算:
测量距离 = (高电平时间 × 声速) / 2
声速 = 340m/s = 34000cm/s
距离(cm) = (高电平时间us × 34000) / 2000000
= 高电平时间us × 0.017
光敏电阻是一种随入射光强度变化而改变电阻值的器件:
// 光敏模块读取示例
unsigned char read_light(void) {
unsigned char adc_value;
// 启动ADC转换
ADC_START = 1;
// 等待转换完成
while(!ADC_FLAG);
// 读取结果
adc_value = ADC_RESULT;
// 清除标志位
ADC_FLAG = 0;
return adc_value;
}
红外避障模块使用发射管发出红外线,接收管接收反射回来的红外线:
使用注意:
L298N是一款双H桥直流电机驱动芯片,主要特点:
ENA/ENB IN1/IN3 IN2/IN4 电机状态
1 1 0 正转
1 0 1 反转
1 0 0 制动
1 1 1 制动
0 x x 停止
// 定时器0初始化(PWM发生器)
void timer0_init(void) {
TMOD &= 0xF0;
TMOD |= 0x01; // 16��定时器模式
TH0 = 0xFF; // PWM周期设置
TL0 = 0x00;
ET0 = 1; // 使能定时器0中断
TR0 = 1; // 启动定时器0
}
// PWM中断服务程序
void timer0_isr() interrupt 1 {
static unsigned char pwm_count = 0;
// 重装初值
TH0 = 0xFF;
TL0 = 0x00;
// PWM计数
pwm_count++;
if(pwm_count >= 100) pwm_count = 0;
// 输出PWM
ENA = (pwm_count < motor_speed_left) ? 1 : 0;
ENB = (pwm_count < motor_speed_right) ? 1 : 0;
}
PWM频率计算:
PWM频率 = 晶振频率 / (12 × 计数值)
例如:12MHz晶振,计数值256时
PWM频率 = 12000000 / (12 × 256) ≈ 3.9kHz
HC-05蓝牙模块配置步骤:
// AT指令示例
AT // 测试通信
AT+NAME=Robot // 设置蓝牙名称
AT+PSWD=1234 // 设置配对密码
AT+UART=9600,0,0 // 设置串口参数
AT+ROLE=0 // 设置为从机模式
// 通信协议格式
typedef struct {
unsigned char header; // 帧头 0xAA
unsigned char command; // 命令字
unsigned char param; // 参数
unsigned char checksum; // 校验和
} Command_Frame;
// 发送数据帧
void send_frame(unsigned char cmd, unsigned char param) {
Command_Frame frame;
frame.header = 0xAA;
frame.command = cmd;
frame.param = param;
frame.checksum = frame.header + frame.command + frame.param;
// 发送数据
uart_send_byte(frame.header);
uart_send_byte(frame.command);
uart_send_byte(frame.param);
uart_send_byte(frame.checksum);
}
PID控制算法包含三个部分:
// PID结构体
typedef struct {
float kp; // 比例系数
float ki; // 积分系数
float kd; // 微分系数
float error; // 当前误差
float last_error; // 上次误差
float integral; // 积分项
} PID_TypeDef;
// PID计算函数
float pid_calculate(PID_TypeDef *pid, float target, float current) {
float output;
// 计算误差
pid->error = target - current;
// 计算积分项
pid->integral += pid->error;
// PID输出计算
output = pid->kp * pid->error + // 比例项
pid->ki * pid->integral + // 积分项
pid->kd * (pid->error - pid->last_error); // 微分项
// 更新上次误差
pid->last_error = pid->error;
return output;
}
PID参数整定步骤:
参数调节参考:
// 滑动平均滤波
#define FILTER_N 10
unsigned int filter_buf[FILTER_N];
unsigned char filter_index = 0;
unsigned int average_filter(unsigned int new_value) {
unsigned long sum = 0;
unsigned char i;
// 更新缓冲区
filter_buf[filter_index] = new_value;
filter_index = (filter_index + 1) % FILTER_N;
// 计算平均值
for(i = 0; i < FILTER_N; i++) {
sum += filter_buf[i];
}
return sum / FILTER_N;
}
// 卡尔曼滤波
typedef struct {
float Q; // 过程噪声
float R; // 测量噪声
float P; // 估计误差
float K; // 卡尔曼增益
float X; // 状态估计值
} Kalman_TypeDef;
float kalman_filter(Kalman_TypeDef *kalman, float measurement) {
// 预测
kalman->P = kalman->P + kalman->Q;
// 更新
kalman->K = kalman->P / (kalman->P + kalman->R);
kalman->X = kalman->X + kalman->K * (measurement - kalman->X);
kalman->P = (1 - kalman->K) * kalman->P;
return kalman->X;
}
// 加速度平滑控制
void smooth_speed_control(unsigned char target_speed) {
static unsigned char current_speed = 0;
const unsigned char acc_step = 5; // 加速步进值
if(current_speed < target_speed) {
current_speed = min(current_speed + acc_step, target_speed);
}
else if(current_speed > target_speed) {
current_speed = max(current_speed - acc_step, target_speed);
}
motor_control(current_direction, current_speed);
delay_ms(20); // 控制加速度
}
robot_project/
├── src/ # 源代码目录
│ ├── main.c # 主程序
│ ├── motor.c # 电机控制
│ ├── motor.h
│ ├── sensor.c # 传感器驱动
│ ├── sensor.h
│ ├── bluetooth.c # 蓝牙通信
│ ├── bluetooth.h
│ ├── pid.c # PID控制
│ └── pid.h
├── lib/ # 库文件目录
│ ├── delay.c # 延时函数
│ └── delay.h
├── doc/ # 文档目录
│ ├── schematic.pdf # 电路图
│ └── manual.pdf # 使用手册
├── hardware/ # 硬件设计文件
│ ├── pcb/ # PCB设计
│ └── mechanical/ # 机械结构
└── tools/ # 工具和脚本
└── calibration.exe # 传感器校准工具
/**
* 函数名称: function_name
* 功能描述: 描述函数的主要功能
* 输入参数: param1 - 参数1的说明
* param2 - 参数2的说明
* 返回值: 返回值的说明
* 注意事项: 特殊说明和注意事项
*/
// main.c
#include "motor.h"
#include "sensor.h"
#include "bluetooth.h"
#include "pid.h"
// 系统初始化
void system_init(void) {
// 初始化各个模块
motor_init();
sensor_init();
bluetooth_init();
pid_init();
// 配置中断优先级
IP = 0x20; // 设置Timer0为高优先级
// 使能总中断
EA = 1;
}
// 主循环任务调度
void main(void) {
system_init();
while(1) {
// 任务调度
if(task_flag & TASK_SENSOR) {
sensor_process();
task_flag &= ~TASK_SENSOR;
}
if(task_flag & TASK_CONTROL) {
control_process();
task_flag &= ~TASK_CONTROL;
}
if(task_flag & TASK_COMMUNICATION) {
communication_process();
task_flag &= ~TASK_COMMUNICATION;
}
// 系统监控
system_monitor();
}
}
// 任务标志位定义
#define TASK_SENSOR 0x01
#define TASK_CONTROL 0x02
#define TASK_COMMUNICATION 0x04
#define TASK_MONITOR 0x08
// 定时器中断服务程序
void timer0_isr() interrupt 1 {
static unsigned char count = 0;
// 重装初值
TH0 = 0xFC;
TL0 = 0x18;
// 任务调度
count++;
if(count % 1 == 0) { // 1ms
task_flag |= TASK_SENSOR;
}
if(count % 5 == 0) { // 5ms
task_flag |= TASK_CONTROL;
}
if(count % 10 == 0) { // 10ms
task_flag |= TASK_COMMUNICATION;
}
if(count % 100 == 0) { // 100ms
task_flag |= TASK_MONITOR;
count = 0;
}
}