平衡小车之家高配版全向轮小车部分源码分析(蓝牙控制端和运动控制端)

3 篇文章 1 订阅
2 篇文章 0 订阅

提前说说

intel杯初选赛过了,接下来就是区域决赛。准备时间有两个月,时间还是比较紧张,必须在这两个月内把所有的知识都消化掉。接下来的打算是想面试几家公司,试试自己的水准,打好比赛,INtel和蓝桥杯,十二月份开始准备考研。

这里的全部源码是不会贴了,毕竟还要尊重一下平衡小车之家的知识产权,我这里主要把思路说清楚,具体的内容不会写太细
在这里插入图片描述

开始吧

在这里插入图片描述这个图是平衡小车之家配套的app,用来控制底盘移动以及一些参数的调整。收发方式通过蓝牙,分三种模式重力、摇杆、按键,点击屏幕上的按钮,通过蓝牙端发送字符就能控制底盘,图片中Debug下就是发送的字符。

思路

我的想法是先找到通信端,然后找到控制函数,然后完成整个代码的梳理。不过一上来我就懵了,光通信函数都不少呢!串口1、2、3,IIC、PS2、CAN,幸好有注释,要不然真得一个一个找了。

通过注释我们知道,移动端控制底盘主要是通过串口2进行的,因此我们先来看看串口2的中断函数

/**************************************************************************
函数功能:串口2接收中断
入口参数:无
返回  值:无
**************************************************************************/
int USART2_IRQHandler(void)     //手机蓝牙发送指令
{	
	if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收到数据
	{	      
		static u8 Flag_PID,i,j,Receive[50];    //这些都是静态变量
		static float Data;
		Usart_Receive=USART2->DR;
	
	  if(Usart_Receive==0x4B) Turn_Flag=1;  //进入转向控制界面,由它判断模式。1---按键模式
	  else	if(Usart_Receive==0x49||Usart_Receive==0x4A) 	 Turn_Flag=0;	//方向控制界面。0---重力和摇杆模式
		
		if(Run_Flag==0)//速度控制模式
		{			
				if(Turn_Flag==0)//速度控制模式
				{
						if(Usart_Receive>=0x41&&Usart_Receive<=0x48)  //速度方向 1-8分别从中上 右上 右中到左上
						{	
							Flag_Direction=Usart_Receive-0x40;     //从AB..GH换成数字12..78
						}
						else	if(Usart_Receive<=8)   
						{			
							Flag_Direction=Usart_Receive;
						}	
						else  Flag_Direction=0;
				}
				else	 if(Turn_Flag==1)//如果进入了转向控制界面
				 {
				 if(Usart_Receive==0x43) Flag_Left=0,Flag_Right=1;    //右转
				 else if(Usart_Receive==0x47) Flag_Left=1,Flag_Right=0;   //左转
				 else Flag_Left=0,Flag_Right=0;    //直走或后退
				 if(Usart_Receive==0x41||Usart_Receive==0x45)Flag_Direction=Usart_Receive-0x40;   //如果是前进或者后退,则将方向为直接设置成数字1、5
				 else  Flag_Direction=0;
				 }
	  }	
		//以下是与APP调试界面通讯
		if(Usart_Receive==0x7B) Flag_PID=1;   //APP参数指令起始位
		if(Usart_Receive==0x7D) Flag_PID=2;   //APP参数指令停止位

		 if(Flag_PID==1)  //采集数据
		 {
			Receive[i]=Usart_Receive;
			i++;
		 }
		 if(Flag_PID==2)  //分析数据
		 {
			     if(Receive[3]==0x50) 	 PID_Send=1;
					 else  if(Receive[3]==0x57) 	 Flash_Send=1;
					 else  if(Receive[1]!=0x23) 
					 {								
						for(j=i;j>=4;j--)
						{
						  Data+=(Receive[j-1]-48)*pow(10,i-j);
						}
						switch(Receive[1])
						 {
							 case 0x30:  RC_Velocity=Data;break;           //速度初始值,基准值
							 case 0x31:  RC_Position=Data;break;           //位置初始值,基准值
							 case 0x32:  Position_KP=Data;break;
							 case 0x33:  Position_KI=Data;break;
							 case 0x34:  Position_KD=Data;break;
							 case 0x35:  Velocity_KP=Data;break;
							 case 0x36:  Velocity_KI=Data;break;
							 case 0x37:  break; //预留
							 case 0x38:  break; //预留
						 }
					 }				 
					 Flag_PID=0;//相关标志位清零
					 i=0;
					 j=0;
					 Data=0;
					 memset(Receive, 0, sizeof(u8)*50);//数组清零
		 } 	 
   }
return 0;	
}

那么中断函数具体完成了什么事情?
1.接收从移动端传来的字符
2.首先是模式判断,分别对应app端的按键模式、重力模式、摇杆模式
3.如果传来的不是模式判断字符,则进行速度方向模式判断
4.如果不是速度模式判断字符,则进行参数调整模式判断
这样就完成了app端对底盘的移动控制!

