21年F题送药小车(单车)

哈哈哈,这篇博客想写很久了,但一直拖着没写,毕竟我可是拖延癌晚期患者。OK,话不多说。

这个题是我们学校校赛的题目,当时刚接触stm32才半个月不到吧,刚刚跟完江科大的课程,只做了单纯的一个寻迹小车,哈哈哈。其实对于送药小车这个题目来讲,电控是很简单的,难的是数字识别,你怎么让数字识别的又快又好是这道题的难点(k210这个是我负责视觉模块的队友负责的,但我这里有以前学长的代码,应该也是可以用的,如果有需要可以私信我)。

那么我先说一说电控方向的思路,其实对于第一问来讲,电控真的相当简单,就是让识别到数字1或数字2的时候进入一个你写的函数中(最好别在主函数中写,不然到时候你想改东西的时候可能一不小心改错一个地方就会出事)。然后的代码就是循迹和识别到岔路口转90°了。循迹这个不用说,是最简单的一个东西,不会的朋友可以去看我下一篇博客。至于蓝牙,当时让我另一个队友去搞,其实是很简单的东西,但当时我们课程确实很紧,就没有搞出来。但我后面用了zigbee,就很快搞出来了。但这个题还是没赶得上用,最后我会附上zigbee端的代码(江科大串口收发稍作修改即可)。至于双车,其实有了蓝牙之后双车就很简单了,完全可以自己写出来的。

那么怎么识别到岔路呢?如果你是选择的视觉巡线(用openmv),那你可以就让你视觉模块的队友去星瞳官网上找代码,这些都是有的。直接openmv识别到岔路口转就行了(这种得要延时一下,因为视觉一定是会提前看到的)。如果是灰度循迹,那就直接全部都识别到的时候转就行了。那对于这道题来讲,我个人是推荐视觉的,主要我们就是用视觉做的,哈哈哈。为什么不推荐灰度,因为灰度会有一定概率被数字遮挡导致提前识别,这是一个比较难以避免的问题(选择哪种12路的可能会好一点?)。

然后就是转90°了。这个但是可把我们给害惨了。当时还不太掌握霍尔编码器的使用方法,只能直接给pwm,这就导致了电池的电量也会影响他的转弯,所以我个人是推荐使用编码器的,记录到一遍轮子的一定脉冲数就说明我转过了90,还是相对比较稳定的。当然还可以使用陀螺仪,但一定不要用mpu6050,不然你的程序可能直接就卡死,就会导致循迹也会抽风的情况,对于这道题还是谨慎使用。(这个代码后面没有继续优化了,所以是直接给pwm的)。

至于药品的识别,直接用一个反射式红外传感器调好阈值就可以了。这个代码江科大模数转换那直接拿过来用就可以了。

openmv和k210和stm32通信代码去csdn搜索“Openmv识别Apriltag码并与stm32进行串口通信”,直接拿来用就好了。

下面上代码

主函数:

#include "stm32f10x.h"               // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Serial.h"
#include "encoder.h"
#include "pid.h"
#include "AD.h"
#include "K210.h"
#include "CROSS1.h"
#include "CROSS2.h"
#include "CROSS3.h"
#include "CROSS4.h"
#include "PWM.h"
#include "LED.h"


uint16_t AD_value;

int main(void)
{
	
	OLED_Init();
	Motor_Init();
	Serial_Init();
	Encoder_Init();
	pid_init();
	K210_Init();
	AD_Init();
	LED_Init();
	PWM_Init();
	
	OLED_ShowString(1,1,"speed:");
	OLED_ShowString(2,1,"speed:");
	OLED_ShowString(3,1,"x:");
	
	OLED_ShowString(4,1,"type:");
	OLED_ShowString(1,10,"AD:");
	OLED_ShowString(2,10,"NUM:");
	OLED_ShowString(3,10,"TAY:");
	
	while(1)
	{
			OLED_ShowNum(1,7,num,2);
		OLED_ShowNum(3,3,x_translation ,5);
		OLED_ShowNum(4,6,tag_id,2);
		if(num == 1 | num == 2)
		{
			CROSS1();
			LED_GREEN_ON();
		}
		else if(num == 3 | num == 4)
		{
			CROSS2();
			LED_GREEN_ON();
		}
		else if(num == 5 | num == 6)
		{
			CROSS3();
			LED_GREEN_ON();
		}
		else if(num == 7 | num == 8)
		{
			CROSS4();
			LED_GREEN_ON();
		}
	}
}



