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

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

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

指导老师:姚清元

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

      本文设计的是一款基于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 updatesLOGO_breathing_light(255, 40, 5);   //Gradually light the blue light of the Yhaboom_LOGOSerial.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 updatesClear_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 lefterror = max;}else{//spin righterror = -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 Forwardpwm.setPWM(11, 0, 0);pwm.setPWM(8, 0, Speed2); //Right rear wheel Forwardpwm.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 Forwardpwm.setPWM(12, 0, 0);pwm.setPWM(15, 0, Speed1); //Left front wheel Forwardpwm.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 Forwordpwm.setPWM(11, 0, 0);pwm.setPWM(8, 0, Speed);   //Right rear wheel Forwordpwm.setPWM(9, 0, 0);pwm.setPWM(13, 0, 0);     //Left front wheel Backpwm.setPWM(12, 0, Speed);pwm.setPWM(15, 0, 0);     //Left rear wheel Backpwm.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 Backpwm.setPWM(8, 0, 0);pwm.setPWM(9, 0, Speed);   //Right rear wheel Backpwm.setPWM(13, 0, Speed);   //Left front wheel Forwordpwm.setPWM(12, 0, 0);pwm.setPWM(15, 0, Speed);   //Left rear wheel Forwordpwm.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 valwhile (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 valwhile (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 pinvoid 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 updatesClear_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 Forwardpwm.setPWM(11, 0, 0);pwm.setPWM(8, 0, Speed); //Right rear wheel Forwardpwm.setPWM(9, 0, 0);pwm.setPWM(13, 0, Speed); //Left front wheel Forwardpwm.setPWM(12, 0, 0);pwm.setPWM(15, 0, Speed); //Left front wheel Forwardpwm.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 Reversepwm.setPWM(11, 0, 0);pwm.setPWM(8, 0, Speed);   //Right rear wheel Reversepwm.setPWM(9, 0, 0);pwm.setPWM(13, 0, 0);     //Left front wheel Stoppwm.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 Stoppwm.setPWM(11, 0, 0);pwm.setPWM(8, 0, 0);pwm.setPWM(9, 0, 0);pwm.setPWM(13, 0, Speed);   //Left front wheel Reversepwm.setPWM(12, 0, 0);pwm.setPWM(15, 0, Speed); //Left rear wheel Reversepwm.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 Forwordpwm.setPWM(11, 0, 0);pwm.setPWM(8, 0, Speed);   //Right rear wheel Forwordpwm.setPWM(9, 0, 0);pwm.setPWM(13, 0, 0);      //Left front wheel Backpwm.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 Backpwm.setPWM(8, 0, 0);pwm.setPWM(9, 0, Speed);   //Right rear wheel Backpwm.setPWM(13, 0, Speed);   //Left front wheel Forwordpwm.setPWM(12, 0, 0);pwm.setPWM(15, 0, Speed);   //Left rear wheel Forwordpwm.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 valwhile (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 valwhile (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    29void 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   0x000000const 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; //时间标记点间隔2sint newtime = 0;            //记录系统当前的时间int lasttime = 0;           //记录系统上一次的时间点int g_num = 0;int buzzer = 10;            //定义蜂鸣器的引脚int CarSpeedControl = 80;   //set speed of carint 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;//Musicenum 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 698const 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;//1000000Lint 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]   index1: s12: s23: s34: s4* @param[in2]   degree (0 <= degree <= 180)* @retval       void*/void Servo180(int num, int degree){long us = (degree * 1800 / 180 + 600); // 0.6 ~ 2.4long pwmvalue = us * 4096 / 20000;   // 50hz: 20,000 uspwm.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;    //LeftDistancestatic int RightDistance = 0;   //RightDistancestatic int FrontDistance = 0;   //FrontDistancestatic 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 180if (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 RGBif (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 whistleif (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 DataDistance_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】智能循迹避障小车

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Codeforces Round 114 (Div. 1) C. Wizards and Numbers(思维题 辗转相除+博弈 巴什博弈)

