stm32小车红外对管的循迹

为了参加校内赛写了分技术报告和相关代码

但是不想就这么浪费了

https://www.yuque.com/docs/share/a64051d0-cc1e-4e45-a704-db7a9af5cb76?# 《电控技术报告》

电控技术报告

一.电控整体设计思路

1.循迹

关于循迹,我们是通过将小车放在每一种可能的路线,对红外对管采集到的数据进行整理和分析,然后可以得到一个阈值。再对每一种路线的特点,设置每一个对管的阈值,并与其进行比较。如左转的话,就主要判断左侧的灯管是否大于阈值,若大于便进行左转函数,当然这只是简化了想法,因为实际上如果只关注左侧灯管的话,遇到T字路口,小车的循迹自然就会有漏洞,这个时候我们就要考虑所有灯管在每个特别的路线的实际情况并与阈值对比。比如如果左转的话那么除了左侧的电平升高,还有右侧的电平会降低;而如果是T字路口,左侧的灯管升高的同时,右侧的灯管也会升高。又考虑到小车转弯的不完美,我们还需要矫正函数时刻保证小车在黑线上走,按照期望的效果执行程序。

2.资源区遥控

关于遥控,我们选择的是蓝牙模块,用手机进行遥控。主要是蓝牙中断函数,可以做到当手机连接到小车蓝牙时,便可以操纵小车,让小车优先执行蓝牙模块。

3.资源区机械结构

关于机械结构,原来打算用遥控器控制机械铲子的升高、下降和伸缩。但由于在装车的那天,遥控装置的通讯模块的板子芯片被烧了,我们也不得不写蓝牙控制的函数了。关于这边,我主要参考了例程里的motor的相关函数。控制它的正转和逆转。但是由于学长加固了我们的机械臂,让我们的支架活动范围变窄,使得机械臂无法达到它的最大高度,让我一度以为是我的代码写错了但又找不到bug。

二.小车的各项功能及实现方式

我不理解,为什么会要求有视频,可能是觉得我们的逻辑不能解决实际上遇到的问题吗?

但当时谁想得到拍视频啊TAT

1.逻辑实现的前提

也可以是准备阶段吧,就是收集红外对管的信息。我们通过蓝牙串口,将小车红外对管的实际电压输入到接受区。

// 局部变量,用于保存转换计算后的电压值
float ADC_ConvertedValueLocal[NOFCHANEL];

//将 ADC 外设采集到的数据通过串口打印到串口调试助手上

void Test_ADC_PRINTF(void)
{
    while (1)
    {
       // ADC_ConvertedValueLocal[0] = (float)ADC_ConvertedValue[0] / 4096 * 3.3 * 1.5;
       // ADC_ConvertedValueLocal[1] = (float)ADC_ConvertedValue[1] / 4096 * 3.3 * 1.5;
        ADC_ConvertedValueLocal[2] = (float)ADC_ConvertedValue[2] / 4096 * 3.3 * 1.5;
       // ADC_ConvertedValueLocal[3] = (float)ADC_ConvertedValue[3] / 4096 * 3.3 * 1.5;
       //ADC_ConvertedValueLocal[4] = (float)ADC_ConvertedValue[4] / 4096 * 3.3 * 1.5;
        ADC_ConvertedValueLocal[5] = (float)ADC_ConvertedValue[5] / 4096 * 3.3 * 1.5;
        ADC_ConvertedValueLocal[6] = (float)ADC_ConvertedValue[6] / 4096 * 3.3 * 1.5;
       // ADC_ConvertedValueLocal[7] = (float)ADC_ConvertedValue[7] / 4096 * 3.3 * 1.5;
        ADC_ConvertedValueLocal[8] = (float)ADC_ConvertedValue[8] / 4096 * 3.3 * 1.5;
       // ADC_ConvertedValueLocal[9] = (float)ADC_ConvertedValue[9] / 4096 * 3.3 * 1.5;
        ADC_ConvertedValueLocal[10] = (float)ADC_ConvertedValue[10] / 4096 * 3.3 * 1.5;
       // ADC_ConvertedValueLocal[11] = (float)ADC_ConvertedValue[11] / 4096 * 3.3 * 1.5;

      //  printf("\r\n CH0 value = %f V \r\n", ADC_ConvertedValueLocal[0]);
      //  printf("\r\n CH1 value = %f V \r\n", ADC_ConvertedValueLocal[1]);
        printf("\r\n CH2 value = %f V \r\n", ADC_ConvertedValueLocal[2]);
      // printf("\r\n CH3 value = %f V \r\n", ADC_ConvertedValueLocal[3]);
      //  printf("\r\n CH4 value = %f V \r\n", ADC_ConvertedValueLocal[4]);
        printf("\r\n CH5 value = %f V \r\n", ADC_ConvertedValueLocal[5]);
        printf("\r\n CH6 value = %f V \r\n", ADC_ConvertedValueLocal[6]);
      //  printf("\r\n CH7 value = %f V \r\n", ADC_ConvertedValueLocal[7]);
        printf("\r\n CH8 value = %f V \r\n", ADC_ConvertedValueLocal[8]);
      // printf("\r\n CH9 value = %f V \r\n", ADC_ConvertedValueLocal[9]);
        printf("\r\n CH10 value = %f V \r\n", ADC_ConvertedValueLocal[10]);
      //  printf("\r\n CH11 value = %f V \r\n", ADC_ConvertedValueLocal[11]);

        Delay_MS(500);
    }
}

