需要用到的材料:Arduino Uno板,L298N电机驱动模块,两个12V直流减速电机,舵机一个,超声波模块,11.1V锂电池,小车车架等。
因为我后续还要添加其他功能,如加装机械臂和openmv模块,所以选用了功率较大的12V电机。以后还会推出相关的博文。
Uno板:
L298N:
12V直流减速电机:
11.1V锂电池
超声波模块+舵机:
面向对象顾名思义就是把现实中的事务都抽象成为程序设计中的“对象”,其基本思想是一切皆对象,是一种“自下而上”的设计语言,先设计组件,再完成拼装。
首先我们需要写一个arduino库,这个库的作用是用来存储我们写的类,包含.h文件和.cpp文件。其中.h文件是类的声明,.cpp文件是成员函数的定义。
关于如何自建arduino库可以参考: Arduino自定义库的编写。
关于类的创建我这里就不详细讲解了,自行百度就能搜到很多。
废话不多说,直接上代码~
头文件.h:
#ifndef TENNIMATE_H_
#define TENNIMATE_H_
#include "Arduino.h"
#include
// 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
#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 = 90; pos -= 1) {
TM.setHeadPos(pos);
delay(3);
}
delay(500);
//检查左右距离并做出转向决定
if ( rightDist < DIST_THRESHOLD && leftDist = 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教程 – 专项教程篇