极客工坊

 找回密码
 注册

QQ登录

只需一步,快速开始

查看: 17727|回复: 8

求助大神!!!小车走直线的程序老是没效果

[复制链接]
发表于 2017-4-30 22:49:51 | 显示全部楼层 |阅读模式
在本论坛吸取别人的程序经验,然后自己稍加修改,发现我自己做的PID控制怎么调也不能实现两个轮子的速度一致,虽然速度传感器一直在进行检测,但是就是说算法不好使
现贴上程序请大家帮忙参谋下,感谢!!!

#include <PID_v1.h>
double Setpoint=0, Input, Output;
double   Kp=20, Ki=3, Kd=0.08;   
PID myPID(&Input, &Output, &Setpoint, Kp, Ki, Kd, DIRECT);
int motorIA1 = 9;左轮输入
int motorIB1 = 8;
int motorIA2 = 11;右轮输入
int motorIB2 = 10;
int scode_L = 0; //左轮编码器码盘脉冲计数值
int scode_R =0;  //右轮编码器码盘脉冲计数值
unsigned long old_time=0,new_time=0; // 总时间标记
unsigned long time1 = 0, time2 = 0; //左1,右2 轮 时间标记
double val_right=0,val_left=0,pwm = 180;
float spdLft, spdRgt;
boolean  flag= true;
char code; //定义字符变量
void setup()
{
  Serial.begin(9600);
  pinMode(motorIA1,OUTPUT);
  pinMode(motorIB1,OUTPUT);
  pinMode(motorIA2,OUTPUT);
  pinMode(motorIB2,OUTPUT);
attachInterrupt(0, Code1, FALLING);//中断设置
attachInterrupt(1, Code2, FALLING);
//PID设置参数
Setpoint=0;  //左右差为0 为目标   
myPID.SetOutputLimits(-40,40); //输出限制         
myPID.SetTunings(Kp,Ki,Kd);     
myPID.SetSampleTime(1000);     
myPID.SetMode(AUTOMATIC);

}
// 左侧车轮电机的编码器码盘计数中断子程序
void Code1()
{   
  if((millis()-time1)>5) scode_L += 1;
   time1=millis();
}
void Code2()
{
   if((millis()-time2)>5)  scode_R += 1;
  time2=millis();
}
//前进函数
void advance()
{
  analogWrite(motorIA1, val_left);//使直流电机(左)顺时针转,可调速
  digitalWrite(motorIB1,LOW);  
  analogWrite(motorIA2,255- val_right);//使直流电机(右)逆时针转,可调速     
  digitalWrite(motorIB2,HIGH);
  flag= true;
}
//后退函数
void backup()
{
  analogWrite(motorIA1,255- val_left);//使直流电机(左)逆时针转,可调速     
  digitalWrite(motorIB1,HIGH);  
  analogWrite(motorIA2, val_right);//使直流电机(右)顺时针转,可调速     
  digitalWrite(motorIB2,LOW);
flag= false;
}
//停止函数
void stopa()
{
  digitalWrite(motorIA1,HIGH);//使直流电机(左)制动      
  digitalWrite(motorIB1,HIGH);  
  digitalWrite(motorIA2,HIGH);//使直流电机(右)制动      
  digitalWrite(motorIB2,HIGH);
}

void loop()
{  
  if(Serial.available() > 0) // 判断缓冲区中有无数据
  {
              code = Serial.read();    // 读取数据
              switch(code)
              {
                case  '1': advance(); break;  //前进
                case  '2': backup(); break;   //后退
                case  '5': stopa(); break;    //停止
                default:break;
              }
  }
          new_time=millis();
          if(abs(new_time - old_time) >= 1000) // 如果计时时间已达1秒   
          {     
               detachInterrupt(0); // 关闭外部中断0   
              detachInterrupt(1); // 关闭外部中断1                 
              spdLft =(float)scode_L*60/20;//小车左车轮电机转速               
              spdRgt =(float)scode_R*60/20; //小车右车轮电机转速  
              Input=(spdLft-spdRgt);
              myPID.Compute();
              val_right=pwm-Output;         
              val_left=pwm+Output;
               scode_L = 0; //恢复到编码器测速的初始状态        
               scode_R = 0;         
              old_time=  millis();            
              attachInterrupt(0, Code1,FALLING); // 重新开放外部中断0,左  
              attachInterrupt(1, Code2,FALLING); // 重新开放外部中断1,右   
         }  

}
回复

使用道具 举报

发表于 2017-5-1 10:19:39 | 显示全部楼层
本帖最后由 275891381 于 2017-5-1 10:32 编辑

