机器人制作开源方案 | 智能循迹避障小车

作者:刘元青、邹海峰、付志伟、秦怀远、牛文进

单位:哈尔滨信息工程学院

指导老师:姚清元

      智能小车是移动式机器人的重要组成部分,而移动机器人不仅能够在经济、国防、教育、文化和生活中起到越来越大的作用,也是研究复杂智能行为的产生、探索人类思维模式的有效工具与实验平台。

      本文设计的是一款基于Arduino的智能小车,它利用Arduino作为主控系统。它用蓝牙模块进行无线数据传输,实现无线控制。它利用红外线反射等原理,实现自动循迹效果。同时它利用超声波测距模块来进行测距,将测得的距离数据传给Arduino,经过Arduino处理给出反馈,驱动电机转动,实现自动避障的功能。

1. 系统设计与实现

1.1 系统设计

1.1.1 系统架构

      Roboduino智能小车车身由Arduino开发板和舵机等设备组成,其功能实现则由Arduino IDE编写程序以及Helloblock的调试来完成。

1.1.2 功能架构

      基于Arduino的Roboduino智能小车通过烧录WiFi遥控程序,利用超声波测距原理实现自动避障,利用红外线反射原理实现自动循迹,此外还有其他功能模式。

1.1.3 技术架构

      通过在Arduino IDE平台编写C代码来实现功能模块,将WIFI无线连接程序烧录至Arduino开发板,内含相关功能模块代码,利用超声波测距技术和红外线传感器分别实现自动避障和自动循迹功能。

1.2 系统实现

1.2.1 超声波测距

       实验原理:超声波模块是利用超声波特性检测距离的传感器。其带有两个超声波探头,分别用作发射和接收超声波。本次实验我们所使用的模块,其测量的范围是 0-3m。

      超声波时序图:以下时序图表明你只需要提供一个10uS 以上脉冲触发信号,该模块内部将发出8个40kHz周期电平并检测回波。一旦检测到有回波信号则输出回响信号。回响信号的脉冲宽度与所测的距离成正比。由此通过发射信号到收到的回响信号时间间隔可以计算得到距离。公式: uS/58=厘米或者uS/148=英寸;或是:距离=高电平时间*声速(340M/S)/2;建议测量周期为60ms 以上,以防止发射信号对回响信号的影响。

超声波模块引脚:根据硬件接口速查手册可知,超声波模块的超声波功能由Uno板的 Pin12引脚直接驱动。

实验程序

超声波测距(RGBUltrasonic_Ranging.ino)

/*

* @par Copyright (C): 2010-2019, Shenzhen Yahboom Tech

* @file         Ultrasonic_Ranging.c

* @author       Cindy

* @version      V1.0

* @date         2018.10.18

* @brief        Ultrasonic_Ranging

* @details

* @par History   

*

*/

#include <Wire.h>

#include "Adafruit_PWMServoDriver.h"

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

const int SingPin = 12;

float distance;

/**

* Function       setup

* @author        liusen

* @date          2017.07.25

* @brief         Initial configuration

* @param[in]     void

* @retval        void

* @par History   no

*/

void setup()

{

   pwm.begin();

   pwm.setPWMFreq(60);                 //Analog servos run at ~60 Hz updates

   LOGO_breathing_light(255, 40, 5);   //Gradually light the blue light of the Yhaboom_LOGO

   Serial.begin(9600);

   Serial.println("Ultrasonic sensor:");

}

/**

* Function       LOGO_light(brightness,time,increament)

* @author        wusicaijuan

* @date          2019.06.26

* @brief         LOGO_light

* @param[in1]    brightness

* @param[in2]    time

* @param[in3]    increament

* @param[out]    void

* @retval        void

* @par History   no

*/

void LOGO_breathing_light(int brightness, int time, int increament)

{

  if (brightness < 0)

  {

    brightness = 0;

  }

  if (brightness > 255)

  {

    brightness = 255;

  }

  for (int b = 0; b < brightness; b += increament)

  {

    int newb = map(b, 0, 255, 0, 4095);

    pwm.setPWM(7, 0, newb);

    delay(time);

  }

}

/**

* Function       loop

* @author        Cindy

* @date          2019.07.30

* @brief         main fuction

* @param[in]     void

* @retval        void

* @par History   no

*/

void loop()

{

  pinMode(SingPin,OUTPUT);

  digitalWrite(SingPin, LOW);

  delayMicroseconds(2);

  digitalWrite(SingPin, HIGH);

  delayMicroseconds(10);

  digitalWrite(SingPin, LOW);

 

  pinMode(SingPin, INPUT);

  delayMicroseconds(50);

  distance = pulseIn(SingPin, HIGH) / 58.00;

  Serial.print("distance is :");

  Serial.print(distance);

  Serial.print("cm");

  Serial.println();

  delay(1000);

}

操作流程

      我们需要通用 Arduino IDE 软件打开RGBUltrasonic_Ranging.ino文件,然后点击菜单栏中的“√”编译程序,并且等待左下角出现“编译成功”的字样。

在 Arduino IDE 的菜单栏中,我们需要选择【工具】---【端口】---选择设备管理器中刚刚显示端口号。

选择完成后,点击菜单栏下的“→”将代码上传到 UNO 板。 当左下角出现“上 传完成”字样时,表示程序已成功上传到 UNO 板。

程序下载完成之后,我们需要打开 Arduino IDE 界面右上角的“串口监视器”。

在串口监视器中选择和程序中相同的波特率。

接下来,我们就可以在串口监视器上面看到超声波模块当前所测量的距离被打印出来了。

1.2.2 巡线测试

      实验原理:红外传感器巡线的基本原理是利用物体的反射性质,我们本次实验是巡黑线行驶,当红外线发射到黑线上时会被黑线吸收掉,发射到其他的颜色的材料上会有反射到红外的接手管上。

      巡线模块引脚:根据硬件接口速查手册可知,三路巡线模块分别用Uno板的A0、A1、A2引脚驱动。

实验程序

① Tracking_PID_v1.ino

/*

* @par Copyright (C): 2010-2019, Shenzhen Yahboom Tech

* @file         Tracking_test.c

* @author       wusicaijuan

* @version      V1.0

* @date         2019.08.05

* @brief        Tracking_test

* @details

* @par History   

*

*/

#include "Adafruit_PWMServoDriver.h"

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

float max = 3.85;

float s = 100;

float Kp = 37, Ki = 4, Kd = 60;  

float error = 0, P = 0, I = 0, D = 0, PID_value = 0;

float previous_error = 0, previous_I = 0;

int sensor[3] = {0, 0, 0};

int initial_motor_speed = 40;

const int key = 7;

void read_sensor_values(void);

void calculate_pid(void);

void motor_control(void);

void keyscan(void);

void Clear_All_PWM(void);

/**

* Function       setup

* @author        wusicaijuan

* @date          2019.08.05

* @brief         Initial configuration

* @param[in]     void

* @retval        void

* @par History   no

*/

void setup()

{

// put your setup code here, to run once:

pwm.begin();

pwm.setPWMFreq(60); // Analog servos run at ~60 Hz updates

Clear_All_PWM();

pinMode(A0, INPUT);

pinMode(A1, INPUT);

pinMode(A2, INPUT);

pinMode(key, INPUT);

keysacn();

}

/**

* Function       loop

* @author        wusicaijuan

* @date          2019.07.30

* @brief         main fuction

* @param[in]     void

* @retval        void

* @par History   no

*/

void loop()

{

read_sensor_values();

calculate_pid();

motor_control();

}

/**

* Function       read_sensor_values

* @author        wusicaijuan

* @date          2019.07.30

* @brief         read sensor value to change car movement

* @param[in]     void

* @retval        void

* @par History   no

*/

void read_sensor_values()

{

sensor[0] = analogRead(A0);

sensor[1] = analogRead(A1);

sensor[2] = analogRead(A2);

if (sensor[0] > 100)

{

sensor[0] = 1;

}

else

{

sensor[0] = 0;

}

if (sensor[1] > 100)

{

sensor[1] = 1;

}

else

{

sensor[1] = 0;

}

if (sensor[2] > 100)

{

sensor[2] = 1;

}

else

{

sensor[2] = 0;

}

if ((sensor[0] == 0) && (sensor[1] == 0) && (sensor[2] == 1))

{

error = 2;

}

else if ((sensor[0] == 0) && (sensor[1] == 1) && (sensor[2] == 1))

{

error = 1;

}

else if ((sensor[0] == 0) && (sensor[1] == 1) && (sensor[2] == 0))

{

error = 0;

}

else if ((sensor[0] == 1) && (sensor[1] == 1) && (sensor[2] == 0))

{

error = -1;

}

else if ((sensor[0] == 1) && (sensor[1] == 0) && (sensor[2] == 0))

{

error = -2;

}

else if ((sensor[0] == 0) && (sensor[1] == 0) && (sensor[2] == 0))

{

if (error > 0)

{

//spin left

error = max;

}

else

{

//spin right

error = -max;

}

}

}

