零知智能避障小车系列1 - L298N模块使用 电机驱动 TT马达

电机驱动模块在电子制作中会经常用到,下面以L298N型号的模块为例介绍其使用方法。模块引脚说明如下:

1、实验材料

电机驱动板1块
零知-标准板
杜邦线若干
直流减速电机TT马达2个,带车轮(便于实验观察)

2、接线

我们把零知标准板和电机驱动板相连接,驱动2路电机工作。接线图如下:

引脚连接:

最后我们把电机驱动板的12v接到一个9v的电源上(如干电池组),然后GND连接到零知标准板的地。

3、软件设计

根据上述连接线后,我们使用程序来控制电机的运行:


												
	/**
	*    文件: 电机驱动.ino      by 零知实验室([url=http://www.lingzhilab.com]www.lingzhilab.com[/url])
	*    -^^- 零知开源,让电子制作变得更简单! -^^-
	*    时间: 2018/10/13 15:01
	*    说明: 
	**/
	 
	 
	//控制命令
	#define        STOP                 0
	#define        FORWARD                1
	#define        BACKWARD        2
	#define        TURNLEFT        3
	#define        TURNRIGHT        4
	 
	//电机控制引脚
	#define LEFT_MOTOR1 2
	#define LEFT_MOTOR2        3
	#define RIGHT_MOTOR1 4
	#define RIGHT_MOTOR2 5
	#define LEFT_ENA 11
	#define RIGHT_ENB 12
	 
	int LED_STATE = 0;
	 
	 
	 
	 
	void setup() {
	    // put your setup code here, to run once:
	         
	        Serial.begin(9600);
	         
	        pinMode(LED_BUILTIN, OUTPUT);
	         
	        pinMode(LEFT_MOTOR1, OUTPUT);
	        pinMode(LEFT_MOTOR2, OUTPUT);
	        pinMode(RIGHT_MOTOR1, OUTPUT);
	        pinMode(RIGHT_MOTOR2, OUTPUT);
	        pinMode(LEFT_ENA,OUTPUT);
	        pinMode(RIGHT_ENB,OUTPUT);
	         
	        motor_run(STOP);
	         
	}
	 
	 
	void loop() {
	    // put your main code here, to run repeatedly:
	 
	        int cmd = 1;
	 
	        motor_run(cmd);
	 
	        delay(1000);
	         
	        motor_run(0);
	}											
						
引脚模式设置:

								
		void setup() {
		        //引脚模式设置
		        pinMode(LED_BUILTIN, OUTPUT);//板载LED
		        Serial.begin(9600);//用于调试
		         
		        pinMode(LEFT_MOTOR1, OUTPUT);
		        pinMode(LEFT_MOTOR2, OUTPUT);
		        pinMode(RIGHT_MOTOR1, OUTPUT);
		        pinMode(RIGHT_MOTOR2, OUTPUT);
		        pinMode(LEFT_ENA,OUTPUT);
		        pinMode(RIGHT_ENB,OUTPUT);
		         
		        pinMode(SR04_TRIG, OUTPUT);
		        pinMode(SR04_ECHO, INPUT);
		         
		        myServo.attach(SERVO_PWM);
		         
		        pinMode(LINE1,INPUT);
		        pinMode(LINE2,INPUT);
		        pinMode(LINE3,INPUT);
		        pinMode(LINE4,INPUT);
		         
		        BLE_SERIAL.begin(9600);
		        BLE_SERIAL.println("AT+NAMELingzhiBleCar");//设置蓝牙设备名称
		}
		
电机驱动函数接口

