前言:2022年TI杯大学生电子设计竞赛,小车跟随行驶系统(C题)要求:设计一套小车跟随行驶系统,采用TI的MCU,由一辆领头小车和一辆跟随小车组成,要求小车具有循迹功能,且速度在0.3~1m/s可调......本文着重介绍速度在0.3~1m/s可调的一种实现方式。
正文:
一、首先了解编码电机测速的原理(移步下方链接,不过多赘述)
霍尔增量式编码器左右车轮线速度的计算_许你一世阳光yyds的博客-CSDN博客
二、获取关键参数,及oled使用
由此我们得知,我们需要获取的关键数据为编码电机 轮子转动一个脉冲走过的距离(m/脉) 这个参数可以是 1. 从你所购买电机的店铺获得;2. 自己做实验测出来。下面我们从2展开。
要测这个参数最起码,你的单片机要能够读取到编码电机A相或B相在旋转时所发出的脉冲,然后通过使用CCS debug功能看变量的值,或者直接显示在oled上。
四针oled显示:把下面链接的文件除了main.c( main.c用 三、里的 )都复制到你的工程文件里面
msp430f5529——OLED屏显示文字与图片_会动的栗子的博客-CSDN博客_msp430oled显示
三、获取 脉冲数 代码(注释的内容可完全删除也不会影响)
原理:定时器中断(连续计数模式),配置上升沿捕获,我在完整代码上注释了 X_1=0;X_2=0; (将这两个变量定时清0)的语句,此时的代码只能进行获取 脉冲总数 的实验,并不能获得当前速度。
main.c
#include #include "oled.h" #include "type.h" #include "bmp.h" //#include "motor.h" 这是我自己写的电机控制代码,无关紧要,对应的是下面 调试电机转向 //num代表每个定时器周期内(也就是0.4619989秒内)的脉冲数,X代表从单片机开始工作时记录的脉冲总数 int num1=0; //对应P2.0引脚 int num2=0; //对应P2.4 int X_1=0; //对应P2.0 int X_2=0; //对应P2.4 float PID_calc(float Set_Temp , float V_1) //PID算法子程序 输入:(目标值,当前值) { float P=0.800,I=0.15,D=800.0;//初始化P,I,D,当前值,设置值 常数 float PID_OUT=0,PWM_Duty=0; //PID输出 float P_OUT=0,I_OUT=0,D_OUT=0; //比例输出,积分输出,微分输出 float Current_Error=0, Last_Error=0; //当前误差 最后误差 float Sum_Error=0,Prev_Error=0; //误差积分 float Gain=1.2,PID_I_MAX=100.0,PID_I_MIN=-100.0,V_DATA_MAX=100,V_DATA_MIN=0; float Rate;//误差变化率 Current_Error = Set_Temp - V_1;//当前误差 Sum_Error +=Current_Error;//误差积分 Prev_Error = Last_Error;//存储误差积分 Last_Error = Current_Error;//存储误差分析 Rate = Current_Error-Last_Error;//变化速率计算 if(Rate>10)//不让ta大于5也不让ta小于5 Rate = 10; if(Rate<-10) Rate = -10; P_OUT = P*Gain*Current_Error;//比列项 I_OUT = I*Gain*Sum_Error;//积分项 //积分限幅处理 if( I_OUT>PID_I_MAX ) I_OUT = PID_I_MAX;//不能超过最大值不能低于最小值 if( I_OUT //微分输出处理 D_OUT = D*Gain*Rate; PID_OUT = P_OUT + I_OUT + D_OUT ; //if ( PID_OUT >= V_DATA_MAX ) PID_OUT = V_DATA_MAX; //if ( PID_OUT <= V_DATA_MIN ) PID_OUT = V_DATA_MIN; return PID_OUT; } float speed1(int num)//金属电机 下面的0.000556906就是我测得我电机 m/脉 的参数 { float v; //v = (num * 0.000556906) / 0.4619989;//这里把测得的 m/脉 取代0.000556906算出来的是m/s v = num * 1.2054271124;//mm/s 这句我为了方便单片机计算,自己已经算好了系数然后转化为mm/s return v; } float speed2(int num)//小蓝电机 如果你只是做一辆小车,就写一个speed计算函数就好了 { float v; //v = (num * 0.00037109091) / 0.4619989; v = num * 0.8032289903;//mm/s return v; } void TIME()//配置编码电机接口 { TA2CTL |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零 TA1CTL |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零 TA2CCTL1 |= CAP+CM_1+CCIS_0+CCIE; //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许 TA1CCTL1 |= CAP+CM_1+CCIS_0+CCIE; //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许 P2DIR &=~ BIT4; //初始化捕获IO口 P2SEL |= BIT4; P2DIR &=~ BIT0; //初始化捕获IO口 P2SEL |= BIT0; } int main(void) { WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer OLED_Init(); //初始化 OLED_Clear(); //清屏 TIME(); //motor_IO_int(); __bis_SR_register(GIE); float v1; float v2; //X_1 p2.0 //X_2 p2.4 //front(); float a1=0, b1=0; int a2=0, b2=0; while(1) { /*v1=speed2(num1); v2=speed2(num2); a1 = v1 ; //左电机 接的是p2.0 b1 = v2 ; //右 a1 = a1 + PID_calc(294 , a1); b1 = b1 + PID_calc(303 , b1);*/ OLED_ShowNum(1,1,num1,3,20);//oled第一行显示P2.0引脚捕获的脉冲数 OLED_ShowNum(1,2,num2,3,20);//第二行显示p2.4 //__delay_cycles(1600000);//1s延时 //a2 = a1 * 0.3; //b2 = b1 * 0.3; //mv_go(a2,b2); //front(); //OLED_ShowNum(1,1,v1,3,20); //OLED_ShowNum(1,2,v2,3,20); } /* 调试电机转向 mv_go(90,90); __delay_cycles(1600000*2); mv_back(90,90); __delay_cycles(1600000*2); mv_L(90,90); __delay_cycles(1600000*2); mv_R(90,90);*/ } #pragma vector=TIMER2_A1_VECTOR //Timer_A捕获中断向量 p2.4 __interrupt void Timer_A2(void) { switch(TA2IV) { case 2 : X_2++; break; default: num2=X_2 ; //X_2=0; 注释这里,代码只能实现测 m/脉 去掉注释则只能测速度 break; } } #pragma vector=TIMER1_A1_VECTOR //Timer_A捕获中断向量 p2.0 __interrupt void Timer_A1(void) { switch(TA1IV) { case 2 : X_1++; break; default: num1=X_1 ; //X_1=0; 同X_2 break; } } 通过上面的代码,自己用手转动车轮一周记录数值变化,多测几次取平均值,然后依据 一、自己算 m/脉 然后替换掉speed1或2里的公式. 四、匀速0.3m/s总代码 main.c + motor.h + motor.c 参考 main.c #include #include "oled.h" #include "type.h" #include "bmp.h" #include "motor.h" //num代表每个定时器周期内(也就是0.4619989秒内)的脉冲数,X代表从单片机开始工作时记录的脉冲总数 int num1=0; //对应P2.0引脚 int num2=0; //对应P2.4 int X_1=0; //对应P2.0 int X_2=0; //对应P2.4 float PID_calc(float Set_Temp , float V_1) //匀速PID算法子程序 输入:(目标值,当前值) { float P=0.800,I=0.15,D=800.0;//初始化P,I,D,当前值,设置值 常数 float PID_OUT=0,PWM_Duty=0; //PID输出 float P_OUT=0,I_OUT=0,D_OUT=0; //比例输出,积分输出,微分输出 float Current_Error=0, Last_Error=0; //当前误差 最后误差 float Sum_Error=0,Prev_Error=0; //误差积分 float Gain=1.2,PID_I_MAX=100.0,PID_I_MIN=-100.0,V_DATA_MAX=100,V_DATA_MIN=0; float Rate;//误差变化率 Current_Error = Set_Temp - V_1;//当前误差 Sum_Error +=Current_Error;//误差积分 Prev_Error = Last_Error;//存储误差积分 Last_Error = Current_Error;//存储误差分析 Rate = Current_Error-Last_Error;//变化速率计算 if(Rate>10)//不让ta大于5也不让ta小于5 Rate = 10; if(Rate<-10) Rate = -10; P_OUT = P*Gain*Current_Error;//比列项 I_OUT = I*Gain*Sum_Error;//积分项 //积分限幅处理 if( I_OUT>PID_I_MAX ) I_OUT = PID_I_MAX;//不能超过最大值不能低于最小值 if( I_OUT //微分输出处理 D_OUT = D*Gain*Rate; PID_OUT = P_OUT + I_OUT + D_OUT ; //if ( PID_OUT >= V_DATA_MAX ) PID_OUT = V_DATA_MAX; 如果你有加速度存在限制的需求,则启用这两句 //if ( PID_OUT <= V_DATA_MIN ) PID_OUT = V_DATA_MIN; return PID_OUT; } float speed1(int num)//金属电机 { float v; //v = (num * 0.000556906) / 0.4619989; v = num * 1.2054271124;//mm/s return v; } float speed2(int num)//小蓝电机 { float v; //v = (num * 0.00037109091) / 0.4619989; v = num * 0.8032289903;//mm/s return v; } void TIME()//配置编码电机接口 { TA2CTL |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零 TA1CTL |= TASSEL_2+MC_2+TAIE+TACLR+ ID_3;//SMCLK,连续计数,中断允许,计数器清零 TA2CCTL1 |= CAP+CM_1+CCIS_0+CCIE; //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许 TA1CCTL1 |= CAP+CM_1+CCIS_0+CCIE; //捕获模式,上升沿捕获,CCI1A输入,同步捕获,中断允许 P2DIR &=~ BIT4; //初始化捕获IO口 P2SEL |= BIT4; P2DIR &=~ BIT0; //初始化捕获IO口 P2SEL |= BIT0; } int main(void) { WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer OLED_Init(); //初始化 OLED_Clear(); //清屏 TIME();//接编码电机引脚初始化 motor_IO_int();//接线l298n的引脚初始化 __bis_SR_register(GIE); float v1; float v2; //X_1 p2.0 //X_2 p2.4 front(); float a1=0, b1=0; int a2=0, b2=0; while(1) { v1=speed2(num1); //自己写哪个speed函数就改成哪个 v2=speed2(num2); a1 = v1 ; //左电机 b1 = v2 ; a1 = a1 + PID_calc(300 , a1); //这里的用法是我希望速度在300mm/s,用增量式计算出a1(pwm输出)当前的值 /*但是这里的a1还不是pwm输出值,具体看下方a2,由于摩擦力的存在,设300mm/s不一定就是0.3m/s,具体看oled上,如果显示290,那可以设成(310,a1)具体多少自己调*/ b1 = b1 + PID_calc(300 , b1);//与a1同理 OLED_ShowNum(1,3,a1,3,20); OLED_ShowNum(1,4,b1,3,20); //__delay_cycles(1600000);//1s延时 a2 = a1 * 0.3;//设置好速度和pwm的比例转换 /*经实验,我的motor.c中TA0CCR0设置为100,我的电机在pwm输出90时速度大致为0.3m/s,90(下方调试电机转向的第一句)与300(mm/s)数字比例为0.3*/
上一篇:基于MSP430F5529的红外循迹小车,
下一篇:msp430f5529——OLED屏显示文字与图片
推荐阅读最新更新时间:2024-10-31 03:13
设计资源 培训 开发板 精华推荐
- TS4995 1.2W全差分音频功率放大器典型应用,可选待机和6db固定增益
- 具有 3MHz 开关频率和顺序上电的 LTC3374IFE 组合降压器的典型应用电路
- 【RA】智能风扇控制器
- LT1021DCN8-7 具有升压输出电流且无电流限制的电压基准的典型应用
- TCR2EN125、200mA、1.25V 输出电压 CMOS 低压降稳压器的典型应用
- LT1366 的典型应用 - 双路精密轨至轨输入和输出运算放大器
- 使用 Analog Devices 的 LTC2609CGN 的参考设计
- MAXREFDES1010:24V / 300mA,无光隔离反激式DC-DC转换器
- TWR-LCD: 图形LCD塔式系统模块
- ADM00352,MCP16301 演示板,用于背板分布式电源的 2 至 15V DC 至 DC 单输出电源