第一问(tag_id就是openmv识别到不同路口类型传不同的参数):


#include "stm32f10x.h"                  // Device header

#include "Delay.h"
#include "motor.h"
#include "Serial.h"
#include "pid.h"
#include "AD.h"
#include "K210.h"
#include "LED.h"
#include "OLED.h"

uint16_t AD_value1 ;
uint16_t see ;//标识符
uint16_t way1;//停下来的次数,第二次停下就不能再转180
uint16_t dir1;//第几次转弯

#define A 2500//阈值
#define B 2000

#define S1 31//左轮速度
#define S2 31//右轮速度
#define V1 32//补转弯时的误差速度
#define V2  29 //转180速度
#define T1  435//小车转弯前直行延时
#define T2  915 //小车左转弯时延时
#define T5  885 //小车右转弯时延时
#define T6  935 //小车右转弯时延时//数字2
#define T7  915 //小车左转弯时延时//数字2
#define T3  900//小车转180延时
#define T4  375//识别到药房继续往前走

void CROSS1(void)
{
	K210_Init();
	Motor_Init();
	Serial_Init();
	pid_init();
	AD_Init();
	LED_Init();
	
	while(see != 3)
	{
		OLED_ShowNum(1,13,AD_GetValue(),4); 
		OLED_ShowNum(2,14,num,2);
		OLED_ShowNum(3,14,tay,2);
		OLED_ShowNum(1,7,left_speed,2);
        OLED_ShowNum(2,7,right_speed,2);
		OLED_ShowNum(3,3,x_translation ,5);
		OLED_ShowNum(4,6,tag_id,2);
		OLED_ShowNum(4,10,dir1,2);
		if(num == 1)//识别到数字1,要左转
		{
			STOP();
			if(see == 0)
				{
					
					STOP();
					if(AD_GetValue() > A)//装上药品
					{
						see = 1;
					}
				}
			
		
			else if(see == 1)
				{
					pid_contorl();
					if(dir1 == 0)
					{
						if(tag_id == 1)//识别到路口了,此时是数字1,要左转
						{
							ON(V1,V1);
							Delay_ms(T1);
							ON(0,S2);
							Delay_ms(T2);
						}
						
						if(tag_id == 3)//送到药房了,要开始卸药
						{
							ON(S1,S2);
							Delay_ms(T4);
							uint16_t i = 1;
							LED_RED_ON();
							Delay_ms(50);
							STOP();
							Delay_ms(1000);
							way1 += 1;//第一次停下
							while(i != 0)
								{
						if(AD_GetValue() < B)//卸下药品了
						{
							see = 2;
							LED_RED_OFF();
							i = 0; 
						}
						if( i == 0)
						{
						break;
						}
							}
						}
					}
					else if(dir1 == 1)
					{
						if(tag_id == 1 )//第二次识别到路口了,此时是返回,要右转
						{
						ON(V1,V1);
						Delay_ms(T1);
						ON(S1,0);
						Delay_ms(T5);
							STOP();
							Delay_ms(200);
						}
						
						else if(tag_id == 3)//回到药房了
						{
							ON(S1,S2);
							Delay_ms(T4);
						LED_GREEN_ON();
						STOP();
						Delay_ms(1500);
							see = 3;
						}
					}
			
				}
				else if(see == 2)//向后转180
				{
						dir1 += 1;
					Delay_ms(500);
					Turnover(V2,V2);
					Delay_ms(T3);
					STOP();
					Delay_ms(600);
					tag_id = 0;
					see = 1;
					
				}
				else if(see == 3)//回到出发点了
				{
					ON(S1,S2);
					Delay_ms(T4);
					LED_GREEN_ON();
					STOP();
				}
			}
		
			
			if(num == 2)//要右转
			{
			STOP();
			if(see == 0)
				{
					STOP();
					if(AD_GetValue() > 	A)//装上药品
					{
						see = 1;
					}
				}
			
		
			else if(see == 1)
				{
					pid_contorl();
					if(dir1 == 0)
					{
						if(tag_id == 1 )//识别到路口了,此时是数字2,要右转
						{
							ON(V1,V1);
							Delay_ms(T1);
							ON(S1,0);
							Delay_ms(T6);
						}
						if(tag_id == 3)//送到药房了,要开始卸药
						{
							uint16_t i = 1;
							ON(S1,S2);
							Delay_ms(T4);
						LED_RED_ON();
						Delay_ms(50);
						STOP();
						Delay_ms(1000);
							way1 += 1;
							while(i != 0)
								{
						if(AD_GetValue() < B)//卸下药品了
						{
							see = 2;
							LED_RED_OFF();
							i = 0; 
						}
							}
						}
					}
			
					else if(dir1 == 1)
					{
						if(tag_id == 1 )//第二次识别到路口了,要左转(返回时)
						{
							ON(V1,V1);
							Delay_ms(T1);
							ON(0,S2);			
							Delay_ms(T7);
						}
						else if(tag_id == 3)//送到药房了,要开始卸药
						{
							ON(S1,S2);
							Delay_ms(T4);
							LED_GREEN_ON();
							Delay_ms(50);
							STOP();
							Delay_ms(1500);
							see = 3;
						}
					}
					
				}
				else if(see == 2)//向后转180
				{
					dir1++;
					Delay_ms(500);
					Turnover2(V2,V2);
					Delay_ms(T3);
					STOP();
					Delay_ms(600);
					tag_id = 0;
					see = 1;
					
				}
				else if(see == 3)//回到出发点了
				{
					ON(S1,S2);
					Delay_ms(T4);
					LED_GREEN_ON();
					STOP();
				}
			}
}
}




