这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » boyiss的平衡车学习进程贴~(加入整参视频和加避障后的绕八字走不了8字的来看

共24条 2/3 1 2 3 跳转至
菜鸟
2015-10-29 22:19:07     打赏
11楼

无线双工


其实NRF24L01+并没有这个模式,也可以说是“伪双工”,可是效果和正在的双工也差不了多少,思路是学习匿名的,有些地方他没有细讲,我自己查的数据手册,

采用的策略是其实无线接收端每收到数据都向发射端返回确认信号(ACK),然后呢,它有个功能,可以然你把接收端想发的数据先放到发射端的模块里,这样发送端对接收端发数据,接收端就能收到ACK,而这个ACK里面是带有你想要发送的数据的。

而在发射端是通过查询法来检测是否收到信号的,这里可以再优化。

接收端流程看数据手册如图




这是初始化的程序

/*
 * 函数名:Nrf24l01_Init
 * 描述  :初始化Nrf24l01,设置通信模式和通道
 * 输入  :通信模式,通道
 * 输出  :无
 * 调用  :外部调用
 */
void Nrf24l01_Init(u8 model, u8 ch)
{
	SPI_CE_L();
	NRF_Write_Buf(NRF_WRITE_REG+RX_ADDR_P0,RX_ADDRESS,RX_ADR_WIDTH);//写RX节点地址 
	NRF_Write_Buf(NRF_WRITE_REG+TX_ADDR,TX_ADDRESS,TX_ADR_WIDTH); 	//写TX节点地址  
	NRF_Write_Reg(NRF_WRITE_REG+EN_AA,0x01); 						//使能通道0的自动应答 
	NRF_Write_Reg(NRF_WRITE_REG+EN_RXADDR,0x01);					//使能通道0的接收地址 
	NRF_Write_Reg(NRF_WRITE_REG+SETUP_RETR,0x1a);					//设置自动重发间隔时间:500us;最大自动重发次数:10次 
	NRF_Write_Reg(NRF_WRITE_REG+RF_CH,ch);							//设置RF通道为CHANAL
	NRF_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x0f); 					//设置TX发射参数,0db增益,2Mbps,低噪声增益开启
	//NRF_Write_Reg(NRF_WRITE_REG+RF_SETUP,0x07); 				    //设置TX发射参数,0db增益,1Mbps,低噪声增益开启
	/////////////////////////////////////////////////////////
	if(model==1)				//RX
	{
		NRF_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);		//选择通道0的有效数据宽度 
		NRF_Write_Reg(NRF_WRITE_REG + CONFIG, 0x0f);   		 		// IRQ收发完成中断开启,16位CRC,主接收
	}
	else if(model==2)			//TX
	{
		NRF_Write_Reg(NRF_WRITE_REG+RX_PW_P0,RX_PLOAD_WIDTH);		//选择通道0的有效数据宽度 
		NRF_Write_Reg(NRF_WRITE_REG + CONFIG, 0x0e);   			 	//IRQ收发完成中断开启,16位CRC,主发送
	}
	else if(model==3)			//RX2	伪双工(应答数据中有数据)
	{
		NRF_Write_Reg(FLUSH_TX,0xff);
		NRF_Write_Reg(FLUSH_RX,0xff);
		NRF_Write_Reg(NRF_WRITE_REG + CONFIG, 0x0f);   		 // IRQ收发完成中断开启,16位CRC,主接收
		
		
		/*		激活用于自动应答的一些指令所在的寄存器,是之可读
		NRF_Write_Reg(ACTIVATE,0x73);//(好像24L01+不需要激活,数据手册没有这个命令)
		*/
		

		NRF_Write_Reg(NRF_WRITE_REG+0x1c,0x01);
		NRF_Write_Reg(NRF_WRITE_REG+0x1d,0x07);
	}
	else								//TX2	伪双工
	{
		NRF_Write_Reg(NRF_WRITE_REG + CONFIG, 0x0e);   		 // IRQ收发完成中断开启,16位CRC,主发送
		NRF_Write_Reg(FLUSH_TX,0xff);
		NRF_Write_Reg(FLUSH_RX,0xff);
		
		/*		激活用于自动应答的一些指令所在的寄存器,是之可读
		NRF_Write_Reg(ACTIVATE,0x73);//(好像24L01+不需要激活,数据手册没有这个命令)
		//		Spi_RW(0x50);
		//		Spi_RW(0x73);
		*/
		NRF_Write_Reg(NRF_WRITE_REG+0x1c,0x01);
		NRF_Write_Reg(NRF_WRITE_REG+0x1d,0x07);
	}
	SPI_CE_H();
}

 