但使用while循环的时候,由于手机接收时,经常出现乱码,于是我们同时设置了按键中断。方便查看电压。

遇到的问题:在准备阶段,我们就耗费了很多时间,一开始我们只使用了三个灯管,但是接收到的数据非常不灵敏,但车头很窄,机械臂又会阻挡灯管的检测。

解决:我们用纸板延伸,并加长了宽度放置五个灯管,并用热熔胶粘住灯管的接线处,防止接线不

稳导致测量值误差较大。

2.小车行走的功能

对电机前进和后退的控制,是通过对其转速差来调节的

而对转弯的控制,是通过舵机角度来实现的,但由于迷宫转弯的空间限制,我们需要在舵机打向之前停一下,并后退一段时间停止,再打舵机,保证小车在转弯时不会卡住。

void Go_Back()
{
		Motor_Run(motor_2,500);
		Motor_Run(motor_3,500);
}

void Run()
{
		Motor_Run(motor_1,500);
		Motor_Run(motor_4,500);
}
void SRun()
{
		Motor_Run(motor_1,300);
		Motor_Run(motor_4,300);
}
void Turn_Right_()
{
			Stop();
			Go_Back();
			Delay_MS(700);
			Stop();
	    TIM_SetCompare1(SERVO2_TIM, 500);
	    Motor_Run(motor_1,0);
		Motor_Run(motor_4,500);
      Delay_MS(1000);
	    Delay_MS(1000);
			Delay_MS(700);
		Delay_MS(1000);
	    TIM_SetCompare1(SERVO2_TIM, 800);
			Stop();
			Delay_MS(100);
			Go_Back();
			Delay_MS(1000);
			Stop();
}

void Turn_Left_()
{
		    Stop();
			Go_Back();
			Delay_MS(700);
			Stop();
	    TIM_SetCompare1(SERVO2_TIM, 1400);
	    Motor_Run(motor_1,500);
	    Motor_Run(motor_4,0);
      Delay_MS(1000);
	    Delay_MS(1000);
			Delay_MS(1000);
	    TIM_SetCompare1(SERVO2_TIM, 800);
			Stop();
			Delay_MS(100);
			Go_Back();
			Delay_MS(1000);
			Stop();
}

3.判断路线上的黑线位置

ATTENTION!!!该函数并不代表小车行动的最终决定

我们分为黑线可能同一时间,有中间,左,右三个位置出现的情况,分开判断黑线是否在这三个位置出现。若出现则返回值为1,若没有黑线则返回值为0。而有无黑线是根据红外灯管的电压进行判断。

int ADCL1()//left 
{
	if((float)ADC_ConvertedValue[10] / 4096 * 3.3 * 1.5>2.5)
		return 1;
	if((float)ADC_ConvertedValue[10] / 4096 * 3.3 * 1.5<2.5)
		return 0;
}
int ADCL2()//left 
{
	if((float)ADC_ConvertedValue[8] / 4096 * 3.3 * 1.5>3)
		return 1;
	if((float)ADC_ConvertedValue[8] / 4096 * 3.3 * 1.5<3)
		return 0;
}
int ADCM()//中间
{
	if((float)ADC_ConvertedValue[1] / 4096 * 3.3 * 1.5>2)
		return 1;
	if((float)ADC_ConvertedValue[1] / 4096 * 3.3 * 1.5<2)
		return 0;
}
int ADCR1()//right
{
	if((float)ADC_ConvertedValue[7] / 4096 * 3.3 * 1.5>1.5)
		return 1;
	if((float)ADC_ConvertedValue[7] / 4096 * 3.3 * 1.5<1.5)
		return 0;
}
int ADCR2()//right
{
	if((float)ADC_ConvertedValue[5] / 4096 * 3.3 * 1.5>2.5)
		return 1;
	if((float)ADC_ConvertedValue[5] / 4096 * 3.3 * 1.5<2.5)
		return 0;
}

4.迷宫的函数

我们通过对特定的路线的黑线特点,用if判断各个位置的黑线情况,让小车在黑线上行走。比如当我们进入左转弯道时,那么便会有左边出现黑线,中间出现黑线,但右边不会有黑线。当出现左侧,中间函数返回1,而右侧函数返回0,我们便知道是左转路口了。

并且由于转弯之后小车可能会不完全压着黑线走,那么我们就需要矫正,让车回到黑线处。

关于分岔路口,我们是寄希望于按键中断

