51蓝牙PID循迹超声波急停避障小车

实现功能:

  • 能够实现自动识别黑线,完成5路循迹。
  • 能够通过蓝牙于89C52系列芯片串口通信控制小车
  • 能够自动感应障碍物实现自动刹停。

准备材料:

  • 89C52芯片及最小系统板 ,
  • L298n模块,
  • 模拟量5路循迹模块,
  • 稳压模块,
  • HC-06模块,
  • 超声波模块,
  • 亚巧克力板,
  • 4个电机,
  • 12vDC电源,
  • DC转接母头

普通小车使能实现原理:

利用51单片机IO口控制L298n模块引脚供电实现对电机触发。再通过EN使能端进行PWM调速,实现转弯,漂移,转圈等特殊转速。
IN为L298N模块控制引脚
EN为L298N模块使能引脚
L298N模块单边IN为0,0时,电机停止转动,为1,0 、0,1时正转、反转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <REGX52.H>
unsigned char Duty_left,Duty_right,j=0,i=0;
//l298n
sbit IN11=P0^2;//左前轮
sbit IN12=P0^1;

sbit IN13=P0^3;//右前轮
sbit IN14=P0^4;

sbit IN21=P1^1;//左后轮
sbit IN22=P1^2;

sbit IN23=P1^4;//右后轮
sbit IN24=P1^3;

//使能端
sbit EN1A=P0^0;//左前
sbit EN1B=P0^5;//右前1
sbit EN2A=P1^6;//左后
sbit EN2B=P1^7;//右后1

//定时器+中断
void Timer0_Init() //100微秒@11.0592MHz
{
TMOD|=0x01;//定时器T0,,工作方式都为方式1
TH0=0xff;//定时ff9c0.1ms fe0c0.5ms
TL0=0x9c;

EA=1; //开启总中断
ET0=1;//开启中断允许
TR0=1;//开启定时器中断
}
//利用中断,PWM调速
void Timer0_Routine() interrupt 1
{
TH0=0xff;//定时ff9c0.1ms fe0c0.5ms
TL0=0x9c;

i++;
j++;

if(i<=Duty_left)
{
EN1A=1;
EN2A=1;
}
else
{
EN1A=0;
EN2A=0;
}
if(j<=Duty_right)
{
EN1B=1;
EN2B=1;
}
else
{
EN1B=0;
EN2B=0;
}

if(i==100) //设置pwm周期=0.1ms*100=10ms,这样开头定义的变量正好表示占空比数值
{
i=0;//加到100后变为0,重新计数
}
if(j==100) //设置pwm周期=0.1ms*100=10ms,这样开头定义的变量正好表示占空比数值
{
j=0;
}

}

蓝牙小车原理:

通过将蓝牙模块的TXRX 与单片机交叉相接进行串口通信,实现收发数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <REGX52.H>
//l298n
sbit IN11=P0^2;//左前轮
sbit IN12=P0^1;

sbit IN13=P0^3;//右前轮
sbit IN14=P0^4;

sbit IN21=P1^1;//左后轮
sbit IN22=P1^2;

sbit IN23=P1^4;//右后轮
sbit IN24=P1^3;

char i;
unsigned char ReceiveData; //接收到的数据存放变量
//向左转
void correct_left()
{
IN11=0;
IN12=0;
IN21=0;
IN22=0;

IN13=1;
IN14=0;
IN23=1;
IN24=0;


}
//向右转
void correct_right()
{
IN11=1;
IN12=0;
IN21=1;
IN22=0;

IN13=0;
IN14=0;
IN23=0;
IN24=0;


}

//左转
void Left_turning()
{

IN11=0;
IN12=1;
IN21=0;
IN22=1;

IN13=1;
IN14=0;
IN23=1;
IN24=0;
}
//右转
void Right_turning()
{

IN11=1;
IN12=0;
IN21=1;
IN22=0;

IN13=0;
IN14=1;
IN23=0;
IN24=1;
}
//小车直走前行,不拐弯速度赋值
void forward_move()
{

IN11=1;
IN12=0;
IN21=1;
IN22=0;

IN13=1;
IN14=0;
IN23=1;
IN24=0;
}