那么接着往下看,主函数里并没有对底盘的移动控制,只有一些显示函数。那么如何控制的呢?我们自然而然可以想到中断控制
以下是部分主函数图:
在这里插入图片描述
最后通过外部中断初始化,我们找到是通过EXTI15_10_IRQHandler来进行控制的,那么接下来我们对这个函数做一下剖析

int EXTI15_10_IRQHandler(void) 
{    
	 if(INT==0)		
	{     
		  EXTI->PR=1<<15;                                                      //清除LINE5上的中断标志位  		
		  Flag_Target=!Flag_Target;
		  if(delay_flag==1)
			 {
				 if(++delay_50==10)	 delay_50=0,delay_flag=0;                     //给主函数提供50ms的精准延时
			 }
		  if(Flag_Target==1)                                                  //5ms读取一次陀螺仪和加速度计的值
			{
					if(Usart_Flag==0&&PS2_ON_Flag==0&&Usart_ON_Flag==1)  memcpy(rxbuf,Urxbuf,8*sizeof(u8));	//如果解锁了串口控制标志位,进入串口控制模式
					Read_DMP();                                                           //===更新姿态		
			  	Key();//扫描按键变化	
			return 0;	                                               
			}                                                                   //===10ms控制一次,为了保证M法测速的时间基准,首先读取编码器数据
			Encoder_A=Read_Encoder(2)/20;                                          //===读取编码器的值
			Position_A+=Encoder_A;                                              //===积分得到速度   
			Encoder_B=Read_Encoder(3)/20;                                          //===读取编码器的值
			Position_B+=Encoder_B;                                              //===积分得到速度   
			Encoder_C=Read_Encoder(4)/20;                                          //===读取编码器的值
			Position_C+=Encoder_C;                                              //===积分得到速度   
	  	Read_DMP();                                                         //===更新姿态	
  		Led_Flash(100);                                                     //===LED闪烁;常规模式 1s改变一次指示灯的状态	
			Voltage_All+=Get_battery_volt();                                    //多次采样累积
			if(++Voltage_Count==100) Voltage=Voltage_All/100,Voltage_All=0,Voltage_Count=0;//求平均值 获取电池电压	   
      if(PS2_KEY==4)PS2_ON_Flag=1,CAN_ON_Flag=0,Usart_ON_Flag=0;						
		  if(CAN_ON_Flag==1||Usart_ON_Flag==1||PS2_ON_Flag==1) CAN_N_Usart_Control();       //接到串口或者CAN遥控解锁指令之后,使能CAN和串口控制输入
			if(RC_Velocity>0&&RC_Velocity<25)  RC_Velocity=25;                //避免电机进入低速非线性区
		  if(Turn_Off(Voltage)==0)               //===如果电池电压不存在异常
		 { 			 
		  if(Run_Flag==0)//速度模式
			{		
				if(CAN_ON_Flag==0&&Usart_ON_Flag==0&&PS2_ON_Flag==0)  Get_RC(Run_Flag);   //===串口和CAN控制都未使能,则接收蓝牙遥控指
				Motor_A=Incremental_PI_A(Encoder_A,Target_A);                        			//===速度闭环控制计算电机A最终PWM
				Motor_B=Incremental_PI_B(Encoder_B,Target_B);                         		//===速度闭环控制计算电机B最终PWM
				Motor_C=Incremental_PI_C(Encoder_C,Target_C);                         		//===速度闭环控制计算电机C最终PWM
			}
			 else//位置模式
			{
					if(CAN_ON_Flag==0&&Usart_ON_Flag==0&&PS2_ON_Flag==0)//===串口和CAN控制都未使能,则接收蓝牙遥控指令
					 {	
						if(Turn_Flag==0) 	Flag_Direction=click_RC();     
						Get_RC(Run_Flag);
					 }
					Motor_A=Position_PID_A(Position_A,Target_A)>>8;//位置闭环控制,计算电机A速度内环的输入量
					Motor_B=Position_PID_B(Position_B,Target_B)>>8;//位置闭环控制,计算电机B速度内环的输入量
					Motor_C=Position_PID_C(Position_C,Target_C)>>8;//位置闭环控制,计算电机C速度内环的输入量
					
					if(rxbuf[0]!=2)  Count_Velocity();   //这是调节位置控制过程的速度大小
					else 	
					Xianfu_Velocity(RC_Velocity,RC_Velocity,RC_Velocity); 
					Show_Data_Mb=Motor_B;
					Motor_A=Incremental_PI_A(Encoder_A,-Motor_A);         //===速度闭环控制计算电机A最终PWM
					Motor_B=Incremental_PI_B(Encoder_B,-Motor_B);         //===速度闭环控制计算电机B最终PWM
					Motor_C=Incremental_PI_C(Encoder_C,-Motor_C);         //===速度闭环控制计算电机C最终PWM
			 }	 
			 Xianfu_Pwm(6900);                     											//===PWM限幅
			 Set_Pwm(Motor_A*84/72,Motor_B*84/72,Motor_C*84/72);     		//===赋值给PWM寄存器  
		 }
 }
	 return 0;	 
} 

