草庐IT

单片机原理及应用——Arduino四驱四路循迹小车

糍粑年年糕 2023-04-25 原文

摸摸鱼,把大二的课堂作业总结一下发出来,很基础的小白代码,希望能给入门的同学一点参考。

由于实物卖掉啦,只能尽量用文字配上接线图等,写详细一点。

一、元器件清单

Arduino UNO R3开发板(带USB)

1个

XD-201 4路红外传感器

1个

DRV8833  2路直流电机驱动

2个

母对母、公对母杜邦线

若干

直流减速电机

4个

4WD底盘

1个

接插件

若干

二、硬件连线图

根据实际接线情况绘制,需要配合代码使用的端口进行接线。

三、主要硬件介绍

3.1 Arduino UNO R3开发板

使用前需了解清楚每个端口的作用哦~

3.2 DRV8833电机驱动模块

为了控制转速和满足功率,所以需要电机驱动模块。老师要求,所以选了这一款,但拿到手蛮小巧可爱的,就是比起常用的L298多花了一番功夫寻找控制原理。

四、代码介绍

       首先定义引脚,设置输出和输入情况,同时进行初始化。

      使用到的5,6,10,11号脚不仅是I/O口,还可以输出PWM。则搭配7,8,12,13号引脚控制四个电机。PWM控制转速,普通I/O口输出高低电平控制转向。

       连续从红外循迹模块读取四个传感器的高低电平,输入到单片机,motorRun函数根据传感器相应数据,输出不同的PWM值和高低电平,控制电机的转向和转速,以适应不同的情况。

五、整体代码

#include <Servo.h>  
//函数调用说明:调用servo.h里的库函数,类似stdio.h是标准输入输出,常用的 printf scanf 这些输入输出函数 以及fopen fclose一系列的文件操作函数都是在这个头文件里面的,所以这个最常见
#define STOP      0
#define FORWARD   1
#define BACKWARD  2
#define TURNLEFT  3
#define TURNRIGHT 4

const int leftMotor1_1 = 5;      // 定义5引脚为左前侧电机,PWM控制速度
const int leftMotor1_2 = 7;      // 定义7引脚为左前侧电机控制方向
const int leftMotor2_1 = 6;      // 定义6引脚为左后侧电机,PWM控制速度
const int leftMotor2_2 = 8;      // 定义8引脚为左后侧电机控制方向

const int rightMotor1_1 = 10;      // 定义10引脚为右前侧电机,PWM控制速度
const int rightMotor1_2 = 12;      // 定义12引脚为右前侧电机控制方向
const int rightMotor2_1 = 11;      // 定义11引脚为右后侧电机,PWM控制速度
const int rightMotor2_2 = 13;      // 定义13引脚为右后侧电机控制方向

const int trac1 = 9; //从车头方向的最左边开始排序 (传感器)
const int trac2 = 2; 
const int trac3 = 3; 
const int trac4 = 4; 

void setup() {

  Serial.begin(9600);  //串口初始化
 
  pinMode(leftMotor1_1, OUTPUT);// 配置引脚为输出或输出模式,它是一个无返回值函数,函数有两个参数pin和mode
  pinMode(leftMotor1_2, OUTPUT);
  pinMode(leftMotor2_1, OUTPUT);
  pinMode(leftMotor2_2, OUTPUT);

  pinMode(rightMotor1_1, OUTPUT);
  pinMode(rightMotor1_2, OUTPUT);
  pinMode(rightMotor2_1, OUTPUT);
  pinMode(rightMotor2_2, OUTPUT);
 
  pinMode(trac1, INPUT);
  pinMode(trac2, INPUT);
  pinMode(trac3, INPUT);
  pinMode(trac4, INPUT);
}

void loop()
{
  tracing();   //是一个函数,在下面有对这个函数的定义
}