/*
 * 函数名:Nrf24l01_Check
 * 描述  :读写Nrf24l01,检测是否连接成功
 * 输入  :无
 * 输出  :无
 * 调用  :外部调用
 */
u8 Nrf24l01_Check(void)
{ 
	u8 buf1[5]; 
	u8 i; 
	/*写入5个字节的地址. */ 
	NRF_Write_Buf(NRF_WRITE_REG+TX_ADDR,TX_ADDRESS,5); 
	/*读出写入的地址 */ 
	NRF_Read_Buf(TX_ADDR,buf1,5); 
	/*比较*/ 
	for(i=0;i<5;i++) 
	{ 
		if(buf1[i]!=TX_ADDRESS[i]) 
			break; 
	} 
	if(i==5)
		return SUCCESS ; //MCU与NRF成功连接 
	else
		return ERROR ; //MCU与NRF不正常连接 
}




接收端的发射函数和发射端的接收函数,让我设个回复可见

——回复可见内容——


菜鸟
2015-10-31 06:35:42     打赏
12楼

调参准备


其实觉得自己无线部分还没讲清楚,可是为了完成不烂尾,还是让我先往后讲吧~这次讲的是调节PID前的一些准备。


首先大方向要正确,这样才能更快的调节好参数,

首先电池电量要足,防止因电量问题调半天。

然后就是要确认编码器,MPU6050读的值对不对,更新数据的时间,数据不对,怎么也不行。

然后看定时器配置进入中断时间有没有问题,是不是自己想要的调节时间,这也很重要,

当初我曾用库函数配置定时器1,没配置对,在中断内设置某一脚取反,示波器看好像没问题,可是放程序进去却怎么也和实际是对不上的。所以当一直调不平衡,也要看看控制的频率对不对。控制频率太低,自然不行(例如只有平衡环,你手抓小车向一边猛倾,如果肉眼可见过一段时间才有反应,那当然不对)

然后就是PID中各环节正负判断

屏蔽速度,转向,平衡环只有P时,略调大,如果手压时加速倒下那是方向反了

而平衡环的d如果拿小车旋转车轮是反向转也是方向错了。

而速度环根据encoder=(Encoder_left+Encoder_right);可知,若一轮用手向一边转,另一轮应该向同方向转,这是我们要的“正反馈”,而P和I是同符号的。若不好分辨转向,可以考虑粘个纸条或是其他。

然后是转向环,因为开了超声波,只用了Z轴角速度,可以屏蔽其他,然后手把车抓在空中,然后向一边旋转,看看车轮转向,或者更直接,车轮着地,旋转,看看是加速旋转了,还是旋转的比较困难,由此确认方向。

当调完参数后遥控可能不稳定,这是还要看看自己积分限幅是不是给的太大了,太大可能也不行。

确定了极性,调起PID就更方便了。



菜鸟
2015-10-31 07:04:53     打赏
13楼

调PID

调这个小车的PID花费了不少时间,看了不少资料。有个口诀

=========================================

参数整定找最佳, 从小到大顺序查。
先是比例后积分, 最后再把微分加。
曲线振荡很频繁, 比例度盘要放大。
曲线漂浮绕大弯, 比例度盘往小扳。
曲线偏离回复慢, 积分时间往下降。
曲线波动周期长, 积分时间再加长。
曲线振荡频率快, 先把微分降下来。
动差大来波动慢, 微分时间应加长。
理想曲线两个波, 前高后低四比一。
一看二调多分析, 调节质量不会低。

