IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> stm32小车红外对管的循迹 -> 正文阅读

[嵌入式]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;
	  }	
    }
}
  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-11-22 12:30:12  更:2021-11-22 12:30:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 11:45:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码