void tracing()           // tracing()函数定义:上面void loop里面只有tracing这一个函数,arduino标准程序必须包含setup函数和loop函数,loop函数其实是一个循环,因为这个程序很简单没循环做的事情,所以循环函数为空,但必须要保留,这是arduino的语法规范。
{
  int data[4];                //定义一个数组data[]包含四个数据,读取来自9,2,3,4引脚的信号存储到这个数组中,而9,2,3,4引脚连接的就是四个传感器,因此这个数组的功能就是记录四个传感器的信号,由下面的程序可以知道,通过对这几个传感器信号的判断,并根据这几个不同的情况作出不同的反应
  data[0] = digitalRead(9);
  data[1] = digitalRead(2);
  data[2] = digitalRead(3);
  data[3] = digitalRead(4);
  
  //高电平-关断状态(没有反射)-有黑线-灯不亮;低电平-接收(反射)-无黑线-灯亮
  
  //左右都没有检测到黑线,直行
  if(!data[0] && !data[1] && !data[2] && !data[3])     //0000,灯亮
  {
    motorRun(FORWARD, 220);
  }
  if(!data[0] && data[1] && data[2] && !data[3])   //0110
  {
    motorRun(FORWARD, 220);
  }

  //右边检测到黑线,右转
  if(!data[0] && !data[1] && data[2] && !data[3])    //小转 0010
  {
    motorRun(TURNRIGHT, 150);
  }
  if(!data[0] && !data[1] && data[2] && data[3])   //中转 0011
  {
    motorRun(TURNRIGHT, 200);
  }
  if(!data[0] && !data[1] && !data[2] && data[3])    //大转 0001
  {
    motorRun(TURNRIGHT, 220);
  }
  
  //左边检测到黑线,左转
  if(!data[0] && data[1] && !data[2] && !data[3])    //小转 0100
  {
    motorRun(TURNLEFT, 120);
  }
  if(data[0] && data[1] && !data[2] && !data[3])   //中转 1100
  {
    motorRun(TURNLEFT, 150);
  }
  if(data[0] && !data[1] && !data[2] && !data[3])    //大转 1000
  {
    motorRun(TURNLEFT, 200);
  }
  
  //左右都检测到黑线是停止
  if(data[0] && data[1] && data[2] && data[3])     //0000
  {
    motorRun(STOP, 0);
    while(1);
  }

  Serial.print(data[0]);
  Serial.print("---");
  Serial.print(data[1]);
  Serial.print("---");
  Serial.print(data[2]);
  Serial.print("---");
  Serial.println(data[3]);
}

