这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » xxJian智能车DIY进程贴

共34条 4/4 |‹ 1 2 3 4 跳转至
菜鸟
2015-08-20 16:44:42     打赏
31楼

 [开发进程]实验十、另类走8

背景:

  上文通过对左右编码器采样出来的数据进行积分(distance = distance + (float)Encoder_Left*0.005;)得到了小车前进的距离。

其中distance是自己定义的小车前进距离。

那么,同样的道理,可以通过对左右编码器之差进行积分turning = turning + (float)(Encoder_Left-Encoder_Right)*0.005;,得到小车转弯的大概的角度。

  笔者试验室的地板是方块的,60cm*60cm,那么为什么不可以走方形的八字呢。这种八字就是数码管的8

 

原理:

本试验的原理很简单,要使车子走数码管8字,只需要使小车按照这个路线图走就可以了。


只是调试过程比较痛苦……



实验代码

这个实验基于<Mini Balance V2.5 标准版源码卡尔曼版本> 

修改了两个源文件。分别是show.c和MiniBalance.c

以下是这两个修改后的文件的代码:


//show.c
#include "show.h"
#include "MiniBalance.h"
/**************************************************************************
作者:平衡小车之家 
淘宝店铺:http://shop114407458.taobao.com/
**************************************************************************/
unsigned char i;          //计数变量
unsigned char Send_Count; //串口需要发送的数据个数
/**************************************************************************
函数功能:OLED显示
入口参数:无
返回  值:无
作    者:平衡小车之家
**************************************************************************/
static u32 Count;



void oled_show(void)
{
		int disSHow;
		int turSHow;
			
	
	  Count=0;
		OLED_Display_On();  //显示屏打开


		//turSHow = turning*1.087613;
		turSHow = turning;
		disSHow = -distance*2.176542;
			
						
			if(ThreeSecFlag==1)
			{
				
				if(disSHow<300)
				{	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;}
				else if(turSHow<85)
				{	Flag_Qian=0,Flag_Hou=0,Flag_Left=1,Flag_Right=0;}
				else if(disSHow<900)
				{	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;}
				else if(turSHow<175)
				{	Flag_Qian=0,Flag_Hou=0,Flag_Left=1,Flag_Right=0;}	
				else if(disSHow<1300)
				{	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;}
				else if(turSHow<274)
				{	Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=1;}	
				else if(disSHow<2000)
				{	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;}
				else if(turSHow<373)
				{	Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=1;}	
				else if(disSHow<2700)
				{	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;}
				else if(turSHow<472)
				{	Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=1;}	
				else if(disSHow<3300)
				{	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;}
				else if(turSHow<540)
				{	Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=1;}	
				else if(disSHow<3900)
				{	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;}
				else if(turSHow<625)
				{	Flag_Qian=0,Flag_Hou=0,Flag_Left=1,Flag_Right=0;}	
				else if(disSHow<4400)
				{	Flag_Qian=1,Flag_Hou=0,Flag_Left=0,Flag_Right=0;}
				else if(turSHow<715)
				{	Flag_Qian=0,Flag_Hou=0,Flag_Left=1,Flag_Right=0;}	
				else						
					Flag_Qian=0,Flag_Hou=0,Flag_Left=0,Flag_Right=0;
				
				
			}

		if(disSHow<0)
		{
			OLED_ShowString(0,0,"Dist -");
			OLED_ShowNumber(45,0, (-disSHow),5,12);
		}
		else
		{
			OLED_ShowString(0,0,"Dist  ");
			OLED_ShowNumber(45,0, disSHow,5,12);
		}		
		
		if(turSHow<0)
		{
			OLED_ShowString(0,10,"Turn -");
			OLED_ShowNumber(45,10, (-turSHow),5,12);
		}
		else
		{
			OLED_ShowString(0,10,"Turn  ");
			OLED_ShowNumber(45,10, turSHow,5,12);
		}
		
		
		//=============显示温度=======================//	
		/*
		                      OLED_ShowString(00,10,"Wendu");
		                      OLED_ShowNumber(45,10,Temperature/10,2,12);
		                      OLED_ShowNumber(68,10,Temperature%10,1,12);
		                      OLED_ShowString(58,10,".");
		                      OLED_ShowString(80,10,"`C");
													*/
		//=============显示编码器1=======================//	
		                      OLED_ShowString(00,20,"Enco1");
		if( Encoder_Left<0)		OLED_ShowString(45,20,"-"),
		                      OLED_ShowNumber(65,20,-Encoder_Left,5,12);
		else                 	OLED_ShowString(45,20,"+"),
		                      OLED_ShowNumber(65,20, Encoder_Left,5,12);
  	//=============显示编码器2=======================//		
		                      OLED_ShowString(00,30,"Enco2");
		if(Encoder_Right<0)		OLED_ShowString(45,30,"-"),
		                      OLED_ShowNumber(65,30,-Encoder_Right,5,12);
		else               		OLED_ShowString(45,30,"+"),
		                      OLED_ShowNumber(65,30,Encoder_Right,5,12);	
		//=============显示电压=======================//
		                      OLED_ShowString(00,40,"Volta");
		                      OLED_ShowString(58,40,".");
		                      OLED_ShowString(80,40,"V");
		                      OLED_ShowNumber(45,40,Voltage/100,2,12);
		                      OLED_ShowNumber(68,40,Voltage%100,2,12);
		 if(Voltage%100<10) 	OLED_ShowNumber(62,40,0,2,12);
		//=============显示角度=======================//
		                      OLED_ShowString(0,50,"Angle");
		if(Angle_Balance<0)		OLED_ShowNumber(45,50,Angle_Balance+360,3,12);
		else					        OLED_ShowNumber(45,50,Angle_Balance,3,12);
		//=============刷新=======================//
		OLED_Refresh_Gram();	
	}
