2019-11-19 12:51:31 weixin_42958308 阅读数 69
  • arduino实战

    学习如何在arduino中使用各种传感器,包括人体红外传感器,超声波传感器,舵机控制,温湿度传感器,激光接收传感器等,空气质量传感器,wifi模块等....课程内容会不断的更新增加,只要发现比较有趣的传感器就会做对应的实战视频

    405 人正在学习 去看看 陈贤能

arduino四轮小车程序前篇

前言

利用arduino控制四轮小车我感觉很方便,其中一个原因是arduino公开程序代码较多,二是看stm32编程直接把我劝退了,利用ros也可以,但我感觉有点杀鸡用牛刀。不过在网上看了很多篇关于arduino控制四轮小车的方案,多多少少很杂、摸不到头脑,因此梳理了思路,编了一边程序,摸到一点门道。在这里的程序以rikirobot控制四轮小车的程序为模板,并对该模板进行些许改变。

程序流程图

根据rikirobot的ros代码,我梳理了一下流程图,其如下所示:
在这里插入图片描述
分析这个流程图可以发现这个代码写的比我这个菜鸟强太多了,我通常的编程思路只会顺序编程,也就是执行完上一行程序就执行下一行程序,根本也不会考虑程序之间时间间隔问题,顶多就会在代码里面添加delay()函数假装控制时间,而这个程序很好的控制了相同功能程序之间的时间间隔,避免了执行过程中无用代码的执行,同时也让代码更加简洁清楚
其中需要替换的是ros程序中在监听command命令时触发的回调函数和之后发布的电机转速、imu姿态等话题信息。除此之外,里面的一些库可以进行些许改动后使用。因此更改后的流程图如下所示:
在这里插入图片描述
其中,while(Serial.available())代表串口命令接收功能,再接收到命令后,还需对其进行拆解和转化,具体参见上一篇博客arduino字符串命令接收与拆解

程序代码

最终程序如下所示:

#include <JY901.h>
#include <Wire.h>
#include "config.h"
#include "Encoder.h"
#include "Motor.h"
#include "Kinematics.h"
#include "PID.h"

#define ENCODER_OPTIMIZE_INTERRUPTS

//left side motors
Motor motor1(MOTOR1_PWM, MOTOR1_IN_A, MOTOR1_IN_B); //front
Motor motor3(MOTOR3_PWM, MOTOR3_IN_A, MOTOR3_IN_B);// rear
//right side motors
Motor motor2(MOTOR2_PWM, MOTOR2_IN_A, MOTOR2_IN_B); // front
Motor motor4(MOTOR4_PWM, MOTOR4_IN_A, MOTOR4_IN_B); // rear

//COUNTS_PER_REV = 0 if no encoder
int Motor::counts_per_rev_ = COUNTS_PER_REV;

//left side encoders
Encoder motor1_encoder(MOTOR1_ENCODER_A,MOTOR1_ENCODER_B); //front
Encoder motor3_encoder(MOTOR3_ENCODER_A,MOTOR3_ENCODER_B); //rear

//right side encoders
Encoder motor2_encoder(MOTOR2_ENCODER_A,MOTOR2_ENCODER_B); //front
Encoder motor4_encoder(MOTOR4_ENCODER_A,MOTOR4_ENCODER_B); //rear

Kinematics kinematics(MAX_RPM, WHEEL_DIAMETER, BASE_WIDTH, PWM_BITS);

PID motor1_pid(-255, 255, K_P, K_I, K_D);
PID motor2_pid(-255, 255, K_P, K_I, K_D);
PID motor3_pid(-255, 255, K_P, K_I, K_D);
PID motor4_pid(-255, 255, K_P, K_I, K_D);

double g_req_angular_vel_z = 0;
double g_req_linear_vel_x = 0;
double g_req_linear_vel_y = 0;

unsigned long g_prev_command_time = 0;
unsigned long g_prev_control_time = 0;
unsigned long g_publish_vel_time = 0;
unsigned long g_prev_imu_time = 0;
unsigned long g_prev_debug_time = 0;

bool g_is_first = true;

char g_buffer[50];
String command = "";
//  int x_int;
//  int y_int;
//  int z_int;
void setup()
{
  Wire.begin();
  delay(5);
  Serial3.begin(115200);
  //Serial2.begin(115200);
  Serial2.begin(115200);
  Serial.begin(115200);
}

void loop()
{
  static bool imu_is_initialized;
  //this block drives the robot based on defined rate

  while(Serial2.available()>0)
  {
      command +=char(Serial2.read());    
//    g_req_linear_vel_x = cmd_msg.linear.x;
//    g_req_linear_vel_y = cmd_msg.linear.y;
//    g_req_angular_vel_z = cmd_msg.angular.z;
      delay(2);
      g_prev_command_time = millis();
  }
  int x_placeinstring = 0;
  int y_placeinstring = 0;
  int z_placeinstring = 0;
  String x_command;
  String y_command;
  String z_command;
  int x_int;
  int y_int;
  int z_int;
  if(command.length()>0)
  {
//    Serial.print(command);
    x_placeinstring = command.indexOf('x');
    y_placeinstring = command.indexOf('y');
    z_placeinstring = command.indexOf('z');
    x_command = command.substring(x_placeinstring+1,y_placeinstring);
    y_command = command.substring(y_placeinstring+1,z_placeinstring);
    z_command = command.substring(z_placeinstring+1);
    x_int = x_command.toInt();
    y_int = y_command.toInt();
    z_int = z_command.toInt();
    g_req_linear_vel_x = x_int / 100.0;
    g_req_linear_vel_y = y_int / 100.0;
    g_req_angular_vel_z = z_int / 100.0;
//    Serial.println(x_int);
//    Serial.println(y_int);
//    Serial.println(z_int);
    command = "";   
  }


  
  if ((millis() - g_prev_control_time) >= (1000 / COMMAND_RATE)){
    moveBase();
    g_prev_control_time = millis();
  }

  //this block stops the motor when no command is received
  if ((millis() - g_prev_command_time) >= 400){
    stopBase();
  }

  //this block publishes velocity based on defined rate
  if ((millis() - g_publish_vel_time) >= (1000 / VEL_PUBLISH_RATE)){
    publishVelocities();
    g_publish_vel_time = millis();
  }

  //this block publishes the IMU data based on defined rate
  if ((millis() - g_prev_imu_time) >= (1000 / IMU_PUBLISH_RATE)){
    if (!imu_is_initialized){
      imu_is_initialized = initIMU();
      if(imu_is_initialized){
        motor1_encoder.write(0);
        motor2_encoder.write(0);
        motor3_encoder.write(0);
        motor4_encoder.write(0);
       //nh.loginfo("IMU Initialized");
       Serial.println("IMU Initialized");
      }else{
       //nh.logfatal("IMU failed to initialize. Check your IMU connection.");
       Serial.println("IMU failed to initialize. Check your IMU connection.");
      }
    }else{
      publishIMU();
    }
//
    g_prev_imu_time = millis();
  }

  //this block displays the encoder readings. change DEBUG to 0 if you don't want to display
  if(DEBUG){
    if ((millis() - g_prev_debug_time) >= (1000 / DEBUG_RATE)){
      printDebug();
      g_prev_debug_time = millis();
    }
  }

}

