目录
简介
步骤一 材料准备
硬件准备:
arduino uno
arduino nano R3
HC-05 蓝牙模块
MPU6050
DC motor 12V
轮子
L239D H桥电机驱动
9v 电池
软件准备:
arduino IDE
步骤二 原理说明
这是关于如何自己制作手势控制的车子。基本上,这是MPU-6050 3轴陀螺仪,加速度计的简单应用。您可以做更多的事情。通过了解如何使用它,如何将其与Arduino接口以及如何通过蓝牙模块传输其数据。在本文中,我将重点介绍两个HC-05蓝牙模块之间的蓝牙到蓝牙通信。
步骤三 电路搭建
现在让我们谈谈蓝牙模块的配置。基本上,HC-05蓝牙模块带有从属模块出厂设置。这意味着我们可以通过插入数据将数据发送到jus模块。无需进行任何其他设置即可将数据从移动设备发送到HC-05模块。只需输入其默认密码(1234/0000)即可进行连接。但是,如果我们想使用此模块将数据发送到其他相同模块或移动设备,该怎么办?
在这个项目中,我们做同样的事情,通过蓝牙模块发送数据。由mpu-6050陀螺仪传感器收集到另一个蓝牙模块。
为此,首先我们需要配置这两个蓝牙模块。以便它们在开机后可以自动相互绑定。在这里,第一个模块充当从设备,它将接收来自远程单元的信号并将其安装在汽车上。并将第二个设备配置为主设备,该主设备将充当发送器单元并将数据发送到从设备,因此,首先将第一个蓝牙模块配置为从设备。为此,请按照此接线图将其与Arduino连接。
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(10, 11); // RX | TX
void setup()
{
Serial.begin(9600);
Serial.println("Enter AT commands:");
BTSerial.begin(38400); // HC-05 default speed in AT command more
}
void loop()
{
// Keep reading from HC-05 and send to Arduino Serial Monitor
if (BTSerial.available())
Serial.write(BTSerial.read());
// Keep reading from Arduino Serial Monitor and send to HC-05
if (Serial.available())
BTSerial.write(Serial.read());
}
断开模块。按住模块上的ky,然后将其重新连接。您会看到led on模块的闪烁速度变慢。每2秒一次。这意味着HC-05处于AT命令模式。现在打开串行监视器,将波特率更改为9600,并将输出类型分别设置为NL和CR。现在在发送框中键入AT并将其发送。如果回答确定,则表示一切正常。但是,如果没有,并且出现一些错误,请再次发送AT。直到它回复正常或检查连接并再次发送AT…
从模块获得OK响应后,依次输入以下命令,
AT + ORGL并将其发送。此命令将模块设置为出厂设置。
AT + RMAAD此命令将从先前的任何配对中释放模块
AT + UART?检查模块的当前波特率
AT + UART = 38400,0,0设置波特率为38400
AT +角色?检查角色是从属角色还是主角色。它回答0或1。如果模块是从设备,则回答0,如果它是主设备,则它将回答1。
将角色设置为从设备。输入AT + ROLE = 0
AT + ADDR?检查模块地址。
记下该地址。按模块回答。获取此地址后,将完成从站模块的配置
现在该配置第二个蓝牙模块作为主设备了。将此模块与Arduino开发板连接,并使其进入AT模式。就像上一个一样。
按给定顺序输入这些AT命令。
AT + ORGL
AT + RMAAD
AT + UART?
AT + UART = 38400,0,0
AT +角色?
将此模块设置为主设备。AT +角色= 1
AT + CMODE = 0,因此模块将仅连接单个设备。默认设置为0
现在将此模块与从属设备绑定以执行此输入,
AT + BIND =“从站模块的地址”并全部完成
现在安装用于MPU-6050传感器I2C通讯的库。由于MPU-6050陀螺仪传感器具有I2C接口。从此处下载库和源代码:http : //www.mediafire.com/file/l8mru5emulb8x93/gesture_control_robot.rar/file
如果您已预先安装了这些库,请跳过此步骤。
现在,使用USB电缆将汽车单元与PC连接。选择正确的串口和单板类型。并以名称“ Gesture_controled_Robot__car_unit_”上传程序。上载程序时,请确保电池和蓝牙模块未与汽车连接。
//program by Shubham Shinganapure on 3-10-2019
//
//for Gesture controled Robotic Car
int lm1=8; //left motor output 1
int lm2=9; //left motor output 2
int rm1=10; //right motor output 1
int rm2=11; //right motor output 2
char d=0;
void setup()
{
pinMode(lm1,OUTPUT);
pinMode(lm2,OUTPUT);
pinMode(rm1,OUTPUT);
pinMode(rm2,OUTPUT);
Serial.begin(38400);
sTOP();
}
void loop()
{
if(Serial.available()>0)
{
d=Serial.read();
if(d=='F')
{
ForWard();
}
if(d=='B')
{
BackWard();
}
if(d=='L')
{
Left();
}
if(d=='R')
{
Right();
}
if(d=='S')
{
sTOP();
}
}
}
void ForWard()
{
digitalWrite(lm1,HIGH);
digitalWrite(lm2,LOW);
digitalWrite(rm1,HIGH);
digitalWrite(rm2,LOW);
}
void BackWard()
{
digitalWrite(lm1,LOW);
digitalWrite(lm2,HIGH);
digitalWrite(rm1,LOW);
digitalWrite(rm2,HIGH);
}
void Left()
{
digitalWrite(lm1,LOW);
digitalWrite(lm2,HIGH);
digitalWrite(rm1,HIGH);
digitalWrite(rm2,LOW);
}
void Right()
{
digitalWrite(lm1,HIGH);
digitalWrite(lm2,LOW);
digitalWrite(rm1,LOW);
digitalWrite(rm2,HIGH);
}
void sTOP()
{
digitalWrite(lm1,LOW);
digitalWrite(lm2,LOW);
digitalWrite(rm1,LOW);
digitalWrite(rm2,LOW);
}
对远程单元执行相同的操作。通过远程名称打开程序。并将其上传到远程单元。
//program modified on 3/10/19 by // by Shubham Shinganapure.
//
//for Gesture controled Robotic Car (remote )
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
//#include "MPU6050.h" // not necessary if using MotionApps include file
// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 mpu;
#define OUTPUT_READABLE_YAWPITCHROLL
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
VectorFloat gravity;
Quaternion q;
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(10, 11); // RX | TX
int bt=8;
int x =1;
void setup() {
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
// initialize serial communication
// (115200 chosen because it is required for Teapot Demo output, but it's
// really up to you depending on your project)
Serial.begin(115200);
BTSerial.begin(38400);
// while (!Serial); // wait for Leonardo enumeration, others continue immediately
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// wait for ready
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788);
// make sure it worked (returns 0 if so)
if (devStatus == 0) {
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
} else {
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
// configure LED for output
pinMode(bt,INPUT);
}
// ================================================================
// === MAIN PROGRAM LOOP ===
// ================================================================
void loop() {
if(digitalRead(bt)==HIGH)
{
x++;
delay(150);
}
if((x%2)==0){
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize) {
// other program behavior stuff here
// .
// .
// .
// if you are really paranoid you can frequently test in between other
// stuff to see if mpuInterrupt is true, and if so, "break;" from the
// while() loop to immediately process the MPU data
// .
// .
// .
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & 0x02) {
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
#ifdef OUTPUT_READABLE_YAWPITCHROLL
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
if((ypr[1] * 180/M_PI)<= -25)
{BTSerial.write('F');
}
else if((ypr[1] * 180/M_PI)>= 25)
{BTSerial.write('B');
}
else if((ypr[2] * 180/M_PI)<= -25)
{BTSerial.write('L');
}
else if((ypr[2] * 180/M_PI)>= 20)
{BTSerial.write('R');
}
else{
BTSerial.write('S');
}
#endif
}
}
else{
BTSerial.write('S');
}
}
将从属蓝牙模块插入汽车单元,并在远程单元上主控蓝牙模块。一切都完成了。
让我们打开电源,即可开始……。
步骤四 编写程序
//program modified on 3/10/19 by // by Shubham Shinganapure.
//
//for Gesture controled Robotic Car (remote )
#include "I2Cdev.h"
#include "MPU6050_6Axis_MotionApps20.h"
//#include "MPU6050.h" // not necessary if using MotionApps include file
// Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation
// is used in I2Cdev.h
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
#include "Wire.h"
#endif
// class default I2C address is 0x68
// specific I2C addresses may be passed as a parameter here
// AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board)
// AD0 high = 0x69
MPU6050 mpu;
#define OUTPUT_READABLE_YAWPITCHROLL
// MPU control/status vars
bool dmpReady = false; // set true if DMP init was successful
uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU
uint8_t devStatus; // return status after each device operation (0 = success, !0 = error)
uint16_t packetSize; // expected DMP packet size (default is 42 bytes)
uint16_t fifoCount; // count of all bytes currently in FIFO
uint8_t fifoBuffer[64]; // FIFO storage buffer
VectorFloat gravity;
Quaternion q;
float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector
uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' };
volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high
void dmpDataReady() {
mpuInterrupt = true;
}
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(10, 11); // RX | TX
int bt=8;
int x =1;
void setup() {
#if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE
Wire.begin();
TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz)
#elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE
Fastwire::setup(400, true);
#endif
// initialize serial communication
// (115200 chosen because it is required for Teapot Demo output, but it's
// really up to you depending on your project)
Serial.begin(115200);
BTSerial.begin(38400);
// while (!Serial); // wait for Leonardo enumeration, others continue immediately
Serial.println(F("Initializing I2C devices..."));
mpu.initialize();
// verify connection
Serial.println(F("Testing device connections..."));
Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed"));
// wait for ready
// load and configure the DMP
Serial.println(F("Initializing DMP..."));
devStatus = mpu.dmpInitialize();
// supply your own gyro offsets here, scaled for min sensitivity
mpu.setXGyroOffset(220);
mpu.setYGyroOffset(76);
mpu.setZGyroOffset(-85);
mpu.setZAccelOffset(1788);
// make sure it worked (returns 0 if so)
if (devStatus == 0) {
// turn on the DMP, now that it's ready
Serial.println(F("Enabling DMP..."));
mpu.setDMPEnabled(true);
// enable Arduino interrupt detection
Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)..."));
attachInterrupt(0, dmpDataReady, RISING);
mpuIntStatus = mpu.getIntStatus();
// set our DMP Ready flag so the main loop() function knows it's okay to use it
Serial.println(F("DMP ready! Waiting for first interrupt..."));
dmpReady = true;
// get expected DMP packet size for later comparison
packetSize = mpu.dmpGetFIFOPacketSize();
} else {
// ERROR!
// 1 = initial memory load failed
// 2 = DMP configuration updates failed
// (if it's going to break, usually the code will be 1)
Serial.print(F("DMP Initialization failed (code "));
Serial.print(devStatus);
Serial.println(F(")"));
}
// configure LED for output
pinMode(bt,INPUT);
}
// ================================================================
// === MAIN PROGRAM LOOP ===
// ================================================================
void loop() {
if(digitalRead(bt)==HIGH)
{
x++;
delay(150);
}
if((x%2)==0){
// if programming failed, don't try to do anything
if (!dmpReady) return;
// wait for MPU interrupt or extra packet(s) available
while (!mpuInterrupt && fifoCount < packetSize) {
// other program behavior stuff here
// .
// .
// .
// if you are really paranoid you can frequently test in between other
// stuff to see if mpuInterrupt is true, and if so, "break;" from the
// while() loop to immediately process the MPU data
// .
// .
// .
}
// reset interrupt flag and get INT_STATUS byte
mpuInterrupt = false;
mpuIntStatus = mpu.getIntStatus();
// get current FIFO count
fifoCount = mpu.getFIFOCount();
// check for overflow (this should never happen unless our code is too inefficient)
if ((mpuIntStatus & 0x10) || fifoCount == 1024) {
// reset so we can continue cleanly
mpu.resetFIFO();
Serial.println(F("FIFO overflow!"));
// otherwise, check for DMP data ready interrupt (this should happen frequently)
} else if (mpuIntStatus & 0x02) {
// wait for correct available data length, should be a VERY short wait
while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount();
// read a packet from FIFO
mpu.getFIFOBytes(fifoBuffer, packetSize);
// track FIFO count here in case there is > 1 packet available
// (this lets us immediately read more without waiting for an interrupt)
fifoCount -= packetSize;
#ifdef OUTPUT_READABLE_YAWPITCHROLL
// display Euler angles in degrees
mpu.dmpGetQuaternion(&q, fifoBuffer);
mpu.dmpGetGravity(&gravity, &q);
mpu.dmpGetYawPitchRoll(ypr, &q, &gravity);
Serial.print("ypr\t");
Serial.print(ypr[0] * 180/M_PI);
Serial.print("\t");
Serial.print(ypr[1] * 180/M_PI);
Serial.print("\t");
Serial.println(ypr[2] * 180/M_PI);
if((ypr[1] * 180/M_PI)<= -25)
{BTSerial.write('F');
}
else if((ypr[1] * 180/M_PI)>= 25)
{BTSerial.write('B');
}
else if((ypr[2] * 180/M_PI)<= -25)
{BTSerial.write('L');
}
else if((ypr[2] * 180/M_PI)>= 20)
{BTSerial.write('R');
}
else{
BTSerial.write('S');
}
#endif
}
}
else{
BTSerial.write('S');
}
}
步骤五 验证结果
接下来我们就可以看到小车可以跟随手势进行移动了。