第二问和第三问(题目要求3-8随机摆放,所以这里只给出3和4的,5-8其实是一摸一样的。):

#include "stm32f10x.h"                  // Device header

#include "Delay.h"
#include "motor.h"
#include "Serial.h"
#include "pid.h"
#include "AD.h"
#include "K210.h"
#include "LED.h"
#include "OLED.h"

uint16_t see2 ;//标识符
uint16_t way2;//停下来的次数,第二次停下就不能再转180
uint16_t dir2;//第几次转弯 
uint16_t far;//远端药房标识符
uint16_t wey1,wey2;//1:数字三返回标识符,2:数字4返回标识符
uint16_t lyq1,lyq2;//中段药房返回时标识符

#define C 2500 //光敏电阻阈值
#define D 1800

#define S3 31//左轮速度
#define S4 31//右轮速度
#define V3 32//补转弯时的误差速度
#define V4  29 //转180速度
#define T4  430//小车转弯前直行延时
#define T6  905//小车转180延时
#define T5  890 //小车左转弯时延时//左端的药房
#define T7  865//小车右转弯延时//左端的药房
#define T8  400//小车识别到药房前进延时
#define T9  965 //小车左转弯时延时//右边的药房
#define T10  960 //小车右转弯时延时//右边的药房
#define T11  300


void CROSS2(void)

