stm32F407+k210视觉小车


前言

暑假没事做,从学校带回了一些开发板(stm32F407和k210),在B站学习了串口的多位通信以及串级PID,制作了一个具有来拒去留、可控制位置、控制轮子转速以及舵机转向功能的视觉小车。在这里做一个开源,顺便讲解一下该视觉小车的制作思路和过程。演示视频戳这里https://www.bilibili.com/video/BV15Y4y1F7VQ?spm_id_from=333.999.0.0&vd_source=25a8aa89291db245e99a1e95bd6f2b19
(开源链接在文章最后)

小车整体


一、制作思路

小车最基本的就是它的轮子了,其实每个小车基本上都会用到读轮子的编码器以及用PID控制它的转速。知道了这一点,那么接下来便是操控它的转速,来靠近目标。而想要识别目标位置,则需要用到视觉模块(这里用的是k210)。
在这里插入图片描述
当识别到目标的时候,k210会将图中白框的面积以及中间的十字坐标通过串口传将数据包递给stm32,stm32获得数值(下图OLED屏幕最下面一行就是数据包)
在这里插入图片描述

二、讲解与说明

1.控制部分代码思路

上文说到,k210会回传解析包。单片机则会解析它们,具体方式也就是取余数或者是取百位/千位。比如我这里使用十位数据进行数据包传输,其中后三位数据是位置判断,那么就需要对十位数据DATA_NUM进行取余运算。

Orientation=DATA_NUM%1000;
Distance=(DATA_NUM-Orientation)/1000%1000000;

这样就得到了方位-Orientation,以及距离-Distance
通过白框的面积大小判断与物体的间距,通过中心十字判断左右。
距离控制:当白框变大且小车在不断靠近目标的时候,白框变大,轮子将会减速,并停在期望位置,此时再拉开小车与目标的距离,白框变小,轮子将会加速。
方向控制:假定期望数值是目标在中间的数值(我测出为190),那么小于190时,就该左转,大于时候要右转。

该怎样控制距离呢?是距离增大我就一股脑的往前冲,然后距离减小我就直接往后退?显然这样是不行的,假设目标距离为4000,这时候你靠近了,变成了3999,小车就会突然后退,而它一旦后退,3999就变化成大于4000的数字,这时又向前冲…这便产生了振荡。这并不是我们想要的现象,所以我们应该在目标刚靠近小车时,缓慢后退,靠近的越多,退的越快。靠近的时候如果手里的“目标物”突然停下,小车会从刚才的快速后退变成慢速后退,直到不断接近期望距离。这种思路便是PID了。PID这一部分的讲解,推荐各位去B站一个叫“天下行走”的UP那里学习,他讲的很详细,这里不在赘述。
该UP的个人空间链接
轮子速度控制部分代码

int Speed_PID_1(float true_speed)
{
	hope_speed_1=Speed_PID();
	err=hope_speed_1-true_speed;
	integral += err;
	if(integral<0)integral=0;
	out= KP*err+
		 KI*integral+ 
		 KD * (err - err_last);
	if(Speed_PID()==0||Distance==0)out=0;
	if(out>=899)out=899;
	return out;
		
}

小车位置控制部分代码

	S_Distance=Hope_Distance-Distance;
	if(S_Distance<0)
	{
		GO_flag=0;
		S_err=Distance-Hope_Distance;
		S_integral += S_err/1000;
		if(S_integral<0)S_integral=0;
		if(S_integral>1000000)S_integral=1000000;

		S_out= S_KP*S_err+
					 S_KI*S_integral+ 
					 S_KD * (S_err - S_err_last);
		if(Distance==0)S_out=0;
		S_out=(S_out>0)?S_out:0;
		S_out=(S_out<99999)?S_out:99999;

   }

2.通信部分代码讲解