void back()
{

IN11=0;
IN12=1;
IN21=0;
IN22=1;

IN13=0;
IN14=1;
IN23=0;
IN24=1;
}

void stop()
{

IN11=0;
IN12=0;
IN21=0;
IN22=0;

IN13=0;
IN14=0;
IN23=0;
IN24=0;
}



void receive(unsigned char m)//执行函数
{
switch(m)
{
case '8':forward_move();break;
case '5':stop();break;//停止
}
}

void Uart_Init() //9600bps@11.0592MHz
{
PCON = 0x00; //波特率不倍速s
SCON = 0x50; //8位数据,可变波特率
TMOD = 0x20; //设定定时器1为8位自动重装方式
TL1 = 0xFD; //设定定时初值
TH1 = 0xFD; //设定定时器重装值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
EA=1; //总中断
ES=1; //打开串口中断
}

void Uart_Routine() interrupt 4 //中断
{
stop();
RI=0; //清除接收中断标志位
ReceiveData=SBUF; //除去接受的数据
receive(ReceiveData);
}

void main()
{
Uart_Init();
while(1);
}

超声波急停避障小车原理:

1.采用 IO 口 TRIG 触发测距,给一个 10us 的高电平信号;
2.模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回;
3.有信号返回,通过 IO 口 ECHO 输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。
4.测试距离=(高电平时间*声速(340M/S))/2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <REGX52.H>
#include "delay.h"
sbit Trig=P3^3;//定义超声波模块引脚
sbit Echo=P3^2;
unsigned int a,distance;

int csb()
{


TMOD &= 0x0F; //设置定时器模式

TL1 = 0; //设置定时初始值
TH1 = 0; //设置定时初始值


Trig=1;//发射20US的脉冲
Delay10us(2);
Trig=0;//关闭


while(!Echo);//等待返回脉冲
TR1=1;
while(!Echo);//返回脉冲结束
TR1=0;

a=TH1*256+TL1;
TH1=0;
TL1=0;

distance= a*1.7/100; //计算公式,按照数据手册为准,不同环境不同计算方法。

Trig=0;
Echo=0;
return distance;
}

5路PID循迹小车原理:

简单的循迹就是通过循迹模块反馈给单片机的高低电平信号进行不断判断和迭代,列出全部情况,这里就不做赘述。在此分享和学习关于PID算法循迹。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
#include <REGX52.H>

// 定义PID控制的参数
float Kp = 0.5; // 比例系数
float Ki = 0.2; // 积分系数
float Kd = 0.1; // 微分系数

int error = 0; // 当前误差
int lastError = 0; // 上一次的误差
int integral = 0; // 误差的积分
int derivative = 0; // 误差的微分

// 红外循迹传感器定义...
// L298N控制引脚定义...
// 使能端引脚定义...
// 延时函数定义...
// Timer0 初始化函数定义...
// 电机控制函数定义...

void CalculatePID() {
// 根据传感器的读数计算误差,这里假设按照传感器是5个连续的数值
int sensorValues = (left1 << 4) | (left2 << 3) | (mid3 << 2) | (right4 << 1) | right5;

switch(sensorValues) {
case 0b00000:
case 0b11111:
// 特殊情况处理,比如急转弯或者丢线
break;
case 0b00100:
error = 0; // 小车在中心线上
break;
//...其它情况根据传感器值来设置误差值
}

// PID计算
integral = integral + error;
derivative = error - lastError;
int turn = (int)(Kp * error + Ki * integral + Kd * derivative);
lastError = error;

// 根据turn计算左右电机的占空比
Duty_left = 26 + turn;
Duty_right = 26 - turn;

// 确保占空比在0-100之间
Duty_left = Duty_left < 0 ? 0 : (Duty_left > 100 ? 100 : Duty_left);
Duty_right = Duty_right < 0 ? 0 : (Duty_right > 100 ? 100 : Duty_right);
}

// 中断服务函数,用于更新PWM信号
void Timer0_Routine() interrupt 1 {
// 更新定时器...
// PWM控制代码...
}

void main() {
Timer0_Init(); // 定时器初始化
while(1) {
CalculatePID(); // 计算PID
// 控制电机...
}
}