{
	Motor_Init();
	Serial_Init();
	pid_init();
	AD_Init();
	K210_Init();
	LED_Init();
	
while(see2!=6)
	{
		OLED_ShowNum(1,13,AD_GetValue(),4); 
		OLED_ShowNum(2,14,num,2);
		OLED_ShowNum(3,14,tay,2);
		
		OLED_ShowNum(1,7,left_speed,2);
        OLED_ShowNum(2,7,right_speed,2);
		OLED_ShowNum(3,3,x_translation ,5);
		OLED_ShowNum(4,6,tag_id,2);
//		OLED_ShowNum(4,10,dir2,2);
		if(  num == 3 & tay == 9)//开始时从k210接收到的数字信息
		{
			if(see2 == 0)
			{
				STOP();
				if(AD_GetValue() > C)//装上药品
				{
					see2 = 2;
				}
			}
		}
			
		if( num == 4 & tay == 9)//开始时从k210接收到的数字信息
		{
			if(see2 == 0)
			{
				OLED_ShowNum(3,14,tay,2);
				STOP();
				if(AD_GetValue() > C)//装上药品
				{
					see2 = 3;
				}
			}
		}
			
			if(see2 == 2)//一开始识别数字3
			{
				pid_contorl();
				
				if (dir2 == 0)//前去药房
				{
					OLED_ShowNum(3,14,tay,2);
					
					if (tag_id == 1 & tay == 1)//数字3在左边,且是中端药房
					{
						OLED_ShowNum(3,14,tay,2);
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);						
						Delay_ms(T5);
						wey1 = 1;
						STOP();
						Delay_ms(T11);
						OLED_ShowNum(4,10,wey1,2);
					}
					
					else if(tag_id == 1 & tay ==2)//数字3在右边,且是中端药房
					{
						OLED_ShowNum(3,14,tay,2);
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);						
						Delay_ms(T7);
						wey1 = 2;
						STOP();
						Delay_ms(T11);
					}
					
					else if (tag_id == 2  & tay ==1)//数字3在左边,且是远端药房
					{
						OLED_ShowNum(3,14,tay,2);
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);						
						Delay_ms(T5);
						far+=1;	
						STOP();
						Delay_ms(T11);						
					}
					
					else if(tag_id == 2  & tay == 2)//数字3在右边,且是远端药房
					{
						OLED_ShowNum(3,14,tay,2);
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);					
						Delay_ms(T7);
						far+=2;
						STOP();
						Delay_ms(T11);
					}
					
					if( far == 1 & tag_id == 2)//远端药房的第二次转弯(第一次向左转)
					{
						if( tay == 1)//数字三在左边(左下方)第二次识别到数字3且还是在左边
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);						
						Delay_ms(T5);
						wey1 = 3;//远端药房
						STOP();
						Delay_ms(T11);
						}
						else if ( tay ==2 )//数字3在右端(左上方)第二次识别到数字3且是在右边
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);						
						Delay_ms(T7);
						wey1 = 4;//远端药房
							STOP();
						Delay_ms(T11);
						}
					}
					
					if(far == 2 & tag_id == 2)//远端药房的第二次转弯(第一次向右转)
					{
						if(tay == 1)//数字三在左边(右上方)第二次识别到数字3且是在左边
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);						
						Delay_ms(T5);
						wey1 = 5;//远端药房
							STOP();
						Delay_ms(T11);
						}
						else if (tay == 2)//数字3在右端(右下方)第二次识别到数字3且是在右边
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);
						Delay_ms(T7);
						wey1 = 6;//远端药房
							STOP();
						Delay_ms(T11);
						}
					}
					
					if(tag_id == 3)//送到药房了,要开始卸药
					{
						ON(S3,S4);//因为是视觉巡线,就会导致无法完全到达终点,所以再给一个速度
						Delay_ms(T8);
						uint16_t i = 1;
						LED_RED_ON();
						Delay_ms(50);
						STOP();
						way2 += 1;//第一次停下
						while(i!=0)
						{
							if(AD_GetValue() < D)//卸下药品了
							{
								LED_RED_OFF();
								see2 = 4;//开始转180
								i = 0; 
							}
						}
					}
					
				}
				
				else if(dir2 == 1)//返回了
				{
					OLED_ShowNum(4,10,wey1,2);
				if(wey1 == 1)//开始3在左边向左转(中端药房)
				{
					if( tag_id == 1 )//第二次识别到路口了,此时是返回,要右转,因为一开始识别到3在左边
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);
						Delay_ms(T7);
						STOP();
						Delay_ms(T11);
						wey1 =0;
					}
				}
				
				else if(wey1 == 2)//开始3在右边向右转(中端药房)
				{
					if(tag_id == 1 )//第二次识别到路口了,要左转
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);				
						Delay_ms(T5);
						STOP();
						Delay_ms(T11);
						wey1 = 0;
					}
				}
				
				else if(wey1 == 3)//三在远端药房的左下方
				{
					uint16_t tm3 = 0;//第几次识别到T型路口
					if(tag_id == 2)
					{
						if(tm3 == 0)
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);		
						Delay_ms(T7);
							STOP();
						Delay_ms(T11);
						tm3++;
						}
						else if(tm3 == 1 )
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);	
						Delay_ms(T7);
							STOP();
						Delay_ms(T11);
							tm3++;
						}
					}
				}
				
				else if(wey1 == 4)//三在远端药房的左上方
				{
					uint16_t tn3 = 0; //第几次识别到T型路口
					if(tag_id == 2)
					{
						if(tn3 == 0)
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);
						Delay_ms(T5);
						tn3++;
							STOP();
						Delay_ms(T11);
						}
						else if(tn3 == 1 )
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);						
						Delay_ms(T7);
							STOP();
						Delay_ms(T11);
							tn3++;
						}
					}
					
				}
				
				else if(wey1 == 5)//三在远端药房的右上方
				{
					uint16_t tt3 = 0;//第几次识别到T型路口
					if( tag_id == 2)
					{
						if(tt3 == 0)
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);				
						Delay_ms(T7);
						tt3++;
							STOP();
						Delay_ms(T11);
						}
						else if(tt3 == 1)
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);				
						Delay_ms(T5);
							STOP();
						Delay_ms(T11);
							tt3 ++;
						}
					}
				}
				
				else if(wey1 == 6)//三在远端药房的右下方
				{
					uint16_t tq3 = 0;//第几次识别到T型路口
					if( tag_id == 2)
					{
						if(tq3 == 0)
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);					
						Delay_ms(T5);
						tq3++;
							STOP();
						Delay_ms(T11);
						}
						else if(tq3 == 1 )
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);					
						Delay_ms(T5);
							STOP();
						Delay_ms(T11);
							tq3++;
						}
					}
				}
				
				if(tag_id == 3)//回到出发点
					{
					ON(S3,S4);
					Delay_ms(T8);
					LED_GREEN_ON();
					STOP();
					Delay_ms(1500);
					see2 = 6;
					}
				
				}
			}
				
				
				else if(see2 == 3)//一开始识别数字4
				{
				pid_contorl();
				
				if (dir2 == 0)//第一次转弯
				{
//					if(tay == 1)//左边
//					{
//						lyq2 += 2; 
//					}
//					if(tay == 2)//右边
//					{
//						lyq2 += 3;
//					}
					if (tag_id == 1 & tay == 1)//数字4在左边,且是中端药房
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);						
						Delay_ms(T5);
						tag_id = 0;  
						Delay_ms(250);
						wey2 = 1;	
					}
					
					else if(tag_id == 1 & tay == 2)//数字4在右边,且是中端药房
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);
						Delay_ms(T7);
						tag_id = 0;  
						Delay_ms(250);
						wey2 = 2;
					}
					
					else if (tag_id == 2  & tay == 1)//数字4在左边,且是远端药房
					{	
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);					
						Delay_ms(T5);
						far+=1;
					}
					
					else if(tag_id == 2  & tay == 2)//数字4在右边,且是远端药房
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);
						Delay_ms(T7); 
						far+=2;
					}
					
					if( far == 1 & tag_id == 2)//远端药房的第二次转弯(第一次向左转)
					{
						if( tay == 1)//数字4在左边(左下方)
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);
						Delay_ms(T5);
						wey2 = 3;//远端药房
							Delay_ms(T11);
						}
						else if ( tay == 2)//数字4在右端(左上方)
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);
						Delay_ms(T7);
						wey2 = 4;//远端药房
							Delay_ms(T11);
						}
					}
					
					if( far == 2 & tag_id == 2)//远端药房的第二次转弯(第一次向右转)
					{
						if( tay == 1)//数字4在左边(右上方)
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);						
						Delay_ms(T5);
						wey2 = 5;//远端药房
							Delay_ms(T11);
						}
						
						else if ( tay == 2)//数字3在右端(右下方)
						{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);					
						Delay_ms(T7);
						wey2 = 6;//远端药房
							Delay_ms(T11);
						}
					}
					
					if(tag_id == 3)//送到药房了,要开始卸药
					{
						ON(S3,S4);//因为是视觉巡线,就会导致无法完全到达终点,所以再给一个速度
						Delay_ms(T8);
						uint16_t i = 1;
						LED_RED_ON();
						Delay_ms(50);
						STOP();
						way2 += 1;//第一次停下
						while(i!=0)
						{
							if(AD_GetValue() < D)//卸下药品了
							{
								LED_RED_OFF();
								see2 = 5;//开始转180
								i = 0; 
							}
						}
					}
				}
				
				else if(dir2 == 1)
				{
					if(wey2 == 1)//开始4在左边向左转(中端药房)
					{
					if(tag_id == 1 )//第二次识别到路口了,此时是返回,要右转,因为一开始识别到4在左边
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);
						Delay_ms(T7);
					}
				}
				
				else if(wey2 == 2)//开始4在右边向右转(中端药房)
				{
					if(tag_id == 1 )//第二次识别到路口了,要左转
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);
						Delay_ms(T5);
					}
				}
				
				else if(wey2 == 3)//4在远端药房的左下方
				{
					uint16_t wm3 = 0;//第几次识别到T型路口
					if(wm3 == 0 & tag_id == 2)
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);
						Delay_ms(T7);
						wm3++;
					}
					
					else if(wm3 == 1 & tag_id == 2)
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);
						Delay_ms(T7);
					}
				}
				
				else if(wey2 == 4)//4在远端药房的左上方
				{
					uint16_t wn3 = 0;//第几次识别到T型路口
					if(wn3 == 0 & tag_id == 2)
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);
						Delay_ms(T5);
						wn3++;
					}
					
					else if(wn3 == 1 & tag_id == 2)
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);
						Delay_ms(T7);
					}
				}
				
				else if(wey2 == 5)//4在远端药房的右上方
				{
					uint16_t wt3 = 0;//第几次识别到T型路口
					if(wt3 == 0 & tag_id == 2)
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(S3,0);
						Delay_ms(T7);
						wt3++;
					}
					
					else if(wt3 == 1 & tag_id == 2)
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);
						Delay_ms(T5);
					}
				}
				
				else if(wey2 == 6)//三在远端药房的右下方
				{
					uint16_t wq3 = 0;//第几次识别到T型路口
					if(wq3 == 0 & tag_id == 2)
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);
						Delay_ms(T5);
						wq3++;
					}
					
					else if(wq3 == 1 & tag_id == 2)
					{
						ON(V3,V3);
						Delay_ms(T4);
						ON(0,S4);
						Delay_ms(T5);
					}
				}
				
				if(tag_id == 3)//回到出发点
					{
					ON(S3,S4);
					Delay_ms(T8);
					LED_GREEN_ON();
					STOP();
					Delay_ms(1500);
					see2 = 6;
					}
				
				}
				}
				
				else if(see2 == 4)//识别3向后转180
				{
					Delay_ms(500);
					Turnover(V4,V4);
					Delay_ms(T6);
					STOP();
					tag_id = 0;
					Delay_ms(800);
					see2 = 2;
					dir2++;	
				}
				
				else if(see2 == 5)//识别4向后转180
				{
					Delay_ms(500);
					Turnover(V4,V4);
					Delay_ms(T6);
					STOP();
					tag_id = 0;
					Delay_ms(800);
					see2 = 3;
					dir2++;	
				}
				
				else if(see2 == 6)//回到出发点了
				{
					LED_GREEN_ON();
					STOP();
				}
		
	}
}

