用面向对象的编程方法制作一台超声波避障arduino小车(代码+接线+供电详解)

1.材料准备

需要用到的材料:Arduino Uno板,L298N电机驱动模块,两个12V直流减速电机,舵机一个,超声波模块,11.1V锂电池,小车车架等。
因为我后续还要添加其他功能,如加装机械臂和openmv模块,所以选用了功率较大的12V电机。以后还会推出相关的博文。

Uno板:在这里插入图片描述
L298N:
在这里插入图片描述
12V直流减速电机:
在这里插入图片描述
11.1V锂电池
在这里插入图片描述
超声波模块+舵机:
在这里插入图片描述

2.代码实现

面向对象顾名思义就是把现实中的事务都抽象成为程序设计中的“对象”,其基本思想是一切皆对象,是一种“自下而上”的设计语言,先设计组件,再完成拼装。
首先我们需要写一个arduino库,这个库的作用是用来存储我们写的类,包含.h文件和.cpp文件。其中.h文件是类的声明,.cpp文件是成员函数的定义。
关于如何自建arduino库可以参考: Arduino自定义库的编写
关于类的创建我这里就不详细讲解了,自行百度就能搜到很多。
废话不多说,直接上代码~

头文件.h:

#ifndef TENNIMATE_H_
#define TENNIMATE_H_
#include "Arduino.h"
#include <Servo.h>


// Tennimate类
class Tennimate{
  public:
  // 提供电机的转向控制引脚,电机速度控制引脚, 测距传感器引脚参数以及舵机控制引脚参数的构造函数
    Tennimate(int ENA_pin, int In1_pin, int In2_pin,  int In3_pin, int In4_pin, int ENB_pin, byte M_Speed,int trigPin, int echoPin, int servoPin);
    
    void forward();       // 前进
    void backward();      // 后退
    void turnL();       // 左转
    void turnR();         // 右转
    void forwardL();      // 左前
    void forwardR();      // 右前
    void backwardL();     // 左后
    void backwardR();     // 右后 
    void stop();                // 停车
    
    int getDistance();          // 获取距离传感器读数
    void setHeadPos(int pos);   // 设定舵机角度
    int getHeadPos();           // 获取舵机角度
    void headServoIni();      // 舵机初始化
      
  private:

    Servo headServo;      // 建立头部舵机对象
    int headServoPin;       // 舵机控制引脚
    byte motorSpeed;     //电机速度

    int ENA;     //L298N上的ENA引脚
    int In1;     //L298N上的In1引脚
    int In2;     ////L298N上的In2引脚
    int ENB;
    int In3;
    int In4;

    int hcTrig;  // 超声测距传感器Trig引脚
    int hcEcho;  // 超声测距传感器Echo引脚
    long duration, cm;   // 超声测距传感器用变量
};
#endif  

.cpp:

#include "Tennimate.h"

// 提供电机的转向控制引脚,电机速度控制引脚, 测距传感器引脚参数以及舵机控制引脚参数的构造函数
Tennimate::Tennimate(int ENA_pin, int In1_pin, int In2_pin,  int In3_pin, int In4_pin, int ENB_pin, byte M_Speed,int trigPin, int echoPin, int servoPin)
{
  
  headServoPin = servoPin;
  hcTrig = trigPin;
  hcEcho = echoPin;
  
  motorSpeed = M_Speed;

  ENA = ENA_pin;     //L298N上的ENA引脚
  In1 = In1_pin;     //L298N上的In1引脚
  In2 = In2_pin;
  ENB = ENB_pin;
  In3 = In3_pin;
  In4 = In4_pin;

  pinMode(ENA,OUTPUT);  //设置为输出模式
  pinMode(In1,OUTPUT);
  pinMode(In2,OUTPUT);
  pinMode(ENB,OUTPUT);
  pinMode(In3,OUTPUT);
  pinMode(In4,OUTPUT);
  pinMode(hcTrig, OUTPUT);
  pinMode(hcEcho, INPUT);  
}

// 初始化舵机
void Tennimate::headServoIni(){
  headServo.attach(headServoPin);               
}

// 设置舵机位置
void Tennimate::setHeadPos(int pos){
    headServo.write(pos);               
}

