2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 基于PID算法下STM32控制的坡道行驶电动小车(电赛)

基于PID算法下STM32控制的坡道行驶电动小车(电赛)

时间:2020-12-17 17:02:49

相关推荐

基于PID算法下STM32控制的坡道行驶电动小车(电赛)

文章目录

前言一、题目设计二、硬件选择三、软件设计四、程序设计

前言

本题源于TI杯大学生电子设计竞赛C题-----坡道行驶电动小车

由于手上没有MSP430/MSP432 板子,所以本篇采用stm32实现

一、题目设计

任务

利用 TI 的 MSP430/MSP432 平台,设计制作一个四轮电动小车。要求小车能沿着指定路线在坡道上自动循迹骑线行驶。小车必须独立运行,车外不能使用任何设备(包括电源)。小车(含电池)重量小于 1.5kg,外形尺寸在地面投影不大于 25cm×25cm。坡道用长、宽约 1m 的细木工板制作,允许板上有木质本色及自然木纹。木工板表面铺设画有 1cm×1cm 黑白间隔的纸条(以下简称为标记线)作为路线指示;标记线起始段为直线,平行于木板两边;标记线在坡顶转向 90°,转弯半径 20cm;标记线平行坡顶距≥30cm,距坡顶距离≤20cm;标记线总长度为 1m。停车标记为宽 1cm 长 5cm 的黑色线条,垂直于坡顶标记线。小车坡度

角示意及行驶线路顶视图如图 1 所示。

要求

(1) 坡度角 θ=0°,电动小车能够沿标记线自动骑线行驶,在停车点停车; 小车上标记点到停车标记中心线的垂直距离误差≤2cm。停车时立即发出声音提示。小车行驶过程中,其地面投影不得脱离标记线。(15 分)

(2) 在完成(1)的基础上,电动小车能够设定行驶时间,自动控制小车匀速通过 1 米长的线路,在停车点停车。行驶时间可在 10s~20s 间设定。误差绝对值≤1s。行驶过程中不得碾压、脱离标记线。时间误差每超过1s 扣 1 分。 (20 分)

(3) 坡度角 θ=10°,完成要求(2)的动作。 (20 分)

(4) 可任意指定坡度角 θ 在 11°~30°,完成要求(2)的动作。 (20 分)

(5) 在完成(4)后,尽量增加坡度角 θ,完成要求(2)动作。 (20 分)

(6) 其他。 (5 分)

(7) 设计报告: (20 分)

二、硬件选择

1. 主控芯片

采用STM32F103c8t6作为主控芯片

2. 电机

采用霍尔编码器电机(4个)

3. 电机驱动

需要采用2个TB6612电机驱动,分别控制左前轮左后轮、右前轮右后轮

4. 轮胎

选用黑色轮胎 海绵内胎,软橡胶外胎,金属轮毂(4个)

5. 红外传感器

采用三路循迹模块

6. 小车底板

采用铝合金底板

7. 电池

采用7.4v 3600mah锂电池

8. 面包板

插接导线,搭建电路

9. 下载器

采用ST-LINK V2仿真器

10. 其余工具零件

若干杜邦线、螺母、铜柱、螺丝刀、剥线钳等

实物图:

三、软件设计

1. stm32最小系统板

插在面包板上,方便引脚连接。由于stm32最小系统板没有基本定时器,所以最多可采用4个定时器( 1个高级定时器TIM1和3个通用定时器TIM2、TIM3、TIM4 ),在这里需要全部用上。

2. 定时器

TIM1:配置四个电机的PWM输出

左前:PA8 左后:PA9 右前:PA10 右后:PA11

TIM2:配置编码器模式,通道1和通道2分别作为小车左后轮编码器A、B相

PA0、PA1

TIM3:配置编码器模式,通道1和通道2分别作为小车右后轮编码器A、B相

PA6、PA7

TIM4:在中断读取TIM2、TIM3计数器(cnt)的值,设置TIM4每隔5ms溢出一次,每向上计数四次cnt的值加1,并且在中断函数中读取完cnt的值后就清零,那么每次进入TIM4溢出中断时,cnt的值都不会太大,可直接将cnt的值作为小车的当前速度。

3. 三路红外传感器

Vcc和GND连接TB6612模块上的Vcc和GND

红外输出: 左 中 右 分别接线PA2、PA3、PA4

(1) 当左右均未踩到黑线时,小车匀速直行

(2) 当左没踩到,右踩到黑线时,小车右转

(3) 当左踩到,右没踩到黑线时,小车左转

(4) 当左中右都踩到黑线时,小车停止

4. TB6612驱动

逻辑输入:

左前轮 配置为通道1连接PB12,PB13