zigbee端代码(使用时赋好值Zigbee_TxPacket[4], Zigbee_RxPacket[4],然后使用Zigbee_SendPacket()即可,zigbee配置去b站上搜索zigbee配置,出来第一个跟着做就好了):

#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include <stdarg.h>

uint8_t Zigbee_TxPacket[4];				//定义发送数据包数组,数据包格式:FF 01 02 03 04 FE
uint8_t Zigbee_RxPacket[4];				//定义接收数据包数组
uint8_t Zigbee_RxFlag;					//定义接收数据包标志位

/**
  * 函    数:串口初始化
  * 参    数:无
  * 返 回 值:无
  */
void Zigbee_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);	//开启USART1的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);	//开启GPIOA的时钟
	
	/*GPIO初始化*/
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PA9引脚初始化为复用推挽输出
	
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOB, &GPIO_InitStructure);					//将PA10引脚初始化为上拉输入
	
	/*USART初始化*/
	USART_InitTypeDef USART_InitStructure;					//定义结构体变量
	USART_InitStructure.USART_BaudRate = 115200;				//波特率
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;	//硬件流控制,不需要
	USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;	//模式,发送模式和接收模式均选择
	USART_InitStructure.USART_Parity = USART_Parity_No;		//奇偶校验,不需要
	USART_InitStructure.USART_StopBits = USART_StopBits_1;	//停止位,选择1位
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;		//字长,选择8位
	USART_Init(USART3, &USART_InitStructure);				//将结构体变量交给USART_Init,配置USART1
	
	/*中断输出配置*/
	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);			//开启串口接收数据的中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);			//配置NVIC为分组2
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;					//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;		//选择配置NVIC的USART1线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;			//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;		//指定NVIC线路的抢占优先级为1
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;		//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);							//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*USART使能*/
	USART_Cmd(USART3, ENABLE);								//使能USART1,串口开始运行
}