void motorRun(int cmd,int value) // 定义一个函数motorRun()
{

  // 设置PWM输出,即设置速度 analogWrite(pin, value) 通过PWM的方式在引脚上输出一个模拟量,较多的应用在LED亮度控制、电机转速控制等方面。该函数支持以下引脚:3、5、6、9、10、11。在Arduino控制板上引脚号旁边标注~的就是可用作PWM的引脚,analogWrite函数为无返回值函数,有两个参数pin和value,参数pin表示所要设置的引脚,只能选择函数支持的引脚;参数value表示PWM输出的占空比,范围在0~255的区间,对应的占空比为0%~100%
  analogWrite(leftMotor1_1, value);  
  analogWrite(leftMotor2_1, value); 
  analogWrite(rightMotor1_1, value);
  analogWrite(rightMotor2_1, value); 
  //方向
  switch(cmd)
{
   //判断cmd的状态:如果cmd==FOTWARD,输出“FORWARD/r/n”的状态,为LEFTMotor1设置为高电平,为leftMotor2设置为低电平,为RightMotor1设置为高电平,为RightMotor2设置为低电平
    case FORWARD:
      Serial.println("FORWARD"); //输出状态
   //Arduino 的输出基本就用两个函数 print 和 println,区别在于后者比前者多了回车换行,Serial.println(data)
   //从串行端口输出数据,跟随一个回车('r')和一个换行符('n')。这个函数所取得的值与 Serial.print()一样。
   //其作用为设置引脚的输出电压为高\低电平。该函数无返回值,有两个参数pin和value,pin参数表示所要设置的引脚,value参数表示输出的电压,HIGH为高电平,LOW为低电平。在使用digitalWrite(pin, value)函数之前要将引脚设置为OUTPUT模式。例:pinMode(x, OUTPUT);其中x为引脚编号。pinMode函数用于设置引脚模式。
      digitalWrite(leftMotor1_2,HIGH );
      digitalWrite(leftMotor2_2, LOW);
      digitalWrite(rightMotor1_2, HIGH);
      digitalWrite(rightMotor2_2, LOW);
      break; //完成以上一系列函数后结束这个函数
    
      break;
     case TURNLEFT:
      Serial.println("TURN  LEFT"); //输出状态
      digitalWrite(leftMotor1_2, LOW);
      digitalWrite(leftMotor2_2, HIGH);
      digitalWrite(rightMotor1_2, HIGH);
      digitalWrite(rightMotor2_2, LOW);
      break;
     case TURNRIGHT:
      Serial.println("TURN  RIGHT"); //输出状态
      digitalWrite(leftMotor1_2, HIGH);
      digitalWrite(leftMotor2_2, LOW);
      digitalWrite(rightMotor1_2, LOW);
      digitalWrite(rightMotor2_2, HIGH);
      break;
     default:
//如果以上情况都不是,输出STOP,所有电机输出为低电平
      Serial.println("STOP"); //输出状态
      digitalWrite(leftMotor1_2, LOW);
      digitalWrite(leftMotor2_2, LOW);
      digitalWrite(rightMotor1_2, LOW);
      digitalWrite(rightMotor2_2, LOW);
  }
}

六、实物图

只能找到咸鱼上视频截图了,前面那个白色块是充电宝。

七、展望

       现在看来,可以使用PID控制算法,当时写得太死板了,完全是一一对应的一个萝卜一个坑的速度,慢且机械,还是PID顺滑~~。同时咧,车车的四个轮,参照现实中的轿车,分为驱动轮方向轮,啊,我这写的嘛呀,四个轮又转向又驱动。很待改进,但摆烂了,在这里献丑了。

有关单片机原理及应用——Arduino四驱四路循迹小车的更多相关文章

  1. ruby - 将差异补丁应用于字符串/文件 - 2

    对于具有离线功能的智能手机应用程序,我正在为Xml文件创建单向文本同步。我希望我的服务器将增量/差异(例如GNU差异补丁)发送到目标设备。这是计划:Time=0Server:hasversion_1ofXmlfile(~800kiB)Client:hasversion_1ofXmlfile(~800kiB)Time=1Server:hasversion_1andversion_2ofXmlfile(each~800kiB)computesdeltaoftheseversions(=patch)(~10kiB)sendspatchtoClient(~10kiBtransferred)Cl

  2. ruby-on-rails - Rails 应用程序之间的通信 - 2

    我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此

  3. ruby - 无法运行 Rails 2.x 应用程序 - 2

    我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby​​:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r

  4. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  5. ruby-on-rails - 如何在我的 Rails 应用程序 View 中打印 ruby​​ 变量的内容? - 2

    我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby​​中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R

  6. ruby-on-rails - 如何在 Gem 中获取 Rails 应用程序的根目录 - 2

    是否可以在应用程序中包含的gem代码中知道应用程序的Rails文件系统根目录?这是gem来源的示例:moduleMyGemdefself.included(base)putsRails.root#returnnilendendActionController::Base.send:include,MyGem谢谢,抱歉我的英语不好 最佳答案 我发现解决类似问题的解决方案是使用railtie初始化程序包含我的模块。所以,在你的/lib/mygem/railtie.rbmoduleMyGemclassRailtie使用此代码,您的模块将在

  7. 世界前沿3D开发引擎HOOPS全面讲解——集3D数据读取、3D图形渲染、3D数据发布于一体的全新3D应用开发工具 - 2

    无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD

  8. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  9. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  10. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

随机推荐