void moveBase()
{
  Kinematics::output req_rpm;
  //get the required rpm for each motor based on required velocities
  req_rpm = kinematics.getRPM(g_req_linear_vel_x, g_req_linear_vel_y, g_req_angular_vel_z);

  //the required rpm is capped at -/+ MAX_RPM to prevent the PID from having too much error
  //the PWM value sent to the motor driver is the calculated PID based on required RPM vs measured RPM
  motor1.spin(motor1_pid.compute(constrain(req_rpm.motor1, -MAX_RPM, MAX_RPM), motor1.rpm));
  motor3.spin(motor3_pid.compute(constrain(req_rpm.motor3, -MAX_RPM, MAX_RPM), motor3.rpm));
  motor2.spin(motor2_pid.compute(constrain(req_rpm.motor2, -MAX_RPM, MAX_RPM), motor2.rpm));
  motor4.spin(motor4_pid.compute(constrain(req_rpm.motor4, -MAX_RPM, MAX_RPM), motor4.rpm));
//  Serial.print("x:");Serial.println(g_req_linear_vel_x);
//  Serial.print("y:");Serial.println(g_req_linear_vel_y);
//  Serial.print("z:");Serial.println(g_req_angular_vel_z);
  Serial.print("Encoder1:");Serial.print(req_rpm.motor1);Serial.print("\t");Serial.println(motor1.rpm);
  Serial.print("Encoder2:");Serial.print(req_rpm.motor2);Serial.print("\t");Serial.println(motor2.rpm);
  Serial.print("Encoder3:");Serial.print(req_rpm.motor3);Serial.print("\t");Serial.println(motor3.rpm);
  Serial.print("Encoder4:");Serial.print(req_rpm.motor4);Serial.print("\t");Serial.println(motor4.rpm);
}

void stopBase()
{
  g_req_linear_vel_x = 0.0;
  g_req_linear_vel_y = 0.0;
  g_req_angular_vel_z = 0.0;
}

void publishVelocities()
{
  //update the current speed of each motor based on encoder's count
  motor1.updateSpeed(motor1_encoder.read());
  motor2.updateSpeed(motor2_encoder.read());
  motor3.updateSpeed(motor3_encoder.read());
  motor4.updateSpeed(motor4_encoder.read());

  Kinematics::velocities vel;
  vel = kinematics.getVelocities(motor1.rpm, motor2.rpm, motor3.rpm, motor4.rpm);
//  Serial.print("Encoder1:");Serial.print(req_rpm.motor1);Serial.print("\t");Serial.println(motor1.rpm);
//  Serial.print("Encoder2:");Serial.print(req_rpm.motor2);Serial.print("\t");Serial.println(motor2.rpm);
//  Serial.print("Encoder3:");Serial.print(req_rpm.motor3);Serial.print("\t");Serial.println(motor3.rpm);
//  Serial.print("Encoder4:");Serial.print(req_rpm.motor4);Serial.print("\t");Serial.println(motor4.rpm);
//  Serial.print("velocities is:");Serial.print(vel.linear_x);Serial.print("\t");Serial.print(vel.linear_y);Serial.print("\t");Serial.print(vel.angular_z);Serial.println("\t");

  //fill in the object
  //raw_vel_msg.linear_x = vel.linear_x;
  //raw_vel_msg.linear_y = vel.linear_y;
  //raw_vel_msg.angular_z = vel.angular_z;

  //publish raw_vel_msg object to ROS
  //raw_vel_pub.publish(&raw_vel_msg);
}

bool initIMU()
{
    bool accel, gyro, mag;
    accel = initAccelerometer();
    gyro = initGyroscope();
    mag = initMagnetometer();

    if(accel && gyro && mag)
        return true;
    
    else
        return false;
}

bool initAccelerometer()
{
    delay(1000);
    return true;
}

bool initGyroscope()
{
    delay(1000);
    return true;
}

bool initMagnetometer()
{
    delay(1000);
    return true;
}
struct msg
{
  float linear_acceleration[3];
  float angular_velocity[3];   
  float angular[3];
};
void publishIMU()
{
//  struct msg
//  {
//    float linear_acceleration[3];
//    float angular_velocity[3];
//    float magnetic_field[3];
//  }
  
  msg raw_imu_msg;
  raw_imu_msg.linear_acceleration[0] = (float) JY901.stcAcc.a[0]/32768*16;
  raw_imu_msg.linear_acceleration[1] = (float) JY901.stcAcc.a[1]/32768*16;
  raw_imu_msg.linear_acceleration[2] = (float) JY901.stcAcc.a[2]/32768*16;

  raw_imu_msg.angular_velocity[0] = (float) JY901.stcGyro.w[0]/32768*2000;
  raw_imu_msg.angular_velocity[1] = (float) JY901.stcGyro.w[1]/32768*2000;
  raw_imu_msg.angular_velocity[2] = (float) JY901.stcGyro.w[2]/32768*2000;
  
  raw_imu_msg.angular[0] = (float) JY901.stcAngle.Angle[0]/32768*180;
  raw_imu_msg.angular[1] = (float) JY901.stcAngle.Angle[1]/32768*180;
  raw_imu_msg.angular[2] = (float) JY901.stcAngle.Angle[2]/32768*180;
  //Serial.print("acceleration is:");Serial.print(raw_imu_msg.linear_acceleration[0]);Serial.print("\t");Serial.print(raw_imu_msg.linear_acceleration[1]);Serial.print("\t");Serial.print(raw_imu_msg.linear_acceleration[2]);Serial.println();
  //Serial.print("angular_velocity is:");Serial.print(raw_imu_msg.angular_velocity[0]);Serial.print("\t");Serial.print(raw_imu_msg.angular_velocity[1]);Serial.print("\t");Serial.print(raw_imu_msg.angular_velocity[2]);Serial.println();
  //Serial.print("angular is:");Serial.print(raw_imu_msg.angular[0]);Serial.print("\t");Serial.print(raw_imu_msg.angular[1]);Serial.print("\t");Serial.print(raw_imu_msg.angular[2]);Serial.println();

  while (Serial3.available()) 
  {
    JY901.CopeSerialData(Serial3.read()); //Call JY901 data cope function
  }
  //pass accelerometer data to imu object
  //raw_imu_msg.linear_acceleration = readAccelerometer();

  //pass gyroscope data to imu object
  //raw_imu_msg.angular_velocity = readGyroscope();

  //pass accelerometer data to imu object
  //raw_imu_msg.magnetic_field = readMagnetometer();

  //publish raw_imu_msg object to ROS
  //raw_imu_pub.publish(&raw_imu_msg);
}

void printDebug()
{
  sprintf (g_buffer, "Encoder FrontLeft: %ld", motor1_encoder.read());
//  nh.loginfo(g_buffer);
  sprintf (g_buffer, "Encoder RearLeft: %ld", motor3_encoder.read());
//  nh.loginfo(g_buffer);
  sprintf (g_buffer, "Encoder FrontRight: %ld", motor2_encoder.read());
//  nh.loginfo(g_buffer);
  sprintf (g_buffer, "Encoder RearRight: %ld", motor4_encoder.read());
//  nh.loginfo(g_buffer);
//  Serial.print("Encoder1:");Serial.println(motor1_encoder.read());
//  Serial.print("Encoder2:");Serial.println(motor2_encoder.read());
//  Serial.print("Encoder3:");Serial.println(motor3_encoder.read());
//  Serial.print("Encoder4:");Serial.println(motor4_encoder.read());
}

完整代码包我回头再看看哪里可以上传arduino四轮小车代码
github代码

实验结果

arduino程序调试视频