/**
  * 函    数:串口发送一个字节
  * 参    数:Byte 要发送的一个字节
  * 返 回 值:无
  */
void Zigbee_SendByte(uint8_t Byte)
{
	USART_SendData(USART3, Byte);		//将字节数据写入数据寄存器,写入后USART自动生成时序波形
	while (USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);	//等待发送完成
	/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}

/**
  * 函    数:串口发送一个数组
  * 参    数:Array 要发送数组的首地址
  * 参    数:Length 要发送数组的长度
  * 返 回 值:无
  */
void Zigbee_SendArray(uint8_t *Array, uint16_t Length)
{
	uint16_t i;
	for (i = 0; i < Length; i ++)		//遍历数组
	{
		Zigbee_SendByte(Array[i]);		//依次调用Serial_SendByte发送每个字节数据
	}
}

/**
  * 函    数:串口发送一个字符串
  * 参    数:String 要发送字符串的首地址
  * 返 回 值:无
  */
void Zigbee_SendString(char *String)
{
	uint8_t i;
	for (i = 0; String[i] != '\0'; i ++)//遍历字符数组(字符串),遇到字符串结束标志位后停止
	{
		Zigbee_SendByte(String[i]);		//依次调用Serial_SendByte发送每个字节数据
	}
}

/**
  * 函    数:次方函数(内部使用)
  * 返 回 值:返回值等于X的Y次方
  */