/**

* Function       calculate_pid

* @author        wusicaijuan

* @date          2019.06.25

* @brief         calculate_pid

* @param[out]   

* @retval       

* @par History   no

*/

void calculate_pid()

{

P = error;

I = I + previous_I;

D = error - previous_error;

PID_value = (Kp * P) + (Ki * I) + (Kd * D);

// Serial.println(PID_value);

previous_I = I;

previous_error = error;

}

void motor_control()

{

// Calculating the effective motor speed:

int left_motor_speed = initial_motor_speed - PID_value;

int right_motor_speed = initial_motor_speed + PID_value;

// The motor speed should not exceed the max PWM value

// left_motor_speed = constrain(left_motor_speed, -255, 255);

// right_motor_speed = constrain(right_motor_speed, -255, 255);

left_motor_speed = constrain(left_motor_speed, -s, s);

right_motor_speed = constrain(right_motor_speed, -s, s);

run(left_motor_speed, right_motor_speed);

}

/**

* Function       run

* @author        wusicaijuan

* @date          2019.06.25

* @brief         run

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void run(float Speed1, float Speed2)

{

Speed1 = map(Speed1, -255, 255, -4095, 4095);

Speed2 = map(Speed2, -255, 255, -4095, 4095);

if (Speed2 > 0)

{

pwm.setPWM(10, 0, Speed2); //Right front wheel Forward

pwm.setPWM(11, 0, 0);

pwm.setPWM(8, 0, Speed2); //Right rear wheel Forward

pwm.setPWM(9, 0, 0);

}

else

{

pwm.setPWM(10, 0, 0);

pwm.setPWM(11, 0, abs(Speed2));

pwm.setPWM(8, 0, 0);

pwm.setPWM(9, 0, abs(Speed2));

}

if (Speed1 > 0)

{

pwm.setPWM(13, 0, Speed1); //Left front wheel Forward

pwm.setPWM(12, 0, 0);

pwm.setPWM(15, 0, Speed1); //Left front wheel Forward

pwm.setPWM(14, 0, 0);

}

else

{

pwm.setPWM(13, 0, 0);

pwm.setPWM(12, 0, abs(Speed1));

pwm.setPWM(15, 0, 0);

pwm.setPWM(14, 0, abs(Speed1));

}

}

/**

* Function       sleft

* @author        wusicaijuan

* @date          2019.06.25

* @brief         turn left(Left wheel stop,Right wheel advance)

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void sleft(float Speed)

{

pwm.setPWM(10, 0, Speed); //Right front wheel Forword

pwm.setPWM(11, 0, 0);

pwm.setPWM(8, 0, Speed);   //Right rear wheel Forword

pwm.setPWM(9, 0, 0);

pwm.setPWM(13, 0, 0);     //Left front wheel Back

pwm.setPWM(12, 0, Speed);

pwm.setPWM(15, 0, 0);     //Left rear wheel Back

pwm.setPWM(14, 0, Speed);

}

/**

* Function       sright

* @author        wusicaijuan

* @date          2019.06.25

* @brief         spin_right(Left wheel advance,Right wheel back)

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void sright(float Speed)

{

pwm.setPWM(10, 0, 0);

pwm.setPWM(11, 0, Speed);   //Right front wheel Back

pwm.setPWM(8, 0, 0);

pwm.setPWM(9, 0, Speed);   //Right rear wheel Back

pwm.setPWM(13, 0, Speed);   //Left front wheel Forword

pwm.setPWM(12, 0, 0);

pwm.setPWM(15, 0, Speed);   //Left rear wheel Forword

pwm.setPWM(14, 0, 0);

}

/**

* Function       keysacn

* @author        wusicaijuan

* @date          2019.06.04

* @brief         keysacn

* @param[in1]    void

* @retval        void

* @par History   no

*/

void keysacn()

{

int val;

val = digitalRead(key); //Read the level value of   digital 7 port assigned to val

while (val == HIGH)   //Cycles when the button is not pressed

{

val = digitalRead(key);

}

while (val == LOW)     //When button is not pressed

{

delay(1);

val = digitalRead(key); //Read the level value of   digital 7 port assigned to val

while (val == HIGH)   //Determine if the button is released

{

break;

}

}

}

/*

* Function       Clear_All_PWM

* @author        wusicaijuan

* @date          2019.07.04

* @brief         Turn off PWM

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void Clear_All_PWM()

{

for (int i = 0; i < 16; i++)

{

pwm.setPWM(i, 0, 0);

}

}

② Tracking_test.ino

/*

* @par Copyright (C): 2010-2019, Shenzhen Yahboom Tech

* @file         Tracking_test.c

* @author       wusicaijuan

* @version      V1.0

* @date         2019.08.05

* @brief        Tracking_test

* @details

* @par History   

*

*/

#include "Adafruit_PWMServoDriver.h"

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

int sensor[3];

const int key = 7; //Define key pin

void keyscan(void);

void Clear_All_PWM(void);

#define   L_Value   analogRead(A2)

#define   M_Value   analogRead(A1)

#define   R_Value   analogRead(A0)

/**

* Function       setup

* @author        liusen

* @date          2017.07.25

* @brief         Initial configuration

* @param[in]     void

* @retval        void

* @par History   no

*/

void setup()

{

  // put your setup code here, to run once:

  pwm.begin();

  pwm.setPWMFreq(60); // Analog servos run at ~60 Hz updates

  Clear_All_PWM();

  pinMode(A0, INPUT);

  pinMode(A1, INPUT);

  pinMode(A2, INPUT);

  pinMode(key, INPUT);

  keysacn();

}

/**

* Function       loop

* @author        wusicaijuan

* @date          2019.07.30

* @brief         main fuction

* @param[in]     void

* @retval        void

* @par History   no

*/

void loop()

{

  read_sensor_values();

}

/**

* Function       read_sensor_values

* @author        wusicaijuan

* @date          2019.07.30

* @brief         read sensor value to change car movement

* @param[in]     void

* @retval        void

* @par History   no

*/

/*450,350,400分别是中间探头左侧探头右侧探头黑白线之间的临界值,

  * 请用户一定要根据实际情况打印观察这三个数据,并进行修改。

  */

void read_sensor_values()

{

    if(M_Value>340)  

    {

      run(65);

    }

    else if(L_Value > 350)

    {

      sleft(75);

      while(L_Value>350);

    }

    else if(R_Value > 400)   

    {

      sright(75);

      while(R_Value > 400);   

    }

}

/**

* Function       run

* @author        wusicaijuan

* @date          2019.06.25

* @brief         run

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void run(float Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, Speed); //Right front wheel Forward

  pwm.setPWM(11, 0, 0);

  pwm.setPWM(8, 0, Speed); //Right rear wheel Forward

  pwm.setPWM(9, 0, 0);

  pwm.setPWM(13, 0, Speed); //Left front wheel Forward

  pwm.setPWM(12, 0, 0);

  pwm.setPWM(15, 0, Speed); //Left front wheel Forward

  pwm.setPWM(14, 0, 0);

}

/**

* Function       left

* @author        wusicaijuan

* @date          2019.06.26

* @brief         turn left(Left wheel stop,Right wheel advance)

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void left(float Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, Speed);   //Right front wheel Reverse

  pwm.setPWM(11, 0, 0);

  pwm.setPWM(8, 0, Speed);   //Right rear wheel Reverse

  pwm.setPWM(9, 0, 0);

  pwm.setPWM(13, 0, 0);     //Left front wheel Stop

  pwm.setPWM(12, 0, 0);

  pwm.setPWM(15, 0, 0);

  pwm.setPWM(14, 0, 0);

}

/**

* Function       right

* @author        wusicaijuan

* @date          2019.06.26

* @brief         turn right (Left wheel advance,Right wheel stop)

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void right(float Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, 0);   //Right front wheel Stop

  pwm.setPWM(11, 0, 0);

  pwm.setPWM(8, 0, 0);

  pwm.setPWM(9, 0, 0);

  pwm.setPWM(13, 0, Speed);   //Left front wheel Reverse

  pwm.setPWM(12, 0, 0);

  pwm.setPWM(15, 0, Speed); //Left rear wheel Reverse

  pwm.setPWM(14, 0, 0);

}

/**

* Function       sleft

* @author        wusicaijuan

* @date          2019.06.25

* @brief         spin_left(Left wheel back,Right wheel advance)

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void sleft(float Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, Speed);   //Right front wheel Forword

  pwm.setPWM(11, 0, 0);

  pwm.setPWM(8, 0, Speed);   //Right rear wheel Forword

  pwm.setPWM(9, 0, 0);

  pwm.setPWM(13, 0, 0);      //Left front wheel Back

  pwm.setPWM(12, 0, Speed);

  pwm.setPWM(15, 0, 0);

  pwm.setPWM(14, 0, Speed);   //Left rear wheel Back

}

/**

* Function       sright

* @author        wusicaijuan

* @date          2019.06.25

* @brief         spin_right(Left wheel advance,Right wheel back)

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void sright(float Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, 0);

  pwm.setPWM(11, 0, Speed);   //Right front wheel Back

  pwm.setPWM(8, 0, 0);

  pwm.setPWM(9, 0, Speed);   //Right rear wheel Back

  pwm.setPWM(13, 0, Speed);   //Left front wheel Forword

  pwm.setPWM(12, 0, 0);

  pwm.setPWM(15, 0, Speed);   //Left rear wheel Forword

  pwm.setPWM(14, 0, 0);

}

/**

* Function       keysacn

* @author        wusicaijuan

* @date          2019.06.04

* @brief         keysacn

* @param[in1]    void

* @retval        void

* @par History   no

*/