该函数接口实现根据不同命令进行电机的动作,包含:前进、后退、左转、右转和停止功能。具体代码如下:


									
	void motor_run(int cmd){
	        switch(cmd)
	        {
	                case FORWARD:
	                        #ifdef DEBUG_PRINT
	                        Serial.println("向前");
	                        #endif
	                        analogWrite(LEFT_ENA,highSpeed);
	                        digitalWrite(LEFT_MOTOR1, HIGH);
	                        digitalWrite(LEFT_MOTOR2, LOW);
	                        analogWrite(RIGHT_ENB,highSpeed);
	                        digitalWrite(RIGHT_MOTOR1, HIGH);
	                        digitalWrite(RIGHT_MOTOR2, LOW);
	                break;
	                case BACKWARD:
	                        #ifdef DEBUG_PRINT
	                        Serial.println("向后");
	                        #endif
	                        digitalWrite(LEFT_MOTOR1,  LOW);
	                        digitalWrite(LEFT_MOTOR2, HIGH);
	                        digitalWrite(RIGHT_MOTOR1, LOW);
	                        digitalWrite(RIGHT_MOTOR2, HIGH);
	                break;
	                case TURNLEFT:
	                        #ifdef DEBUG_PRINT
	                        Serial.println("左转");
	                        #endif
	                        analogWrite(LEFT_ENA,lowSpeed);
	                        analogWrite(RIGHT_ENB,highSpeed);
	                        digitalWrite(LEFT_MOTOR1, HIGH);
	                        digitalWrite(LEFT_MOTOR2, LOW);//C
	                        digitalWrite(RIGHT_MOTOR1, HIGH);
	                        digitalWrite(RIGHT_MOTOR2, LOW);
	                break;
	                case TURNRIGHT:
	                        #ifdef DEBUG_PRINT
	                        Serial.println("右转");
	                        #endif
	                        analogWrite(LEFT_ENA,highSpeed);
	                        analogWrite(RIGHT_ENB,lowSpeed);
	                        digitalWrite(LEFT_MOTOR1, HIGH);
	                        digitalWrite(LEFT_MOTOR2, LOW);
	                        digitalWrite(RIGHT_MOTOR1, HIGH);//C
	                        digitalWrite(RIGHT_MOTOR2, LOW);
	                break;
	                case STOP:
	                default:
	                        #ifdef DEBUG_PRINT
	                        Serial.println("停止");
	                        #endif
	                        digitalWrite(LEFT_ENA,1);
	                        digitalWrite(RIGHT_ENB,1);
	                        digitalWrite(LEFT_MOTOR1, LOW);
	                        digitalWrite(LEFT_MOTOR2, LOW);
	                        digitalWrite(RIGHT_MOTOR1, LOW);
	                        digitalWrite(RIGHT_MOTOR2, LOW);
	                break;
	        }
	}		
		
然后在loop()函数中调用,进行调试:

										
	void loop() {
	         
	        if(Serial.available()>0)
	        {
	                Serial.print("cmd:");
	                 
	                int cmd = Serial.read() - 48;
	                Serial.println(cmd);
	                 
	                motor_run(cmd);
	        }
	         
	}								
		

调试时根据小车实际运行的方向进行参数的调试,直到实际运行的方向与预定方向一致。

超声避障

该功能先调试在固定方向时的距离测量,具体代码如下:


									
	int getInstance()
	{
	        digitalWrite(SR04_TRIG, LOW);
	        delayMicroseconds(2);
	        digitalWrite(SR04_TRIG, HIGH);
	        delayMicroseconds(10);
	        digitalWrite(SR04_TRIG, LOW);
	         
	        int distance = pulseIn(SR04_ECHO, HIGH);
	        distance = distance/58.0;
	         
	        return distance;
	}						
		