/**************************************************************************
函数功能:虚拟示波器往上位机发送数据 关闭显示屏
入口参数:无
返回  值:无
作    者:平衡小车之家
**************************************************************************/
void DataScope(void)
{   
	  if(++Count==1)
		{	
		OLED_Clear();  
		OLED_Display_Off();		
		}	
		DataScope_Get_Channel_Data( Angle_Balance, 1 );
		DataScope_Get_Channel_Data( Encoder_Right, 2 );
		DataScope_Get_Channel_Data( Encoder_Left, 3 ); 
		DataScope_Get_Channel_Data( Voltage , 4 );   
		DataScope_Get_Channel_Data(0, 5 ); //用您要显示的数据替换0就行了
		DataScope_Get_Channel_Data(0 , 6 );//用您要显示的数据替换0就行了
		DataScope_Get_Channel_Data(0, 7 );
		DataScope_Get_Channel_Data( 0, 8 ); 
		DataScope_Get_Channel_Data(0, 9 );  
		DataScope_Get_Channel_Data( 0 , 10);
		Send_Count = DataScope_Data_Generate(10);
		for( i = 0 ; i < Send_Count; i++) 
		{
		while((USART1->SR&0X40)==0);  
		USART1->DR = DataScope_OutPut_Buffer[i]; 
		}
		delay_ms(50); //20HZ
}

 




//MiniBalance.c
#include "MiniBalance.h"
#include "math.h"
#include "led.h"
#include "mpu6050.h"
#define PI 3.14159265
/**************************************************************************
作者:平衡小车之家 
淘宝店铺:http://shop114407458.taobao.com/
**************************************************************************/

/**************************************************************************
函数功能:5MS定时中断函数 5MS控制周期
入口参数:无
返回  值:无
作    者:平衡小车之家
**************************************************************************/
int Balance_Pwm,Velocity_Pwm,Turn_Pwm;
int FiveMSCount=0;
int ThreeSecFlag=0;

		
void TIM1_UP_TIM16_IRQHandler(void)  
{    
	
	

	
	if(TIM1->SR&0X0001)//5ms定时中断
	{   
			
			FiveMSCount++;
			if(FiveMSCount==600)
				ThreeSecFlag=1;
		
			if(ThreeSecFlag==1)
			{
			distance = distance + (float)Encoder_Left*0.005;
			
			if((Encoder_Left-Encoder_Right)>0)
				turning = turning + (float)(Encoder_Left-Encoder_Right)*0.005;
			else
				turning = turning + (float)(Encoder_Right-Encoder_Left)*0.005;
			}
			
		  TIM1->SR&=~(1<<0);                                       //===清除定时器1中断标志位		 
			readEncoder();                                           //===读取编码器的值
  		Led_Flash(400);                                          //===LED闪烁;	
  		Get_battery_volt();                                      //===获取电池电压	          
			key(100);                                                //===扫描按键状态
		  Get_Angle(Way_Angle);                                    //===更新姿态	
 			Balance_Pwm =balance(Angle_Balance,Gyro_Balance);        //===平衡PID控制	
 			Velocity_Pwm=velocity(Encoder_Left,Encoder_Right);       //===速度环PID控制
 	    Turn_Pwm    =turn(Encoder_Left,Encoder_Right,Gyro_Turn); //===转向环PID控制     
 		  Moto1=Balance_Pwm+Velocity_Pwm-Turn_Pwm;                 //===计算左轮电机最终PWM
 	  	Moto2=Balance_Pwm+Velocity_Pwm+Turn_Pwm;                 //===计算右轮电机最终PWM
   		Xianfu_Pwm();                                            //===PWM限幅
      if(Turn_Off(Angle_Balance,Voltage)==0)                   //===如果不存在异常
 			Set_Pwm(Moto1,Moto2);                                    //===赋值给PWM寄存器    		
	}       
} 