void keysacn()

{

  int val;

  val = digitalRead(key); //Read the level value of   digital 7 port assigned to val

  while (val == HIGH)     //Cycles when the button is not pressed

  {

    val = digitalRead(key);

  }

  while (val == LOW)      //When button is not pressed

  {

    delay(10);       

    val = digitalRead(key); //Read the level value of   digital 7 port assigned to val

    while (val == HIGH)     //Determine if the button is released

    {

      break;

    }

  }

}

/*

* Function       Clear_All_PWM

* @author        wusicaijuan

* @date          2019.07.04

* @brief         Turn off PWM

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void Clear_All_PWM()

{

  for (int i = 0; i < 16; i++)

  {

    pwm.setPWM(i, 0, 0);

  }

}

③ TrackingSensorTest.ino

//30    27    29

void setup()

{

Serial.begin(115200);

pinMode(A0, INPUT);

pinMode(A1, INPUT);

pinMode(A2, INPUT);

}

void loop()

{

delay(50);

Serial.print(analogRead(A0));   

Serial.print(",");

Serial.print(analogRead(A1));

Serial.print(",");

Serial.println(analogRead(A2));

}

实验现象

      对于程序Tracking_test.ino,首先打开在TrackingSensorTest文件夹中 TrackingSensorTest.ino。并通过数据线将小车(或者是已经插入扩展板的 Uno板)与电脑连接。然后将三路巡线模块处于白色底部上(必须是你的小车将要行驶的巡线赛道的白色底部),如下图所示将三路巡线模块处于黑色赛道上(必须是你的小车将要行驶的巡线黑色赛道)。

在Arduino IDE界面的右上角打开串口监视器,需要选择与程序中所设置的相同的波特率。

当巡线传感器的三个探头检测到白色时,检测到黑色时,打印出当前输出的模拟值。

如果数据显示一点波动,这是正常的。

打开 Tracking_test 文件夹中的 Tracking_test.ino 文件,并且根据上一步打印出来的数值,取一个最佳的临界值,修改程序中的数据。

      修改完成之后,保存程序并将程序下载小车上。

      对于程序:Tracking_PID.ino首先,打开在TrackingSensorTest 文件夹中 TrackingSensorTest.ino。并通过数据线将小车(或者是已经插入扩展板的 Uno 板)与电脑连接。然后,将三路巡线模块处于白色底部上(必须是你的小车将要行驶的巡线赛道的白色底部)。

在 Arduino IDE 界面的右上角打开串口监视器,需要选择与程序中所设置的相同的波特率。

当巡线传感器的三个探头检测到白色时,打印出当前输出的模拟值。

如果数据显示一点波动,这是正常的,可以取五个数据的平均值。

打开Tracking_PID 文件夹中的 Tracking_PID.ino 文件,并且修改程序中的数据。

1.2.3 WIFI摄像头控制

实验原理:根据硬件接口速查手册可知,WIFI 摄像头模块是通过串口进行通讯的。

实验程序

WIFI模块(WIFI_control_car_15.ino)

/**

* @par Copyright (C): 2010-2019, Shenzhen Yahboom Tech

* @file         Wifi control car.c

* @author       Cindy

* @version      V1.0

* @date         2019.09.11

* @brief       

* @details

* @par History  

*

*/

#include <Arduino.h>

#include "Adafruit_PWMServoDriver.h"

#include "Adafruit_NeoPixel.h"       

#include "RGBLed.h"

#include "avr/pgmspace.h"

#define RGB_GREEN    0xFF0000    //定义RGB灯的颜色

#define RGB_RED   0x00FF00

#define RGB_BLUE    0x0000FF

#define RGB_YELLOW   0xFFFF00

#define RGB_PURPLE   0x00FFFF

#define RGB_WHITE   0xFFFFFF

#define RGB_CYAN   0xFF00FF

#define RGB_OFF   0x000000

const int RgbPin = 11;    //定义七彩超声波模块上面RGB灯的引脚

RGBLed mRgb(RgbPin,2);

Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver(0x40);

#define SERVOMIN 150     

#define SERVOMAX 600     

#define PIN 6   //定义RGB的引脚

#define MAX_LED 1 //扩展板上仅有一个同类型的板载RGB灯

Adafruit_NeoPixel strip = Adafruit_NeoPixel(MAX_LED, PIN, NEO_RGB + NEO_KHZ800);

const char enServo[] = {0, 1, 2, 3};

const unsigned char music_max[5] = {42,39,36,70,25};   //音乐歌曲的最大长度

#define run_car '1'       

#define back_car '2'     

#define left_car '3'     

#define right_car '4'   

#define spin_left_car '5'  

#define spin_right_car '6'

#define stop_car '7'     

#define H_servoL 'L'       

#define H_servoR 'R'     

#define H_servoS 'S'   

#define V_servoU 'U'

#define V_servoD 'D'

#define V_servoS 'S'

int Servo_LR = 90;

int Servo_UD = 90;

const int flag_time = 2000; //时间标记点间隔2s

int newtime = 0;            //记录系统当前的时间

int lasttime = 0;           //记录系统上一次的时间点

int g_num = 0;

int buzzer = 10;            //定义蜂鸣器的引脚

int CarSpeedControl = 80;   //set speed of car

int SingPin = 12;         

int distance = 0;

int red, green, blue;

int initial_motor_speed = 100;

int sensor[3];

int time = 40000;

int count = 10;

/*小车运动状态枚举*/

const typedef enum {

  enRUN = 1,

  enBACK,

  enLEFT,

  enRIGHT,

  enSPINLEFT,

  enSPINRIGHT,

  enSTOP

} enCarState;

/*舵机状态枚举*/

const typedef enum {

  enHServoL = 1,

  enHServoR,

  enHServoS

} enHServoState;

int IncomingByte = 0;            //接收到的数据字节

String InputString = "";         //用来存储接收到的数据

boolean NewLineReceived = false; //上一次数据结束的标志

boolean StartBit   = false;       //协议开始标志

String ReturnTemp = "";          //用来存储返回值

static int g_CarState = enSTOP;         //1前进 2后退 3左转 4 右转 0停止

static int g_HServoState = enHServoS; //1:左转 2:右转 3:停止

int g_modeSelect = 0;   //0:默认的状态

int g_modeMusic = 0; //0:默认的状态

int g_musicSelect = 1;

int g_modeCar = 0;

boolean g_motor = false;

//Music

enum enMusic

  {

    enStar=1,

    Bingo=2,

    MerryChristmas=3,

    Ode=4,

    Birthday=5

  };

#define G5 392

#define A6 440

#define B7 494

#define c1 525

#define d2 587

#define e3 659

#define f4 698

#define g5 784

#define a6 880

#define b7 988

#define C1 1047

#define D2 1175

#define E3 1319

#define F4 1397

#define GG5 1568

#define AA6 1769

#define g4 392

#define c5 523

#define a4 440

#define d5 587

#define e5 659

#define b4 494

#define c6 1047

#define d6 1175

#define b5 988

#define a5 880

#define g5 784

#define e6 1319

#define f6 1397

#define a5 880

#define f5 698

const PROGMEM   int ODe[70][2]   //歌曲--欢乐颂

