• 【毕业设计】基于单片机的智能避障超声波跟随小车 - 物联网 嵌入式



    0 前言

    🔥 这两年开始毕业设计和毕业答辩的要求和难度不断提升,传统的毕设题目缺少创新和亮点,往往达不到毕业答辩的要求,这两年不断有学弟学妹告诉学长自己做的项目系统达不到老师的要求。

    为了大家能够顺利以及最少的精力通过毕设,学长分享优质毕业设计项目,今天要分享的是

    🚩 基于单片机的智能避障超声波跟随小车

    🥇学长这里给一个题目综合评分(每项满分5分)

    • 难度系数:4分
    • 工作量:4分
    • 创新点:3分

    🧿 选题指导, 项目分享:

    https://gitee.com/dancheng-senior/project-sharing-1/blob/master/%E6%AF%95%E8%AE%BE%E6%8C%87%E5%AF%BC/README.md


    1 项目背景

    一些行业仍在努力掌握工业 4.0 的真正理念,即每台机器都通过互联网相互连接。这个想法意味着要使用传感器、实施控制和智能编程来确保机器或机器人有效地执行任务。

    这样的应用一般是在工厂中,材料必须由铁路线控制的机器人在托盘或推车中移动。我们的项目就是受到这个想法的启发,即建立一个机器人系统。这些机器人协同工作以遵循计算好的路径并相互支持。

    它还通过引入自主方面来改进概念,只允许对前面的机器人进行编程以改变目标路径并消除对跟随路径的需要。这直接导致了我们的 IR 墙壁感应和障碍物检测机器人以及视觉感应机器人的灵感涌现。

    2 实现效果

    在这里插入图片描述
    墙壁感应和避障机器人:

    在这里插入图片描述

    视觉传感机器人:

    在这里插入图片描述

    3 设计原理

    它的基本要点是第一辆机器人汽车使用红外传感器(安装在右侧)跟随墙壁和安装在汽车前部的超声波传感器来检测障碍物并转弯。从航位推算获得的坐标信息在第一个 MyRio 的帮助下传输到计算机上的 LabVIEW 软件,在计算机上显示坐标,并且地图绘制了汽车行驶的路径。

    此外,我们可以在 LabVIEW 中动态更改多达 10 个参数(速度、增益、转弯速率等),这些参数控制转弯发生的距离、转弯时的速度、墙跟随时的速度、与被跟踪墙壁的距离,以及相关的增益和阈值参数。

    在这里插入图片描述
    在这里插入图片描述
    允许我们在此处控制 C 代码片段中的参数,这些参数用于控制如何使用来自超声波和 IR 传感器的数据。

    在这里插入图片描述
    第二辆车使用 USB 摄像头,它有自己的 LabVIEW 程序和 MyRio 来回传输信息。在我们的例子中,USB 摄像头跟踪红色并使用其视图中“红色”的质心将转向值从 LabVIEW 程序发送回 myRio,从而发送到使用该信息进行转向的机器人汽车,方向将其与看到的颜色对齐。

    第一辆汽车的后部贴有一块红色纸板,这辆车紧随其后。使用第一个 LabVIEW 代码中显示的排序系统来计算转向参考值。然后在电机转向的转向控制中实现。

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    HC-SR04超声波模块

    简介

    HC-SR04超声波模块常用于机器人避障、物体测距、液位检测、公共安防、停车场检测等场所。HC-SR04超声波模块主要是由两个通用的压电陶瓷超声传感器,并加外围信号处理电路构成的。如图:
    在这里插入图片描述
    两个压电陶瓷超声传感器,一个用于发出超声波信号,一个用于接收反射回来的超声波信号。由于发出信号和接收信号都比较微弱,所以需要通过外围信号放大器提高发出信号的功率,和将反射回来信号进行放大,以能更稳定地将信号传输给单片机。模块整体电路如图:

    在这里插入图片描述

    模块参数

    (1)模块主要电气参数

    • 使用电压:DC—5V
    • 静态电流:小于2mA
    • 电平输出:高5V
    • 电平输出:底0V
    • 感应角度:不大于15度
    • 探测距离:2cm-450cm
    • 高精度 可达0.2cm

    (2)模块引脚
    超声波模块有4个引脚,分别为Vcc、 Trig(控制端)、 Echo(接收端)、 GND;其中VCC、GND接上5V电源, Trig(控制端)控制发出的超声波信号,Echo(接收端)接收反射回来的超声波信号。模块如图
    在这里插入图片描述

    5 部分代码

    ```c
    
    /************************** .h文件******************************/
    
    
    #ifndef _pid_H
    #define _pid_H
    
    #define     MODEL_P       1
    #define     MODEL_PI      2
    #define     MODEL_PID     3
    
    typedef struct
    {
        u8 choose_model;    		//使用哪个模式调节,以方便分布调试
        
        float curr;                   //当前值
        float set;                    //设定值
        
        float En;                     //当前时刻误差值
        float En_1;                   //前一时刻误差值
        float En_2;                   //前二时刻误差值
            
        float Kp;                     //比例系数
        float T;                      //每隔T控制器输出一次PID运算结果
    
        u16   Tdata;                  //判断PID周期到没到
        float Ti;                     //积分时间常数
        float Td;                     //微分时间常数
        
        float Dout;                   //增量PID计算本次应该输出的增量值-本次计算的结果
    
        float OUT0;                   //一个维持的输出,防止失控
        
        short currpwm;                //当前的pwm宽度
        u16 pwmcycle;                 //pwm周期
    
    }PID;
    
    extern u8 STATUS;
    extern PID pid;
    void PID_Init(void);      //增量式PID初始化
    void pid_calc(void);              //pid计算 并输出
    
    
    /************************** .c文件******************************/
    
    
    #endif
    #include "pid.h"
    #include "PWM_Config.h"
    #include "USART_Config.h"   		//USART设置
    
    PID pid;
    
    void PID_Init()  		//
    {
        pid.choose_model = MODEL_PID;
        pid.T=330;                		//采样周期,定时器使用1ms,则最小执行PID的周期为330ms
        
        pid.set =280;            		//用户设定值
        pid.Kp=0.5;                	//比例系数
        pid.Ti=40;               		//微分系数常数
        pid.Td=10;                		//积分时间常数
        pid.OUT0=0;                	//一个维持的输出
    
        pid.pwmcycle = 330;    		//PWM的周期
    }
        
    void pid_calc()  			
    {
     	float dk1;float dk2;
      	float t1,t2,t3;
        
        if(pid.Tdata < (pid.T))  		//最小计算周期未到
         {
                return ;
         }
        pid.Tdata = 0;
        
        pid.En=pid.set-pid.curr;  		//本次误差
        dk1=pid.En-pid.En_1;   		//本次偏差与上次偏差之差
        dk2=pid.En-2*pid.En_1+pid.En_2;
        
      	t1=pid.Kp*dk1;                   	//比例
        
        t2=(pid.Kp*pid.T)/pid.Ti;      	//积分
        t2=t2*pid.En;
        
        t3=(pid.Kp*pid.Td)/pid.T;        	//微分
        t3=t3*dk2;
        
        switch(pid.choose_model)
         {
             case MODEL_P:    		//仅用P
    				pid.Dout= t1;                    
    				printf("使用P运算\r\n") ;
                 	break;
             
             case MODEL_PI:  		//仅用PI
    				pid.Dout= t1+t2;                
    				printf("使用PI运算\r\n") ;
                	break;
                     
             case MODEL_PID: 		//用PID
    				pid.Dout= t1+t2+t3;        
    				printf("使用PID运算\r\n") ;
    				break;
    	 } 
              
        pid.currpwm+=pid.Dout;  		//本次应该输出的PWM
        printf("PID算得的OUT:\t%d\r\n",(int)pid.currpwm) ;
         
        if(pid.currpwm>pid.pwmcycle)       //确保值在0-pid.pwmcycle之间
        {
          pid.currpwm=pid.pwmcycle;
        }
        if(pid.currpwm<0)
        {
         pid.currpwm=0;
        }
        
        printf("实际输出使用的OUT:\t%d\r\n",(int)pid.currpwm) ;
        pid.En_2=pid.En_1;
        pid.En_1=pid.En;    
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    void tim4_pwm_init(u16 arr,u16 psc)
    {  
    	
    	RCC->APB1ENR |= 1<<2;    //时钟复位TIM4
    	TIM4->CR1=0x0080;     //ARPE 使能,Timx_ARR寄存器被装入缓冲器
    	TIM4->CR1|=0x01;      //使能定时器 4
      
    	  RCC->APB2ENR |= 1<<3;       //时钟使能GPIOB  由电路图知GPIO6和TIM4的通道一复用 
    	  GPIOB->CRL&=0X00F00000;
    	  GPIOB->CRL|=0XBB033333;    //pb6  7复用功能输出   PB6输出ch1,pb7输出ch2
    	  TIM4->ARR = arr;            //设置计数器自动重装载值
    	  TIM4->PSC = psc;              //预分频器设计
    	
    	  TIM4->CCMR1 =0X6060;  //配置Tim4输入捕获模式
    	
    	  TIM4->CCMR1|=1<<3;            //CH1 预装载使能   
    	  TIM4->CCMR1|=1<<11;           //CH 2 预装载使能
    	
    	  TIM4->CCER |=1<<0;            //OC1 输出使能 
    	  TIM4->CCER |=1<<4;            //OC2 输出使能
    	
    	  
    }
    这里用到的寄存器全部可以在stm32中文手册里查到。
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    
    
    • 1

    6 最后

  • 相关阅读:
    计算机网络学习记录 网络层 Day4(下)
    【数据结构】树 有关树的认识
    uni-app:js二维数组与对象数组之间的转换
    【docker学习记录】docker安装mysql、redis
    用户运营都做些什么?你需要知道这些
    第十七章 开发Productions - ObjectScript Productions - 对业务服务、流程和操作进行编程
    SpringBoot——》@KafkaListener
    在电脑上实现微信多开的技巧教程
    浙大计算机学院2024届推免直博生名单
    Talk | SIGGRAPH‘23 Best Paper 秦颖思:分罗曼三维显示器—各点独立变焦显示技术
  • 原文地址:https://blog.csdn.net/m0_71572576/article/details/126786229