1秒调节一次慢了点吧
myPID.SetOutputLimits(-40,40); //输出限制40 你的kp=20  误差俩脉冲就满了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-5-1 10:46:26 | 显示全部楼层
275891381 发表于 2017-5-1 10:19
1秒调节一次慢了点吧
myPID.SetOutputLimits(-40,40); //输出限制40 你的kp=20  误差俩脉冲就满了

我之前设置的是KP=0.2,KI=0,KD=0.05,采样时间设为100ms,结果还是不明显,才加大的KP参数
回复 支持 反对

使用道具 举报

发表于 2017-5-1 14:44:11 | 显示全部楼层
本帖最后由 275891381 于 2017-5-1 14:50 编辑
无间的路 发表于 2017-5-1 10:46
我之前设置的是KP=0.2,KI=0,KD=0.05,采样时间设为100ms,结果还是不明显,才加大的KP参数


你这个最好用ki控制,让误差积累,你可以看看小车偏离最大容忍角度时两边脉冲是多少,kp=255/脉冲*0.6左右
还有你把
if((millis()-time1)>5) scode_L += 1;
   time1=millis();
改成scode_L += 1;增加脉冲数,修正效果应该好点吧
再者,你pid控制单位时间两边的脉冲差等于控制两边的速度相等,不太好。应该用pid控制两边的脉冲总和的差,相当于控制两边的位移相等就更加直线了
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-5-1 19:00:52 | 显示全部楼层
275891381 发表于 2017-5-1 14:44
你这个最好用ki控制,让误差积累,你可以看看小车偏离最大容忍角度时两边脉冲是多少,kp=255/脉冲*0.6 ...

请问您做过这种相关的控制么,能不能让我学习下您写的程序?
回复 支持 反对

使用道具 举报

发表于 2017-5-1 23:19:19 | 显示全部楼层
无间的路 发表于 2017-5-1 19:00
请问您做过这种相关的控制么,能不能让我学习下您写的程序?


给你改了个


  1. #include <L298.h>
  2. #include <PID.h>
  3. L298 motor(5,4,6,7);

  4. int shudu=100;

  5. long leftspeed_maichong,rightspeed_maichong;

  6. int siqu_left=10,siqu_right=10;
  7. int pwm_left,pwm_right;

  8. double weiyi_set=10,weiyi_input,weiyi_output;
  9. double Kp=1, Ki=0.1, Kd=0;
  10. PID myPID(&weiyi_input, &weiyi_output, &weiyi_set, &Kp, &Ki, &Kd);

  11. unsigned long old_time=0,new_time=0; // 总时间标记
  12. void setup()
  13. {
  14.   Serial.begin(115200);
  15.   attachInterrupt(0, right_speed ,FALLING);
  16.   attachInterrupt(1, left_speed  ,FALLING);

  17.   motor.MotorA_SiQu(siqu_left);
  18.   motor.MotorB_SiQu(siqu_right);
  19.   
  20.   myPID.SetSampleTime(50);
  21.   myPID.SetOutputLimits(-100,100);
  22.   delay(100);
  23. }

  24. void loop()
  25. {

  26.         new_time=millis();
  27.         if(abs(new_time - old_time) >= 50) // 如果计时时间已达1秒
  28.         {
  29.             if((leftspeed_maichong>10000)&&(rightspeed_maichong>10000))
  30.             {
  31.                leftspeed_maichong=leftspeed_maichong-10000;
  32.                rightspeed_maichong=rightspeed_maichong-10000;
  33.             }
  34.               weiyi_input=leftspeed_maichong-rightspeed_maichong;
  35.               myPID.Compute();
  36.               pwm_left=shudu-weiyi_output;
  37.               pwm_right=shudu+weiyi_output;
  38.               pwm_left=constrain(pwm_left,0,255);
  39.               pwm_right=constrain(pwm_right,0,255);
  40.               motor.MotorA(pwm_left);
  41.               motor.MotorB(pwm_right);
  42.               
  43.               //Serial.print(rightspeed_maichong);Serial.print(",");
  44.               //Serial.print(leftspeed_maichong);Serial.print(",");
  45.               Serial.print(weiyi_input);Serial.print(",");
  46.               Serial.print(weiyi_output);Serial.print(",");
  47.               Serial.println();
  48.               old_time=  millis();  
  49.         }
  50. }


  51. void left_speed(void)
  52. {
  53.   if(digitalRead(7)==0)leftspeed_maichong=leftspeed_maichong+1;
  54.   else leftspeed_maichong=leftspeed_maichong-1;
  55. }
  56. void right_speed(void)
  57. {
  58.   if(digitalRead(4)==0)rightspeed_maichong=rightspeed_maichong+1;
  59.   else rightspeed_maichong=rightspeed_maichong-1;
  60. }
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-5-2 20:53:07 | 显示全部楼层
多谢老师的耐心答疑,奈何自己过于愚钝啊。
我用您的库,修改了下程序,发现前进和后退时的速度有所变化,速度调整的话还没有太大起色,不过我不会放弃的哈哈
回复 支持 反对

