其实单环我们先出了所有题目,但是效果显然没有串级PID的效果好,有人需要的话可以把程序包发出来,板球运行视屏也有
另外:天下舵机参差不齐(哪怕型号相同),想要好的效果就多写两行程序:X,Y轴两套PID参数,当然,一套参数也能出结果
单环-位置环
能做题,但一般的机械结构都需要最后做一些补偿,这里就不赘述了。贴两行控制程序
//PID函数
double PID_Realize(double roll,double SetAngle,PID_Date *pid)
{
double incrementSpeed;
pid->SetSpeed = SetAngle;
pid->ActualSpeed= roll;
pid->err=pid->SetSpeed-pid->ActualSpeed;
incrementSpeed=pid->Kp*pid->err+pid->Ki*pid->sum+pid->Kd*(pid->err-pid->err_next);
pid->err_last=pid->err_next;
pid->err_next=pid->err;
pid->sum+=pid->err;
return incrementSpeed;
}
PWM_X = PID_Realize(now_x_position,X_Angle,&pid_x); //X轴舵机输出PWM
PWM_Y = PID_Realize(now_y_position,Y_Angle,&pid_y); //Y轴舵机输出PWM
/**********************************************************************************************
最终可以完成任务,但系统总体的鲁棒性很差,稍微有些外界干扰,最后小球的位置
都可能有所偏差
***********************************************************************************************/
串级PID-速度环作为内环
程序贴在下方,大概描述一下PID调节过程
我们采用两种方式做出了串级
- 内环PI 外环PD
- 内环PD 外环PI
第一种不需要积分限幅,而第二种我们最后做了积分分离
由于第二种更容易理论解释,所以本文中讲述的是第二种方式
pid_x
pid_y 为外环两组PID结构体
pid_spe_x
pid_spe_y 为外环两组PID结构体
SX_PWM为X轴舵机速度期望
SY_PWM为Y轴舵机速度期望
-
第一步速度期望SX_PWM,SY_PWM都设置为零,只给速度环PID的KP,在一定范围内,可以达到效果:动一下小球,小球快速停下来,在我的系统里参数为60~80,最终我给了X方向上速度环KP65,Y方向上速度环KP是75。
-
速度限幅,调内环KD,下面程序限幅为±1.6(一次中断的时间小球最多走1.6像素点距离)。在幅值内任意给几个速度,我们测试了0.8,1.6,-1.6,通过现象来整定内环KD。小球在板子上基本匀速即可,不要有太大震荡。
-
内环速度已经稳定了,现在可以把外环输出给内环做期望速度了,SY_PWM = Kalman4(PID_Realize(now_y_position,set_goal_y,&pid_y));然后开始调节外环KP。
-
外环KP直接从内环KP/100开始向上调。set_goal_y给定为板子上九点某一点。差不多小球停在期望坐标周围就可以了。当然优秀的机械结构,这道题目基本算是做完了。
-
在上一条,我们达到了小球在目标点附近的效果,此时加入分段积分,即外环KI,判断当小球在目标点附近时加入积分。让小球进入最终死区。别忘了把积分效果清零哦。
整个串级处理程序:放在了定时中断里
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static u8 times=0;
static double data_cloud_xy[2][3]={0};
if(htim==(&TIM4_Handler))
{
image_binarize(rgb_data_buf); //二值化处理程序,用时2.17ms
Find_Nine_Point(); //找到九个点坐标,0.90ms
if(((int)now_x_position+(int)now_y_position)==0) //找不到球重新找球
find_ball();
else
find_next_ball(data_cloud_xy[0][times],data_cloud_xy[1][times]);
times=(times+1)%2;
data_cloud_xy[0][times] = ball_start.x;
data_cloud_xy[1][times] = ball_start.y;
if(times==0){
now_x_position = Kalman1(0.95*data_cloud_xy[0][0]+0.05*data_cloud_xy[0][1]);//+0.05*data_cloud[0][1];
now_y_position = Kalman2(0.95*data_cloud_xy[1][0]+0.05*data_cloud_xy[1][1]);//+0.05*data_cloud[1][1];
spe_x = now_x_position - data_cloud_xy[0][2]; //X轴方向速度 一次中断运行的距离
spe_y = now_y_position - data_cloud_xy[1][2]; //Y轴方向速度
// if(fabs(spe_x)<0.3)spe_x=0; //用来做速度为0的速度环 测试
// if(fabs(spe_y)<0.3)spe_y=0;
data_cloud_xy[0][2] = now_x_position;
data_cloud_xy[1][2] = now_y_position;
//速度限幅
if(Kalman3(PID_Realize(now_x_position,set_goal_x,&pid_x))>=1.6)
{
SX_PWM = 1.6;
}
else if(Kalman3(PID_Realize(now_x_position,set_goal_x,&pid_x))<=-1.6)
{
SX_PWM = -1.6;
}
else{ SX_PWM = Kalman3(PID_Realize(now_x_position,set_goal_x,&pid_x));}
if(Kalman4(PID_Realize(now_y_position,set_goal_y,&pid_y))>1.6)
{
SY_PWM = 1.6;
}
else if(Kalman4(PID_Realize(now_y_position,set_goal_y,&pid_y))<-1.6)
{
SY_PWM = -1.6;
}
else{ SY_PWM = Kalman4(PID_Realize(now_y_position,set_goal_y,&pid_y));}
PWM_X = PID_Realize(spe_x,SX_PWM,&pid_spe_x); //X,Y舵机输出
PWM_Y = PID_Realize(spe_y,SY_PWM,&pid_spe_y);
if(system_flag==1)
{
Motor_Contor(PWM_X,PWM_Y);
}
else{ //消除历史积分
Motor_Stop();
pid_x.sum = 0;
pid_y.sum = 0;
pid_spe_x.sum = 0;
pid_spe_y.sum = 0;
pid_x.err_next = 0;
pid_y.err_next = 0;
pid_x.err_last = 0;
pid_y.err_last = 0;
pid_spe_x.err_next = 0;
pid_spe_y.err_next = 0;
pid_spe_x.err_last = 0;
pid_spe_y.err_last = 0;
}
//Repoter_XY(now_x_position*10,PWM_X,set_goal_x*10,now_y_position*10,PWM_Y,set_goal_y*10); //回传参数,用于观察PID曲线效果
f=ov_frame;
ov_frame=0;
}
}
}
预祝大家的串级PID顺利调好。