实验材料

Arduino Mega2560
双路15A大功率电机驱动 - Lite
维特智能陀螺仪WT901C

参考资料

[1]: https://item.taobao.com/item.htm?spm=a1z09.2.0.0.321d2e8dAGa8c8&id=42861201221&_u=j1mvvlo140d8
(里面有WT901C的资料链接)
[2]:http://wiki.dfrobot.com.cn/index.php/(SKU:DRI0018)%E5%8F%8C%E8%B7%AF15A%E5%A4%A7%E5%8A%9F%E7%8E%87%E7%94%B5%E6%9C%BA%E9%A9%B1%E5%8A%A8_Lite

2013-03-17 21:03:08 iteye_11969 阅读数 348
  • arduino实战

    学习如何在arduino中使用各种传感器,包括人体红外传感器,超声波传感器,舵机控制,温湿度传感器,激光接收传感器等,空气质量传感器,wifi模块等....课程内容会不断的更新增加,只要发现比较有趣的传感器就会做对应的实战视频

    405 人正在学习 去看看 陈贤能

 基于android和arduino 的小车控制,小车部分采用的是 arduino 开源硬件实现,arduino 学起来比较简单,看看文档在看几个例子基本就会了。官网是http://arduino.cc/

我是在某宝上购买的一组套件,最终发现很多都没有用上,有点眼高手低。android 和 arduino 通信 是通过蓝牙。小车控制比较简单,前、后、左、右、向左转圈、向右转圈。

以下是源码;

1:arduino 源码:

 

 

#include <BaseCar.h>


// log
const boolean VERBOSE = true;
const int SERIAL_SPEED = 9600;

// for Car
//1-ok
const int MOTO1_PIN = 2;
const int I11_PIN = 3;
const int I12_PIN = 4;
//2
const int MOTO2_PIN = 5;
const int I21_PIN = 6;
const int I22_PIN = 7;
//3-ok
const int MOTO3_PIN = 8;
const int I31_PIN = 9;
const int I32_PIN =10;
//4
const int MOTO4_PIN = 11;
const int I41_PIN = 12;
const int I42_PIN = 13;

 
// speed
const int SPEED_VAL =500;

// delay
const int DELAY_TIME = 300;

//bouttooth
 char RECEIVE_VALUE='q_z_1';
// car control
BaseCar car(MOTO1_PIN, MOTO2_PIN, MOTO3_PIN, MOTO4_PIN,I11_PIN, I12_PIN, I21_PIN, I22_PIN,I31_PIN, I32_PIN, I41_PIN, I42_PIN);

const int adj = 19;
 
void setup() {
  if (VERBOSE) {
    Serial.begin(SERIAL_SPEED);
  }
}

void loop() {
  // RECEIVE_VALUE=Serial.read();
  //Serial.println(RECEIVE_VALUE);
   if(RECEIVE_VALUE=='q_z_1'){   
      // 左前一度
      car.forward(SPEED_VAL, adj);
   }else if(RECEIVE_VALUE=='q_z_2'){
   // 左前二度
      car.turnLeft(SPEED_VAL, adj);
   }else if(RECEIVE_VALUE=='q_z_3'){
   // 左前三度-向左旋转
      car.rotateLeft(SPEED_VAL, adj);
   }else if(RECEIVE_VALUE=='q_y_1'){
   // 右前一度
      car.forward(SPEED_VAL, adj);
   }else if(RECEIVE_VALUE=='q_y_2'){
   // 右前二度
      car.turnRight(SPEED_VAL, adj);
   }else if(RECEIVE_VALUE=='q_y_3'){
   // 右前三度-向右旋转
      car.rotateRight(SPEED_VAL, adj);
   }else{
   // 刹车
     car.standBy();
   }
}






 

1.1 将 以下两个类放在libiriary 下 文件夹名命名为 car


#include "Arduino.h"
#include "BaseCar.h"

BaseCar::BaseCar(int left_back_speed, int left_forward_speed,int right_forward_speed, int right_back_speed, int left_back_1, int left_back_2, int left_forward_1, int left_forward_2,int right_forward_1, int right_forward_2,int right_back_1, int right_back_2){

    _left_back_speed = left_back_speed;
    _left_forward_speed = left_forward_speed;
	_right_forward_speed = right_forward_speed;
	_right_back_speed = right_back_speed;

    pinMode(left_back_speed, OUTPUT);
    pinMode(left_forward_speed, OUTPUT);
	pinMode(right_forward_speed, OUTPUT);
    pinMode(right_back_speed, OUTPUT);

    _left_back_1 = left_back_1;
    _left_back_2 = left_back_2;
    _left_forward_1 = left_forward_1;
    _left_forward_2 = left_forward_2;
	_right_forward_1 = right_forward_1;
    _right_forward_2 = right_forward_2;
    _right_back_1 = right_back_1;
    _right_back_2 = right_back_2;

    pinMode(left_back_1, OUTPUT);
    pinMode(left_back_2, OUTPUT);
    pinMode(left_forward_1, OUTPUT);
    pinMode(left_forward_2, OUTPUT);
	pinMode(right_forward_1, OUTPUT);
    pinMode(right_forward_2, OUTPUT);
    pinMode(right_back_1, OUTPUT);
    pinMode(right_back_2, OUTPUT);
}
//停止
void BaseCar::standBy() {
    _status.left_back_speed_h = 0;
    _status.left_forward_speed_h = 0;
	_status.right_forward_speed_h = 0;
    _status.right_back_speed_h = 0;
    _status.left_back_1_h = HIGH;
    _status.left_back_2_h = HIGH;
    _status.left_forward_1_h = HIGH;
    _status.left_forward_2_h = HIGH;
	//
	_status.right_forward_1_h = HIGH;
    _status.right_forward_2_h = HIGH;
    _status.right_back_1_h = HIGH;
    _status.right_back_2_h = HIGH;
    _go();
}
//向前
void BaseCar::forward(int moto_speed, int adj) {
    _status.left_back_speed_h = moto_speed + adj;
    _status.left_forward_speed_h = moto_speed + adj;
	_status.right_forward_speed_h = moto_speed + adj;
    _status.right_back_speed_h = moto_speed + adj;

    _status.left_back_1_h = LOW;
    _status.left_back_2_h = HIGH;

    _status.left_forward_1_h = LOW;
    _status.left_forward_2_h = HIGH;
	 
    _status.right_forward_1_h = LOW;
    _status.right_forward_2_h = HIGH;

    _status.right_back_1_h = LOW;
    _status.right_back_2_h = HIGH;
    _status.adj = adj;
    _go();
}
//向后
void BaseCar::backward(int moto_speed, int adj) {
    _status.left_back_speed_h = moto_speed + adj;
    _status.left_forward_speed_h = moto_speed + adj;
	_status.right_forward_speed_h = moto_speed + adj;
    _status.right_back_speed_h = moto_speed + adj;

    _status.left_back_1_h = HIGH;
    _status.left_back_2_h = LOW;

    _status.left_forward_1_h = HIGH;
    _status.left_forward_2_h = LOW;
	 
	_status.right_forward_1_h = HIGH;
    _status.right_forward_2_h = LOW;

    _status.right_back_1_h = HIGH;
    _status.right_back_2_h = LOW;

    _status.adj = adj;
    _go();
}
//左转
void BaseCar::turnLeft(int moto_speed, int adj) {
    _status.left_back_speed_h = moto_speed - adj;
    _status.left_forward_speed_h = moto_speed - adj;

	_status.right_forward_speed_h = moto_speed + adj;
    _status.right_back_speed_h = moto_speed + adj;

    _status.left_back_1_h = LOW;
    _status.left_back_2_h = HIGH;

    _status.left_forward_1_h = LOW;
    _status.left_forward_2_h = HIGH;

	_status.right_forward_1_h = LOW;
    _status.right_forward_2_h = HIGH;

    _status.right_back_1_h = LOW;
    _status.right_back_2_h = HIGH;
    _status.adj = adj;
    _go();
}
//右转
void BaseCar::turnRight(int moto_speed, int adj) {
    _status.left_back_speed_h = moto_speed + adj;
    _status.left_forward_speed_h = moto_speed + adj;

	_status.right_forward_speed_h = moto_speed - adj;
    _status.right_back_speed_h = moto_speed - adj;

    _status.left_back_1_h = LOW;
    _status.left_back_2_h = HIGH;

    _status.left_forward_1_h = LOW;
    _status.left_forward_2_h = HIGH;

	_status.right_forward_1_h = LOW;
    _status.right_forward_2_h = HIGH;

    _status.right_back_1_h = LOW;
    _status.right_back_2_h = HIGH;
    _status.adj = adj;
    _go();
}
//向左旋转(右边向前转,左边向后转)
void BaseCar::rotateLeft(int moto_speed, int adj) {
    _status.left_back_speed_h = moto_speed + adj;
    _status.left_forward_speed_h = moto_speed + adj;

	_status.right_forward_speed_h = moto_speed + adj;
    _status.right_back_speed_h = moto_speed + adj;

    _status.left_back_1_h = HIGH;
    _status.left_back_2_h = LOW;

    _status.left_forward_1_h = HIGH;
    _status.left_forward_2_h = LOW;

	_status.right_forward_1_h = LOW;
    _status.right_forward_2_h = HIGH;

    _status.right_back_1_h = LOW;
    _status.right_back_2_h = HIGH;
    _status.adj = adj;
    _go();
}
//向右旋转
void BaseCar::rotateRight(int moto_speed, int adj) {
    _status.left_back_speed_h = moto_speed + adj;
    _status.left_forward_speed_h = moto_speed + adj;

	_status.right_forward_speed_h = moto_speed + adj;
    _status.right_back_speed_h = moto_speed + adj;

    _status.left_back_1_h = LOW;
    _status.left_back_2_h = HIGH;

    _status.left_forward_1_h = LOW;
    _status.left_forward_2_h = HIGH;

	_status.right_forward_1_h = HIGH;
    _status.right_forward_2_h = LOW;

    _status.right_back_1_h = HIGH;
    _status.right_back_2_h = LOW;
    _status.adj = adj;
    _go();
}