=========================================

还有很多其他资料,可是讲的内容大致都是这个口诀的。

我看了不少网上的帖,论坛的帖子,对于调车大概这样比较简单

平衡P往上加到震荡,然后开始加D,至抖动然后再减小一点。(这是手把车向下压应该也大概能平衡)

然后系数乘约0.6,0.7,减弱平衡环

这时加入速度环,P和I有个大致关系,I在P的两百分之一左右,具体还要看自己的车

然后看什么时候推着能较快回复,什么时候抖动,这时就差不多是最大了。

然后是速度环,稍微加点,多前后走,看什么时候大概是直线,也就可以了。这样参数就大概好了

然后再测试下抗干扰,逐渐优化参数。


我觉得767472021的帖子写的不错,而且视频很详细,可以看看

http://forum.eepw.com.cn/thread/275162/1#6


下面是我调节的视频

首先是只有平衡环,记得调好要乘0.6噢~


视频地址:http://player.youku.com/player.php/sid/XMTM3NTg4NTEzNg==/v.swf


然后是加入速度环,以能较快恢复且抖动不强为目标,具体需要多次下地,多次测试,转向也是如此


视频地址:http://player.youku.com/player.php/sid/XMTM3NTg5NDk1Ng==/v.swf



菜鸟
2015-10-31 07:07:54     打赏
14楼


视频地址:http://player.youku.com/player.php/sid/XMTM3Mzg3OTMyNA==/v.swf

发个我现在遥控的开机画面给大家看看


菜鸟
2015-11-02 15:30:27     打赏
15楼

体感遥控

根据之前的移植DMP,已经可以获取姿态角Roll,Pitch,Yaw了。

然后我仿照着手机游戏那般,设置Pitch角为前后油门,Roll角为控制左右。做出了一个遥控,同时在OLED上显示

相关信息,可是暂时算法写得不太好,还没做到某个角度就是某个值,倾斜越大油门越大,于是暂时采用变速控制,

之后再进行改进。为了防止一开机就控制小车乱走,还加了个双击解锁开遥控功能,同时防止了上位机和遥控同时控制引发干扰。解锁后通过单击选择油门档位。共有5档变速。

具体程序如下


小车端程序改为(其中1500为静止,这是配合匿名2.X版本上位机改的)

	Throt_a=Throt-1500;//以1500为静止
	
	//遥控前进后退部分

	#if UltrasonicWave_ON==1
	
	//实现变速
	if((Throt_a>99) ||(Throt_a<-99))//Throt=1500时
	Throt_Speed=1;
	if((Throt_a>199)||(Throt_a<-199))
	Throt_Speed=2;
	if((Throt_a>299)||(Throt_a<-299))
	Throt_Speed=3;
	if((Throt_a>399)||(Throt_a<-399))
	Throt_Speed=4;
	if((Throt_a>499)||(Throt_a<-499))
	Throt_Speed=5;	
	switch(Throt_Speed)
	{
		case 1:Movement=45;break;
		case 2:Movement=60;break;
		case 3:Movement=80;break;
		case 4:Movement=100;break;
		case 5:Movement=120;break;
		default: Movement=0;//120
	}
	if(Throt_a<0)
		Movement=-Movement;//大于1500为后退

 



遥控端有如下程序(开启后单击不同次数是1~5档,6是走8字,然后7回到1档)效果如上视频:http://forum.eepw.com.cn/thread/276651/2#14

 Key_stat=Double_Key_Scan(80);//80*5=0.4s的双击检测间隔时间
		if(Key_stat==2)//双击
		{
			if(Turn_ON==0)
			{
				Turn_ON=1;
				Throt_Speed=1;
			}

			else
			{
				Turn_ON=0;
				Throt_Speed=0;
				Key_count=0;
			}
			Double_Hoop=0;
		
		}
		if(Key_stat==1)//单击
		{
			if(Turn_ON==1)
			{
				Throt_Speed++;
				Key_count++;
				
				if(Key_count==5)
				{
					Double_Hoop=1;
					Throt_Speed=0;
				}
				if(Key_count==6)
				{
					Double_Hoop=0;
					Throt_Speed=1;
					Key_count=0;
				}
						
			}
				
		}