{

  {e3,2},{e3,2},{f4,2},{g5,2},{g5,2},{f4,2},{e3,2},{d2,2},{c1,2},{c1,2},{d2,2},{e3,2},{e3,3},{d2,1},{d2,4},

  {e3,2},{e3,2},{f4,2},{g5,2},{g5,2},{f4,2},{e3,2},{d2,2},{c1,2},{c1,2},{d2,2},{e3,2},{d2,3},{c1,1},{c1,4},

  {d2,2},{d2,2},{e3,2},{c1,2},{d2,2},{e3,1},{f4,1},{e3,2},{c1,2},

  {d2,2},{e3,1},{f4,1},{e3,2},{d2,2},{c1,2},{d2,2},{G5,2},

  {e3,2},{e3,2},{f4,2},{g5,2},{g5,2},{f4,2},{e3,2},{d2,2},{c1,2},{c1,2},{d2,2},{e3,2},{d2,3},{c1,1},{c1,4},

};

const PROGMEM   int BIrthday[25][2] //歌曲--生日快乐

{

  {G5,2},{A6,2},{G5,2},{c1,2},{B7,4},

  {G5,2},{A6,2},{G5,2},{d2,2},{c1,4},

  {G5,2},{g5,2},{e3,2},{c1,2},{B7,2},{A6,2},

  {f4,2},{e3,2},{c1,2},{d2,2},{c1,2}

};

const PROGMEM   int STar[42][2] //歌曲--小星星

{

  {c1,2},{c1,2},{g5,2},{g5,2},{a6,2},{a6,2},{g5,4},

  {f4,2},{f4,2},{e3,2},{e3,2},{d2,2},{d2,2},{c1,4},

  {g5,2},{g5,2},{f4,2},{f4,2},{e3,2},{e3,2},{d2,4},

  {g5,2},{g5,2},{f4,2},{f4,2},{e3,2},{e3,2},{d2,4},

  {c1,2},{c1,2},{g5,2},{g5,2},{a6,2},{a6,2},{g5,4},

  {f4,2},{f4,2},{e3,2},{e3,2},{d2,2},{d2,2},{c1,4},

};

const PROGMEM   int MErryChristmas[36][2]   //歌曲--圣诞快乐

{

  {g5,1},{g5,1},{c6,2},{c6,1},{d6,1},{c6,1},{b5,1},{a5,2},{a5,2},{0,2},

  {a5,1},{a5,1},{d6,2},{d6,1},{e6,1},{d6,1},{c6,1},{b5,2},{g5,2},{0,2},

  {g5,1},{g5,1},{e6,2},{e6,1},{f6,1},{e6,1},{d6,1},{c6,2},{a5,2},{0,2},

  {g5,1},{g5,1},{a5,1},{d6,1},{b5,1},{c6,2}

};

const PROGMEM   int BIngo[39][2]   //歌曲--宾果

{

  {g4,1},{c5,1},{c5,1},{c5,1},{g4,1},{a4,1},{a4,1},{g4,1},{g4,1},

  {c5,1},{c5,1},{d5,1},{d5,1},{e5,2},{c5,1},{0,1},

  {e5,2},{e5,2},{f5,1},{f5,1},{f5,2},{d5,2},{d5,2},{e5,1},{e5,1},{e5,2},

  {c5,2},{c5,2},{d5,1},{d5,1},{d5,1},{c5,1},{b4,1},{g4,1},{a4,1},{b4,1},{c5,2},{c5,1},{c5,1}

};

int serial_putc( char c, struct __file * )

{

  Serial.write( c );

  return c;

}

void printf_begin(void)

{

  fdevopen( &serial_putc, 0 );

}

/*

* Function       Clear_All_PWM

* @author        wusicaijuan

* @date          2019.07.04

* @brief         关闭PCA9685的所有PWM

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   No

*/

void Clear_All_PWM()

{

  for (int i = 0; i < 16; i++)

  {

    pwm.setPWM(i, 0, 0);

  }

}

/**

* Function       setup

* @author        Cindy

* @date          2019.09.11

* @brief         初始化设置

* @param[in]     void

* @retval        void

* @par History   no

*/

void setup()

{

  Serial.begin(9600);

  printf_begin();

  strip.begin();

  strip.show();

  pwm.begin();

  pwm.setPWMFreq(60);

  Clear_All_PWM();

  Servo180(1,90); //将三个舵机都初始化至90度

  Servo180(2,90);

  Servo180(3,90);

  breathing_light(255, 40, 5);

  pinMode(buzzer, OUTPUT);

  digitalWrite(buzzer, HIGH);

}

/**

* Function       setBuzzer_Tone

* @author        Cindy

* @date          2019.09.02

* @brief         设置蜂鸣器的音调

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void setBuzzer_Tone(uint16_t frequency, uint32_t duration)

{

  int period = 1000000L / frequency;//1000000L

    int pulse = period / 2;

 

    for (long i = 0; i < duration * 200000L; i += period)

    {

    digitalWrite(buzzer, 1);

      delayMicroseconds(pulse);

      digitalWrite(buzzer, 0);

      delayMicroseconds(pulse);

    }

    if(frequency==0)

    delay(230*duration);  

  delay(20);

}

/**

* Function       5-music

* @author        Cindy

* @date          2019.09.02

* @brief         五首歌曲

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void birthday(int j)    //生日快乐

{

    setBuzzer_Tone(pgm_read_word_near(&BIrthday[j][0]), pgm_read_word_near(&BIrthday[j][1]));

}

void ode(int j)     //欢乐颂

{

    setBuzzer_Tone(pgm_read_word_near(&ODe[j][0]), pgm_read_word_near(&ODe[j][1]));

}

void star(int j)   //小星星

{

    setBuzzer_Tone(pgm_read_word_near(&STar[j][0]), pgm_read_word_near(&STar[j][1]));

}

void merryChristmas(int j)   //圣诞快乐

{

    setBuzzer_Tone(pgm_read_word_near(&MErryChristmas[j][0]), pgm_read_word_near(&MErryChristmas[j][1]));

}

void bingo(int j)   //宾果

{

    setBuzzer_Tone(pgm_read_word_near(&BIngo[j][0]), pgm_read_word_near(&BIngo[j][1]));

}

/**

* Function       music_Play

* @author        Cindy

* @date          2019.09.11

* @brief         播放音乐

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void music_Play(uint8_t v_song, uint8_t index)

{

  switch(v_song)

  {

    case enStar:

    {

      star(index);

      break;

    }

    case Bingo:

    {

      bingo(index);   

      break;

    }

    case MerryChristmas:

    {

      merryChristmas(index);     

      break;

    }

    case Ode:

    {

      ode(index);

      break;

    }

    case Birthday:

    {

      birthday(index);  

      break;

    }

  }

}

/*

* Function      Servo180(num, degree)

* @author       wusicaijuan

* @date         2019.06.25

* @bried        控制180度舵机旋转

* @param[in1]   index

                    1: s1

                    2: s2

                    3: s3

                    4: s4

* @param[in2]   degree (0 <= degree <= 180)

* @retval       void

*/

void Servo180(int num, int degree)

{

  long us = (degree * 1800 / 180 + 600); // 0.6 ~ 2.4

  long pwmvalue = us * 4096 / 20000;   // 50hz: 20,000 us

  pwm.setPWM(enServo[num - 1], 0, pwmvalue);

}

/**

* Function       Distance_test

* @author        Cindy

* @date          2019.09.11

* @brief         超声波测距

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void Distance_test()

{

  pinMode(SingPin,OUTPUT);

  digitalWrite(SingPin, LOW);

  delayMicroseconds(2);

  digitalWrite(SingPin, HIGH);

  delayMicroseconds(15);

  digitalWrite(SingPin, LOW);

 

  pinMode(SingPin, INPUT);

  delayMicroseconds(50);

  distance = pulseIn(SingPin, HIGH) / 58;

  //Serial.print("distance is :");

  //Serial.print(distance);

  //Serial.print("cm");

  //Serial.println();

  //delay(1000);

}

/**

* Function       PCB_RGB(R,G,B)

* @author        wusicaijuan

* @date          2019.06.26

* @brief         控制板载的RGB

* @param[in1]    R

* @param[in2]    G

* @param[in3]    B

* @param[out]    void

* @retval        void

* @par History   

*

*/

void PCB_RGB(int R, int G, int B)

{

  uint8_t i = 0;

  R = map(R, 0, 255, 0, 200);  

  G = map(G, 0, 255, 0, 200);

  B = map(B, 0, 255, 0, 200);

  uint32_t color = strip.Color(G, R, B);

  strip.setPixelColor(i, color);

  strip.show();

}

/**

* Function       Ultrasonic_RGB(R,G,B)

* @author        wusicaijuan

* @date          2019.06.26

* @brief         控制超声波模块上面的七彩灯

* @param[in1]    R

* @param[in2]    G

* @param[in3]    B

* @param[out]    void

* @retval        void

* @par History   no

*/

void   Ultrasonic_RGB(int R, int G, int B)

{

  mRgb.setColor(0,G,R,B);

  mRgb.show();

}

/**

* Function       advance

* @author        wusicaijuan

* @date          2019.06.25

* @param[in]     小车前进

* @param[out]    void

* @retval        void

* @par History   no

*/