现在我们再结合舵机,在前方、左边、右边方向上不断探测小车前面的障碍物距离,并且在障碍物距离为70cm时候按照当前方向进行调整,实现自动避障。具体实现如下:


								
	void avoidance(int avoidDistance)
	{
	        int i = 0;
	         
	        int time = 30;//舵机速度
	         
	        int disFor = 0;//探测的距离
	        int disLeft = 0;
	        int disRight = 0;
	         
	        int d = 70;
	         
	        //首先探测正前方
	        myServo.write(d);
	        for(i=0;i<5;i++)
	        {
	                disFor += getDistance();
	                delay(50);
	        }
	        disFor = disFor/5;
	        #ifdef DEBUG_PRINT
	                Serial.print(" 前方距离:");
	                Serial.println(disFor);
	        #endif
	         
	        if(disFor <= avoidDistance)//正前方有障碍物
	        {
	                //停车
	                motor_run(0);
	                 
	                int t = 0;
	                int total = 0;
	                 
	                //探测右边
	                for(i=d; i>0;i--)
	                {
	                        myServo.write(i);
	                        delay(time);
	                }
	                for(i=0;i<5;i++)
	                {
	                        disRight += getDistance();
	                        delay(50);
	                }
	                disRight = disRight/5;
	                 
	                #ifdef DEBUG_PRINT
	                        Serial.print(" 右侧距离:");
	                        Serial.println(disRight);
	                #endif
	                 
	                myServo.write(d);
	                 
	                //探测左边
	                for(i=d; i<170;i++)
	                {
	                        myServo.write(i);
	                        delay(time);
	                }
	                for(i=0;i<5;i++)
	                {
	                        disLeft += getDistance();
	                        delay(50);
	                }
	                disLeft = disLeft/5;
	                 
	                #ifdef DEBUG_PRINT
	                        Serial.print(" 左侧距离:");
	                        Serial.println(disLeft);
	                #endif
	                 
	                myServo.write(d);
	                 
	                int rollTime = 1000;
	                 
	                //两边都有障碍物,掉头
	                if((disLeft <= avoidDistance) && (disRight <= avoidDistance))
	                {
	                        motor_run(3);
	                        Serial.println("--掉头");
	                        delay(rollTime*2);
	                }
	                else if(disLeft >= disRight)
	                {
	                        motor_run(3);//左转
	                        delay(rollTime);
	                }
	                else if(disLeft < disRight)
	                {
	                        motor_run(4);//右转
	                        delay(rollTime);
	                }
	                motor_run(0);//停止
	                 
	        }else{
	                motor_run(1);//无障碍,前进
	        }
	         
	        //delay(300);        
	}
	

程序中的有些参数需根据自己实际调试时的情况进行调整。在loop中调用该函数,把小车放在一个有很多隔板的场地,然后就可以观察到小车会根据前方障碍物的情况自动避障。

自动巡线

自动巡线是根据红外探测黑白区域实现,这里是在黑色区域上沿着白线前进,遇到一方出白线时自动调整方向以便一直沿着白线前进,知道遇到两边检测到都是黑色,结束。代码如下:


								
	void lineTrace()
	{
	        int IR_1 = digitalRead(LINE1);
	        int IR_2 = digitalRead(LINE2);
	         
	        if(IR_1 == 0 && IR_2 == 0)//在白色线上
	        {
	                motor_run2(1);//前进
	        }
	        if(IR_1 == 1 && IR_2 == 0)//Line1 出白线
	        {
	                motor_run2(3);//左转
	        }
	        if(IR_1 == 0 && IR_2 == 1)//Line2 出白线
	        {
	                motor_run2(4);//右转
	        }
	}								
	

由于这里在拐弯时需要尽量短时间完成,因此把电机驱动改为如下motor_run2();如下:


													
	//拐弯时候一个轮子动,一个不懂,更快拐弯
	void motor_run2(int cmd)
	{
	        int low = 90;
	        int high = 120;
	        switch(cmd)
	        {
	                case FORWARD:
	                        #ifdef DEBUG_PRINT
	                        Serial.println("向前");
	                        #endif
	                        analogWrite(LEFT_ENA,low);
	                        analogWrite(RIGHT_ENB,low);
	                        digitalWrite(LEFT_MOTOR1, HIGH);
	                        digitalWrite(LEFT_MOTOR2, LOW);
	                        digitalWrite(RIGHT_MOTOR1, HIGH);
	                        digitalWrite(RIGHT_MOTOR2, LOW);
	                break;
	                case BACKWARD:
	                        #ifdef DEBUG_PRINT
	                        Serial.println("向后");
	                        #endif
	                        digitalWrite(LEFT_MOTOR1,  LOW);
	                        digitalWrite(LEFT_MOTOR2, HIGH);
	                        digitalWrite(RIGHT_MOTOR1, LOW);
	                        digitalWrite(RIGHT_MOTOR2, HIGH);
	                break;
	                case TURNLEFT:
	                        #ifdef DEBUG_PRINT
	                        Serial.println("左转");
	                        #endif
	                        analogWrite(LEFT_ENA,low);
	                        analogWrite(RIGHT_ENB,low);
	                        digitalWrite(LEFT_MOTOR1, LOW);
	                        digitalWrite(LEFT_MOTOR2, LOW);//C
	                        digitalWrite(RIGHT_MOTOR1, HIGH);
	                        digitalWrite(RIGHT_MOTOR2, LOW);
	                break;
	                case TURNRIGHT:
	                        #ifdef DEBUG_PRINT
	                        Serial.println("右转");
	                        #endif
	                        analogWrite(LEFT_ENA,low);
	                        analogWrite(RIGHT_ENB,low);
	                        digitalWrite(LEFT_MOTOR1, HIGH);
	                        digitalWrite(LEFT_MOTOR2, LOW);
	                        digitalWrite(RIGHT_MOTOR1, LOW);
	                        digitalWrite(RIGHT_MOTOR2, LOW);
	                break;
	                case STOP:
	                default:
	                        #ifdef DEBUG_PRINT
	                        Serial.println("停止");
	                        #endif
	                        digitalWrite(LEFT_ENA,1);
	                        digitalWrite(RIGHT_ENB,1);
	                        digitalWrite(LEFT_MOTOR1, LOW);
	                        digitalWrite(LEFT_MOTOR2, LOW);
	                        digitalWrite(RIGHT_MOTOR1, LOW);
	                        digitalWrite(RIGHT_MOTOR2, LOW);
	                break;
	        }
	}					
		