菜鸟
2015-11-02 17:02:24     打赏
16楼

超声波避障


我选用的是4脚的,少了一个OUT脚,功能差不多

接线方式:VCC、trig(控制端)、  echo(接收端)、 GND


基本工作原理:

(1)采用IO口TRIG触发测距,给至少10us的高电平信号;

(2)模块自动发送8个40khz的方波,自动检测是否有信号返回;

(3)有信号返回,通过IO口ECHO输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。测试距离=(高电平时间*声速(340M/S))/2;

由图可知

一个控制口发一个10US以上的高电平,就可以在接收口等待高电平输出.一有输出就可以开定时器计时

,当此口变为低电平时就可以读定时器的值,得到脉宽,此时就为此次测距时间


可算出距离.如此不断的周期测,即可以得到模块与平面的距离




对时间的捕获我用的是原子的定时器捕获的例程,配置好捕获模式,然后这是中断的处理

void TIM2_IRQHandler(void)
{ 
	if((TIM2CH4_CAPTURE_STA&0X80)==0)//还未成功捕获	
	{	  
		if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
		{    
			if(TIM2CH4_CAPTURE_STA&0X40)//已经捕获到高电平了
			{
				if((TIM2CH4_CAPTURE_STA&0X3F)==0X3F)//高电平太长了
				{
					TIM2CH4_CAPTURE_STA|=0X80;//标记成功捕获了一次
					TIM2CH4_CAPTURE_VAL=0XFFFF;
				}
				else
					TIM2CH4_CAPTURE_STA++;
			}	 
		}
		if (TIM_GetITStatus(TIM2, TIM_IT_CC4) != RESET)//捕获1发生捕获事件
		{	
			if(TIM2CH4_CAPTURE_STA&0X40)		//捕获到一个下降沿 		
			{	  			
				TIM2CH4_CAPTURE_STA|=0X80;		//标记成功捕获到一次上升沿
				TIM2CH4_CAPTURE_VAL=TIM_GetCapture4(TIM2);
				TIM_OC4PolarityConfig(TIM2,TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
			}
			else  								//还未开始,第一次捕获上升沿
			{
				TIM2CH4_CAPTURE_STA=0;			//清空
				TIM2CH4_CAPTURE_VAL=0;
				TIM_SetCounter(TIM2,0);
				TIM2CH4_CAPTURE_STA|=0X40;		//标记捕获到了上升沿
				TIM_OC4PolarityConfig(TIM2,TIM_ICPolarity_Falling);		//CC1P=1 设置为下降沿捕获
			}		    
		}			     	    					   
	}
    TIM_ClearITPendingBit(TIM2, TIM_IT_CC4|TIM_IT_Update); //清除中断标志位
}

 


然后以下是我对获得的距离的处理

因为我是变速控制,所以使用了变量aaaaa记录加的movement值,然后我相比其他人加了进出那个距离时对速度进行衰减,具体看我下面的程序,都注释好了



		if(Distance<300) { if(xx==0)//首次进入30厘米内 { encoder_Integral*=0.2;//进入时减速到原20% xx=1; yy=1; } Movement-=150; //当距离小于40cm时将后退 if(Throt_a>0)
				Movement=-aaaaaa;						
		}
		else
		{
			xx=0;//清除距离标志
			if(yy==1)
			{
				encoder_Integral*=0.3;//出去时减速到原30%,防止后退的太猛
				yy=0;
			}
		}







菜鸟
2015-11-02 17:36:08     打赏
17楼

绕8字

一开始做绕八字的时候我发现并没有像其他人做的那么简单,先前左,然后前右,一个8字就做出来了

我琢磨了一阵,发现了问题。

其实问题很简单,我猜应该是我做实验的顺序和别人不同,我是在做了避障后才做的走8字,一个个功能叠起来的。

然后为什么避障影响走8字呢,是因为一边的电机上的编码器输出被超声波占用做定时器捕获,导致没使用到一边的编码器,只是简单的左边等于右边,平时走直线没什么问题,可是当走曲线,转弯比较明显时问题就出现了。


因为转弯时两轮出现了速度不一,可程序里又是两轮一样,导致速度环其起了作用,具体表现是可能会出现有点像小车跳舞的效果(华尔兹?),一轮子转得挺快,另一个向前转一下,然后又向后一下,整部车绕着一个轮子转圈。可是左转和右转又有不同效果,

这是因为测的是其中一个轮子,那么左转右转可能分别是另一轮测得值比实际要大和比实际要小。


那么程序就不是简单的一轮加转向PID,另一轮减同样的值了。

简单点的处理就是通过数次测试,分别为两个半圆分别给不同的油门和转向值(我的程序里是转向值一样,油门两个不同)

注意我的程序里也有个积分值*0.3对其衰减,是因为那一边积分加得挺大,对转另一半圆就影响大了,所以减小了一下。同时我是为了和上位机对应,Rc_Get.AUX1 ==100是开启走8字功能

然后fun1>7000的7000是7秒,静止时Rc_Get.PITCH和Rc_Get.ROLL为1500,其他分别对应不同的参数。

觉得我做的有点复杂,希望有人能交流指正。

if(Rc_Get.AUX1 ==100)
		{
			if(fun1>7000)
			{
				fun1=0;
				fun1_count++;
				if(fun1_count==2)//7000-3000的等待时间
					fun1=3000;
				if(fun1_count==3)
					fun1=3000;
				if(fun1_count==4)
					fun1=7000;
			}
			fun1++;
			if(fun1_count==0)
			{
				Rc_Get.PITCH=1500;
				Rc_Get.ROLL=1500;
							
			}
			else if(fun1_count==1)
			{
				Rc_Get.PITCH=1510;
				Rc_Get.ROLL=2000;
	
			}
			else if(fun1_count==2)
			{
				Rc_Get.PITCH=1520;
				Rc_Get.ROLL=1500;
			}
			else if(fun1_count==3)
			{

				Rc_Get.PITCH=1530;
				Rc_Get.ROLL=1000;
			}
			else if(fun1_count==4)
			{
				Rc_Get.PITCH=1500;
				Rc_Get.ROLL=1500;
				encoder_Integral*=0.3;
				fun1_count=0;
			}
			
		}
		else
		{
			fun1=0;fun1_count=0;
		}








菜鸟
2015-11-03 09:59:32     打赏
18楼
关于CCD

当做到CCD时,我发现了个问题,就是CCD和无线引脚有冲突,可是我想做的是一部车集所有功能于一体,如果走8字,那就是在遥控按一按就是那个功能,而不是换一个功能重下一次程序。

CCD就只能放弃无线了,我大概按照767472021的教程配置了一下,发现是没问题的。可是要添加这个功能就要重新画PCB板,有点麻烦,以后完善时再改。

如果想做CCD,就按照他的教程来吧

http://forum.eepw.com.cn/thread/275162/3#26




菜鸟
2015-11-03 20:54:27     打赏
19楼

暂时的结束

调这部小车花费了不少时间可也学到了不少东西。有空时玩一玩,不知不觉比赛结束的时间就到了

可是对我来说这只是暂时的结束,因为其中还有很多细节可以优化,而且有些功能还没添加

之后可以考虑加上CCD,然后再来个几部车一起跳舞之类的,

还有就是再对之前写的不详细的地方完善一下。

这次项目学到的东西不少,把stm32的一些基本的外设都学习了一遍,因为是自己从头开始,所以其中遇到一次次的失败,可又一次次解决,成就感十足。了解了不少M3的内部工作原理,对PID有了一个了解。

以后还有类似的活动,我还会再参加的!


菜鸟
2015-11-26 23:16:14     打赏
20楼
今天我也是在passoni店里的大车底板,已经发货了,估计周一能收到。很期待。你做的不错。回头请教你一些。我在EEPW群里。方便的话,留言。

共24条 2/3 1 2 3 跳转至

回复

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