设定一个变量数组fangxiang,通过按键给它赋值。在检测到分岔路口的时候,我们便读取fangxiang数值来选择方向。

	  while(1)
	  {
// jiaozheng
				
						if(!ADCM()&&!zuanwan)
						{
							Go_Back();}
							if(!ADCM()&&!zuanwan&&(ADCL1()||ADCL2()))
							{
								TIM_SetCompare1(SERVO2_TIM, 1200);
							}
							if(!ADCM()&&!zuanwan&&(ADCR1()||ADCR2()))
							{
								TIM_SetCompare1(SERVO2_TIM, 600);
							}	
					
				//	TIM_SetCompare1(SERVO2_TIM, 800);
//					jiaozheng=0;
				
			if(ADCM()&&ADCR1()&&ADCR2()&&!ADCL1())
			{
				Stop();
				Delay_MS(100);
				Run();
				Delay_MS(100);
				Stop();
				Delay_MS(100);
				if(!ADCM()){
						Turn_Right_();
						Run();
				}
				else{
						if(fangxiang[i])
						{
							Run();
							i++;
						}
						if(!fangxiang[i])
						{
							Turn_Right_();
							Run();
							i++;
						}
				}	
			}
			if(ADCM()&&ADCL1()&&ADCL2()&&!ADCR1())
			{
				Stop();
				Delay_MS(100);
				Run();
				Delay_MS(200);
				Stop();
				Delay_MS(100);
				if(!ADCM()){
						Turn_Left_();
						Run();
				}

关于分岔路口,我们是寄希望于按键中断

设定一个变量数组fangxiang,通过按键给它赋值

void KEY_IRQHandle1(void)
{
    if (EXTI_GetFlagStatus(KEY1_EXTI_LINE)!=RESET)
    {
        if (KEY1 == 0)
        {
					fangxiang[n]=0;
					n++;
        }
        EXTI_ClearITPendingBit(KEY1_EXTI_LINE);
    }

    if (EXTI_GetFlagStatus(KEY2_EXTI_LINE)!=RESET)
    {
        if (KEY2 == 0)
        {
          fangxiang[n]=1;
					n++;
//           Usart_SendString(USARTx, "wanna222\n");
        }
        EXTI_ClearITPendingBit(KEY2_EXTI_LINE);
    }
}
						

在检测到分岔路口的时候,我们便读取fangxiang数值来选择方向

                         if(fangxiang[i])
						{
							Run();
							i++;
						}
						if(!fangxiang[i])
						{
							Turn_Left_();
							Run();
							i++;
						}
					
			
			if(ADCR1()&&ADCR2()&&ADCL1()&&ADCL2())
			{
				if(!fangxiang[i])
				{
					Turn_Left_();
					Run();
				}
				if(fangxiang[i])
				{
					Turn_Right_();
					Run();
				}
			

5.蓝牙控制

通过蓝牙中断来进行操作。我们是采用switch语句,每一个输入对应相应的控制

void USART_IRQHandler(void)
{	  
		
    if (USART_GetITStatus(UART5, USART_IT_RXNE) != RESET)//??????
    {

				x=USART_ReceiveData(UART5);//??????????i
				USART_SendData(UART5, x);//??i??			
			
       switch(x)
       {			 
		 /* w: go_ahead  */case 119:USART_SendData(UART5, x);  Run();                           break;
	     /* a: turn_left */case 97 :USART_SendData(UART5, x);  TIM_SetCompare1(SERVO2_TIM, 1400);                    break;
		 /* d: turn_right*/case 100:USART_SendData(UART5, x);  TIM_SetCompare1(SERVO2_TIM, 500);                   break;
		 /* f: forward   */case 102:USART_SendData(UART5, x);  TIM_SetCompare1(SERVO2_TIM,800); break;
		 /* x: stop      */case 120:USART_SendData(UART5, x);  Stop();                          break;
         /* 0: test ADC  */case 48 :USART_SendData(UART5, x); printf("%d %d %d %d %d",fangxiang[0],fangxiang[1],fangxiang[2],fangxiang[3],fangxiang[4]);  break;
		 /* s: go_back   */case 115:USART_SendData(UART5, x);  Go_Back();                       break;
		 /* s: go_back   */case 49:USART_SendData(UART5, x);  fangxiang[n]=0; n++;              break;
		 /* s: go_back   */case 50:USART_SendData(UART5, x);  fangxiang[n]=1;n++;              break;
	  }	
    }
}

版权声明:本文为CSDN博主「Lu.Polar」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_61911001/article/details/121461839

生成海报
点赞 0

Lu.Polar

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

RT-Thread Studio移植LAN8720A驱动

RTT网络协议栈驱动移植(霸天虎) 1、新建工程 ​ 工程路径不含中文路径名,工程名用纯英文不含任何符号。 2、用CubeMx配置板子外设 2.1、配置时钟 ​ 按照自己板子配置相应时钟。

【STM32Cube笔记】12-配置外部中断

【STM32Cube笔记】系列文章目录 1-基于STM32的VSCode入门级教程前言 2-STM32Cube安装教程 3-STM32CubeIDE汉化 4-STM32Cube配置时钟设置 5-跑马灯引脚配置 6-Cortex-M7内核基本配

stm32cubemx+HAL+串口接收中断

stm32cubemxHAL串口接收中断 在cubemx配置完串口和global interrupt后需要在keil中添加如下代码。 第一步:在main函数中添加接收中断标志位开启函数 HAL_UART_Receive_IT