由于该题我们没有去参加比赛,只是做着好玩,所以就没有按照竞赛题目的要求用msp430作为主控芯片,而是用了手头上正好有的一块stm32最小系统板,下面是设计思路:
一、硬件电路:
1、电机驱动的选择
我们采用的是L298N电机驱动模块。L298N输出电压最高可达50V,由于我们选用了三节4v的电池供电,正好可以作为L298的12v输入,并且可以输出5v电压给stm32及其他模块供电;此外,L298的四个使能端也可以作为PWM输出口,来控制电机的转速。
2、传感器的选择
由于题目要求我们必须循迹,我们直接用了一个三红外寻迹模块(我自己取的名哈,只要是红外寻迹的就行,就是下面那玩意),上面那三个黄色的旋钮可以调节灵敏度,当他检测到黑线时,对应的GPIO口就会输出高电平,按照题目的意思就是只有在需要转向的时候才会检测到黑线,当它需要转向了,输出的高电平就会反馈到stm32中,再通过调节输出pwm的那几个GPIO口的占空比来控制四个电机的转速,从而实现转向的功能。并且当三个红外全部检测到黑线,即全部输出高电平时,使四个电机反转,注意这个反转的时间很小,占空比也很小,过了这段时间后马上将占空比调节为0。
3、车轮及车胎的选择
我认为,这个题目中,车轮及车胎的选择至关重要,一定要选择摩擦力足够大的车胎,毕竟想要获得高分,你的车就必须要能爬上30度的坡,并且想要爬上30度的坡,除了摩擦力要足够大之外,你的12v的电池肯定是不够的,因为我们的车只能爬10度的坡,哈哈哈~。
二、实物图
丑了吧唧的。。。。
三、PID控制
这个PID控制是我后来学了PID之后才加上去的,所以实物图上面并没有用到编码器电机来测速,这里PID控速还是挺重要的,因为随着角度的提升,小车的速度会越来越慢,以至于后面会爬不上去,但是如果初始占空比给大了,速度又太快,可能导致红外模块刚检测到黑线还没反应过来,小车就已经冲过去了,那还怎么玩是吧。但是有了PID控速的话,只要给了他一个初始速度,他就能自己通过调节占空比来大概的保持这个速度,当编码器测得的实际速度过小时,通过PID算法(增量式用于测速较好),将得到的一个占空比更大的PWM波返回,达到增加速度的目的。PID部分代码如下(最好是将PID部分的代码做成一个结构体,放在另一个.C文件中):
uint16_t pwm_duty; //增量式PID中PWM的增量
uint16_t pwm_out; //最后算出需要输出的PWM
uint8_t speed_now = 100; //实际速度
uint8_t speed_want = 80; //期望速度(初始速度)
uint8_t P = 50,I = 20,D = 2;
float error = 0.0; //本次差值
float error_pre = 0.0; //上次差值
float error_pre_pre = 0.0; //上上次差值
float error_add = 0; //差值的累加
//假设当前速度为100r/s(实际速度可由编码器采集获取)
uint16_t PID_PLACE(uint8_t speed_want) //位置式
{
error = speed_want – speed_now;
error_add += error;
pwm_out = (int)(P*error + I*error_add + D*(error-error_pre));
error_pre = error;
return pwm_out;
}
uint16_t PID_ADD(uint8_t speed_want) //增量式
{
error = speed_want – speed_now;
pwm_duty = (int)(P*(error – error_pre) + I*error + D*(error-2*(error_pre)+error_pre_pre));
pwm_out += pwm_duty;
error_pre = error;
error_pre_pre = error_pre;
return pwm_out;
}
四、软件部分代码
1、我这里用的是TIM3的四个通道来输出四个PWM波,代码类似,只需要对照引脚定义图看是哪个引脚就行了。这里只列举了TIM3通道1的初始化代码:
void TIM3_CH1_PWM_Init(uint16_t per,uint16_t psc) //自动重装值和预分频器的值
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用功能输出
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = per;
TIM_TimeBaseInitStructure.TIM_Prescaler = psc;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0x00;
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值(CCR的值
TIM_OC1Init(TIM3,&TIM_OCInitStructure);
//开启定时器
TIM_Cmd(TIM3,ENABLE);
TIM_OC1PreloadConfig(TIM3,TIM_OCPreload_Enable); //使能TIM3的通道2
TIM_ARRPreloadConfig(TIM3,ENABLE); //使能TIM3在ARR上的预装载寄存器
}
2、然后还需要将L298所用到的八个GPIO口初始化:
void GPIOA_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
void GPIOB_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB,&GPIO_InitStructure);
}
3、将TIM3的初始化封装和设置小车的初始占空比:
void TIM3_Init(void)
{
TIM3_CH1_PWM_Init(10000-1,144-1); // 72MHZ/10000/144 = 50HZ = 20ms
TIM3_CH2_PWM_Init(10000-1,144-1);
TIM3_CH3_PWM_Init(10000-1,144-1);
TIM3_CH4_PWM_Init(10000-1,144-1);
}
void Car_Forward(int8_t speed)
{
TIM_SetCompare1(TIM3,speed);
TIM_SetCompare2(TIM3,speed);
TIM_SetCompare3(TIM3,speed);
TIM_SetCompare4(TIM3,speed);
}
除此之外还有编码器测速部分和红外模块的部分代码,红外模块的代码比较简单,只需要控制三个GPIO口输入高低电平即可,编码器测速部分参考了下面这篇博客,原理、代码注释啥的讲的很清楚:STM32——编码器测速原理及STM32编码器模式_卡卡南安的博客-CSDN博客_stm32编码器测速
文章知识点与官方知识档案匹配,可进一步学习相关知识C技能树首页概览114447 人正在系统学习中
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!