毕业设计 基于STM32的自动跟随小车

201 篇文章 119 订阅
187 篇文章 117 订阅

0 前言

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

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

🚩 毕业设计 基于STM32的自动跟随小车

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

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

🧿 选题指导, 项目分享:

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 简介

自动跟随小车系统由两部分组成:跟随小车和移动目标携带装置。
工作原理:跟随小车系统通过无线通信模块发送寻找信号,同时超声波接收器开始计时,如果移动目标接收到无线寻找信号,则立即发送超声波信号。这样小车的三角超声波接收器陆续收到超声波信号,CPU通过每个超声波模块接收到的时间,计算出移动目标到3个超声波接收点的距离,通过三边定位算法即可确定移动目标的位置。如果计算出来的距离大于设定距离,则控制电机向目标方向移动,如果计算出来的距离小于设定距离,则控制电机停止,从而实现小车的自动跟随功能。

2 主要器件

  • STM32F103RCT6主控单片机

  • NRF2401无线收发模块

  • TL852超声波接收模块

  • L298N电源模块

  • 电机及电机驱动模块

  • 报警模块

3 实现效果

KMLKOA8}YD@{C(EPEHJJKRR.png在这里插入图片描述

4 设计原理

4.1 硬件设计

小车硬件设计:

自动跟随小车硬件模块包括控制器模块、无线收发模块、超声波接收模块、电机及电机驱动模块、报警模块、电源模块组成,下面对每个模块做具体介绍。

由于跟随小车需要进行实时目标位置定位计算、无线信号收发处理、电机管理、电源管理等任务 ,采用普通单片机其资源及速度难以满足使用要求,需要高性能DSP处理器才能够完成,因此选择STM32F103RCT6作为控制器。

引脚图

在这里插入图片描述

无线收发是用来实现同步,当小车发射无线信号,同时人手携带装置接收到无线信号时,人手携带装置发射超声波。所以本次设计选用NRF2401做为无线收发模块。

NRF2401各引脚功能为:

(1)CSN:芯片的片选线,CSN为低电平工作。

(2)SCK:芯片控制的时钟线(SPI时钟)。

(3)MISO:芯片控制数据线 。

(4)IRQ:中断信号,无线通信过程中MCU主要是通过IRQ与NRF2401通信。

(5)CE:芯片的模式控制线。

(6)MOSI:芯片控制数据线。

在这里插入图片描述

NRF的数据收发,是一包一包进行的,一包(帧)数据:包括了前导码、目标地址、包控制域、有效数据、CRC, 但我们只管有效数据,其它的不用我们负责,NRF发送时自动打包,接收到数据时自动拆包。

每一包的有效数据最大为32个字节。当然,也可以只发一个字节的数据。

要发送的数据大于32字节,就要分包进行,自行手动分包处理。

因为在配置部分时,已配置好了频道,速率,重发次数等各种参数,在需要发送数据时,只要往芯片写入要发送的数据和地址,然后切换为发送状态,芯片就会自动发送。

发送成功(收到ack),会产生TX_DS中断。

发送失败了(达到最大重发次数), 也会产生MAR_RT中断。

在中断函数里,根据情况作处理就好。

void vNrf24l01_TxPacket(u8 *txbuf)
{
    CE_LOW; 
    Nrf24l01_WriteBuf(W_TX_PAYLOAD,           txbuf,          32);   // 写数据到TX_BUFF    
    Nrf24l01_WriteBuf(W_REGISTER+TX_ADDR,    (u8*)TX_ADDRESS,  5);   // 写入要发送的目标地址 
    Nrf24l01_WriteBuf(W_REGISTER+RX_ADDR_P0, (u8*)TX_ADDRESS,  5);   // 通道0的地址设为和目标地址一致,以接收自动回复auto_ack信号 
    Nrf24l01_WriteReg(W_REGISTER+CONFIG,      0x0E);	             // 设置为发送模式,开启所有中断	    
    CE_HIGH;	
}

TL852超声波接收模块

超声波接收模块是采用具有单独接收功能的模块,如图所示。其中接收模块核心部分是由专用超声波接收集成电路TL852构成的超声波信号检测电路,这部分主要完成的是回波的检测和放大。
B6FFG}LB~ZVM@FUO4]@%{Y4.png
直流电机的控制很简单,性能出众,直流电源也容易实现。这种直流电机的驱动及控制需要电机驱动模块进行驱动,采用L298N电源模块。

系统电源

系统电源采用7.4V可充电锂电池。7.4V锂电池组属于多串并锂电池组。

在这里插入图片描述

目标携带装置硬件设计:

由于跟随小车需要进行实时目标位置定位计算、无线信号收发处理、电机管理、电源管理等任务 ,采用普通单片机其资源及速度难以满足使用要求,需要高性能DSP处理器才能够完成,因此选择STM32F103RCT6作为控制器。

无线收发是用来实现同步,当小车发射无线信号,同时人手携带装置接收到无线信号时,人手携带装置发射超声波。所以本次设计选用NRF2401做为无线收发模块。

超声波发射模块是采用具有单独发射功能的模块,如图所示。其中发射模块中的P1 、R4、R5。因为利用了变压器和发射头的谐振,好处是能得到近似正弦波。但附带的问题是:在驱动信号停止后,由于谐振的原因,发射头还会持续较长时间发射,直至能量在变压器的次级线包直流电阻上消耗完,这样就导致在近距离测量时,回波都到了,余波还未结束,导致测量失败。所以设计了一个余波抑制电路,将变压器初级构成回路,利用初级较小的电阻快速消耗掉次级的能量。为此,要多占一个MCU的I/O口。而且,由于驱动电压的原因,必须使用OC(或者开漏)驱动,否则会无法可靠关断P1,导致正常发射不正常。如果测量的距离较远,或者觉得余波不影响测量,则不必接这个信号。如若使用,一定要注意和发射驱动信号的配合,不要两个同时有效,导致发射效率大减。从原理图上看,如果要提高驱动能量,可以适当提高驱动电压,但要要注意MOS管的耐压只有20V,发射头的最高电压是80V。

在这里插入图片描述

4.2 软件设计

小车控制流程图
在这里插入图片描述
目标携带装置控制流程图
在这里插入图片描述

5 部分核心代码

#define DEBUG 0 // set to 1 to print to serial monitor, 0 to disable
#include <Servo.h>
Servo headservo; //
// Constants
const int EchoPin = 2;       //
const int TrigPin = 3;       //
const int leftmotorpin1 = 4; //
const int leftmotorpin2 = 5;
const int rightmotorpin1 = 6;
const int rightmotorpin2 = 7 const int HeadServopin = 9; //
const int Sharppin = 11;                                 //
const int maxStart = 800;                                // run dec time
// Variables
int isStart = maxStart; //
int currDist = 0;       //
boolean running = false;
void setup()
{
    Serial.begin(9600); //
    pinMode(EchoPin, INPUT);
    pinMode(Sharppin, INPUT);
    for (int pinindex = 3; pinindex < 11; pinindex++)
    {
        pinMode(pinindex, OUTPUT); // set pins 3 to 10 as outputs
    }
    //
    headservo.attach(HeadServopin);
    //
    headservo.write(70);
    delay(2000);
    headservo.write(20);
    delay(2000);
}
void loop()
{
    if (DEBUG)
    {
        Serial.print("running:");
        if (running)
        {
            Serial.println("true");
        }
        else
        {
            Serial.println("false");
        }
    }
    if (isStart <= 0)
    {
        if (running)
        {
            totalhalt(); //
        }
        int findsomebody = digitalRead(Sharppin);
        if (DEBUG)
        {
            Serial.print("findsomebody:");
            Serial.println(findsomebody);
        }
        if (findsomebody > 0)
        {
            isStart = maxStart;
        }
        delay(4000);
        return;
    }
    isStart--;
    delay(100);
    if (DEBUG)
    {
        Serial.print("isStart: ");
        Serial.println(isStart);
    }
    currDist = MeasuringDistance(); //
    if (DEBUG)
    {
        Serial.print("Current Distance: ");
        Serial.println(currDist);
    }
    if (currDist > 30)
    {
        nodanger();
    }
    else if (currDist < 15)
    {
        backup();
        randTrun();
    }
    else
    {
        // whichway();
        randTrun();
    }
}
/
    long MeasuringDistance()
{
    long duration;
    // pinMode(TrigPin, OUTPUT);
    digitalWrite(TrigPin, LOW);
    delayMicroseconds(2);
    digitalWrite(TrigPin, HIGH);
    delayMicroseconds(5);
    digitalWrite(TrigPin, LOW);
    // pinMode(EchoPin, INPUT);
    duration = pulseIn(EchoPin, HIGH) return duration / 29 / 2;
}
//
void nodanger()
{
    running = true;
    digitalWrite(leftmotorpin1, LOW);
    digitalWrite(leftmotorpin2, HIGH);
    digitalWrite(rightmotorpin1, LOW);
    digitalWrite(rightmotorpin2, HIGH);
    return;
}
//
void backup()
{
    running = true;
    digitalWrite(leftmotorpin1, HIGH);
    digitalWrite(leftmotorpin2, LOW);
    digitalWrite(rightmotorpin1, HIGH);
    digitalWrite(rightmotorpin2, LOW);
    delay(1000);
}
//
void whichway()
{
    running = true;
    totalhalt(); // first stop!
    headservo.write(20);
    delay(1000);
    int lDist = MeasuringDistance(); // check left distance
    totalhalt();                     //
    headservo.write(120);            // turn the servo right
    delay(1000);
    int rDist = MeasuringDistance(); // check right distance
    totalhalt();                     //
    if (lDist < rDist)
    {
        body_lturn();
    }
    else
    {
        body_rturn();
    }
    return;
}
//
void totalhalt()
{
    digitalWrite(leftmotorpin1, HIGH);
    digitalWrite(leftmotorpin2, HIGH);
    digitalWrite(rightmotorpin1, HIGH);
    digitalWrite(rightmotorpin2, HIGH);
    headservo.write(70); // set servo to face forward
    running = false;
    return;
    delay(1000);
}
//
void body_lturn()
{
    running = true;
    digitalWrite(leftmotorpin1, LOW);
    digitalWrite(leftmotorpin2, HIGH);
    digitalWrite(rightmotorpin1, HIGH);
    digitalWrite(rightmotorpin2, LOW);
    delay(1500);
    totalhalt();
}
//
void body_rturn()
{
    running = true;
    digitalWrite(leftmotorpin1, HIGH);
    digitalWrite(leftmotorpin2, LOW);
    digitalWrite(rightmotorpin1, LOW);
    digitalWrite(rightmotorpin2, HIGH);
    delay(1500);
    totalhalt();
}
void randTrun()
{
    long randNumber;
    randomSeed(analogRead(0));
    randNumber = random(0, 10);
    if (randNumber > 5)
    {
        body_rturn();
    }
    else
    {
        body_lturn();
    }
}

最后

  • 3
    点赞
  • 85
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值