/**************************************************************************
函数功能:直立PD控制
入口参数:角度、角速度
返回  值:直立控制PWM
作    者:平衡小车之家
**************************************************************************/
int balance(float Angle,float Gyro)
{  
   float Bias;
	 int balance;
	 Bias=Angle+0;              //===求出平衡的角度中值 和机械相关 +0意味着身重中心在0度附近 如果身重中心在5度附近 那就应该减去5
	 balance=35*Bias+Gyro*0.125;//===计算平衡控制的电机PWM  PD控制 
	 return balance;
}

/**************************************************************************
函数功能:速度PI控制
入口参数:左轮编码器、右轮编码器
返回  值:速度控制PWM
作    者:平衡小车之家
**************************************************************************/
int velocity(int encoder_left,int encoder_right)
{  
	  static int Velocity,Encoder_Least,Encoder,Movement;
	  static int Encoder_Integral;
	  //=============遥控前进后退部分=======================//
		if(1==Flag_Qian)	Movement=-700;	             //===如果前进标志位置1 位移为负
		else if(1==Flag_Hou)	  Movement=700;          //===如果后退标志位置1 位移为正
	  else  Movement=0;	
   //=============速度PI控制器======================//	
		Encoder_Least =Encoder_Left+Encoder_Right;     //===获取最新速度偏差
		Encoder *= 0.8;		                             //===一阶低通滤波器       
		Encoder += Encoder_Least*0.2;	                 //===一阶低通滤波器    
	  if(Turn_Off(Angle_Balance,Voltage)==0)         //为了防止积分影响用户体验,只有电机开启的时候才开始积分
		{	
  	Encoder_Integral +=Encoder;                     //===积分出位移 积分时间:5ms
		Encoder_Integral=Encoder_Integral-Movement;     //===接收遥控器数据,控制前进后退
		}
		if(Encoder_Integral>360000)  	Encoder_Integral=360000;          //===积分限幅
		if(Encoder_Integral<-360000)	Encoder_Integral=-360000;         //===积分限幅	
		Velocity=Encoder*4+Encoder_Integral/140;                        //===速度PI控制器	
		if(Turn_Off(Angle_Balance,Voltage)==1)   Encoder_Integral=0;    //===电机关闭后清除积分
	  return Velocity;
}

/**************************************************************************
函数功能:转向PD控制
入口参数:左轮编码器、右轮编码器、Z轴陀螺仪
返回  值:转向控制PWM
作    者:平衡小车之家
**************************************************************************/
int turn(int encoder_left,int encoder_right,float gyro)//转向控制
{
  	static int Turn_Target,Turn,Encoder_temp,Turn_Convert=3,Turn_Count;
	  //int Turn_Bias,Turn_Amplitude=1500/Way_Angle+800;     //===Way_Angle为滤波方法,当是1时,即由DMP获取姿态,Turn_Amplitude取大,卡尔曼和互补是,取小,因为这两种滤波算法效果稍差。
	  int Turn_Bias,Turn_Amplitude=700;
	static long Turn_Bias_Integral;
	  //=============遥控左右旋转部分=======================//
  	if(1==Flag_Left||1==Flag_Right)                      //这一部分主要是根据旋转前的速度调整速度的起始速度,增加小车的适应性
		{
			if(++Turn_Count==1)
			Encoder_temp=myabs(encoder_left+encoder_right);
			Turn_Convert=2000/Encoder_temp;
			if(Turn_Convert<3)Turn_Convert=3;
			if(Turn_Convert>10)Turn_Convert=10;
		}	
	  else
		{
			Turn_Convert=3;
			Turn_Count=0;
			Encoder_temp=0;
		}			
		if(1==Flag_Left)	           Turn_Target+=Turn_Convert; //左转
		else if(1==Flag_Right)	     Turn_Target-=Turn_Convert; //右转
		else Turn_Target=0;                                     //停止
    if(Turn_Target>Turn_Amplitude)  Turn_Target=Turn_Amplitude;    //===转向速度限幅
	  if(Turn_Target<-Turn_Amplitude) Turn_Target=-Turn_Amplitude;
  	//=============转向PD控制器=======================//
		 Turn_Bias=Encoder_Left-Encoder_Right;         //===计算转向速度偏差  
		if(Turn_Off(Angle_Balance,Voltage)==0)         //为了防止积分影响用户体验,只有电机开启的时候才开始积分
		{	
		Turn_Bias_Integral+=Turn_Bias;                //转向速度偏差积分得到转向偏差
		Turn_Bias_Integral-=Turn_Target;              //获取遥控器数据
		}
		if(Turn_Bias_Integral>1800)  	Turn_Bias_Integral=1800;          //===积分限幅
		if(Turn_Bias_Integral<-1800)	Turn_Bias_Integral=-1800;         //===积分限幅	
	  Turn=Turn_Bias_Integral*2+gyro/12;                              //===结合Z轴陀螺仪进行PD控制
	  return Turn;
}