// 获取舵机位置
int Tennimate::getHeadPos(){
    return headServo.read();               
}

// 读取传感器距离读数(单位为厘米)
int Tennimate::getDistance(){
   
  digitalWrite(hcTrig, LOW);
  delayMicroseconds(5);
  digitalWrite(hcTrig, HIGH);
  delayMicroseconds(10);
  digitalWrite(hcTrig, LOW);

  duration = pulseIn(hcEcho, HIGH);
  cm = (duration/2) / 29.1;
  
  return cm;
}


// 前进
void Tennimate::forward(){  
  digitalWrite(In1,HIGH); //给高电平
  digitalWrite(In2,LOW);  //给低电平
  analogWrite(ENA,motorSpeed);  //pwm控制速度
  
  digitalWrite(In3,HIGH); //给高电平
  digitalWrite(In4,LOW);  //给低电平
  analogWrite(ENB,motorSpeed);
}


// 后退
void Tennimate::backward(){
  digitalWrite(In1,LOW); 
  digitalWrite(In2,HIGH);  
  analogWrite(ENA,motorSpeed);
  
  digitalWrite(In3,LOW); 
  digitalWrite(In4,HIGH); 
  analogWrite(ENB,motorSpeed);  
}


// 左转
void Tennimate::turnL(){
  digitalWrite(In1,HIGH); 
  digitalWrite(In2,LOW); 
  analogWrite(ENA,motorSpeed);
  
  digitalWrite(In3,LOW); 
  digitalWrite(In4,HIGH); 
  analogWrite(ENB,motorSpeed); 
}


// 右转
void Tennimate::turnR(){
  digitalWrite(In1,LOW); 
  digitalWrite(In2,HIGH);  
  analogWrite(ENA,motorSpeed);
  
  digitalWrite(In3,HIGH); 
  digitalWrite(In4,LOW); 
  analogWrite(ENB,motorSpeed);       
}


// 左前
void Tennimate::forwardL(){
  digitalWrite(In1,HIGH);
  digitalWrite(In2,LOW);  
  analogWrite(ENA,motorSpeed);
  
  digitalWrite(In3,LOW); 
  digitalWrite(In4,LOW); 
  analogWrite(ENB,0);
}


// 右前
void Tennimate::forwardR(){
  digitalWrite(In1,LOW); 
  digitalWrite(In2,LOW);  
  analogWrite(ENA,0);
    
  digitalWrite(In3,HIGH);
  digitalWrite(In4,LOW);  
  analogWrite(ENB,motorSpeed); 
}


// 左后
void Tennimate::backwardL(){
  digitalWrite(In1,LOW); 
  digitalWrite(In2,HIGH);  
  analogWrite(ENA,motorSpeed);
  
  digitalWrite(In3,LOW); 
  digitalWrite(In4,LOW);
  analogWrite(ENB,0);
}


// 右后
void Tennimate::backwardR(){
  digitalWrite(In1,LOW);
  digitalWrite(In2,LOW); 
  analogWrite(ENA,0);
  
  digitalWrite(In3,LOW); 
  digitalWrite(In4,HIGH); 
  analogWrite(ENB,motorSpeed);  
}


// 停止
void Tennimate::stop(){
  digitalWrite(In1,LOW); 
  digitalWrite(In2,LOW);  
  analogWrite(ENA,0);
  
  digitalWrite(In3,LOW); 
  digitalWrite(In4,LOW);  
  analogWrite(ENB,0);   
}

在创建好类之后,我们需要将其实例化,然后就能调用各种成员函数。如果需要添加其他功能,只需要修改或添加数据成员和成员函数即可。

下面就是实现超声波避障小车的程序:

#include <Tennimate.h>
#define DIST_THRESHOLD 35        // 避障距离阈值(cm)

#define TURN_LEFT_90   900        // 左转90度延迟参数
#define TURN_RIGHT_90   1000      // 右转90度延迟参数
#define TURN_BACK   1600          // 掉头延迟参数