void BaseCar::_go() {

    digitalWrite(_left_back_1, _status.left_back_1_h);
    digitalWrite(_left_back_2, _status.left_back_2_h);
    digitalWrite(_left_forward_1, _status.left_forward_1_h);
    digitalWrite(_left_forward_2, _status.left_forward_2_h);

    digitalWrite(_right_forward_1, _status.right_forward_1_h);
    digitalWrite(_right_forward_2, _status.right_forward_2_h);
    digitalWrite(_right_back_1, _status.right_back_1_h);
    digitalWrite(_right_back_2, _status.right_back_2_h);

    analogWrite(_left_back_speed, _status.left_back_speed_h);
    analogWrite(_left_forward_speed, _status.left_forward_speed_h);
	analogWrite(_right_forward_speed, _status.right_forward_speed_h);
    analogWrite(_right_back_speed, _status.right_back_speed_h);
}

CarStatus BaseCar::getStatus() {
    return _status;
}

 
#ifndef BaseCar_h
#define BaseCar_h
#include "Arduino.h"

struct CarStatus {
	 
    int left_back_1_h;
    int left_back_2_h;
    int left_forward_1_h;
    int left_forward_2_h;
	 
	int right_forward_1_h;
    int right_forward_2_h;
	int right_back_1_h;
    int right_back_2_h;
    int left_back_speed_h;
    int left_forward_speed_h;
    int right_forward_speed_h;
    int right_back_speed_h;
    int adj;
};

class BaseCar {
    public:
        BaseCar(int left_back_speed, int left_forward_speed,int right_forward_speed, int right_back_speed, int left_back_1, int left_back_2, int left_forward_1, int left_forward_2,int right_forward_1, int right_forward_2,int right_back_1, int right_back_2);
        void standBy();
        void forward(int moto_speed, int adj);
        void backward(int moto_speed, int adj);
        void turnLeft(int moto_speed, int adj);
        void turnRight(int moto_speed, int adj);
        void rotateLeft(int moto_speed, int adj);
        void rotateRight(int moto_speed, int adj);
        CarStatus getStatus();
    private:
        int _left_back_speed;
        int _left_forward_speed;
        int _right_forward_speed;
        int _right_back_speed;

        int _left_back_1;
        int _left_back_2;
        int _left_forward_1;
        int _left_forward_2;
		int _right_forward_1;
        int _right_forward_2;
        int _right_back_1;
        int _right_back_2;
        CarStatus _status;
        void _go();
};
#endif
  
 

 

2:android 源码如下:

package com.robot;

import java.io.IOException;
import java.io.OutputStream;
import java.util.UUID;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