/**************************************************************************
函数功能:赋值给PWM寄存器
入口参数:左轮PWM、右轮PWM
返回  值:无
作    者:平衡小车之家
**************************************************************************/
void Set_Pwm(int moto1,int moto2)
{
			if(moto1<0)			AIN2=1,			AIN1=0;
			else 	          AIN2=0,			AIN1=1;
			PWMA=myabs(moto1);
		  if(moto2<0)	BIN1=0,			BIN2=1;
			else        BIN1=1,			BIN2=0;
			PWMB=myabs(moto2);	
}
/**************************************************************************
函数功能:读取编码器的数据并进行数据类型转换
入口参数:无
返回  值:无
作    者:平衡小车之家
**************************************************************************/
void readEncoder(void)
{
	  u16 Encoder_L,Encoder_R;       //===左右编码器的脉冲计数
		Encoder_R = TIM4 -> CNT;       //===获取正交解码1数据	
		TIM4 -> CNT=0;                 //===计数器清零  
	  Encoder_L= TIM2 -> CNT;        //===获取正交解码2数据	
	  TIM2 -> CNT=0;	               //===计数器清零
		if(Encoder_L>32768)  Encoder_Left=Encoder_L-65000; else  Encoder_Left=Encoder_L;  //===数据类型转换
  	Encoder_Left=-Encoder_Left;
	  if(Encoder_R>32768)  Encoder_Right=Encoder_R-65000; else  Encoder_Right=Encoder_R;//===数据类型转换
}

/**************************************************************************
函数功能:限制PWM赋值 
入口参数:无
返回  值:无
作    者:平衡小车之家
**************************************************************************/
void Xianfu_Pwm(void)
{	
	  int Amplitude=3500;    //===PWM满幅是3600 限制在3500
    if(Moto1<-Amplitude) Moto1=-Amplitude;	
		if(Moto1>Amplitude)  Moto1=Amplitude;	
	  if(Moto2<-Amplitude) Moto2=-Amplitude;	
		if(Moto2>Amplitude)  Moto2=Amplitude;		
	
}

/**************************************************************************
函数功能:异常关闭电机
入口参数:倾角和电压
返回  值:1:异常  0:正常
作    者:平衡小车之家
**************************************************************************/
u8 Turn_Off(float angle, int voltage)
{
	    u8 temp;
			if(angle<-40||angle>40||1==Flag_Stop||Voltage<1110)//===电压低于11.1V 关闭电机
			{	                                                 //===倾角大于40度关闭电机
      temp=1;                                            //===Flag_Stop置1关闭电机
			AIN1=0;                                            //===可自行增加主板温度过高时关闭电机
			AIN2=0;
			BIN1=0;
			BIN2=0;
      }
			else
      temp=0;
      return temp;			
}
	