题目 t(t<1e4)组询问&#xff0c;每次询问(a,b)&#xff08;0<a,b<1e18&#xff09;&#xff0c; 不妨a<b&#xff08;a>b时需要交换两个数考虑&#xff09; ①令b减去a的k次方&#xff08;k>1&#xff09;&#xff0c;要求减完之后b非负 ②令bb%a 当a和…

java处理16进制字符串的一些方法和基础知识

前言&#xff1a;本篇文章是对于基础数据的处理的一些简单经验总结里边包含了一些基础的数据储存和数据转化的一些知识&#xff0c;同样也包含有部分快捷的数据处理方法。主要用于个人知识的一个记录和方便进行对应的数据转换和处理。 1、bit,字节和字的关系 1.1 bit和字节的…

2024全新开发API接口调用管理系统网站源码 附教程

2024全新开发API接口调用管理系统网站源码 附教程 用layui框架写的 个人感觉很简洁 方便使用和二次开发

腾讯云MPS为出海媒体企业助力

在如今互联网发达的时代&#xff0c;一个视频通过网络发布即可供给全球用户进行观看。其中视频媒体企业便其中的领头先锋&#xff0c;为了让创作者们以及全球各大用户的视频进行快速推广&#xff0c;出海则是不二之选。但是因为各地区域的不同&#xff0c;带宽的不同与网络的限…

深信服技术认证“SCSA-S”划重点:逻辑漏洞

为帮助大家更加系统化地学习网络安全知识&#xff0c;以及更高效地通过深信服安全服务认证工程师考核&#xff0c;深信服特别推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信服…

桌面显示器type-c接口方案

在当今时代&#xff0c;TYPE-C接口桌面显示器已经成为了我们生活和工作中不可或缺的重要设备之一。与传统显示器相比&#xff0c;新型的TYPE-C接口桌面显示器具有更多的功能和优势&#xff0c;其中最显著的特点就是支持视频传输和充电功能。 首先&#xff0c;TYPE-C接口桌面显示…

[ACM 学习] 最长上升子序列

LIS&#xff08;最长上升子序列&#xff09;的三种经典求法 - 一只不咕鸟 - 博客园 (cnblogs.com) 理解一下第三种方法&#xff08;贪心二分查找&#xff09; 因为构建的是上升子序列&#xff0c;所以是可以用二分查找找到最大的小于当前 A[i] 的在子序列中的 F[j]&#xff0…

关于steam游戏搬砖,想给大家的几点忠告

关于CSGO游戏搬砖&#xff0c;想给大家的几点忠告&#xff1a; 1、新出的箱子&#xff0c;里面开出的皮肤短时间内会溢价&#xff0c;价格虚高&#xff0c;后期会呈逐渐下跌趋势&#xff0c;这就是我们不让大家碰新品的原因&#xff0c;哪怕利润再高也不建议购入或者囤货&…

Linux Mii management/mdio子系统分析之五 PHY状态机分析及其与net_device的关联

&#xff08;转载&#xff09;原文链接&#xff1a;https://blog.csdn.net/u014044624/article/details/123303714 前面几章基本上完成了mdio模块驱动模型的分析&#xff0c;本篇文章主要讲述phy device的状态机以及phy device与net_device的关联。Phy device主要是对phy的抽象…

C语言:自定义类型——结构体

一、什么叫做结构体 C语⾔已经提供了内置类型&#xff0c;如&#xff1a;char、short、int、long、float、double等&#xff0c;但是只有这些内置类型还是不够的&#xff0c;假设我想描述学⽣&#xff0c;描述⼀本书&#xff0c;这时单⼀的内置类型是不⾏的。描述⼀个学⽣需要 …

SMD NTC Thermistor NTC热敏电阻产品基本参数定义

热敏电阻器&#xff08;Thermistor&#xff09;是一种电阻值对温度极为灵敏的半导体元件&#xff0c;温度系数可分为Positive Temperature Coefficient 正温度系数热敏电阻又称PTC热敏电阻和Negative Temperature Coefficient 负温度系数热敏电阻又称NTC热敏电阻. NTC热敏电…