public class AAActivity extends Activity {
	private TextView state;
	private ImageView forward;
	private ImageView back;
	private ImageView left_2;
	private ImageView right_2;
	boolean isStart = false;
	private static final UUID MY_UUID = UUID
			.fromString("00001101-0000-1000-8000-00805F9B34FB");
	private static String address = "00:12:02:06:01:32";
	private static final String TAG = "THINBTCLIENT";
	private static final boolean D = true;
	private BluetoothAdapter mBluetoothAdapter = null;
	private BluetoothSocket btSocket = null;
	private OutputStream outStream = null;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.index);
		initUI();
	}

	@Override
	protected void onStart() {
		super.onStart();
	}

	public void setValue(String message, final String text) {
		if (isStart) {
			state.setText(text);
			byte[] msgBuffer;
			try {
				outStream = btSocket.getOutputStream();
			} catch (IOException e) {
				Log.e(TAG, "ON RESUME: Output stream creation failed.", e);
			}
			msgBuffer = message.getBytes();
			try {
				outStream.write(msgBuffer);
			} catch (IOException e) {
				Log.e(TAG, "ON RESUME: Exception during write.", e);
			}
		} else {
			Toast.makeText(this, "请先选择开启!", Toast.LENGTH_SHORT).show();
		}
	}

	private void initUI() {
		state = (TextView) findViewById(R.id.state);
		CheckBox blueT = (CheckBox) findViewById(R.id.blueT);
		blueT.setOnCheckedChangeListener(new OnCheckedChangeListener() {
			public void onCheckedChanged(CompoundButton buttonView,
					boolean isChecked) {
				isStart = isChecked;
			}
		});
		forward = (ImageView) findViewById(R.id.forward);
		forward.setImageResource(R.drawable.forward);
		forward.setOnTouchListener(new OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				if (event.getAction() == MotionEvent.ACTION_DOWN) {
					forward.setImageResource(R.drawable.forward_r);
					setValue("1", "直走");
				}
				if (event.getAction() == MotionEvent.ACTION_UP) {
					forward.setImageResource(R.drawable.forward);
					setValue("7", "刹车");
				}
				return true;
			}
		});

		back = (ImageView) findViewById(R.id.back);
		back.setImageResource(R.drawable.back);
		back.setOnTouchListener(new OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				if (event.getAction() == MotionEvent.ACTION_DOWN) {
					back.setImageResource(R.drawable.back_r);
					setValue("6", "后退");
				}
				if (event.getAction() == MotionEvent.ACTION_UP) {
					back.setImageResource(R.drawable.back);
					setValue("7", "刹车");
				}
				return true;
			}
		});

		left_2 = (ImageView) findViewById(R.id.left_2);
		left_2.setImageResource(R.drawable.left_2);
		left_2.setOnTouchListener(new OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				if (event.getAction() == MotionEvent.ACTION_DOWN) {
					left_2.setImageResource(R.drawable.left_2_r);
					setValue("3", "左前二度");
				}
				if (event.getAction() == MotionEvent.ACTION_UP) {
					left_2.setImageResource(R.drawable.left_2);
					setValue("7", "刹车");
				}
				return true;
			}
		});

		right_2 = (ImageView) findViewById(R.id.right_2);
		right_2.setImageResource(R.drawable.right_2);
		right_2.setOnTouchListener(new OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				if (event.getAction() == MotionEvent.ACTION_DOWN) {
					right_2.setImageResource(R.drawable.right_2_r);
					setValue("5", "右前二度");
				}
				if (event.getAction() == MotionEvent.ACTION_UP) {
					right_2.setImageResource(R.drawable.right_2);
					setValue("7", "刹车");
				}
				return true;
			}
		});

		if (D)
			Log.e(TAG, "+++ ON CREATE +++");
		mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
		if (mBluetoothAdapter == null) {
			Toast.makeText(this, "蓝牙设备不可用,请打开蓝牙!", Toast.LENGTH_LONG).show();
			finish();
			return;
		}
		if (!mBluetoothAdapter.isEnabled()) {
			Toast.makeText(this, "请打开蓝牙并重新运行程序!", Toast.LENGTH_LONG).show();
			finish();
			return;
		}
		if (D)
			Log.e(TAG, "+++ DONE IN ON CREATE, GOT LOCAL BT ADAPTER +++");
	}

	public void onPause() {
		if (getRequestedOrientation() != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
			setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
		}
		super.onPause();
		if (D)
			Log.e(TAG, "- ON PAUSE -");
		if (outStream != null) {
			try {
				outStream.flush();
			} catch (IOException e) {
				Log.e(TAG, "ON PAUSE: Couldn't flush output stream.", e);
			}
		}
		try {
			btSocket.close();
		} catch (IOException e2) {
			DisplayToast("套接字关闭失败!");
		}
	}

	public void onStop() {
		super.onStop();
		if (D)
			Log.e(TAG, "-- ON STOP --");
	}

	public void onDestroy() {
		super.onDestroy();
		if (D)
			Log.e(TAG, "--- ON DESTROY ---");
	}

	public void DisplayToast(String str) {
		Toast toast = Toast.makeText(this, str, Toast.LENGTH_LONG);
		toast.setGravity(Gravity.TOP, 0, 220);
		toast.show();
	}

	public void onResume() {
		super.onResume();
		if (D) {
			Log.e(TAG, "+ ON RESUME +");
			Log.e(TAG, "+ ABOUT TO ATTEMPT CLIENT CONNECT +");

		}
		DisplayToast("正在尝试连接智能小车,请稍后····");
		BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
		try {
			btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
		} catch (IOException e) {
			e.printStackTrace();
			DisplayToast("套接字创建失败!");
		}
		DisplayToast("成功连接智能小车!可以开始操控了~~~");
		mBluetoothAdapter.cancelDiscovery();
		try {
			btSocket.connect();
			DisplayToast("连接成功建立,数据连接打开!");
		} catch (IOException e) {
			try {
				btSocket.close();
			} catch (IOException e2) {
				DisplayToast("连接没有建立,无法关闭套接字!");
			}
		}
		if (D)
			Log.e(TAG, "+ ABOUT TO SAY SOMETHING TO SERVER +");

	}
}

 

 

 

 

 

 

 

2019-11-30 15:44:28 dark_cy 阅读数 171
  • arduino实战

    学习如何在arduino中使用各种传感器,包括人体红外传感器,超声波传感器,舵机控制,温湿度传感器,激光接收传感器等,空气质量传感器,wifi模块等....课程内容会不断的更新增加,只要发现比较有趣的传感器就会做对应的实战视频

    405 人正在学习 去看看 陈贤能

  是菜鸟的作品,实现了机械臂的控制,和小车前后左右的控制。连接 WiFi 后可实现TCP 通信。主要模块用到了一个Arduino nuo 板、一个红板、一个 TD-06 WiFi模块、四个舵机和两个小车驱动电机。

  实物图如下

在这里插入图片描述
  代码如下

#include <Servo.h>

//--------------------------------------------------舵机引脚
#define PIN_SERV7 7 //舵机号(IO口)
#define PIN_SERV8 8 //舵机号(IO口)
#define PIN_SERV9 9 //舵机号(IO口)
#define PIN_SERV10 10 //舵机号(IO口)
//--------------------------------------------------小车引脚
#define input1 3 // 定义uno的pin 3 向 input1 输出
#define input2 4 // 定义uno的pin 4 向 input2 输出----//3、4引脚控制左轮
#define input3 5 // 定义uno的pin 5 向 input3 输出
#define input4 6 // 定义uno的pin 6 向 input4 输出----//5、6引脚控制右轮
//-----------------------------------------------------------------------------------
//两驱动小车

Servo myservo_7, myservo_8, myservo_9, myservo_10;

//----------------------小车控制函数--------------------------------------------------

char Forward() //前进
{
  digitalWrite(input1, HIGH); //给高电平1
  digitalWrite(input2, LOW); //给低电平0
  digitalWrite(input3, HIGH); //给高电平1
  digitalWrite(input4, LOW); //给低电平0
  while(1)
  {
    if(Serial.available())      // 如果要选择新的模式:逆时针转,停止
    {
        char mode = Serial.read();
        Stop();
        return mode;            // 返回新的指令
    }
   }
}

char Backward() //后退
{
  digitalWrite(input1, LOW);
  digitalWrite(input2, HIGH);
  digitalWrite(input3, LOW);
  digitalWrite(input4, HIGH);
  while(1)
  {
    if(Serial.available())      // 如果要选择新的模式:逆时针转,停止
    {
        char mode = Serial.read();
        Stop();
        return mode;            // 返回新的指令
    }
   }
}

void Stop()//------------------停止
{
  digitalWrite(input1, LOW);
  digitalWrite(input2, LOW);
  digitalWrite(input3, LOW);
  digitalWrite(input4, LOW);
}

char  TurnRight()//-------------右转
{
  digitalWrite(input1, HIGH);
  digitalWrite(input2, LOW);
  digitalWrite(input3, HIGH);
  while(1)
  {
    digitalWrite(input4, LOW);
    delay(80);
    digitalWrite(input4, HIGH);
    delay(20);
    if(Serial.available())      // 如果要选择新的模式:逆时针转,停止
    {
        char mode = Serial.read();
        Stop();
        return mode;            // 返回新的指令
    }
  }
}