/**************************************************************************
函数功能:获取角度
入口参数:获取角度的算法 1:无  2:卡尔曼 3:互补滤波
返回  值:无
作    者:平衡小车之家
**************************************************************************/
void Get_Angle(u8 way)
{ 
	    float Accel_Y,Accel_X,Accel_Z,Gyro_Y,Gyro_Z;
	    if(way==1)                                      //DMP没有涉及到严格的时序问题,在主函数读取
			{	
			}			
      else
      {
			Gyro_Y=(I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_YOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_YOUT_L);    //读取Y轴陀螺仪
			Gyro_Z=(I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_ZOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_GYRO_ZOUT_L);    //读取Z轴陀螺仪
		  Accel_X=(I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_XOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_XOUT_L); //读取X轴加速度记
	  	Accel_Z=(I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_ZOUT_H)<<8)+I2C_ReadOneByte(devAddr,MPU6050_RA_ACCEL_ZOUT_L); //读取Z轴加速度记
		  if(Gyro_Y>32768)  Gyro_Y-=65536;     //数据类型转换
			if(Gyro_Z>32768)  Gyro_Z-=65536;     //数据类型转换
	  	if(Accel_X>32768) Accel_X-=65536;    //数据类型转换
		  if(Accel_Z>32768) Accel_Z-=65536;    //数据类型转换
			Gyro_Balance=-Gyro_Y;                                  //更新平衡角速度
	   	Accel_Y=atan2(Accel_X,Accel_Z)*180/PI;                 //计算与地面的夹角	
		  Gyro_Y=Gyro_Y/16.4;                                    //陀螺仪量程转换	
      if(Way_Angle==2)		  	Kalman_Filter(Accel_Y,-Gyro_Y);//卡尔曼滤波	
			else if(Way_Angle==3)   Yijielvbo(Accel_Y,-Gyro_Y);    //互补滤波
	    Angle_Balance=angle;                                   //更新平衡倾角
			Gyro_Turn=Gyro_Z;                                      //更新转向角速度
	  	}
}

 

而后在MiniBalance.h中放入extern int ThreeSecFlag;即可。



实验视频在审核,估计得在下一楼再 放出。。



菜鸟
2015-08-20 16:51:59     打赏
32楼

本贴试验十视频:

首先说明,暂时只能实现小车走1个8字。并不持续走。。



视频地址:http://player.youku.com/player.php/sid/XMTMxNDY3NTk4OA==/v.swf
ps第31楼中的代码,特别是参数,估计若在另外的平台上,要改变相应的数字。。欢迎大家讨论!

助工
2015-08-22 14:02:55     打赏
33楼
感谢你这两天以来的知道,来帮你顶贴,嘿嘿

菜鸟
2015-09-07 16:59:42     打赏
34楼

我跟同事将平衡小车改为寻迹小车part1


我跟同事合作。他软件编程能力不足,有意将我的车子改装成寻迹小车;我不想改硬件。于是他负责将小车进行硬件改造,我做软件代码修改。。。


小车需要5个IO口进行红外模块输入,另外一个IO口作为车头撞车的检测(硬件中就只是一个按键输入电路)。









好在我的板子不配24L01无线模块,模块的接口IO我全部用来配置为输入。

模仿开源程序,

我加入了头文件,命名为trailing.h,

#ifndef __TRAILING_H
#define __TRAILING_H	 

#include "main.h"

#define trailingStrike PAin(6)
#define trailingFrontR PBin(4)
#define trailingFrontM PAin(5)
#define trailingFrontL PAin(7)
#define trailingLeft PAin(15)
#define trailingRight PBin(3)


void trailing_Init(void);

#endif

 

在main.h内加入#include "trailing.h"后,新建一个源文件trailing.c

//trailing.c





#include "main.h"

/*
#define trailingStrike PAin(6)
#define trailingFrontR PBin(4)		//front_right
#define trailingFrontM PAin(5)		//front_middle
#define trailingFrontL PAin(7)		//front_left
#define trailingLeft PAin(15)
#define trailingRight PBin(3)
*/

void trailing_Init(void)
{
	RCC->APB2ENR|=1<<3;    //使能PORTB时钟	   	 
	GPIOB->CRL&=0XFFF00FFF; 
	GPIOB->CRL|=0X00088000;//PB3 PB4 
	//GPIOB->ODR|=0<<3;
	GPIOB->ODR|=0<<4;
	
	RCC->APB2ENR|=1<<2;    //使能PORTA口时钟 
	GPIOA->CRL&=0X000FFFFF; 
	GPIOA->CRL|=0X88800000;//PA5 PA6 PA7 PA12 PA15
	//GPIOA->ODR|=0<<5;
	//GPIOA->ODR|=0<<6;
	//GPIOA->ODR|=0<<7;
	
	GPIOA->CRH&=0X0FF0FFFF; 
	GPIOA->CRH|=0X80080000;
	//GPIOA->ODR|=0<<12;
	GPIOA->ODR|=0<<15;
} 

 

以上就是简单的硬件介绍,以及GPIO的初始化。。



共34条 4/4 |‹ 1 2 3 4 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]