• Arduino红外循迹小车代码笔记和常见问题解决


    本人刚接触Arduino不久,此代码用于参加校内的比赛,采用是4个红外传感器来检测小车是否压倒黑线,小车是有两个电机,也就是两个由电机控制的大轮,和一个可以转动的小轮用来控制方向。

    在烧写小车代码之前,需要确保Arduino的配置正确,选择正确的开发板端口

    并且根据实际硬件的不同来连接小车的线路。

    在我们实际小车在简易路线(没有坡度,障碍等因素影响)运行的过程当中呢,我们所需要面对的情况有直线、十字路口、小幅度弯道、大幅度弯道四种常见情况,为了更好的应对这几种情况,传感器的数量多当然要更加有优势。

    这里四个红外传感器,也就是我们的循迹模块的情况(1为检测到黑线)如下:

    基本情况:

    1.小车红外向前方向,从左往右依次是1~4:
    2.小车放置赛道的时候需要将黑线最好放置在2,3传感器之间

    3.当数值为1时,表示检测到了黑线

    直行:当我们小车没有传感器检测到黑线时,则为一般直行情况

               当小车四个传感器全部检测到黑线时,表示遇到十字路口,依旧直行

    传感器1传感器2传感器3传感器4情况
    0000直行
    1111直行

    大幅度转弯:

    因为我们默认小车上赛道的摆放方式是3,4传感器位于黑色赛道的两边,

    当只有最外侧的1或4传感器检测到黑线的时候,就表明我们已经严重偏离了赛道,需要进行大幅度的矫正

    当左侧或者右侧两个传感器都检测到黑线的时候,表示我们的小车遇到了转弯幅度更为大的弯道甚至是直角弯,这里同样需要进行大幅度转弯,甚至比上面的情况转弯幅度更大。因为在实际的调试过程当中,我发现,如何小车的转弯速度不够快,同样也会导致左侧或右侧两个传感器同时检测到黑线的情况,所以这样写也更为保险一些。

    传感器1传感器2传感器3传感器4情况
    1000大幅度左转
    0001大幅度右转
    1100大幅度左转
    0011大幅度右转

    小幅度转弯:当内侧传感器检测到黑线时,表示我们的小车轻微偏离轨道,则选择小幅度的进行矫正即可

    传感器1传感器2传感器3传感器4情况
    0100小幅度左转
    0010小幅度右转

    代码片段:

    引脚定义片段,这里的引脚定义需要根据实际的板子的型号不同进行选择,我这里3,5,6,9引脚是带有~号样式的引脚,也就是可以使用PWM调速的引脚,这里只对PWM进行简单的使用说明,PWM的范围为0~255,数值越大电机转速越快。

    以下为电机引脚的定义:

    1. // 电机引脚定义
    2. int LeftMotorGo = 3; // 左电机前进 (IN1)
    3. int LeftMotorBack = 5; // 左电机后退 (IN2)
    4. int RightMotorGo = 6; // 右电机前进 (IN3)
    5. int RightMotorBack = 9; // 右电机后退 (IN4)

    和电机相同,红外线传感器也需要在Arduino中进行定义说明:

    1. // 红外传感器引脚定义
    2. const int Sensor1 = 4; // 第一个红外传感器
    3. const int Sensor2 = 7; // 第二个红外传感器
    4. const int Sensor3 = 8; // 第三个红外传感器
    5. const int Sensor4 = 12; // 第四个红外传感器

    那定义完我们的引脚之后还要进,行其他的操作,分为两个部分一个是void setup(),其另一个则是void loop()

    在Arduino编程中,void setup() 用于初始化程序所需的设置和配置。它是Arduino程序的一部分,会在程序开始执行时自动调用。

    void setup() 函数中,可以设置引脚的模式(如输入或输出),在下面的示例中,void setup() 函数执行以下操作:

    1. 配置电机引脚:通过调用 pinMode() 函数,将电机引脚(LeftMotorGoLeftMotorBackRightMotorGoRightMotorBack) 设置为输出模式,用于控制电机的前进和后退。
    2. 配置红外传感器引脚:通过调用 pinMode() 函数,将红外传感器引脚(Sensor1Sensor2Sensor3Sensor4) 设置为输入模式,用于接收来自传感器的信号。

    这两个函数有一个最基本的区别就是,void setup() 函数只在程序开始时执行一次, void loop() 函数在循环中执行代码。

    示例代码:

    1. void setup() {
    2. // 设置电机引脚为输出
    3. pinMode(LeftMotorGo, OUTPUT);
    4. pinMode(LeftMotorBack, OUTPUT);
    5. pinMode(RightMotorGo, OUTPUT);
    6. pinMode(RightMotorBack, OUTPUT);
    7. // 设置红外传感器引脚为输入
    8. pinMode(Sensor1, INPUT);
    9. pinMode(Sensor2, INPUT);
    10. pinMode(Sensor3, INPUT);
    11. pinMode(Sensor4, INPUT);
    12. }

    在定义完之后,我们就来谈谈如何来实际控制我们小车电机的转速,这里我们引入另一个函数, analogWrite(),通过这个函数我们可以轻松的控制脉冲宽度调制 (PWM) 的方式输出模拟信号,从而来实现控制小车电机的转速。同样我们为了代码看起来更加简洁我们定义一个void 类型的函数以便于后面更加简单直观的控制我们的小车。

    1. void Motor_Speed(int Left1_Speed, int Left2_Speed, int Right1_Speed, int Right2_Speed)
    2. {
    3. analogWrite(LeftMotorGo, Left1_Speed);
    4. analogWrite(LeftMotorBack, Left2_Speed);
    5. analogWrite(RightMotorGo, Right1_Speed);
    6. analogWrite(RightMotorBack, Right2_Speed);
    7. }

    这个函数接收四个数值,分别对应四个速度,我们使用 analogWrite函数将接收的速度(PWM)传输到LeftMotorGo等四个变量上去。

    在完成这一步的工作之后,我们就要进入我们的void loop() 函数当中去,让我们的小车开始实现循迹的功能:

    那么在这里先简单介绍一下小车实现转弯的几种方式

    1.正转+停转

    2.正转+反转

    3.停转+反转

    一般就是这几种方式,我所选择的是2.正转+反转,因为我在调试的过程中发现,这种转弯方式更加的快速,对与轨道的吸附能力更强,冲出的情况较少。

    以下为示例代码:

    1. void loop() {
    2. //读取红外传感器的值
    3. int sensorValue1 = digitalRead(Sensor1);
    4. int sensorValue2 = digitalRead(Sensor2);
    5. int sensorValue3 = digitalRead(Sensor3);
    6. int sensorValue4 = digitalRead(Sensor4);
    7. //根据红外传感器的值控制小车的运动
    8. if (sensorValue1 == 0 && sensorValue2 == 1 && sensorValue3 == 0 && sensorValue4 == 0) { //轻微偏离轨道,差速
    9. Motor_Speed(85, 0, 255, 0);
    10. //delay(25); //延迟一段时间,调整转弯幅度
    11. } else if(sensorValue1 == 0 && sensorValue2 == 0 && sensorValue3 == 1 && sensorValue4 == 0) { //轻微偏离轨道,差速
    12. Motor_Speed(240, 0, 85, 0);
    13. //delay(25); //延迟一段时间,调整转弯幅度
    14. } else if(sensorValue1 == 1 && sensorValue2 == 0 && sensorValue3 == 0 && sensorValue4 == 0) { //严重偏离轨道,反转
    15. Motor_Speed(0, 155, 255, 0);
    16. //delay(75); //延迟一段时间,调整转弯幅度
    17. } else if(sensorValue1 == 0 && sensorValue2 == 0 && sensorValue3 == 0 && sensorValue4 == 1) { //严重偏离轨道,反转
    18. Motor_Speed(255, 0, 0, 155);
    19. //delay(75); //延迟一段时间,调整转弯幅度
    20. } else if(sensorValue1 == 1 && sensorValue2 == 1 && sensorValue3 == 0 && sensorValue4 == 0) { //直角转弯,更大的转弯幅度
    21. Motor_Speed(0, 245, 255, 0);
    22. delay(40); //延迟一段时间,调整转弯幅度
    23. } else if(sensorValue1 == 0 && sensorValue2 == 0 && sensorValue3 == 1 && sensorValue4 == 1) { //直角转弯,更大的转弯幅度
    24. Motor_Speed(250, 0, 0, 255);
    25. delay(40); //延迟一段时间,调整转弯幅度
    26. } else { //除了左右转,就只剩下直走
    27. Motor_Speed(240, 0, 255, 0);
    28. }
    29. }

    上面这一段代码并不复杂我们先对四个红外传感器进行了初始化

    1. int sensorValue1 = digitalRead(Sensor1);
    2. int sensorValue2 = digitalRead(Sensor2);
    3. int sensorValue3 = digitalRead(Sensor3);
    4. int sensorValue4 = digitalRead(Sensor4);

    然后使用了if(){}和else if(){}来实现小车循迹不同情况的判断,判断条件上述表格已经写出。在注释里,你会发现我有delay()函数,这个函数的作用是加一个延时,让小车在执行动作后停顿一下,具体时间具体设置,如果需要可以根据实际比赛情况进行调整。

    在else条件当中,我将其他情况设置为了直走。

    到此为止就是全部代码的展示,我也会在结尾附上全部的代码,在实际调试的过程中同样也会遇到很多实际的问题,这里我只说明一些我在调试的时候所遇到的问题,光是使用代码让小车直接实现良好的循迹当然不显示,想要有一个良好的循迹效果需要大家根据实际情况进行反复的调试,这个过程也相当有意思。

    1.当你发现下车在经过黑线的时候红外传感器并没有反应,则需要调整红外传感器的灵敏度,一般在红外传感器上都会有一个类似与螺丝的地方,旋动它就可以调整灵敏度。

    2.当你发现小车在拐弯过程中,小车检测到了黑线(红外指示灯闪烁)但是并未进行转弯操作,这种就是冲出现象,是小车的转弯力度太小了,小车还没来的及转弯就已经冲出去了。解决这个有两种方法:一是调大转弯差速,二是降低小车行驶速度。

    3.小车正常直行无问题,但内侧接触到黑线之后,小车开始剧烈摆动,频繁接触到黑线,甚至冲出赛道。这种情况就是小幅度转弯时候的差速过于大,让小车转弯太过导致的,解决方法就是调小差速喽。

    如果还有其余问题的话也欢迎积极交流分享,我暂时想到的问题就这么多。

    小白第一次写博客,有什么写的不对,不好的地方欢迎大家指出,我虚心求教嘿嘿!

    完整代码展示:

    1. // 电机引脚定义
    2. int LeftMotorGo = 3; // 左电机前进 (IN1)
    3. int LeftMotorBack = 5; // 左电机后退 (IN2)
    4. int RightMotorGo = 6; // 右电机前进 (IN3)
    5. int RightMotorBack = 9; // 右电机后退 (IN4)
    6. // 红外传感器引脚定义
    7. const int Sensor1 = 4; // 第一个红外传感器
    8. const int Sensor2 = 7; // 第二个红外传感器
    9. const int Sensor3 = 8; // 第三个红外传感器
    10. const int Sensor4 = 12; // 第四个红外传感器
    11. void setup() {
    12. // 设置电机引脚为输出
    13. pinMode(LeftMotorGo, OUTPUT);
    14. pinMode(LeftMotorBack, OUTPUT);
    15. pinMode(RightMotorGo, OUTPUT);
    16. pinMode(RightMotorBack, OUTPUT);
    17. // 设置红外传感器引脚为输入
    18. pinMode(Sensor1, INPUT);
    19. pinMode(Sensor2, INPUT);
    20. pinMode(Sensor3, INPUT);
    21. pinMode(Sensor4, INPUT);
    22. }
    23. void Motor_Speed(int Left1_Speed, int Left2_Speed, int Right1_Speed, int Right2_Speed)
    24. {
    25. analogWrite(LeftMotorGo, Left1_Speed);
    26. analogWrite(LeftMotorBack, Left2_Speed);
    27. analogWrite(RightMotorGo, Right1_Speed);
    28. analogWrite(RightMotorBack, Right2_Speed);
    29. }
    30. void loop() {
    31. //读取红外传感器的值
    32. int sensorValue1 = digitalRead(Sensor1);
    33. int sensorValue2 = digitalRead(Sensor2);
    34. int sensorValue3 = digitalRead(Sensor3);
    35. int sensorValue4 = digitalRead(Sensor4);
    36. //根据红外传感器的值控制小车的运动
    37. if (sensorValue1 == 0 && sensorValue2 == 1 && sensorValue3 == 0 && sensorValue4 == 0) { //轻微偏离轨道,差速
    38. Motor_Speed(85, 0, 255, 0);
    39. //delay(25); //延迟一段时间,调整转弯幅度
    40. } else if(sensorValue1 == 0 && sensorValue2 == 0 && sensorValue3 == 1 && sensorValue4 == 0) { //轻微偏离轨道,差速
    41. Motor_Speed(240, 0, 85, 0);
    42. //delay(25); //延迟一段时间,调整转弯幅度
    43. } else if(sensorValue1 == 1 && sensorValue2 == 0 && sensorValue3 == 0 && sensorValue4 == 0) { //严重偏离轨道,反转
    44. Motor_Speed(0, 155, 255, 0);
    45. //delay(75); //延迟一段时间,调整转弯幅度
    46. } else if(sensorValue1 == 0 && sensorValue2 == 0 && sensorValue3 == 0 && sensorValue4 == 1) { //严重偏离轨道,反转
    47. Motor_Speed(255, 0, 0, 155);
    48. //delay(75); //延迟一段时间,调整转弯幅度
    49. } else if(sensorValue1 == 1 && sensorValue2 == 1 && sensorValue3 == 0 && sensorValue4 == 0) { //直角转弯,更大的转弯幅度
    50. Motor_Speed(0, 245, 255, 0);
    51. delay(40); //延迟一段时间,调整转弯幅度
    52. } else if(sensorValue1 == 0 && sensorValue2 == 0 && sensorValue3 == 1 && sensorValue4 == 1) { //直角转弯,更大的转弯幅度
    53. Motor_Speed(250, 0, 0, 255);
    54. delay(40); //延迟一段时间,调整转弯幅度
    55. } else { //除了左右转,就只剩下直走
    56. Motor_Speed(240, 0, 255, 0);
    57. }
    58. }

  • 相关阅读:
    【Java】抽象类和接口
    Flink-窗口概念以及窗口API使用
    图文解释各种电阻阻值的读取方法【硬件篇】
    17岁电竞少年追梦之旅,多次试训无果黯然返乡,沉淀一年,少年杨帆终圆梦~
    Vue基础-02
    (附源码)springboot跨境电商系统 毕业设计 211003
    redis--windows配置--redis基础
    01-http概述
    【物理应用】Matlab模拟水中声呐模型
    【面试:并发篇29:多线程:volatile】原理
  • 原文地址:https://blog.csdn.net/ZFD0131/article/details/132649615