stm32+k210视觉小车——来拒去留+多位串口通信

stm32F407+k210视觉小车

文章目录

  • 前言
  • 一、制作思路
  • 二、讲解与说明
  • 1.控制部分代码思路
  • 2.通信部分代码讲解
  • 3.过程经验以及注意事项
  • 开源网址

  • 前言

    暑假没事做,从学校带回了一些开发板(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ノ
    (

    物联沃分享整理
    物联沃-IOTWORD物联网 » stm32+k210视觉小车——来拒去留+多位串口通信

    发表评论