PID闭环底盘调试记录

新学期和老师做了大创的项目,由我负责底盘的控制,第一做这种项目,内容可能有很多不足,欢迎指正。

文章目录

  • 一.硬件资料

  • 二.CUBEMX配置
  • 三.代码实现

一、硬件资料

开发板:stm32正点原子开发板 战舰

电机驱动×4   电机驱动模块 7A-160

(在使用过程中发现该驱动模块的模拟信号输入区域的接入电压有误,在接入3.3v电压可正常读取信号而输入为5v电压时则会无反应且有可能烧坏驱动模块!)

12v电源          使用时测好电压防止欠压操作

usb转ch340

编码器电机×4    驰海电机 JGB37-520 AB向 11个磁向石

麦克纳姆轮×4

二、CUBEMX配置

先配置时钟树打开外部晶振

配置一个50ms的定时器用于PID的计算(20ms是因为听一个电赛大佬说20ms这个电机编码器测速最准)

 72mhz/72/20000=50hz=20ms     

配置定时器开启编码器模式

 在函数中用HAL_TIM_Encoder_Start开启该模式

用HAL_TIM_GetCounter即可得到该定时器的上升沿和下降沿的次数

具体可看编码器教程

讲的比较详细我也是看着他来做的

再打开pwm通道用来控制电机转速

此外还需要开启几个gpio口的输入用来控制电机驱动的逻辑输入 

三.代码实现

1.引入pid库

我直接用的时大疆rm的pid代码,Pid代码可以再网上任意找

2.编写编码器读取

void motor_count_out(Motor *motorx,pid_type_def *motorx_speed_pid,uint8_t i)
{
      if(i==1){
      int16_t pluse = COUNTERNUM1 - RELOADVALUE1/2;                                            
        motorx->totalAngle = (pluse + motorx->loopNum * RELOADVALUE1/2); 
        }
        else if(i==2){
                int16_t pluse = COUNTERNUM2 - RELOADVALUE2/2;                                            
                motorx->totalAngle = (pluse + motorx->loopNum * RELOADVALUE1/2); 
                }
        else if(i==3){
                int16_t pluse = COUNTERNUM3 - RELOADVALUE3/2;                                            
                motorx->totalAngle = (pluse + motorx->loopNum * RELOADVALUE1/2); 
                }
        else if(i==4){
                int16_t pluse = COUNTERNUM4 - RELOADVALUE4/2;                                            
                motorx->totalAngle = (pluse + motorx->loopNum * RELOADVALUE1/2); 
                }
        motorx->speed = (float)(motorx->totalAngle - motorx->lastAngle)/(4*11*RR)*50*60;  //进行速度计算,根据前文所说的,4倍频,编码器11位,减速比??,
        motorx->lastAngle = motorx->totalAngle;         //更新转过的圈
}

pluse即为编码器读取电压脉冲的增量

用电压脉冲的增量去计算速度

在计算前可先用debug对转子不上电拨动,查看转子转动时返回编码器读数的情况以及判断其正负状态

PID

设置预期值

void set_pid_target(pid_type_def *pid, float temp_val)
{
  pid->set = temp_val;    // 设置当前的目标值
}

PID控制的输入

float speed_pid_contro(Motor *motorx,pid_type_def *motorx_speed_pid,int set)  
{
   
    float cont_val = 0.0;      // 当前控制值 
      uint64_t set_give=0;
    fp64 actual_speed=0;
    
      set_pid_target(motorx_speed_pid,set);
      set_give=motorx_speed_pid->set;
      actual_speed = fabs(motorx->speed);
    cont_val = PID_calc(motorx_speed_pid,actual_speed,set_give);    // 进行 PID 计算    
    return cont_val;
}

PID控制电机

因为使用pid控制电机维持在一个恒定的速度属于一个持续的过程,故不可将PID写在主函数中(因为主函数中程序一步一步执行,如果你在pid控制电机输出后加一个delay那么电机占空比将在这一时刻保持不变就无法进行动态的调整了),所以将pid控制电机输出的占空比写再定时器中

if(htim->Instance==htim6.Instance)		         //20ms中断
	{
		motor_count_out(&motor1,&motor1_speed_pid,1);
		motor_count_out(&motor2,&motor2_speed_pid,2);
		motor_count_out(&motor3,&motor3_speed_pid,3);
		motor_count_out(&motor4,&motor4_speed_pid,4);
		switch(point){
			case 1:ZhiZou(25,25,25,25);break;
			case 2:HouTui(25,25,25,25);break;
			case 3:ZuoYi(25,25,25,25);break;
			case 4:YouYi (25,25,25,25);break;
			case 5:stop();break;
			default:break;
			
		}

以下为电机指令的代码

void ZhiZou(int set1,int set2,int set3,int set4)
{
	zhenzhuan1(set1);
	zhenzhuan2(set2);
	zhenzhuan3(set3);
	zhenzhuan4(set4);
	
}
void zhenzhuan1 (int set)  //1号电机速度正转
{
	uint64_t out=0;
	IN1_1(0);
	IN2_1(1);
	out=speed_pid_contro(&motor1,&motor1_speed_pid,set);  
	__HAL_TIM_SetCompare(&htim8,TIM_CHANNEL_3,out);//out
}

pid参数的整定

仅为我个人的一些看法,再起初调节Pid时我得到的反馈值总是会处于波动的状态,后来发现是需要给一些D的参数值,P一般是用来提供系统快速的响应,但是会和目标值有一定的稳态误差,这是引入I值来消除这些稳态误差,但是这又会带给系统一些振荡,故需要调节D值給一些阻尼,来减小波动


总结

至此可以完成底盘电机的基本控制

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值