void advance(int Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, Speed); //右前方车轮前进

pwm.setPWM(11, 0, 0);

  pwm.setPWM(8, 0, Speed); //右后方车轮前进

  pwm.setPWM(9, 0, 0);

  pwm.setPWM(13, 0, Speed); //左前方车轮前进

  pwm.setPWM(12, 0, 0);

  pwm.setPWM(15, 0, Speed); //左后方车轮前进

  pwm.setPWM(14, 0, 0);

}

/**

* Function       brake

* @author        wusicaijuan

* @date          2019.06.25

* @brief         小车停止

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void brake()

{

pwm.setPWM(8, 0, 0);

  pwm.setPWM(9, 0, 0);

pwm.setPWM(11, 0, 0);

  pwm.setPWM(10, 0, 0);

  pwm.setPWM(12, 0, 0);

  pwm.setPWM(13, 0, 0);

  pwm.setPWM(14, 0, 0);

  pwm.setPWM(15, 0, 0);

}

/**

* Function       left

* @author        wusicaijuan

* @date          2019.06.26

* @brief         小车左转

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void left(int Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, Speed);     //右前方车轮前进

  pwm.setPWM(11, 0, 0);

  pwm.setPWM(8, 0, Speed);      //右后方车轮前进

  pwm.setPWM(9, 0, 0);     

  pwm.setPWM(13, 0, 0);         //左侧车轮停止

  pwm.setPWM(12, 0, 0);

  pwm.setPWM(15, 0, 0);

  pwm.setPWM(14, 0, 0);

}

/**

* Function       right

* @author        wusicaijuan

* @date          2019.06.26

* @brief         小车右转

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void right(int Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, 0);        //右侧车轮停止

  pwm.setPWM(11, 0, 0);

  pwm.setPWM(8, 0, 0);

  pwm.setPWM(9, 0, 0);

  pwm.setPWM(13, 0, Speed);    //左前方车轮前进

  pwm.setPWM(12, 0, 0);

  pwm.setPWM(15, 0, Speed);    //左后方车轮前进

  pwm.setPWM(14, 0, 0);

}

/**

* Function       spin_left

* @author        wusicaijuan

* @date          2019.06.25

* @brief         左旋(左轮后退,右轮前进)

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void spin_left(int Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, Speed);      //右前方车轮前进

  pwm.setPWM(11, 0, 0);

  pwm.setPWM(8, 0, Speed);       //右前方车轮前进

  pwm.setPWM(9, 0, 0);

  pwm.setPWM(13, 0, 0);

  pwm.setPWM(12, 0, Speed);      //左前方车轮后退

  pwm.setPWM(15, 0, 0);

  pwm.setPWM(14, 0, Speed);      //左后方车轮后退

}

/**

* Function       spin_right

* @author        wusicaijuan

* @date          2019.06.25

* @brief         右旋(左轮前进,右轮后退)

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   no

*/

void spin_right(int Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, 0);

  pwm.setPWM(11, 0, Speed);    //右前方车轮后退

  pwm.setPWM(8, 0, 0);

  pwm.setPWM(9, 0, Speed);     //右后方车轮后退

  pwm.setPWM(13, 0, Speed);    //左前方车轮前进

  pwm.setPWM(12, 0, 0);

  pwm.setPWM(15, 0, Speed);    //左后方车轮前进

  pwm.setPWM(14, 0, 0);

}

/**

* Function       back

* @author        wusicaijuan

* @date          2019.06.25

* @brief         小车后退

* @param[in]     Speed

* @param[out]    void

* @retval        void

* @par History   No

*/

void back(int Speed)

{

  Speed = map(Speed, 0, 255, 0, 4095);

  pwm.setPWM(10, 0, 0);

  pwm.setPWM(11, 0, Speed); //右前方车轮后退

  pwm.setPWM(8, 0, 0);

  pwm.setPWM(9, 0, Speed); //右后方车轮后退

  pwm.setPWM(13, 0, 0);

  pwm.setPWM(12, 0, Speed);   //左前方车轮后退

  pwm.setPWM(15, 0, 0);

  pwm.setPWM(14, 0, Speed); //左后方车轮后退

}

/**

* Function       whistle

* @author        Cindy

* @date          2019.09.11

* @brief         鸣笛

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void whistle()

{

  for (int i = 0; i < 100; i++)

  {

    digitalWrite(buzzer, HIGH); //发出声音

    delay(3);         

    digitalWrite(buzzer, LOW);   //不发出声音

    delay(1);         

  }

}

/**

* Function       breathing_light(brightness,time,increament)

* @author        wusicaijuan

* @date          2019.06.26

* @brief         logo内嵌的蓝色RGB灯

* @param[in1]    brightness

* @param[in2]    time

* @param[in3]    increament

* @param[out]    void

* @retval        void

* @par History   no

*/

void breathing_light(int brightness, int time, int increament)

{

  if (brightness < 0)

  {

    brightness = 0;

  }

  if (brightness > 255)

  {

    brightness = 255;

  }

  for (int b = 0; b < brightness; b += increament)

  {

    int newb = map(b, 0, 255, 0, 4095);

    pwm.setPWM(7, 0, newb);

    delay(time);

  }

}

/********************************************************************************************************/

/*模式3-巡线模式*/

/**

* Function       Tracking_Mode

* @author        Cindy

* @date          2019.09.11

* @brief         巡线

* @param[in1]    void

* @param[out]    void

* @retval        void

* @par History   no

*/

//可根据实际情况修改程序中的参数

void Tracking_Mode()

{

  sensor[0] = analogRead(A0);

  sensor[1] = analogRead(A1);

  sensor[2] = analogRead(A2);

  if(sensor[0]>100)   //三个参数100,100,100大家可根据实际情况进行调节

  {

    sensor[0] = 1;

  }

  else

  {

    sensor[0] = 0;

  }

  if(sensor[1]>100)

  {

    sensor[1] = 1;

  }

  else{

    sensor[1] = 0;

  }

  if(sensor[2]>100)

  {

    sensor[2] = 1;

  }

  else

  {

    sensor[2] = 0;

  }

 

  if ((sensor[0] == 0) && (sensor[1] == 0) && (sensor[2] == 1))

  {

    spin_left(80);

  }

  else if ((sensor[0] == 0) && (sensor[1] == 1) && (sensor[2] == 1))

  {

    left(100);

  }

  else if ((sensor[0] == 0) && (sensor[1] == 1) && (sensor[2] == 0))

  {

    advance(70);

  }

  else if ((sensor[0] == 1) && (sensor[1] == 1) && (sensor[2] == 0))

  {

    right(100);

  }

  else if ((sensor[0] == 1) && (sensor[1] == 0) && (sensor[2] == 0))

  {

    spin_right(80);

  }

  else if ((sensor[0] == 0) && (sensor[1] == 0) && (sensor[2] == 0))

  {

    //Clear_All_PWM();//全部检测到白色,保持上一个状态

  }

  else if ((sensor[0] == 1) && (sensor[1] == 1) && (sensor[2] == 1))

  {

    //Clear_All_PWM();//全部检测到黑色,保持上一个状态

  }

}

/********************************************************************************************************/

/*模式2: 超声波避障模式*/

/**

* Function       bubble

* @author        Cindy

* @date          2019.09.11

* @brief         采用冒泡排序法。

* @param[in1]    a:超声波数组的首地址

* @param[in2]    n:超声波数组的大小

* @param[out]    void

* @retval        void

* @par History   no

*/

void bubble(unsigned long *a, int n)

{

  int i, j, temp;

  for (i = 0; i < n - 1; i++)

  {

    for (j = i + 1; j < n; j++)

    {

      if (a[i] > a[j])

      {

        temp = a[i];

        a[i] = a[j];

        a[j] = temp;

      }

    }

  }

  return;

}

/**

* Function       Distance

* @author        Cindy

* @date          2019.09.11

* @brief         去掉最大值和最小值,取中间三个数据的平均值

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   No

*/

void Distance()

{

  unsigned long ultrasonic[5] = {0};

  int num = 0;

  while (num < 5)

  {

    Distance_test();

    ultrasonic[num] = distance;

    //printf("L%d:%d\r\n", num, (int)distance);

    num++;

    delay(10);

  }

  num = 0;

  bubble(ultrasonic, 5);

  distance = (ultrasonic[1] + ultrasonic[2] + ultrasonic[3]) / 3;

  return;

}

enum {

  LEFT_DIRECTION,

  RIGHT_DIRECTION,

  FRONT_DIRECTION,

  ALL_CHECKED_START_ACTION

};