接下来就是速度方向、控制
首先就是读编码器的值,读到速度。平均计算得到电压,然后还判断是否用到了其他通信(所以说这里can,和usart1、3实际上是没有用到的),速度是否已经处于低速,电压是否存在异常等。
如果电池电压没有异常,则进行速度控制等。首先就是摇杆模式下的方向控制

 switch(Flag_Direction)   //方向控制
				 {
				 case 1:  Move_X=0;           Move_Y+=step;  Flag_Move=1;               break;
				 case 2:  Move_X+=step;       Move_Y+=step;  Flag_Move=1;               break;
				 case 3:  Move_X+=step;       Move_Y=0;      Flag_Move=1;               break;
				 case 4:  Move_X+=step;       Move_Y-=step;  Flag_Move=1;               break;
				 case 5:  Move_X=0;           Move_Y-=step;  Flag_Move=1;               break;
				 case 6:  Move_X-=step;       Move_Y-=step;  Flag_Move=1;               break;
				 case 7:  Move_X-=step;       Move_Y=0;      Flag_Move=1;               break;
				 case 8:  Move_X-=step;       Move_Y+=step;  Flag_Move=1;               break; 
				 default: Flag_Move=0;        Move_X=Move_X/1.05f;Move_Y=Move_Y/1.05f;    break;	 
			 }

Flag_Direction对应相应的方向,通过计算能得出XYZ轴目标速度,然后通过一大堆专业的的运动学分析得到电机目标值。具体就是Kinematic_Analysis(Move_X,Move_Y,Move_Z);//得到控制目标值,进行运动学分析
有了编码器得到的速度值以及目标值,然后通过PI计算出每个电机的PWM变量Motor_A=Incremental_PI_A(Encoder_A,Target_A); ,最后再通过用PWM变量计算出每个电机最终的PWM值Set_Pwm(Motor_A84/72,Motor_B84/72,Motor_C*84/72);

具体的运动学分析这里就没有提及,因为我也没搞懂,而且挺复杂的。后面的工作还有很多,再板子来之前自己还有一两天的准备时间,补补32吧

  • 5
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32F103C8T6麦克纳姆轮(全向)智能小车可以使用PS2或蓝牙进行控制。这两种控制方式都能够实现对小车的方向、速度等参数进行控制。 PS2是一种常用的游戏手柄,它通过无线信号传输与小车通信。在使用PS2进行控制时,首先需要将PS2手柄与小车进行配对。然后通过读取PS2手柄的按键状态,获取用户的操作指令。根据不同的操作指令,可以控制小车的前进、后退、左转、右转等动作。同时,还可以根据手柄的摇杆状态来控制小车的速度与方向。 蓝牙控制则是通过蓝牙模块与小车进行通信。用户可以通过手机或其他蓝牙设备与小车进行连接,并发送指令控制小车的运动。在使用蓝牙控制时,需要注意与小车进行蓝牙连接并建立通信。之后,可以通过发送不同的指令来控制小车的运动方向和速度。蓝牙控制具有更大的灵活性,因为手机设备通常具有更丰富的界面和交互方式,用户可以更方便地进行控制。 总之,STM32F103C8T6麦克纳姆轮(全向)智能小车可以通过PS2或蓝牙进行控制。无论采用哪种方式,都可以实现对小车的方向、速度等参数进行控制,让小车按照用户的指令进行运动。 ### 回答2: STM32F103C8T6麦克纳姆轮智能小车是一款基于STM32F103系列单片机的全向运动控制平台。它采用了四个麦克纳姆轮,通过不同速度和方向的转动,实现了全向行驶和转向的功能。 这款智能小车支持多种控制方式,包括PS2手柄和蓝牙控制。对于PS2手柄控制,用户可以通过连接PS2手柄到主控制板上的接口,通过按键和摇杆的输入来控制小车的运动。例如,通过按键控制小车的启动和停止,通过摇杆控制小车的前进、后退、左转和右转等。 另外,该智能小车还支持蓝牙控制。用户只需通过蓝牙与主控制板进行无线通信,通过手机或者PC上的APP来控制小车的运动。可以通过APP上的按钮或者重力感应来控制小车的运动,实现远程控制的便利性和灵活性。 在控制程的实现中,首先需要将PS2手柄或者蓝牙模块与主控制板进行适配和通信的配置。然后,通过读取手柄输入或APP发送的控制信号,解析出对应的指令,根据指令控制四个麦克纳姆轮的转速和方向,从而实现小车运动控制。 总结起来,STM32F103C8T6麦克纳姆轮智能小车支持PS2和蓝牙控制。它提供了方便和灵活的控制方式,可以根据用户的需要,选择适合的方式来控制小车的运动。无论是PS2手柄还是蓝牙控制,都能给用户带来良好的控制体验和操作便利性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值