通信开始后,作为接收方,需要知道这一段数据在什么时候开始,以及在什么时候结束,那么发送方则需要在发送的时候有一个起始位和终止位,这便是一种“协议”,我这边以0x0A代表起始,0x0D代表终止,因为传输的只有数字,是数字0—9,其编码是0x30—0x39,所以不会和协议位冲突。
具体思路:
情况1:传输开始,假设我一开始就检测到了0x0A,则会将状态值RxState 置1,进入模式1,模式1通过循环,将接下来接收到的数字存放在数组中,再将数组的数值进行排列,获得原本的数据。在循环运算完毕后,将状态值置2,等待0x0D的出现。0x0D出现后,将状态值置0,等待0x0A出现,进入下一次循环。
情况2:假设传输开始,但是我没接收到0x0A,接收到了其他的东西,那么由于状态值依然是0,所以将会一直等待0x0A,直到它出现。
这里觉着讲的不是很清楚的话,可以去B站看这一期视频
部分代码如下(示例):

int j=0,i=0;
int DATALONG=10;                   //修改长度
int DATA_NUM=0;
int Distance;                //距离
int Orientation;             //方位
uint8_t Serial_TxPacket[4];				 //FF 01 02 03 04 FE
uint8_t Serial_RxPacket[10];       //修改长度
uint8_t Serial_RxFlag;
void USART1_IRQHandler(void)//串口中断服务函数,在这里面获得信息并进行解析
{
	static uint8_t RxState = 0;
	static uint8_t pRxPacket = 0;
	if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
	{

		uint8_t RxData = USART_ReceiveData(USART1);
		if (RxState == 0)
		{
			if (RxData == 0x0A)
			{
				RxState = 1;
				pRxPacket = 0;
			}
		}
		else if (RxState == 1)
		{
			Serial_RxPacket[pRxPacket] = (RxData/16)*10+RxData%16-30;//十六进制转十进制
			pRxPacket ++;
			if (pRxPacket >= DATALONG)
			{
				for(j=0;j<=DATALONG-1;j++)
				{
					//将信息包整理成一串数字
					DATA_NUM=DATA_NUM*10+Serial_RxPacket[j];
					
					//信息包解析
					Orientation=DATA_NUM%1000;
					Distance=(DATA_NUM-Orientation)/1000%1000000;
//					OLED_ShowNum(0, 0,Orientation , 10,16,1);
					OLED_ShowChinese(0,32,11,16,1);//距
					OLED_ShowChinese(16,32,15,16,1);//:
					OLED_ShowNum(24,32,Distance, 5,16,1);
					
					OLED_ShowChinese(70,32,14,16,1);//位
					OLED_ShowChinese(86,32,15,16,1);//:
					OLED_ShowNum(94,32,Orientation, 3,16,1);
					
    			    OLED_ShowNum(0,48,DATA_NUM, 10,16,1);
				}
				RxState = 2;
			}
		}
		else if (RxState == 2)
		{
			if (RxData == 0x0D)
			{
				DATA_NUM=0;
				RxState = 0;
				Serial_RxFlag = 1;
			}
		}
		
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

3.过程经验以及注意事项

#一般写代码,我会用中景园电子的I2C例程作为底层代码进行搭建,里面含有数字、字符串和中文等的屏幕显示,以及有串口的初始化代码,这些比较基础的就不需要自己再手撕浪费时间了。
#同时,在编写的时候,写一步验证一步,确保这串代码能用之后,再去写下一串,不要一连写好几个C文件插进去,然后出了一些问题,都不知道出现在哪,原地歇逼。
#硬件电路上,一定要小心谨慎,插对没事,插错直接“嘎嘣脆,芯片味”。
#还有就是,有时候出bug不一定是软件上的,比如我这次的k210通信传输,我为了确保传输没问题(是正确传输而不是乱传一串数字),我在前面加上了“10”作为开头标志,但有时候依然会出现传输一串以其他数字组合为开头的数据包,后来才发现,我把k210的5V/GND插在了单片机上,导致供电不足,k210不能正常工作,而并不是stm32这边的代码出了问题。

开源网址

链接:https://pan.baidu.com/s/1OzLqJF2pth75rx61Fuxnzw
提取码:2233
一些硬件引脚连接和注意事项写在txt以及main.c的开头了
┏━━━━━━━━━━━┓
#### 加油!!####
┗━━━━━━━━━━━┛
(\ヽ
  \\ Λ ## Λ
   \(.@—@.)
    > ⌒ヽ
   /   へ\
   /  / \\
   レ ノ   ヽ_つ
  / /
  / /|
 ( (ヽ
 | |、\
 | 丿 \ ⌒)
 | |  ) /
`ノ )   Lノ
(

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