char TurnLeft()//--------------左转
{
  digitalWrite(input2, LOW);//左轮
  digitalWrite(input3, HIGH);//右轮
  digitalWrite(input4, LOW);
  while(1)
  {
    digitalWrite(input1, HIGH);
    delay(80);
    digitalWrite(input1, LOW);
    delay(20);
    if(Serial.available())      // 如果要选择新的模式:逆时针转,停止
    {
        char mode = Serial.read();
        Stop();
        return mode;            // 返回新的指令
    }
  }
}


//------------------------舵机控制函数-------------------------------------------------

char clockwise(Servo myservo, int stop_angle=0) //顺时针    角度减小   后 下 抓 顺
{                                               //                      2, 4, 5, 7   
    int start_angle = myservo.read();//读取舵机目前角度
    
    for(int i=start_angle; i>stop_angle; i--)
    {
        myservo.write(i);           //使舵机转动至指定角度
        //Serial.println(i);
        delay(30);
        if(Serial.available())      // 如果要选择新的模式:逆时针转,停止
        {
            char mode = Serial.read();
            return mode;            // 返回新的指令
        }
    }
    return '9';
}


char anticlockwise(Servo myservo, int stop_angle=180)//逆时针       角度增大  前 上 放 逆
{                                                    //                        1, 3, 6, 8
    int start_angle = myservo.read();         //读取舵机目前角度
    
    for(int i=start_angle; i<stop_angle; i++)
    {
        myservo.write(i); 
        //Serial.println(i);
        delay(30);
        if(Serial.available())        // 如果要选择新的模式:顺时针转,停止
        {
            char mode = Serial.read();
            return mode;
        }
    }
    return '9';
}
//-----------------------------------------------------------------------------------
void setup()
{
  Serial.begin(9600);        //初始化串口
  myservo_7.attach(PIN_SERV7);// 将Servo变量附加到引脚,将7号引脚设置为7号舵机控制端口
  myservo_8.attach(PIN_SERV8);
  myservo_9.attach(PIN_SERV9); 
  myservo_10.attach(PIN_SERV10);

  pinMode(input1, OUTPUT);//小车串口设置为输出模式
  pinMode(input2, OUTPUT);
  pinMode(input3, OUTPUT);
  pinMode(input4, OUTPUT);
  //Serial.println("Test start ");

}
//-----------------------------------------------------------------------------------
void loop()
{
    if(Serial.available())            // 等待接收控制信号(非阻塞)
    {
        char mode_9g = Serial.read(); // 读取模式选择指令
        int jump=1;
        while(1)
        {
            switch(mode_9g)
            {
                case '1':       //-----前 角度增大
              {   
                mode_9g = anticlockwise(myservo_8);// 93/110 --> 180     
                break;
              }
            case '2':       //-----后 角度减小
              {   
                mode_9g = clockwise(myservo_8, 110);// 180 --> 110      
                break;
              }
            case '3':       //-----上 角度增大
              {   
                mode_9g = anticlockwise(myservo_9);// 93/65/80 -->180
                break;
              }
            case '4':       //-----下 角度减小
              {   
                int zz=myservo_8.read();
                if (zz>115)
                  mode_9g = clockwise(myservo_9, 65);// 180 --> 65
                else
                  mode_9g = clockwise(myservo_9, 85);// 93/180 --> 80
                break;
              }
            case '5':       //-----抓 角度减小
              {
                mode_9g = clockwise(myservo_10,90);// 93/180 --> 90
                break;
              }
            case '6':       //-----放 角度增大
              {
                mode_9g = anticlockwise(myservo_10);// 93/85 --> 180
                break;
              }
            case '7':       //-----底座右转(顺) 角度减小
              {
                mode_9g = clockwise(myservo_7); // 93/180 --> 0
                break;
              }
            case '8':       //----底座左转(逆) 角度增大
              {
                mode_9g = anticlockwise(myservo_7);// 93/0 --> 180
                break;
              }
            case 'a':       //左转
              {
                mode_9g = TurnLeft();
                break;
              }
            case 'w':       //前进
              {
                mode_9g = Forward();
                break;
              }
            case 'd':       //右转
              {
                mode_9g = TurnRight();
                break;
              }
            case 's':       //后退
              {
                mode_9g = Backward();
                break;
              }
            case 'p':       //停止
              {
                Stop();
                jump = 0;
                break;
              }
            default:
              {
                 if(mode_9g=='9') // 模式 9 结束转动 或 因转到极限位置而停止
                    Serial.println("over");
                 else
                    Serial.println("Input error");
                 jump = 0;
                 Stop();
                 break;
               }
            }
            if (jump==0)//跳出 while 以接收新的模式指令 
            { 
               break;
            }
        }
    }
}
2019-05-20 10:25:11 qq_16775293 阅读数 34061
  • arduino实战

    学习如何在arduino中使用各种传感器,包括人体红外传感器,超声波传感器,舵机控制,温湿度传感器,激光接收传感器等,空气质量传感器,wifi模块等....课程内容会不断的更新增加,只要发现比较有趣的传感器就会做对应的实战视频

    405 人正在学习 去看看 陈贤能

Arduino智能小车——蓝牙小车

  上一章我们完成了小车的运动控制,虽然小车已经可以运动,但是不能远程遥控,不够高大上。在这一篇,我们将尝试用手机蓝牙遥控小车。

蓝牙模块

  蓝牙( Bluetooth® ):是一种无线技术标准,可实现固定设备、移动设备和楼宇个人域网之间的短距离数据交换(使用2.4—2.485GHz的ISM波段的UHF无线电波)。

  我们在此使用的蓝牙模块(HC-05)已经在内部实现了蓝牙协议,不用我们再去自己开发调试协议。这类模块一般都是借助于串口协议通信,因此我们只需借助串口将我们需要发送的数据发送给蓝牙模块,蓝牙模块会自动将数据通过蓝牙协议发送给配对好的蓝牙设备。

串口通信

  由于要借助串口实现蓝牙通信功能,所以我们在此要先了解下Arduino的串口通信。

  Arduino UNO开发板上的串口为0->RX,1->TX,在开发板内部也已经配置好了串口的功能,我们只需调用函数借口即可。以下列出串口通信里面常用的函数,并加以简单解释,详细用法可在用到时自行查询。

开启串行通信接口并设置通信波特率

Serial.begin(speed); 

关闭串口通信

Serial.end();    

判断串口缓冲器是否有数据写入

Serial.available();

读取串口数据

Serial.read();    

返回下一字节(字符)输入数据,但不删除它

Serial.peek();   

清空串口缓存

Serial.flush();    

写入字符串数据到串口

Serial.print();    

写入字符串数据+换行到串口

Serial.println(); 

写入二进制数据到串口

Serial.write();     

read时触发的事件函数

Serial.SerialEvent();

读取固定长度的二进制流

Serial.readBytes(buffer,length);

打印接到数据十进制表示的ascii码

Serial.println(incomingByte, DEC);

蓝牙模块连接

TX: 接Arduino UNO开发板"RX"引脚
RX: 接Arduino UNO开发板"TX"引脚
GND: 接Arduino UNO开发板"GND"引脚
VCC: 接Arduino UNO开发板"5V"或"3.3V"引脚