/**

* Function       ult_check_distance_and_action

* @author        Cindy

* @date          2019.09.11

* @brief         舵机转动检测距离

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

int ult_check_distance_and_action(uint8_t p_direction)

{

  static int LeftDistance = 0;    //LeftDistance

  static int RightDistance = 0;   //RightDistance

  static int FrontDistance = 0;   //FrontDistance

  static int cnt = 0;

  int ret = 0;

  if (0 == g_modeSelect)

  {

    cnt = 0;

    brake();

    LeftDistance = 0;

    RightDistance = 0;

    FrontDistance = 0;

    ret = -1;

    return ret;

  }

  mRgb.setColor(0,RGB_GREEN);  

  mRgb.show();

  if (LEFT_DIRECTION == p_direction)

  {

    Servo180(1, 180);     

  } else if (RIGHT_DIRECTION == p_direction)

  {

    Servo180(1, 0);     

  } else if (FRONT_DIRECTION == p_direction)

  {

    Servo180(1, 90);     

  }

  else if (ALL_CHECKED_START_ACTION == p_direction)   //舵机转动左,右,前三个方向完成测距

  {

    if (0 == cnt)

    {

      brake();

      delay(50);

    }

    cnt++;   

    if (LeftDistance < 25 && RightDistance < 25 && FrontDistance < 25)   //比较三个方向的距离

    {

      mRgb.setColor(0,RGB_PURPLE);  

      mRgb.show();

      spin_right(80);     

      delay(19);

    } else if (LeftDistance >= RightDistance)

{

      mRgb.setColor(0,RGB_BLUE);  

      mRgb.show();

      spin_left(80);     

      delay(13);

    } else if (LeftDistance < RightDistance)

    {

      mRgb.setColor(0,RGB_YELLOW);  

      mRgb.show();

      spin_right(80);   

      delay(13);

    }

    if (cnt > 50)    //此处是控制小车左旋、右旋的时间19*50/13*50

    {

      brake();

      LeftDistance = 0;

      RightDistance = 0;

      FrontDistance = 0;

      cnt = 0;

      ret = 1;

      return ret;

    }

    else

    {

      return ret;

    }

  }

    delay(20);

    cnt++;

    if (cnt > 20)   //这里是用来控制,让舵机每次转动之后延迟20ms

    {

      cnt = 0;

      Distance();

      if (LEFT_DIRECTION == p_direction)

      {

        LeftDistance = distance;

      } else if (RIGHT_DIRECTION == p_direction)

      {

        RightDistance = distance;

      } else if (FRONT_DIRECTION == p_direction)

      {

        FrontDistance = distance;

      }

      ret = 1;

    }

  return ret;

}

/**

* Function       UltrasonicAvoidServoMode

* @author        Cindy

* @date          2019.09.11

* @brief         超声波避障模式

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void UltrasonicAvoidServoMode()

{

  static int cnt_1 = 0;

  static int distance_smaller_25 = 0;

  static int bak_distance = 0;

  int ret = 0;

  if (0 == distance_smaller_25)

  {

    Distance_test();

    bak_distance = distance;

  }

  if (bak_distance < 25)    //如果检测到的距离小于25,小车开始避障

  {

    if (0 == distance_smaller_25)

    {

      cnt_1 = 0;

      distance_smaller_25 = 1;

      brake();

      delay(50);

    }

    ret = ult_check_distance_and_action(cnt_1);

    if (-1 == ret)

    {

      distance_smaller_25 = 0;

      bak_distance = 0;

      cnt_1 = 0;

      return;

    }

    else if (1 == ret)

   {

      cnt_1 ++;

    }

    if (4 == cnt_1)

    {

      distance_smaller_25 = 0;

      bak_distance = 0;

      cnt_1 = 0;

    }

  }

  else if(bak_distance >= 25)     //如果距离大于25,小车保持前进

  {

    if (1 == distance_smaller_25)

    {

      distance_smaller_25 = 0;

    }

    bak_distance = 0;

    advance(95);

  }

}

/********************************************************************************************************/

/*模式1---七彩灯模式*/

/**

* Function       color_light

* @author        Cindy

* @date          2019.09.11

* @brief         通过舵机转动到不同的角度改变RGB的颜色

* @param[in]     pos:舵机转动的角度

* @param[out]    void

* @retval        void

* @par History   no

*/

void color_light(int pos)

{

  //当舵机转动到150-180度之间,点亮白色

  if (pos > 150)

  {

    mRgb.setColor(0,RGB_WHITE);   

    mRgb.show();

  }

  //当舵机转动到125-150度之间,点亮红色

  else if (pos > 125)

  {

    mRgb.setColor(0,RGB_RED);  

    mRgb.show();

  }

  //当舵机转动到100-125度之间,点亮绿色

  else if (pos > 100)

  {

    mRgb.setColor(0,RGB_GREEN);  

    mRgb.show();

  }

  //当舵机转动到75-100度之间,点亮蓝色

  else if (pos > 75)

mRgb.setColor(0,RGB_BLUE);

    mRgb.show();

  }

  //当舵机转动到50-75度之间,点亮黄色

  else if (pos > 50)

  {

    mRgb.setColor(0,RGB_YELLOW);  

    mRgb.show();

  }

  //当舵机转动到25-50度之间,点亮紫色

  else if (pos > 25)

  {

    mRgb.setColor(0,RGB_PURPLE);  

    mRgb.show();

  }

  //当舵机转动到0-25度之间,点亮青色

  else if (pos > 0)

  {

    mRgb.setColor(0,RGB_CYAN);  

    mRgb.show();

  }

  else

  {

    mRgb.setColor(0,RGB_OFF);  

    mRgb.show();

  }

}

/**

* Function       ServoColorRGBMode

* @author        wusicaijuan

* @date          2019.06.26

* @brief         七彩灯模式

* @param[in1]    brightness

* @param[in2]    time

* @param[in3]    increament

* @param[out]    void

* @retval        void

* @par History   no

*/

static int pos = 0;

static int is_max_pos = 0;

void   ServoColorRGBMode()

{

  if (0 == is_max_pos)

  {

    pos += 25;

    if (pos >= 180)

    {

        is_max_pos = 1;  

    }

  } else {

    pos -= 25;

    if (pos <= 0)

    {

        is_max_pos = 0;

    }

  }

  Servo180(1, pos);

  color_light(pos);

  delay(200);

}