Seata分布式事务

文章目录 Seata分布式事务1.分布式事务问题1.1.本地事务1.2.分布式事务 2.理论基础2.1.CAP定理2.1.1.一致性2.1.2.可用性2.1.3.分区容错2.1.4.矛盾 2.2.BASE理论2.3.解决分布式事务的思路 3.初识Seata3.1.Seata的架构3.2.部署TC服务3.2.1.下载3.2.2.解压3.2.3.修改配置3.2.4.在…

【JVM】常用命令

一、前言 Java虚拟机&#xff08;JVM&#xff09;是Java程序运行的基础设施&#xff0c;它负责将Java字节码转换为本地机器代码并执行。在开发过程中&#xff0c;我们经常需要使用一些命令来监控和管理JVM的性能和状态。本文将详细介绍6个常用的JVM命令&#xff1a;jps、jstat…

Linux平台建立GB28181设备模拟器

目录 下载模拟器解决动态库缺少问题运行模拟器抓包参考资料 在没有GB28181摄像机的情况下,在Linux虚拟机中模拟出一台GB28181摄像机用于调试和学习. 下载模拟器 到网站下载Linux 平台版本: https://www.happytimesoft.com/download.html tar -zxvf happytime-gb28181-device…

腾讯云服务器定价_云服务器价格_云服务器计费模式

腾讯云服务器租用价格表&#xff1a;轻量应用服务器2核2G3M价格62元一年、2核2G4M价格118元一年&#xff0c;540元三年、2核4G5M带宽218元一年&#xff0c;2核4G5M带宽756元三年、轻量4核8G12M服务器446元一年、646元15个月&#xff0c;云服务器CVM S5实例2核2G配置280.8元一年…

创新与竞争:Facebook如何在社交媒体市场站稳脚跟

社交媒体市场一直都是激烈的竞争场地&#xff0c;而Facebook凭借其创新的战略一直站稳脚跟&#xff0c;不断引领行业潮流。本文将深入探讨Facebook是如何通过创新与竞争策略&#xff0c;在社交媒体市场中牢牢占据领导地位的。 多元化产品布局 Facebook并非只是一家单一产品的公…

[Java刷算法]牛客—剑指offer第一天

&#x1f9db;‍♂️个人主页&#xff1a;杯咖啡&#x1f4a1;进步是今天的活动&#xff0c;明天的保证&#xff01;✨目前正在学习&#xff1a;SSM框架,算法刷题&#x1f64c;牛客网&#xff0c;刷算法过面试的神级网站&#xff0c;用牛客你也牛。 &#x1f449;免费注册和我一…

我成为开源贡献者的原因竟然是做MySql-CDC数据同步

今年下半年机缘巧合下公司决定搭建自己的数据中台&#xff0c;中台的建设势必少不了数据集成。首先面临的就是数据集成技术选型的问题&#xff0c;按照社区活跃度、数据源适配性、同步效率等要求对市面上几个成熟度较高的开源引擎进行了深度调研。 最终经过内部讨论决定用Apac…

云服务器部署Stable Diffusion Webui从0到1总结:反复重启+循环debug

文章目录 在学校服务器部署Stable Diffusion Webui&#xff08;一&#xff09;准备工作&#xff08;二&#xff09;环境配置&#xff08;三&#xff09;git拉取项目到本地&#xff08;四&#xff09;运行项目 遇到的问题&#xff1a;&#xff08;一&#xff09;使用git clone时…

JAVAEE初阶 多线程进阶(二)

多线程进阶相关知识点 一.CAS1.1 CAS的原子类1.2 实现自旋锁1.3CAS中的ABA问题1.4 ABA问题的解决 二. callable接口三.reentrantLock3.1 reentrantLock与synchronized区别 四.信息量 semaphore五. CountDownLatch六. concurrentHashMap6.1 concurrentHashMap的优点 一.CAS CAS …