使用道具 举报

 楼主| 发表于 2017-5-3 20:58:29 | 显示全部楼层

我用了您给的库发现改了之后效果依然不理想,我都快疯了,于是直接用P控制观察能否实现,结果发现依然不能实现走直线,老师您能帮我看看是不是这个程序本身就有问题啊?谢谢

int motorIA1 = 9;
int motorIB1 = 8;
int motorIA2 = 11;
int motorIB2 = 10;
int scode_L = 0; //左轮编码器码盘脉冲计数值
int scode_R =0;  //右轮编码器码盘脉冲计数值
unsigned long old_time=0,new_time=0; // 总时间标记
unsigned long time1 = 0, time2 = 0; //左1,右2 轮 时间标记
double val_right=0,val_left=0,pwm = 180,Output=0;
float spdLft, spdRgt;
boolean  flag= true;
char code; //定义字符变量
void setup()
{
  Serial.begin(9600);
  pinMode(motorIA1,OUTPUT);
  pinMode(motorIB1,OUTPUT);
  pinMode(motorIA2,OUTPUT);
  pinMode(motorIB2,OUTPUT);
attachInterrupt(0, Code1, FALLING);//中断设置
attachInterrupt(1, Code2, FALLING);

}
// 左侧车轮电机的编码器码盘计数中断子程序
void Code1()
{   
  if((millis()-time1)>5) scode_L += 1;

   time1=millis();
}
void Code2()
{
   if((millis()-time2)>5)  scode_R += 1;
  time2=millis();
}
//前进函数
void advance()
{
  analogWrite(motorIA1, val_left);//使直流电机(左)顺时针转,可调速
  digitalWrite(motorIB1,LOW);  
  analogWrite(motorIA2,255- val_right);//使直流电机(右)逆时针转,可调速     
  digitalWrite(motorIB2,HIGH);
  flag= true;
}
//后退函数
void backup()
{
  analogWrite(motorIA1,255- val_left);//使直流电机(左)逆时针转,可调速     
  digitalWrite(motorIB1,HIGH);  
  analogWrite(motorIA2, val_right);//使直流电机(右)顺时针转,可调速     
  digitalWrite(motorIB2,LOW);
flag= false;
}
//停止函数
void stopa()
{
  digitalWrite(motorIA1,HIGH);//使直流电机(左)制动      
  digitalWrite(motorIB1,HIGH);  
  digitalWrite(motorIA2,HIGH);//使直流电机(右)制动      
  digitalWrite(motorIB2,HIGH);
}

void loop()
{  
  if(Serial.available() > 0) // 判断缓冲区中有无数据
  {
              code = Serial.read();    // 读取数据
              switch(code)
              {
                case  '1': advance(); break;  //前进
                case  '2': backup(); break;   //后退
                case  '5': stopa(); break;    //停止
                default:break;
              }
  }
          new_time=millis();
          if(abs(new_time - old_time) >= 1000) // 如果计时时间已达1秒   
          {     
               detachInterrupt(0); // 关闭外部中断0   
              detachInterrupt(1); // 关闭外部中断1                 
              spdLft =(float)scode_L*60/20;//小车左车轮电机转速               
              spdRgt =(float)scode_R*60/20; //小车右车轮电机转速  
              Output=6*(spdLft-spdRgt);
              
              val_right=pwm+Output;         
              val_left=pwm-Output;
               scode_L = 0; //恢复到编码器测速的初始状态        
               scode_R = 0;         
              old_time=  millis();            
              attachInterrupt(0, Code1,FALLING); // 重新开放外部中断0,左  
              attachInterrupt(1, Code2,FALLING); // 重新开放外部中断1,右   
         }  

}
回复 支持 反对

使用道具 举报

发表于 2017-5-4 08:25:47 | 显示全部楼层
无间的路 发表于 2017-5-3 20:58
我用了您给的库发现改了之后效果依然不理想,我都快疯了,于是直接用P控制观察能否实现,结果发现依然不 ...


我发的程序可以呀,给你说的你的程序的问题你一个都没改

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则 需要先绑定手机号

Archiver|联系我们|极客工坊

GMT+8, 2024-4-20 08:51 , Processed in 0.039802 second(s), 19 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表