uint32_t Zigbee_Pow(uint32_t X, uint32_t Y)
{
	uint32_t Result = 1;	//设置结果初值为1
	while (Y --)			//执行Y次
	{
		Result *= X;		//将X累乘到结果
	}
	return Result;
}

/**
  * 函    数:串口发送数字
  * 参    数:Number 要发送的数字,范围:0~4294967295
  * 参    数:Length 要发送数字的长度,范围:0~10
  * 返 回 值:无
  */
void Zigbee_SendNumber(uint32_t Number, uint8_t Length)
{
	uint8_t i;
	for (i = 0; i < Length; i ++)		//根据数字长度遍历数字的每一位
	{
		Zigbee_SendByte(Number / Zigbee_Pow(10, Length - i - 1) % 10 + '0');	//依次调用Serial_SendByte发送每位数字
	}
}

/**
  * 函    数:使用printf需要重定向的底层函数
  * 参    数:保持原始格式即可,无需变动
  * 返 回 值:保持原始格式即可,无需变动
  */
int fputc(int ch, FILE *f)
{
	Zigbee_SendByte(ch);			//将printf的底层重定向到自己的发送字节函数
	return ch;
}

/**
  * 函    数:自己封装的prinf函数
  * 参    数:format 格式化字符串
  * 参    数:... 可变的参数列表
  * 返 回 值:无
  */
void Zigbee_Printf(char *format, ...)
{
	char String[100];				//定义字符数组
	va_list arg;					//定义可变参数列表数据类型的变量arg
	va_start(arg, format);			//从format开始,接收参数列表到arg变量
	vsprintf(String, format, arg);	//使用vsprintf打印格式化字符串和参数列表到字符数组中
	va_end(arg);					//结束变量arg
	Zigbee_SendString(String);		//串口发送字符数组(字符串)
}

/**
  * 函    数:串口发送数据包
  * 参    数:无
  * 返 回 值:无
  * 说    明:调用此函数后,Serial_TxPacket数组的内容将加上包头(FF)包尾(FE)后,作为数据包发送出去
  */
void Zigbee_SendPacket(void)
{
	Zigbee_SendByte(0xFF);
	Zigbee_SendArray(Zigbee_TxPacket, 4);
	Zigbee_SendByte(0xFE);
}

/**
  * 函    数:获取串口接收数据包标志位
  * 参    数:无
  * 返 回 值:串口接收数据包标志位,范围:0~1,接收到数据包后,标志位置1,读取后标志位自动清零
  */
uint8_t Zigbee_GetRxFlag(void)
{
	if (Zigbee_RxFlag == 1)			//如果标志位为1
	{
		Zigbee_RxFlag = 0;
		return 1;					//则返回1,并自动清零标志位
	}
	return 0;						//如果标志位为0,则返回0
}