左后轮 配置为通道2连接PB10,PB11

右前轮 配置为通道3连接PB14,PB15

右后轮 配置为通道4连接PB0,PB1

用锂电池给TB6612模块供电,即锂电池的正负极连接TB6612的Vin+和-;然后用该TB6612模块的Vout+和-连接另一块TB6612的Vin+和-,相继再用Vcc和GND与面包饭上的Vcc和GND相连,实现供电。

四、程序设计

1.红外传感设计

void GPIOB_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;/*红外out引脚:PA2,PA3,PA4*/GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_15;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB,&GPIO_InitStructure);/*初始为低电平*/GPIO_ResetBits(GPIOB,GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);}void Change_Target(int t_l,int t_r){target_left = t_l;target_right = t_r;}void forward(int t){Change_Target(t,t);GPIO_SetBits(GPIOB,GPIO_Pin_1| GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_15);GPIO_ResetBits(GPIOB,GPIO_Pin_0| GPIO_Pin_10 | GPIO_Pin_12 | GPIO_Pin_14);}void back(int t){Change_Target(t,t);GPIO_SetBits(GPIOB,GPIO_Pin_0| GPIO_Pin_10 | GPIO_Pin_12 | GPIO_Pin_14);GPIO_ResetBits(GPIOB,GPIO_Pin_1| GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_15);}void turn_left(int t_l,int t_r){Change_Target(t_l,t_r);GPIO_SetBits(GPIOB,GPIO_Pin_1| GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_15);GPIO_ResetBits(GPIOB,GPIO_Pin_0| GPIO_Pin_10 | GPIO_Pin_13 | GPIO_Pin_14);}void turn_right(int t_l,int t_r){Change_Target(t_l,t_r);GPIO_SetBits(GPIOB,GPIO_Pin_1| GPIO_Pin_11 | GPIO_Pin_13 | GPIO_Pin_14);GPIO_ResetBits(GPIOB,GPIO_Pin_0| GPIO_Pin_10 | GPIO_Pin_12 | GPIO_Pin_15);}void stop(void){GPIO_ResetBits(GPIOB,GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);}

2.定时器