/**

* Function       BeepOnOffMode

* @author        Cindy

* @date          2019.09.11

* @brief         切换模式鸣笛

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void BeepOnOffMode()

{

  for (int i = 0; i < 200; i++)

  {

    digitalWrite(buzzer, HIGH); //发出声音

    delay(3);         

    digitalWrite(buzzer, LOW);   //不发出声音

    delay(1);         

  }

}

/**

* Function       serial_data_parse

* @author        Cindy

* @date          2019.09.11

* @brief         Serial port data parsing and specify the corresponding action

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void serial_data_parse()

{

    //Serial.println(InputString);

    //解析串口发来指令并控制舵机云台的转动

    //首先,判断是否进入了模式选择

    if (InputString.indexOf("Mode") > 0 && (InputString.length() == 9) )

    {

      if (InputString[6] == '0'&& InputString[7] == '0') //stop

      {

        Clear_All_PWM();

        g_CarState = enSTOP;

        g_modeSelect = 0;

        BeepOnOffMode();

        Servo180(1,90);

        Ultrasonic_RGB(0,0,0);

      }

      else if(InputString[6] != '0' && InputString[7] == '1')

      {

        switch (InputString[6])

        {

          //case '0': g_modeSelect = 0; Clear_All_PWM(); break;

          case '1': g_modeSelect = 1; BeepOnOffMode(); break;

          case '2': g_modeSelect = 2; BeepOnOffMode(); break;

          case '3': g_modeSelect = 3; BeepOnOffMode(); break;

          default: g_modeSelect = 0;   break;

        }

        //delay(1000);

        //BeepOnOffMode();

      }

        InputString = "";                     

        NewLineReceived = false;

        return;

    }

    if (g_modeSelect != 0)

    {

      InputString = "";                   

      NewLineReceived = false;

      return;

    }

   

    if (InputString.indexOf("Music") > 0 && (InputString.length() == 10))

    {

      //Serial.println(InputString);

      g_modeMusic = 1;   //开启音乐模式

      g_musicSelect = (InputString[8] - 0x30)*10 + (InputString[7] - 0x30);   //将字符串转化成数字

      g_num = 0;

      InputString = "";                     

      NewLineReceived = false;

      return;

    }

    //解析上位机发来的舵机云台的控制指令并执行舵机旋转

   if (InputString.indexOf("Servo") > 0 )

    {

    //$Servo,UD180# servo rotate 180

    if (InputString.indexOf("UD") > 0)   //控制垂直方向的舵机(摄像头上下转动)

    {

      int i = InputString.indexOf("UD"); //寻找以PTZ开头,#结束中间的字符

      int ii = InputString.indexOf("#", i);

      if (ii > i)

      {

        String m_skp = InputString.substring(i + 2, ii);

        int m_kp = m_skp.toInt(); //将找到的字符串变成整型

        Servo180(3, 180 - m_kp);   //让舵机转动到指定角度m_kp

      }

      InputString = ""; //清空串口数据

      NewLineReceived = false;

      return; //跳出循环

    }

    //$Servo,LRS#   舵机停止

    //$Servo,LRL#   舵机左转

    //$Servo,LRR#   舵机右转

      if (InputString.indexOf("LR") > 0)

      {

        switch (InputString[9])

        {

        case H_servoL:

          g_HServoState = enHServoL;

          break;

        case H_servoR:

          g_HServoState = enHServoR;

          break;

        case H_servoS:

          g_HServoState = enHServoS;

          break;

        }

         InputString = ""; //清除串口数据

         NewLineReceived = false;

         return;

      }

    }

  //解析上位机发来的通用协议指令,并执行相应的动作

  //如:$1,0,0,0#    小车前进

  //将数据的长度作为判断的依据

  if ((InputString.indexOf("Mode") == -1) && (InputString.indexOf("Servo") == -1) && (InputString.length() == 9))

  {

    //Light up RGB

    if (InputString[7] == '1')     

    {

       int r = random (0, 255);  

       int g = random (0, 255);  

       int b = random (0, 255);

       PCB_RGB(r,g,b);

       Ultrasonic_RGB(r,g,b);

       InputString = ""; //清除串口数据

      NewLineReceived = false;

      return;

    }

     //Make whistle

    if (InputString[5] == '1')     

    {

      whistle();   //鸣笛

    }

    //调节小车的速度

    if (InputString[3] == '1') //加速

    {

      CarSpeedControl += 20;

      if (CarSpeedControl > 150)

      {

        CarSpeedControl = 150;

      }

      InputString = ""; //清除串口数据

      NewLineReceived = false;

      return;

    }

    if (InputString[3] == '2') //减速

    {

      CarSpeedControl -= 20;

      if (CarSpeedControl < 50)

      {

        CarSpeedControl = 50;

      }

      InputString = ""; //清除串口数据

      NewLineReceived = false;

      return;

    }

    switch (InputString[1])

    {

    case run_car:

      g_CarState = enRUN;

      break;

    case back_car:

      g_CarState = enBACK;

      break;

    case left_car:

      g_CarState = enLEFT;

      break;

    case right_car:

      g_CarState = enRIGHT;

      break;

    case spin_left_car:

      g_CarState = enSPINLEFT;

      break;

    case spin_right_car:

      g_CarState = enSPINRIGHT;

      break;

    case stop_car:

      g_CarState = enSTOP;

      break;

    default:

      g_CarState = enSTOP;

      break;

    }

     InputString = "";         //清除串口数据

    NewLineReceived = false;

   

    //控制小车的运动状态

    switch (g_CarState)

    {

    case enSTOP:

      brake();

      break;

    case enRUN:

      advance(CarSpeedControl);

      break;

    case enLEFT:

      left(CarSpeedControl);

      break;

    case enRIGHT:

      right(CarSpeedControl);

      break;

    case enBACK:

      back(CarSpeedControl);

      break;

    case enSPINLEFT:

      spin_left(CarSpeedControl);

      break;

    case enSPINRIGHT:

      spin_right(CarSpeedControl);

      break;

    default:

      brake();

      break;

    }

  }

  InputString = ""; //清除串口数据

  NewLineReceived = false;

  return;

}

/**

* Function       serial_data_postback

* @author        Cindy

* @date          2019.09.11

* @brief         将采集的传感器数据通过串口传输给上位机显示

* @param[in]     void

* @retval        void

* @par History   NO

*/

void serial_data_postback()

{

  /*将超声波的数据传送到APK并且显示出来 */

  //$CSB,000#

  //Ultrasonic Data

  Distance_test();

  ReturnTemp = "$CSB," ;

  ReturnTemp.concat(distance);

  ReturnTemp += "#";

  Serial.print(ReturnTemp);

  return;

}

/*

* Function       HServo_State

* @author        wusicaijuan

* @date          2019.07.04

* @brief         Control state of servo

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   no

*/

void HServo_State()   //控制摄像头水平方向转动(左---右)

{

  if (g_HServoState != enHServoS)

  {

    if (g_HServoState == enHServoL)

    {

      Servo_LR++;

      if (Servo_LR > 180)

      {

        Servo_LR = 180;

      }

      Servo180(2, Servo_LR);

      delay(5);

    }

    if (g_HServoState == enHServoR)

    {

      Servo_LR--;

      if (Servo_LR < 0)

      {

        Servo_LR = 0;

      }

      Servo180(2, Servo_LR);

      delay(5);

     }

  return;

  }

}

/**

* Function       serialEvent

* @author        Cindy

* @date          2019.09.11

* @brief         

* @param[in]     void

* @param[out]    void

* @retval        void

* @par History   

*/

void serialEvent()   //该函数在Arduino内部被自动调用

{

  while (Serial.available())

  {

    //一个字节一个字节地读,下一句是读到的放入字符串数组中组成一个完成的数据包

    IncomingByte = Serial.read();

    if (IncomingByte == '$')

    {

      StartBit = true;

    }

    if (StartBit == true)

    {

      InputString += (char) IncomingByte;

    }

    if (StartBit == true && IncomingByte == '#')

    {

      NewLineReceived = true;

      StartBit = false;

    }

  }

}

/**

* Function       loop

* @author        Cindy

* @date          2019.09.11

* @brief         主函数

* @param[in]     void

* @retval        void

* @par History   

*/

void loop()

{

  if (NewLineReceived)

  {

    serial_data_parse();  

  }

  newtime = millis();

  if (newtime - lasttime > flag_time)

  {

    lasttime = newtime;

    InputString = "";   //清除串口数据

  }

  switch (g_modeSelect)

  {

    case 1:

      ServoColorRGBMode();   //七彩灯模式

      break;

    case 2:

      UltrasonicAvoidServoMode();//超声波避障模式

      break;

    case 3:

      Tracking_Mode();   //巡线模式

      break;

    case 0:

    default:

      break;

  }

  if( g_modeMusic == 1 )

  {

      switch (g_musicSelect)

      {

        case 11:

          music_Play(1, g_num);

          break;

        case 12:

          music_Play(2, g_num);

          break;

        case 13:

          music_Play(3, g_num);

          break;

        case 14:

          music_Play(4, g_num);

          break;

        case 15:

          music_Play(5, g_num);

          break;  

        case 0:

        default:

          g_modeMusic = 0;

          break;

      }

      g_num++;

      if(g_musicSelect != 0 && g_num >= music_max[g_musicSelect % 10 - 1])

      {

        g_num = 0;

        g_modeMusic = 2; //stop music

      }

  }

  // if (g_modeSelect == 0 && g_modeMusic == 0 && g_motor == false)   //上报超声波距离

  // {

  //   time--;

  //   if (time == 0)

  //   {

  //     count--;

  //     time = 40000;

  //     if (count == 0)

  //     {

  //       serial_data_postback();

  //       time = 40000;

  //       count = 10;

  //     }

  //   }

  // }

  HServo_State();  

}

实验现象

      程序下载完成之后,使用APK控制小车。 手机连接上WIFI摄像头模块的WIFI热点:Yahboom_WIFI,无需密码,可以直接连接。 具体步骤如下:程序下载完成之后,将WIFI 摄像头模块的接线插入扩展板上相应位置。打开小车的电源开关,可以看到WIFI摄像头模块上面红色的指示灯处于闪烁状态。下载APK:安卓用户请用浏览器扫描二维码,下载并安装APK;苹果用户请用相机扫码二维码或者进入APP Store搜索“YahboomRobot”,下载并安装该APK。

      模式选择当模式切换成功之后,我们可以听到蜂鸣器鸣笛。如果你点击了一下选项,没有听到蜂鸣器鸣笛的话,就证明没有进入模式,请APK界面的按钮,或者再次长按APK 界面的按钮再松手,即可成功进入相应的模式。

2. 结论

      相较于其他系统,Arduino表现出编程简易、成本低、平台跨越等一系列优势,将其应用于智能小车设计具有十分重要的现实意义.文章通过阐述整体系统设计方案,对Arduino单片机智能小车硬件设计、软件设计及示例功能的实现展开探讨,旨在为促进智能小车设计的健康稳定发展提供必要借鉴。

      小车以Arduino为控制核心,用单片机产生PWM波,控制小车速度。利用灰度传感器对路面黑色轨迹进行检测,并将路面检测信号反馈给单片机。单片机对采集到的信号予以分析判断,及时控制驱动电机以调整小车转向,从而使小车能够沿着黑色轨迹自动行驶,实现小车自动循迹的目的。

