无间的路 发表于 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;
booleanflag= 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,右   
         }

}

275891381 发表于 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参数

275891381 发表于 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 ...

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

275891381 发表于 2017-5-1 23:19:19

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

给你改了个


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

int shudu=100;

long leftspeed_maichong,rightspeed_maichong;

int siqu_left=10,siqu_right=10;
int pwm_left,pwm_right;

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

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

motor.MotorA_SiQu(siqu_left);
motor.MotorB_SiQu(siqu_right);

myPID.SetSampleTime(50);
myPID.SetOutputLimits(-100,100);
delay(100);
}

void loop()
{

      new_time=millis();
      if(abs(new_time - old_time) >= 50) // 如果计时时间已达1秒
      {
            if((leftspeed_maichong>10000)&&(rightspeed_maichong>10000))
            {
               leftspeed_maichong=leftspeed_maichong-10000;
               rightspeed_maichong=rightspeed_maichong-10000;
            }
            weiyi_input=leftspeed_maichong-rightspeed_maichong;
            myPID.Compute();
            pwm_left=shudu-weiyi_output;
            pwm_right=shudu+weiyi_output;
            pwm_left=constrain(pwm_left,0,255);
            pwm_right=constrain(pwm_right,0,255);
            motor.MotorA(pwm_left);
            motor.MotorB(pwm_right);
            
            //Serial.print(rightspeed_maichong);Serial.print(",");
            //Serial.print(leftspeed_maichong);Serial.print(",");
            Serial.print(weiyi_input);Serial.print(",");
            Serial.print(weiyi_output);Serial.print(",");
            Serial.println();
            old_time=millis();
      }
}


void left_speed(void)
{
if(digitalRead(7)==0)leftspeed_maichong=leftspeed_maichong+1;
else leftspeed_maichong=leftspeed_maichong-1;
}
void right_speed(void)
{
if(digitalRead(4)==0)rightspeed_maichong=rightspeed_maichong+1;
else rightspeed_maichong=rightspeed_maichong-1;
}

无间的路 发表于 2017-5-2 20:53:07

多谢老师的耐心答疑,奈何自己过于愚钝啊。
我用您的库,修改了下程序,发现前进和后退时的速度有所变化,速度调整的话还没有太大起色,不过我不会放弃的哈哈

无间的路 发表于 2017-5-3 20:58:29

275891381 发表于 2017-5-1 23:19
给你改了个


我用了您给的库发现改了之后效果依然不理想,我都快疯了,于是直接用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;
booleanflag= 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,右   
         }

}

275891381 发表于 2017-5-4 08:25:47

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

我发的程序可以呀,给你说的你的程序的问题你一个都没改
页: [1]
查看完整版本: 求助大神!!!小车走直线的程序老是没效果