/**
  * 函    数:USART1中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void USART3_IRQHandler(void)
{
	static uint8_t RxState = 0;		//定义表示当前状态机状态的静态变量
	static uint8_t pRxPacket = 0;	//定义表示当前接收数据位置的静态变量
	if (USART_GetITStatus(USART3, USART_IT_RXNE) == SET)		//判断是否是USART1的接收事件触发的中断
	{
		uint8_t RxData = USART_ReceiveData(USART3);				//读取数据寄存器,存放在接收的数据变量
		
		/*使用状态机的思路,依次处理数据包的不同部分*/
		
		/*当前状态为0,接收数据包包头*/
		if (RxState == 0)
		{
			if (RxData == 0xFF)			//如果数据确实是包头
			{
				RxState = 1;			//置下一个状态
				pRxPacket = 0;			//数据包的位置归零
			}
		}
		/*当前状态为1,接收数据包数据*/
		else if (RxState == 1)
		{
			Zigbee_RxPacket[pRxPacket] = RxData;	//将数据存入数据包数组的指定位置
			pRxPacket ++;				//数据包的位置自增
			if (pRxPacket >= 4)			//如果收够4个数据
			{
				RxState = 2;			//置下一个状态
			}
		}
		/*当前状态为2,接收数据包包尾*/
		else if (RxState == 2)
		{
			if (RxData == 0xFE)			//如果数据确实是包尾部
			{
				RxState = 0;			//状态归0
				Zigbee_RxFlag = 1;		//接收数据包标志位置1,成功接收一个数据包
			}
		}
		
		USART_ClearITPendingBit(USART3, USART_IT_RXNE);		//清除标志位
	}
}

视觉pid(直接调用pid_control(),这里的ON( ,)其实就是驱动电机,这个代码我就不加了,非常简单):

#include <stdio.h>
#include <stdlib.h>
#include "stm32f10x.h" // Device header
#include "Serial.h"
#include "encoder.h"
#include "PWM.h"
#include "motor.h"
#include "OLED.h"

#define PID_KP 0.2
#define PID_KI 0.01
#define PID_KD 18


#define tar 30 //目标速度
#define MAX_SPEED 60 //限幅速度
#define MIN_SPEED 0

   int32_t    left_speed ,right_speed ,  pid_output1;//左轮速度,右轮速度,pid计算后的输出值
struct pid{
	int16_t error;
	int16_t last_error;	
	float ki; 
	float kd;
    float kp;
//	int16_t ferror;//滤波后的误差
//	int16_t flast_error;	//上次滤波后的误差
}pid;



void left_xianfu(void)//限制左轮速度
{
	if(left_speed <= MIN_SPEED)
	{
		left_speed = MIN_SPEED;
	}
	
	else if( left_speed >= MAX_SPEED ) 
	{
		left_speed = MAX_SPEED;
	}
}

void right_xianfu(void)//限制右轮速度
{
	if( right_speed <= MIN_SPEED ) 
		{
		right_speed = MIN_SPEED;
 
	} 
		
	else if( right_speed >= MAX_SPEED )
	{
		right_speed = MAX_SPEED;
	}
}


void pid_init( void )
{
	pid.error= 0;
	pid.last_error = 0;
	pid.kp = PID_KP;
	pid.ki = PID_KI;
	pid.kd = PID_KD;
	
	return;
}
 int32_t pid_output( void) //此时计算出的pid误差值
{
	pid.error = (x_translation-32);//误差传入,靠左接收到的x_translation减小,要右转,左轮速度大
//	float a = 0.3;//滤波
//	pid.ferror = a*pid.error + (1-a)* pid.flast_error;//滤波算法
	int32_t P;
	static int32_t I;
	int32_t D;
	int32_t pid_output;
	
	P = pid.kp * pid.error;                      
	I +=pid.ki * pid.error;
	D = pid.kd * ( pid.error - pid.last_error );
	pid.last_error= pid.error;
//	pid.flast_error= pid.ferror;
	pid_output = P + I + D;
	return pid_output;
}
 
 
void speed_by_pid( int32_t pid_output )//pid改变速度使小车在直线上走
{
  if(!pid.error)
	{
    left_speed  = tar;
    right_speed = tar;
	ON(tar,tar);  
    return;
	}
  
	left_speed =  left_speed + pid_output;
	left_xianfu();
 
	right_speed =  right_speed - pid_output;
 
	right_xianfu();
	ON(left_speed,right_speed); 
		return ;
}



void pid_contorl()
{
	left_speed= 30;
	right_speed= 30;
	pid_output1=pid_output();
	OLED_ShowSignedNum(2,10,pid_output1,4);
	speed_by_pid(pid_output1);
	
	
}


//void pid_control_motor(void)
//{
//	if(pid.error >= 7 && pid.error <= 9){
//      Right2();
//      return;
//    }else if(pid.error >= -9 && pid.error <= -7){
//      Left2();
//}     return;
//	ON2();
//    }
//    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值