void TIM1_PWM_Init(u16 per,u16 psc){/*使能TIM4时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1,ENABLE);/*使能GPIO*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);/*使能AFIO*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);/*配置GPIO*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 |GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);/*设置重映射*///GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3,ENABLE);//部分重映射/*初始化定时器参数*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频为1分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//选择计数模式为向上计数TIM_TimeBaseInitStructure.TIM_Period = per;//配置周期(ARR自动重装器的值)TIM_TimeBaseInitStructure.TIM_Prescaler = psc;//配置PSC预分频器的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,高级计数器才需配置TIM_TimeBaseInit(TIM1,&TIM_TimeBaseInitStructure);//TIM_ClearFlag(TIM4,TIM_FLAG_Update);//先清除标志位,避免刚初始化就进入中断/*初始化PWM参数*/TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCInitStructure.TIM_Pulse = 0;TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset; //选择空闲状态下的非工作状态 低电平TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCNIdleState_Set; //选择互补空闲状态下的非工作状态 低电平TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;//选择PWM1模式TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;//输出极性:高电平有效TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出比较使能TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable; //互补输出比较使能TIM_OC1Init(TIM1,&TIM_OCInitStructure);TIM_OC2Init(TIM1,&TIM_OCInitStructure);TIM_OC3Init(TIM1,&TIM_OCInitStructure);TIM_OC4Init(TIM1,&TIM_OCInitStructure);/*使能TIMX在CCRX上的预装载寄存器*/TIM_OC1PreloadConfig(TIM1,TIM_OCPreload_Enable);TIM_OC2PreloadConfig(TIM1,TIM_OCPreload_Enable);TIM_OC3PreloadConfig(TIM1,TIM_OCPreload_Enable);TIM_OC4PreloadConfig(TIM1,TIM_OCPreload_Enable);TIM_CtrlPWMOutputs(TIM1,ENABLE);/*使能TIMX在ARR上的预装载寄存器允许位*///TIM_ARRPreloadConfig(TIM4,ENABLE);/*开启定时器*/TIM_Cmd(TIM1,ENABLE);}void TIM2_Init(u16 per,u16 psc){/*开启通用定时器TIM2和GPIO时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);/*配置时基单元*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频为1分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//选择计数模式为向上计数/*定时频率 = 72M / (PSC+1) / (ARR+1)*/TIM_TimeBaseInitStructure.TIM_Period = per;//已知编码器线数:线数*4-1TIM_TimeBaseInitStructure.TIM_Prescaler = psc;TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,高级计数器才需配置TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);/*选择时钟源,配置正交解码*/TIM_ICInitTypeDef TIM_ICInitStructure; TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);//TI12:A、B项都计数TIM_ICStructInit(&TIM_ICInitStructure);//缺省输入TIM_ICInitStructure.TIM_ICFilter = 10; //滤波器TIM_ICInit(TIM2, &TIM_ICInitStructure);TIM_SetCounter(TIM2,0X7FFF); //TIM4->CNT=0X7FFF(65536的一半)/*启动定时器*/TIM_Cmd(TIM2,ENABLE);}void TIM3_Init(u16 per,u16 psc){/*开启通用定时器TIM2和GPIO时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA,&GPIO_InitStructure);/*配置时基单元*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频为1分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//选择计数模式为向上计数/*定时频率 = 72M / (PSC+1) / (ARR+1)*/TIM_TimeBaseInitStructure.TIM_Period = per;//已知编码器线数:线数*4-1TIM_TimeBaseInitStructure.TIM_Prescaler = psc;TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器的值,高级计数器才需配置TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);/*选择时钟源,配置正交解码*/TIM_ICInitTypeDef TIM_ICInitStructure; TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_BothEdge ,TIM_ICPolarity_BothEdge);//TI12:A、B项都计数TIM_ICStructInit(&TIM_ICInitStructure);//缺省输入TIM_ICInitStructure.TIM_ICFilter = 0; //滤波器TIM_ICInit(TIM3, &TIM_ICInitStructure);TIM_SetCounter(TIM3,0X7FFF); //TIM4->CNT=0X7FFF(65536的一半)/*启动定时器*/TIM_Cmd(TIM3,ENABLE);}void TIM4_Init(u16 per,u16 psc){/*1、开启通用定时器TIM4时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);/*2、选择时钟源为内部时钟*///TIM_InternalClockConfig(TIM4);//默认为内部时钟/*3、配置时基单元*/TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//选择时钟分频为1分频TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//选择计数模式为向上计数/*定时频率 = 72M / (PSC+1) / (ARR+1)*/TIM_TimeBaseInitStructure.TIM_Period = per;//配置周期(ARR自动重装器的值)TIM_TimeBaseInitStructure.TIM_Prescaler = psc;//配置PSC预分频器的值TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);/*4、使能更新中断*/TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE);/*5、配置NVIC*/NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//选择中断通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//开启中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;//响应优先级NVIC_Init(&NVIC_InitStructure);/*6、启动定时器*/TIM_Cmd(TIM4,ENABLE);}

3.PID设计

int target_left,target_right;int err_now1,err_now2;int err_last1,err_last2;int err_last_last1,err_last_last2;int spd_now1,spd_now2;int jisuan1,jisuan2;int out_left,out_right;int cnt_left,cnt_right;float Kp=0.5;float Ki=0.4;float Kd= 0.1;cnt_left = abs((TIM2->CNT)-0X7FFF);cnt_right = abs((TIM3->CNT)-0X7FFF);TIM2->CNT = 0X7FFF;TIM3->CNT = 0X7FFF;//左电机spd_now1 = cnt_left;err_now1 = target_left - spd_now1;jisuan1 = Kp*(err_now1-err_last1) + Ki*err_now1 + Kd*(err_now1+err_last_last1-2*err_last1);out_left += jisuan1;if(out_left<0)out_left = 0;if(out_left>100) out_left = 100; TIM_SetCompare1(TIM1,out_left); //左前TIM_SetCompare2(TIM1,out_left); err_last_last1 = err_last1;err_last1 = err_now1;//右电机spd_now2 = cnt_right;err_now2 = target_right - spd_now2;jisuan2 = Kp*(err_now2-err_last2) + Ki*err_now2 + Kd*(err_now2+err_last_last2-2*err_last2);out_right += jisuan2;if(out_right<0)out_right = 0;if(out_right>100) out_right = 100; TIM_SetCompare3(TIM1,out_right); //右前轮TIM_SetCompare4(TIM1,out_right);err_last_last2 = err_last2;err_last2 = err_now2; TIM_ClearITPendingBit(TIM4, TIM_IT_Update);

4. main

int main(void){NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组GPIOB_Init();TIM1_PWM_Init(100-1,72-1);TIM2_Init(65535,9);TIM3_Init(65535,9);TIM4_Init(5000,72-1); int i;while(1){if(L==0 && R==0) i=0;if(L==1 && R==0) i=1;if(L==0 && R==1) i=3;if(L==1 && M==1 && R==1) i=5;switch(i){case 0:forward(4);break;case 1:turn_left(4,10);break;case 3:turn_right(10,4);break;case 5:stop();delay_ms(1000);break;}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。