蓝牙控制

蓝牙模块与手机APP进行通信,接收到APP发来的信息后根据命令进行动作:


											
	void bleControl()
	{
	        if(Serial1.available()>0){
	                char x = Serial1.read();
	                 
	                int cmd = (int)(x);//字符转数字
	                 
	                Serial.print("recv cmd:");
	                Serial.println((int)(x));
	                 
	                motor_run(cmd);
	                 
	                //current_dir = cmd;
	        }
	        delay(500);
	}			
				

把电机驱动的操作作为一个函数motor_run(int cmd);如下:


											
	/**
	*        根据命令点击进行相应动作
	*         cmd 命令
	*/
	 
	int highSpeed = 200;
	int lowSpeed = 100;
	 
	void motor_run(int cmd)
	{
	        switch(cmd)
	        {
	                case FORWARD:
	                        Serial.println("FORWARD"); //输出状态
	                        analogWrite(LEFT_ENA,highSpeed);
	                        digitalWrite(LEFT_MOTOR1, LOW);
	                        digitalWrite(LEFT_MOTOR2, HIGH);
	                        analogWrite(RIGHT_ENB,highSpeed);
	                        digitalWrite(RIGHT_MOTOR1, LOW);
	                        digitalWrite(RIGHT_MOTOR2, HIGH);
	                break;
	                case BACKWARD:
	                        Serial.println("BACKWARD"); //输出状态
	                        digitalWrite(LEFT_MOTOR1,  HIGH);
	                        digitalWrite(LEFT_MOTOR2, LOW);
	                        digitalWrite(RIGHT_MOTOR1, HIGH);
	                        digitalWrite(RIGHT_MOTOR2, LOW);
	                break;
	                case TURNLEFT:
	                        Serial.println("TURN  LEFT"); //输出状态
	                        analogWrite(LEFT_ENA,lowSpeed);
	                        analogWrite(RIGHT_ENB,highSpeed);
	                        digitalWrite(LEFT_MOTOR1, LOW);
	                        digitalWrite(LEFT_MOTOR2, HIGH);//C
	                        digitalWrite(RIGHT_MOTOR1, LOW);
	                        digitalWrite(RIGHT_MOTOR2, HIGH);
	                break;
	                case TURNRIGHT:
	                        Serial.println("TURN  RIGHT"); //输出状态
	                        analogWrite(LEFT_ENA,highSpeed);
	                        analogWrite(RIGHT_ENB,lowSpeed);
	                        digitalWrite(LEFT_MOTOR1, LOW);
	                        digitalWrite(LEFT_MOTOR2, HIGH);
	                        digitalWrite(RIGHT_MOTOR1, LOW);//C
	                        digitalWrite(RIGHT_MOTOR2, HIGH);
	                break;
	                case STOP:
	                default:
	                        Serial.println("STOP"); //输出状态
	                        digitalWrite(LEFT_ENA,1);
	                        digitalWrite(RIGHT_ENB,1);
	                        digitalWrite(LEFT_MOTOR1, LOW);
	                        digitalWrite(LEFT_MOTOR2, LOW);
	                        digitalWrite(RIGHT_MOTOR1, LOW);
	                        digitalWrite(RIGHT_MOTOR2, LOW);
	                break;
	        }
	}			
				

我们把程序上传到零知-标准板上,调整程序中cmd的值,看看效果吧

完整工程:电机驱动.7z(点击下载)