以Arduino单片机为控制核心,基于超声波测距的原理,利用超声波传感器,检测小车前方障碍物的距离,然后把数据传送给单片机。当超声波检测到距离小车前方15CM有障碍物时单片机就发出指令让小车左转一定角度,然后停止行进继续探测。如果前方15CM没有障碍物则直行,否则继续左转一定角度。如此通过超声波不断的循环检测周边环境的情况进行自动避障。

      总体来说,基于Arduino的Roboduino智能小车还是有市场前景的,这是人工智能必要的产物,紧跟时代潮流。

更多详情请见:【S054】智能循迹避障小车

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/322971.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Element UI CascaderPanel级联组件使用和踩坑总结

Element UI CascaderPanel级联组件使用和踩坑总结 问题背景 需求中需要用到Element UI的 CascaderPanel组件&#xff0c;并且支持多选&#xff0c;定制化需求&#xff0c;比如某节点被选择&#xff0c;等价于该节点下面所有子节点都被选择&#xff0c; CascaderPanel组件返回…

K6 性能测试教程:常用功能 - HTTP 请求,指标和检查

这篇文章详细介绍了 K6 中的 HTTP 请求&#xff08;http request&#xff09;功能&#xff0c;解析了常用的性能指标和检查功能。通过 HTTP 请求模拟用户行为&#xff0c;了解性能指标以评估系统响应。文章还深入讲解了如何配置和执行检查&#xff0c;确保性能符合预期标准。无…

什么是技术架构?架构和框架之间的区别是什么?怎样去做好架构设计?(一)

什么是技术架构?架构和框架之间的区别是什么?怎样去做好架构设计?(一)。 在软件行业,对于什么是架构,都有很多的争论,每个人都有自己的理解。在不同的书籍上, 不同的作者, 对于架构的定义也不统一, 角度不同, 定义不同。 一、架构是什么 Linux 有架构,MySQL 有架构,J…

基于dinoV2分类模型修改

前言 dinoV2已经发布有一段时间了&#xff0c;faecbook豪言直接说前面的结构我们都不需要进行修改&#xff0c;只需要修改最后的全连接层就可以达到一个很好的效果。我们激动的揣摸了下自己激动的小手已经迫不及待了&#xff0c;这里我使用dinoV2进行了实验&#xff0c;来分享…

7.3 数据库的基本查询

数据库的基本查询 1. 提要2. 简单查询3. 高级查询3.1 数据分页_limit3.2 排序_order by3.3 查询去重_distinct 4. 条件查询 1. 提要 2. 简单查询 3. 高级查询 3.1 数据分页_limit 3.2 排序_order by 3.3 查询去重_distinct 4. 条件查询

“与辉同行”首秀金额过亿,一个东方甄选拆出无数个董宇辉?

董宇辉又爆了&#xff01; 小作文风波后&#xff0c;董宇辉不仅摇身一变成东方甄选新股东&#xff0c;还自立门户成立了新直播间“与辉同行”。 首秀当天在抖音平台正式开播&#xff0c;首秀就创下了惊人的成绩&#xff1a;直播间人气高达1.2亿&#xff0c;销售额超过1.1亿&a…

MongoDB Compass 的教程

第一步&#xff1a;建立连接 点击Save&Connect 增加数据库&#xff1a; 填写数据库名字和文档名字并点击Create Database 删除文档&#xff1a; 创建文档&#xff1a; 插入文档数据 {Id:1001,name:"cyl",age:21} 插入成功&#xff1a; 更改原有数据 删除原有数据…

【LeetCode】202. 快乐数(简单)——代码随想录算法训练营Day06

题目链接&#xff1a;202. 快乐数 题目描述 编写一个算法来判断一个数 n 是不是快乐数。 「快乐数」 定义为&#xff1a; 对于一个正整数&#xff0c;每一次将该数替换为它每个位置上的数字的平方和。然后重复这个过程直到这个数变为 1&#xff0c;也可能是 无限循环 但始终…

Multimodal Contrastive Training for Visual Representation Learning

parameterize the image encoder as f i q _{iq} iq​ query feature q i i _{ii} ii​&#xff0c;key feature k i i _{ii} ii​ parameterize the textual encoder as f c q ( ⋅ ; Θ q , Φ c q ) f_{cq}(; Θ_q, Φ_{cq}) fcq​(⋅;Θq​,Φcq​)&#xff0c;momentum …

西贝柳斯音乐记谱软件Avid Sibelius Ultimate 2023中文激活版

Avid Sibelius(西贝柳斯终极解锁版) 是一款记谱软件&#xff0c;从有抱负的作曲家和词曲作者到教师和学生&#xff0c;任何人都可以快速轻松地开始创作和分享音乐。对于那些还不熟悉使用符号软件的人来说&#xff0c;直观的界面将引导您完成整个过程。磁性布局可防止对象相互碰…

API可视化编排如何实现

企业随着前后端分离架构、微服务架构、中台战略、产业互联互通的实施必将产生大量的各种协议的API服务&#xff0c;API将成为企业的数字化资产且API会越来越多&#xff0c; API服务之间的相互调用和依赖情况也随之越来越多和复杂。业务系统与业务系统之间、关联企业之间的API都…

【mars3d】 graphic.bindPopup(inthtml).openPopup()无需单击小车,即可在地图上自动激活弹窗的效果。

实现效果&#xff1a;new mars3d.graphic.FixedRoute({无需单击小车&#xff0c;即可在地图上实现默认打开弹窗的激活效果。↓↓↓↓↓↓↓↓ 相关链接说明&#xff1a; 1.popup的示例完全开源&#xff0c;可参考&#xff1a;功能示例(Vue版) | Mars3D三维可视化平台 | 火星科…

谷粒商城篇章8 ---- P236-P247 ---- 购物车【分布式高级篇五】

目录 1 环境搭建 1.1 新建购物车服务模块gulimall-cart 1.2 购物车服务相关配置 1.2.1 pom.xml 1.2.2 yml配置 1.2.2.1 application.yml配置 1.2.2.2 bootstrap.yml配置 1.2.3 主类 1.3 SwitchHosts增加配置 1.4 网关配置 1.5 整合SpringSession 1.5.1 session数据…

如何使用LightPicture+cpolar搭建个人云图床随时随地公网访问

文章目录 1.前言2. Lightpicture网站搭建2.1. Lightpicture下载和安装2.2. Lightpicture网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar云端设置3.2.Cpolar本地设置 4.公网访问测试5.结语 1.前言 现在的手机越来越先进&#xff0c;功能也越来越多&#xff0c;而手机…

selenium 做 Web 自动化,鼠标当然也要自动化!

我们在做 Web 自动化的时候&#xff0c;有时候页面的元素不需要我们点击&#xff0c;值需要把鼠标移动上去就能展示各种信息。这个时候我们可以通过操作鼠标来实现&#xff0c;接下来我们来讲一下使用 selenium 做 Web 自动化的时候如何来操作鼠标。鼠标操作&#xff0c;我们可…

接口自动化测试难点:数据库验证解决方案

接口自动化中的数据库验证&#xff1a;确保数据的一致性和准确性 接口自动化测试是现代软件开发中不可或缺的一环&#xff0c;而数据库验证则是确保接口返回数据与数据库中的数据一致性的重要步骤。本文将介绍接口自动化中的数据库验证的原理、步骤以及示例代码&#xff0c;帮…

Nodejs基础3之fs模块的文件重命名和移动、文件的删除、文件夹操作、查看资源状态、fs路径

Nodejs基础二 fs模块文件重命名和移动文件的重命名文件的移动同步重命名和移动 文件的删除使用unlink进行删除unlink异步删除unlinkSync同步删除 使用rm进行删除rm异步删除rmSync同步删除 文件夹操作创建文件夹递归创建文件夹 读取文件夹删除文件夹rmdir删除文件夹删除递归文件…

K8s-Pod资源(二)node调度策略、node亲和性、污点与容忍度

目录 node调度策略nodeName和nodeSelector 指定nodeName 指定nodeSelector node亲和性 node节点亲和性 硬亲和性 软亲和性 污点与容忍度 本文主要介绍了在pod中&#xff0c;与node相关的调度策略&#xff0c;亲和性&#xff0c;污点与容忍度等的内容 node调度策略node…

一文速学-selenium高阶性能优化技巧

一文速学-selenium高阶性能优化技巧 前言 最近写的挺多自动化办公的selenium程序没有做优化&#xff0c;执行效率不高&#xff0c;启动浏览器又慢但是又可能出现其他不可控的因素&#xff0c;总结来说虽然放心运行但是又没那么好用&#xff0c;项目是写完了最后还是需要优化结…