循迹小车,PID控制
本帖最后由 donglaile 于 2017-8-2 10:40 编辑现在x宝上电子模块真心便宜,前段时间心血来潮,买模块拼了个循迹小车玩玩,50块左右就搞定了。
原理图大概这样,没找到L9110s,用L298画了,理解意思就行:
电池使用两节锂电池串联,用了个5A的可调降压模块给电机模块供电。这里主控另外供电,用以前剩下的带移动电源功能项目板子5V升压的。
特别注意一点光电对管的安装间隔最好是黑胶带的宽度,竖着安装(就是模块PCB与小车前进方向平行),用万能的热熔胶粘在废弃的芯片壳上再粘贴到小车前面的。
来张错误安装光电对管的图:
之前这样安装一直没搞好。
控制代码:
//电机电压:6.1V左右的参数,如果不加积分项,可以循线跑,但是无法回到中间传感器位置。
float Kp = 10, Ki = 0.02, Kd = 20;
float error = 0, P = 0, I = 0, D = 0, PID_value = 0;
float previous_error = 0, previous_I = 0;
static int initial_motor_speed = 100; //期望速度
int left_motor_speed = 0;
int right_motor_speed = 0;
uint8_t irs = 0;
//引脚定义
const int IR_PIN[] = {A0, A1, A2, A3, A4}; //红外对管引脚定义
const int IN_A1 = 3; // 电机A1
const int IN_A2 = 9; // 电机A2
const int IN_B1 = 10;// 电机B1
const int IN_B2 = 11;// 电机B2
void read_ir_values(void);
void calculate_pid(void);
void motor_control(void);
void setup()
{
// 初始化
pinMode(IN_A1, OUTPUT);
pinMode(IN_A2, OUTPUT);
pinMode(IN_B1, OUTPUT);
pinMode(IN_B2, OUTPUT);
for (int i = 0; i < 5; i++) {
pinMode(IR_PIN, INPUT);
}
Serial.begin(9600); //调试用
}
void loop()
{
read_ir_values();
calculate_pid();
motor_control();
print_debug();
// motorsWrite(255,255); // 调试电机
// delay(3000);
// motorsWrite(-255,-255);
// delay(3000);
}
void read_ir_values()
{
for (int i = 4; i < 9; i++) {
if (digitalRead(IR_PIN)) {
bitSet(irs, (i - 4));
} else {
bitClear(irs, (i - 4));
}
}
switch (irs) {
case B00000:
if (error < 0) {
error = -9;
} else {
error = 9;
}
break;
case B00001: error = -7; break;
case B00011: error = -5; break;
case B00010: error = -3; break;
case B00110: error = -1; break;
case B00100: error = 0; break;
case B01100: error = 1; break;
case B01000: error = 3; break;
case B11000: error = 5; break;
case B10000: error = 7; break;
//对以上值进行处理,其他的就自由发挥
}
}
void calculate_pid()
{
P = error;
I = I + error;
D = error - previous_error;
PID_value = (Kp * P) + (Ki * I) + (Kd * D);
PID_value = constrain(PID_value, -100, 100);
previous_error = error;
}
void motor_control()
{
//计算每个电机的速度
left_motor_speed = initial_motor_speed - PID_value;
right_motor_speed = initial_motor_speed + PID_value;
constrain(left_motor_speed, -255, 255); //速度限定在(-255,255)
constrain(right_motor_speed, -255, 255);
motorsWrite(left_motor_speed, right_motor_speed);
}
//速度设定范围(-255,255)
void motorsWrite(int speedL, int speedR)
{
if (speedR > 0) {
analogWrite(IN_A1, speedR);
analogWrite(IN_A2, 0);
} else {
analogWrite(IN_A1, 0);
analogWrite(IN_A2, -speedR);
}
if (speedL > 0) {
analogWrite(IN_B1, speedL);
analogWrite(IN_B2, 0);
} else {
analogWrite(IN_B1, 0);
analogWrite(IN_B2, -speedL);
}
}
void print_debug()
{
// 打印串口调试信息
Serial.print("IRS:");
String irs2bin = String(irs, 2);
int len = irs2bin.length();
if(len < 5){
for(int i=0;i<5-len;i++){
irs2bin += "0" + irs2bin;
}
}
Serial.print(irs2bin);
Serial.print(" ML:");
Serial.print(left_motor_speed, OCT);
Serial.print(" MR:");
Serial.print(right_motor_speed, OCT);
Serial.print(" er:");
Serial.print(error, OCT);
//Serial.print(" P:");
//Serial.print(Kp, OCT);
Serial.print(" PID:");
Serial.print(PID_value, OCT);
Serial.println();
}
运行视频:
1.速度较慢的,忘记初速度设的多少了
http://v.youku.com/v_show/id_XMjg5MjE5MTc3Mg==.html?spm=a2h3j.8428770.3416059.1
http://player.youku.com/player.php/sid/XMjg5MjE5MTc3Mg==/v.swf
2.这个是设置140初速度的,比较前面的快点
http://v.youku.com/v_show/id_XMjg5MjE5Mzk2MA==.html?spm=a2h3j.8428770.3416059.1
http://player.youku.com/player.php/sid/XMjg5MjE5Mzk2MA==/v.swf 就这么几个误差值还不如直接手工算存参数呢,省掉大堆的代码呢 楼主,学习啦,好东西
很有借鉴的制作
我也准备搞一个学习学习 楼主,你这段代码编译不了呀? 你好在吗,有个问题请教你,输入口定义的是0-4模拟口,读取怎么是4-9口呢 本帖最后由 JasonChing 于 2018-11-7 19:59 编辑
破石头 发表于 2018-4-29 15:46
你好在吗,有个问题请教你,输入口定义的是0-4模拟口,读取怎么是4-9口呢
这里代表传感器的连接端口,是4,5,6,7,8
页:
[1]