手机蓝牙助手

  想实现手机蓝牙遥控小车,手机APP是必不可少的,目前网上有很多蓝牙串口助手,省去了我们自己写APP的时间,当然如果朋友你有能力或者想自己DIY的话也可以尝试自己写APP,在这里我推荐大家用这款手机蓝牙助手(百度上搜手机蓝牙串口助手就可以搜到,挺好用的)

  如果不想自己去找的话可以到我的百度网盘下载 [点击这里下载](http://pan.baidu.com/s/1pKClRTL)

  下载并安装后打开APP,在这里可能会提示你有新版本请求更新,建议点击以后再说(暂时不更新),以我的经验,一般点击立即更新都会更新失败。

  进入主界面,左上角会提示"蓝牙未连接",这个时候我们可以先对蓝牙助手的界面进行自定义设置。点击右下角的三个点(在我这里是这样的,其他手机可能不同,如果没有这三个点可以试着点击手机的功能键),选择“更多”。
  然后选择“地面站设置”进入自定义界面,往下拖动,找到“自定义按键[x]”,在此我们对按键[1][2][3][4][6]进行自定义设置。
**  点击自定义按键[1],将其“显示名称”属性改为“停止”,“点击发送”属性改为“00”,并点击“确定”保存**
同理更改其他按键:

点击自定义按键[2],将其“显示名称”属性改为“前进”,“点击发送”属性改为“01”,并点击“确定”保存
点击自定义按键[4],将其“显示名称”属性改为“左转”,“点击发送”属性改为“03”,并点击“确定”保存
点击自定义按键[5],将其“显示名称”属性改为“后退”,“点击发送”属性改为“02”,并点击“确定”保存
点击自定义按键[6],将其“显示名称”属性改为“右转”,“点击发送”属性改为“04”,并点击“确定”保存

  以上修改的属性值即为我们点击对应按键之后,蓝牙串口助手自动通过蓝牙发送的数据,与上一篇所定义的小车的几个状态一致,这样方便在Arduino在接收到蓝牙模块的数据后对小车的状态进行控制。

#define STOP      0
#define FORWARD   1
#define BACKWARD  2
#define TURNLEFT  3
#define TURNRIGHT 4

修改后属性如下图

下面就来看看我们修改的效果吧,点击“模式切换”

这时候你就可以看到我们自定义的按键咯,看看修改前后的对比吧。
  接下来我们将连接蓝牙,仍然点击右下角的三个点,然后点击“连接”
  一般没有连接过蓝牙模块的时候“已配对的设备”下面没有可选择的设备名称,因此我们要点击“扫描新设备”来检测我们的蓝牙模块,扫描成功后蓝牙模块的名称将显示在“已配对的设备”一栏中
  点击我们的蓝牙模块的名称,输入密码进行配对。配对成功后在蓝牙串口助手的左上角会显示“蓝牙已连接”字样,恭喜你,这时候你已经连接成功。

小科普:
  蓝牙模块上电(只简单连接"VCC"和"GND"引脚)之后,其他蓝牙设备即可与其连接,一般蓝牙模块默认初始连接密码为"0000"或"1234",如果连接不上蓝牙,请尽快与厂商或者店家联系。蓝牙模块上电后LED指示灯不断闪亮,当有设备连接预期连接之后会隔一段闪两下,蓝牙串口助手也会有相应已连接的提示。

##Arduino代码编写
新建一个工程,将下面代码复制到工程内

#define STOP      0
#define FORWARD   1
#define BACKWARD  2
#define TURNLEFT  3
#define TURNRIGHT 4

int leftMotor1 = 4;
int leftMotor2 = 5;
int rightMotor1 = 6;
int rightMotor2 = 7;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(leftMotor1, OUTPUT);
  pinMode(leftMotor2, OUTPUT);
  pinMode(rightMotor1, OUTPUT);
  pinMode(rightMotor2, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  //usart read
  if(Serial.available()>0)
  {
    char cmd = Serial.read();//读取蓝牙模块发送到串口的数据
  
    Serial.print(cmd);
    motorRun(cmd);
      
  }  
}
void motorRun(int cmd)
{
  switch(cmd){
    case FORWARD:
      Serial.println("FORWARD"); //输出状态
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     case BACKWARD:
      Serial.println("BACKWARD"); //输出状态
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNLEFT:
      Serial.println("TURN  LEFT"); //输出状态
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNRIGHT:
      Serial.println("TURN  RIGHT"); //输出状态
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     default:
      Serial.println("STOP"); //输出状态
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}

  朋友们大概也发现了,这个代码和上一篇的代码基本上相同,只不过增加了串口的内容。

##代码详解
  串口初始化函数,想要通过串口的库函数对串口进行操作,必须在void set_up()函数中对其进行初始化。

Serial.begin(9600);

  在void loop()函数内,加入了检测串口接收内容的函数,并将接收到的命令输入到 void motorRun(int cmd)函数中控制小车运动。

if(Serial.available()>0)
  {
    char cmd = Serial.read();
  
    Serial.print(cmd);
    motorRun(cmd);
  }  

蓝牙小车测试

  下载程序之后,重新连接蓝牙模块,切换到我们自定义的按键界面,快试试蓝牙遥控小车吧。

附件

安卓手机蓝牙串口点击下载,也可以复制链接 https://download.csdn.net/download/qq_16775293/11165678 到浏览器下载。

欢迎各位有兴趣的朋友加入Q群1:511385161 点评、交流(已满,请大家移步2群)
欢迎各位有兴趣的朋友加入Q群2:638346596 点评、交流

2017-08-21 00:53:45 qq_16775293 阅读数 17183
  • arduino实战

    学习如何在arduino中使用各种传感器,包括人体红外传感器,超声波传感器,舵机控制,温湿度传感器,激光接收传感器等,空气质量传感器,wifi模块等....课程内容会不断的更新增加,只要发现比较有趣的传感器就会做对应的实战视频

    405 人正在学习 去看看 陈贤能

#Arduino智能小车——测试篇

    上一章讲解了智能小车的拼装,但是还没有让小车动起来,这章我们将继续拼装,使得小车可以动起来。

##驱动模块安装
    可能有些朋友会问到,驱动是干嘛的,为什么要驱动,小时候玩四驱车的时候直接装上电池小车就跑了,干嘛还要驱动模块。答案很简单,四驱车他只能朝着一个方向运动,而且永远都是以最大速度运行,我们所做的智能小车通常要控制小车电机的转速和运行方向,因此驱动是必不可少的模块。驱动模块的具体工作原理不在这里做详细的介绍,想了解的朋友可以自行查阅资料。

###驱动模块用法简介
    一般拿到一个模块之后都要去对应的官网找到它的资料包,查看其详细用法,在某宝上买的模块一般店家都有整理好的资料包,所以某宝也是一个很好的资料库,大家一定要合理运用哦~

在此我们选用的是L298N模块,该模块引脚分配如下:

**+12V:**该引脚接的电压是驱动模块所能输出给电机的最大电压,一般 直接接电池。12V是由L298N芯片所能接受最大电压而定,一般介入5~12V电压。在此我们接入的电压为两节18650串联的电压,即3.7+3.7=7.4V;

GND: 在该项目中GND即为电源的负极,同时要保证Arduino开发板,驱动模块等所有模块的GND连在一起才可以正常工作。在某些复杂的项目中还需要区分数字地和模拟地,在此不做详细介绍。

+5V: L298N模块(注意不是L298N芯片)内含稳压电路(将高电压转换为低电压的电路),在模块内部将"+12V"引脚输入的电压转化为可供开发板使用的+5V电压,一般将次输出接入到开发板为开发板供电。

L298N有两路输出,所以可以控制小车前进、后退、转弯,其中:
ENA: 代表第一路输出的电压大小。驱动模块输出电压越高,电机转速越快。
1.当其输入为0V的时候,驱动模块输出对第一路电机输出电压为0V;
2.当其输入为3.3V的时候,驱动模块对第一路电机输出电压为"+12V"引脚的输入电压。
3.由于ENA输入电压的高低控制驱动对电机的输出电压,因此当我们需要对小车运动速度进行控制的时候,一般通过PWM对"ENA"引脚进行控制。

**IN1/IN2:**这两个引脚控制电机正反转方向。例:假如IN1输入高电平3.3V,IN2输入低电平0V,ENA为3.3V,电机正转,此时将IN1输入改为0V,IN2输入改为3.3V,其他条件不变,则电机将会反转。

**OUT1/OUT2:**这两个引脚分别接电机的两极。

**ENB,IN3/IN4,OUT3/OUT4引脚控制第二路输出,与上述ENB,IN3/IN4,OUT3/OUT4**功能相似。

驱动安装

    将准备好的驱动模块固定在小车,将从地盘电机延长出来的导线分左右两边分开,左边两个电机中每一个电机的其中一根线OUT1,另外一个接OUT2。同理,右边两个电机中每一个电机的其中一根线OUT3,另外一个接OUT4,并用螺丝刀将拧蓝色接线柱上方的螺丝拧紧。

电池座固定

    将电池固定在小车尾部,将电源的两根线链接到+12V和GND引脚,红色代表正极,接到+12V,黑色代表负极,接到GND(一般电路中默认红色为正,黑色为负),并拧紧螺丝固定。

## Ardunio开发板安装     将Arduno板子用螺丝固定在小车中部,由于小车运动中也需要对开发板供电,此时我们用两根公对公的杜邦线为其供电,红色(也可以为其他颜色)杜邦线一边插入Arduino板的"5V"引脚,一边插到L298N驱动的"+5V"引脚,黑色(也可以为其他颜色)杜邦线一边插入Arduino板的"GND"引脚,一边插到L298N驱动的"GND"引脚。     为控制电机的正反转,此时我们需要拿四根公对母杜邦线,将L298N驱动的IN1/IN2/IN3/IN3引脚与Arduino板的4/5/6/7号引脚对应相连,最终拼装图如下: 抱歉,接线有点乱,大家可以用扎带或者皮筋将其捆好固定

    至此,我们的小车基本拼装完成,接下来就要开始写程序来控制小车运动咯,有没有很激动~

Ardunio程序编写

Arduino的程序编写一般使用Arduino IDE,该软件安装比较简单,大家可以自行安装,安装成功后打开IDE,在程序里写入下述代码:

#include <Servo.h>
//定义五中运动状态
#define STOP      0
#define FORWARD   1
#define BACKWARD  2
#define TURNLEFT  3
#define TURNRIGHT 4
//定义需要用到的引脚
int leftMotor1 = 4;
int leftMotor2 = 5;
int rightMotor1 = 6;
int rightMotor2 = 7;

void setup() {
  // put your setup code here, to run once:
  //设置控制电机的引脚为输出状态
  pinMode(leftMotor1, OUTPUT);
  pinMode(leftMotor2, OUTPUT);
  pinMode(rightMotor1, OUTPUT);
  pinMode(rightMotor2, OUTPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  int cmd;
  for(cmd=0;cmd<5;cmd++)//依次执行向前、向后、向左、想有、停止四个运动状态
  {
    motorRun(cmd);  
    delay(2000);//每个命令执行2s 
  } 
}
//运动控制函数
void motorRun(int cmd)
{
  switch(cmd){
    case FORWARD:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case BACKWARD:
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     case TURNLEFT:
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNRIGHT:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     default:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}

代码详解

为方便代码的编写,提高代码的可读性,在此我们先定义出小车可能的运动状态

#define STOP      0	//停止
#define FORWARD   1	//前进
#define BACKWARD  2	//后退
#define TURNLEFT  3	//左转弯
#define TURNRIGHT 4	//右转弯

电机运动需要经过驱动模块驱动,而驱动模块的输出状态又取决去IN1/IN2/EN,IN3/IN4/ENB这两组引脚的控制。本实验只是简单控制电机的运动,不用控制电机的转速,因此ENA,ENB默认接入高电平(买过来模块的时候,你会发现这两个引脚都通过跳线帽和"+5V"的引脚相连,即输出最大电压),此时我们只需控制IN1/IN2,IN3/IN4两组引脚即可对小车的运动状态进行控制。因此我们在此定义以下四个引脚

//定义需要用到的引脚
int leftMotor1 = 4;
int leftMotor2 = 5;
int rightMotor1 = 6;
int rightMotor2 = 7;

当电机的两个输入端加入的电压有电压差,且电压差满足一定条件时电机才会转动,为控制L298N驱动OUT1/OUT2,OUT3/OUT4两路输出,我们需要了解该模块的使用方法。

下面两个表格为L298N的输入输出对应关系,其中H:高电平,L:低电平,ENA、ENB均为高电平

输入 输出
IN1 IN2 OUT1 OUT1
H L H L
L H L H

输入 输出
IN3 IN4 OUT3 OUT4
H L H L
L H L H

由上述表格可以清晰看出控制小车运动时,只需要将同一边的两个引脚设置成不同的输出电压即可,例如让小车向前运动时左右两边的IN1和IN2可以设置为

digitalWrite(leftMotor1, LOW);
digitalWrite(leftMotor2, HIGH);
digitalWrite(rightMotor1, LOW);
digitalWrite(rightMotor2, HIGH);

为了提高代码的执行效率,我们在此将小车的四种运动状态封装在函数里,方便调用。

void motorRun(int cmd)
{
  switch(cmd){
    case FORWARD:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case BACKWARD:
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     case TURNLEFT:
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
     case TURNRIGHT:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;
     default:
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, LOW);
  }
}

至此,小车应该便可以成功运动起来咯,快为自己庆祝下吧。接下来我们将在此基础上进项进一步的开发,大家再接再厉哦
##调试中可能出现的问题

问题1.

下载完程序,小车向前运动时,有些轮子向前,有些轮子运动方向相反:
将运动方向错误的电机的两根电源线在驱动上的接线位置对换。例如将本来接OUT1的电源线换到OUT2,将接OUT2的电源线换到OUT1。

问题2.

代码没变,但是电机一会儿动一会不动:
这样的情况一般是由于接触不良造成的,检查你的驱动输出到电机的电源线是否有松动或者接触不良的情况。

问题3.

向前运动时他向后,向后运动时他向前,向左运动时他向右:
解决方案1:按照 问题1 中那样的解决方案,一个一个换线
解决方案2:将void motorRun(int cmd)函数中,引脚输出状态反过来,例如解决向前运动时他向后这种错误状态

case FORWARD:	//原来的代码
      digitalWrite(leftMotor1, LOW);
      digitalWrite(leftMotor2, HIGH);
      digitalWrite(rightMotor1, LOW);
      digitalWrite(rightMotor2, HIGH);
      break;
case FORWARD:	//修改后的代码
      digitalWrite(leftMotor1, HIGH);
      digitalWrite(leftMotor2, LOW);
      digitalWrite(rightMotor1, HIGH);
      digitalWrite(rightMotor2, LOW);
      break;

欢迎各位有兴趣的朋友加入Q群1:511385161 点评、交流(已满,请大家移步2群)
欢迎各位有兴趣的朋友加入Q群2:638346596 点评、交流

没有更多推荐了,返回首页