// 建立Tennimate对象(实例化)。其中对象参数分别是:
// (L298N模块的ENA, In1, In2,  In3, In4,ENB引脚
// 车轮电机运转速度,测距传感器TRIG引脚, 测距传感器ECHO引脚,头部舵机信号引脚 )
Tennimate TM(5, 12, 13, 7, 8, 6, 100, 2, 3, 10); 
                                                                                                
                           
void setup() {

  TM.headServoIni();    //头部舵机初始化
  TM.setHeadPos(90);    // 系统启动时将头部设置为90位置
}

void loop() {
autoMode();
}

// 左转90度
void turnL90(){
  TM.turnL();
  delay(TURN_LEFT_90);
}

// 右转90度
void turnR90(){
  TM.turnR();
  delay(TURN_RIGHT_90);  
}

// 掉头
void turnBack(){
  TM.turnL();
  delay(TURN_BACK);  
}

// 检查左右方向距离并返回专项方向
void autoTurn(){
  // 检查右侧距离     
  for (int pos = 90; pos >= 0; pos -= 1) {
    TM.setHeadPos(pos);               
    delay(3);                     
  }
  delay(300);
  int rightDist =  TM.getDistance();

  // 检查左侧距离     
  for (int pos = 0; pos <= 180; pos += 1) {
    TM.setHeadPos(pos);                
    delay(3);                     
  }
  delay(300);
  int leftDist =  TM.getDistance();  

  //将头部调整到正前方
  for (int pos = 180; pos >= 90; pos -= 1) {
    TM.setHeadPos(pos);               
    delay(3);                     
  }
  delay(500);
  
  //检查左右距离并做出转向决定
  if ( rightDist < DIST_THRESHOLD && leftDist < DIST_THRESHOLD){  // 如果左右方向距离均小于允许距离                 
    turnBack();                                     // 掉头
    return;
  } else if ( rightDist >= leftDist){               // 如果右边距离大于左边距离                 
    turnR90();                                      // 右转90度
    return;
  } else {                                         // 如果左边距离大于右边距离                
    turnL90();                                     // 左转90度
    return;
  }
}

void autoMode(){ 
    delay(50);              // 提高系统稳定性等待
    int frontDist = TM.getDistance(); // 检查前方距离
    if(frontDist >= DIST_THRESHOLD){          // 如果检测前方距离读数大于等于允许距离参数
      TM.forward();               // 向前
    } else {                           // 如果检测前方距离小于允许距离参数
      TM.stop();                  // 停止   
      autoTurn();                      // 检测左右侧距离并做出自动转向 
    }
  }

3.接线

关于接线问题,就得好好讲一下L298N。
在这里插入图片描述
在这里插入图片描述
可以看到,L298N有三个跳帽,我们只需要拔掉其中的A Enable和B Enable,这样就可以对电机进行PWM调速了。
关于供电, L298N有两个供电,一个是逻辑控制部分的5V供电,一个是电机的供电。图中的12V是给电机供电的,所以要把锂电池接在这里。板上有一个5伏稳压管,如果板上5VEnabl没有拔掉的话,L298N的逻辑部分供电就从这个稳压管获得,这样就不用外接5伏,此时还可以将得到的5v稳压对其他模块供电。如果外接5伏的话,板上5VEnabl就要拔掉了。
我们这里因为是11.1v供电,所以只需要将锂电池连接到+12V,然后用+12V旁边的+5V给arduino板供电即可。
具体的连线图如下:
在这里插入图片描述
这里再强调一下,A Enable和B Enable需要接到arduino中带~的引脚上,因为带 ~的引脚才能输出PWM;In1-4的话随意。再一个就是arduino和L298N要共地!要共地!
超声波和舵机的接线就没什么好讲的了,定义了哪个接口就接哪个就行。
在测试过程中,我出现过一些问题,就是有一边不工作,具体解决方法参考我这篇博文: #Arduino Uno 关于PWM输出的问题(L298N驱动电机只有一边工作)
觉得有帮助就点个赞吧~ 预告下下期!arduino和openmv通信(传送识别物体的中心坐标)

参考: 零基础入门学用Arduino教程 – 专项教程篇

发布了2 篇原创文章 · 获赞 7 · 访问量 134

猜你喜欢

转载自blog.csdn.net/weixin_44507034/article/details/105613943