请选择 进入手机版 | 继续访问电脑版
凡是官网购买产品,均可以获得积分,(积分可以用来下载技术资料)还可以获得精致技术支持。零知实验室可提供全套电子方案定制服务,了解详情请咨询客服。           
查看: 4308|回复: 5

零知智能避障小车项目 - 人人都能实现的无线控制小车

[复制链接]
发表于 2018-10-13 18:40:44 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
根据前面的各个模块的学习,我们现在就可以实现一个自己的蓝牙无线智能小车了;该小车具有以下基本功能:
  • 避障功能-超声波测距实现;
  • 黑白巡线-红外探测实现;
  • 手机控制-蓝牙通信;
  • 其他小车基本动作;


1、知识预备
该小车仅实现了一个小车的基本功能,旨在学习各个模块的应用并将其整合到一个实际项目中,在此基础上完全可以加入更多的功能,让你的小车可玩性更高。小车主要运用超声测距方式进行避障功能,使用红外探测的方式进行黑白寻迹,使用蓝牙与手机APP进行通信,让小车可以远程控制。制作中可参考以下各个模块的应用:


2、硬件准备
  • 小车底盘+车轮(2个主轮和1个万向轮)
  • 电池9V(干电池组)
  • 蓝牙 JDY18串口透传模块
  • 直流减速电机 TT马达 2个
  • 电机驱动板 L298N 一个
  • 超声避障模块 (包括支架)
  • 舵机(包括云台)
  • 红外寻迹模块 4路(包含控制板)
  • 零知标准板 1个
  • 螺丝、杜邦线等配件

3、组装
组装的灵活度较大,可以根据自己的需求进行合理组装模块。这里的建议是边组装边调试,即组装一个模块就调试一个模块,该模块正常工作后再组装下一个,这样就更容易完成整个小车的组装。接线时注意电源的VCC电压大小,这里有三种:3.3v、5v、9v,要接正确。详细接线图如下:
44.jpg

4、软件设计
软件设计保护两个部分:单片机程序和安卓APP。
(1)单片机程序
单品机程序使用函数封装的形式设计,每个模块都实现相应的功能函数,然后在主程序中调用。
  • 首先需要进行引脚的设置,具体代码如下:
引脚定义:
[C++] 纯文本查看 复制代码
#include <Servo.h>//舵机控制库

//调试控制,注释掉下面两句取消调试
#define DEBUG_PRINT                //调试串口打印输出控制
#define DEBUG_REMOTE        //将获取到的数据发送到手机APP,便于调试

//所有引脚的设置
//1、L298N电机驱动
#define LEFT_MOTOR1         2
#define LEFT_MOTOR2                3
#define RIGHT_MOTOR1         4
#define RIGHT_MOTOR2         5
#define LEFT_ENA                 6        
#define RIGHT_ENB                 9
//2、超声避障SR04
#define SR04_TRIG                10
#define SR04_ECHO                11
//3、舵机(辅助超声避障功能)
#define SERVO_PWM                12
//4、红外寻迹
#define LINE1                        A0
#define LINE2                        A1
#define LINE3                        A2
#define LINE4                        A3
//5、蓝牙模块 JDY-18
#define BLE_SERIAL                Serial1

//控制命令定义
#define        STOP                 0
#define        FORWARD                1
#define        BACKWARD        2
#define        TURNLEFT        3
#define        TURNRIGHT        4

Servo myServo;//舵机


引脚模式设置:
[C++] 纯文本查看 复制代码
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");//设置蓝牙设备名称
}


  • 电机驱动函数接口
该函数接口实现根据不同命令进行电机的动作,包含:前进、后退、左转、右转和停止功能。具体代码如下:
[C++] 纯文本查看 复制代码
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()函数中调用,进行调试:
[C++] 纯文本查看 复制代码
void loop() {
        
        if(Serial.available()>0)
        {
                Serial.print("cmd:");
                
                int cmd = Serial.read() - 48;
                Serial.println(cmd);
                
                motor_run(cmd);
        }
        
}

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

  • 超声避障
该功能先调试在固定方向时的距离测量,具体代码如下:
[C++] 纯文本查看 复制代码
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时候按照当前方向进行调整,实现自动避障。具体实现如下:
[C++] 纯文本查看 复制代码
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中调用该函数,把小车放在一个有很多隔板的场地,然后就可以观察到小车会根据前方障碍物的情况自动避障。
  • 自动巡线
自动巡线是根据红外探测黑白区域实现,这里是在黑色区域上沿着白线前进,遇到一方出白线时自动调整方向以便一直沿着白线前进,知道遇到两边检测到都是黑色,结束。代码如下:
[C++] 纯文本查看 复制代码
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();如下:
[C++] 纯文本查看 复制代码
//拐弯时候一个轮子动,一个不懂,更快拐弯
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发来的信息后根据命令进行动作:

[C++] 纯文本查看 复制代码
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);
}


(2)手机APP程序设计
手机APP程序基于android平台,使用FastBle库进行编写的。有一定基础的可以直接在改程序上进行修改,操作界面如下:
  • 扫描设备

22.jpg

  • 连接设备

23.jpg


  • 操作界面


24.jpg

手机APP程序:
零知智障小车APP.apk (2.14 MB, 下载次数: 38)
回复

使用道具 举报

 楼主| 发表于 2018-10-17 17:37:38 | 显示全部楼层

最后的小车组装图:
13.jpg

12.jpg

回复

使用道具 举报

 楼主| 发表于 2018-10-17 17:40:23 | 显示全部楼层
蓝牙无线控制、自动寻迹(白线)、自动超声避障演示:
回复

使用道具 举报

发表于 2018-10-13 20:55:41 | 显示全部楼层
给个图片~
回复

使用道具 举报

发表于 2019-1-14 23:02:58 | 显示全部楼层
这一套硬件下来多少钱
回复

使用道具 举报

 楼主| 发表于 2019-1-15 09:19:36 | 显示全部楼层
zpxliucai 发表于 2019-1-14 23:02
这一套硬件下来多少钱

万能的淘宝,你可以算下,所有的功能加起来成本应该在一百以内的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版| 小黑屋| 零知实验室 ( 粤ICP备17040594号-3 )

GMT+8, 2020-2-25 21:35 , Processed in 0.063574 second(s), 20 queries .

快速回复 返回顶部 返回列表