小型双轮差速底盘寻迹功能的实现

news2024/9/22 13:42:28

1. 功能说明

寻迹机器人是一种能够跟踪特定物体或线路的机器人。它们通常具有以下功能和特点:

      ① 传感器:寻迹机器人配备了用于感知环境的传感器,如摄像头、灰度传感器等。这些传感器可以探测地面上的标记、颜色、纹理或其他特定特征,以确定要跟踪的目标。

      ② 自主导航:寻迹机器人通常具备自主导航能力,可以根据目标物体的位置和运动轨迹进行移动和调整。它们可能使用轮式、履带或其他移动机构来在地面上移动。

      ③ 跟踪精度:寻迹机器人通常被设计为能够实时跟踪目标物体,并尽可能准确地保持距离和方向。一些高级寻迹机器人还可以通过预测目标物体的运动来提高跟踪的精度。

      ④ 应用场景:寻迹机器人可以应用于多种场景,如工业生产线上的零部件跟踪、物流仓库中的货物识别与追踪、安防领域中的行人监控等。它们在自动化、智能化和效率提升方面具有广泛的应用前景。

本文示例将实现R023样机小型双轮差速底盘机器人沿直线寻迹行走的一个功能。

2. 结构装配

按照下图所示方式进行安装:

3. 电子硬件

在这个示例中,我们采用了以下硬件,请大家参考:

主控板

Basra主控板(兼容Arduino Uno)‍

扩展板

Bigfish2.1扩展板‍

传感器灰度传感器
电池7.4V锂电池

按照下图所示方式进行电路连接:

4. 功能实现

编程环境:Arduino 1.8.19

① 下面提供一个控制轮子转动方向和速度的参考例程(Test1.ino):

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2023-07-14 https://www.robotway.com/

  ------------------------------*/

#include <Servo.h>//调用舵机库


Servo myservo;//声明一个舵机类


void setup()//Arduino的设置函数

{

myservo.attach(4);//绑定控制舵机的引脚

}


void loop()//Arduino的循环函数

{

for(int i=0; i<180; i++){//通过调节i值控制舵机的运行参数

    myservo.write(i);//输出控制舵机的运行参数

    delay(500);//延时

}

}

② 下面提供一个控制轮子前进、停止、左转、右转、左微调、右微调的参考例程(Test2.ino):

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2023-07-14 https://www.robotway.com/

  ------------------------------*/

#include <Servo.h>


#define middle1 88//定义对应舵机的中间值,即停止转动的值

#define middle2 88//此值需要测量,各个舵机不一定相同


Servo myservo[2];//定义一个舵机类数组


void setup()

{

myservo[0].attach(4);

myservo[1].attach(3);

}


void loop()

{

Left();//调用左转函数

delay(1000);


Right();

delay(1000);


Forwards();

delay(1000);


stop();

delay(1000);

}


void Left()//左转函数

{

myservo[0].write(middle1);

myservo[1].write(middle2 + 20);

}


void Right()//右转函数

{

myservo[0].write(middle1 - 20);

myservo[1].write(middle2);

}


void Forwards()//前进函数

{

myservo[0].write(middle1 - 20);

myservo[1].write(middle2 + 20);

}


void stop()//后退函数

{

myservo[0].write(middle1);

myservo[1].write(middle2);

}

③ 下面提供一个将灰度传感器数据显示到LED点阵上的参考例程(Test3.ino):

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2023-07-14 https://www.robotway.com/

  ------------------------------*/

#include <LedControl.h>//调用点阵库函数

#include <Servo.h>


LedControl lc=LedControl(12,11,13,1);//声明点阵类,并设置对应的引脚

int pin[3] = {A0, A4, A3};//设置传感器的对应的三个引脚

byte value;//声明传感器值变量


void setup()

{

LedInit();//初始化点阵

}


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

此程序用到了for与switch的配合框架,可用于多传感器的实时处理,请细细体会!

具体解析:for循环中使用了位处理,这样的结果就是value的一个数据位对应一个传感器的状态,

  此程序value的类型为byte,则可支持8个传感器,如果要使用更多传感器可定义int等。

  传感器触发时返回值为0,因此value值与传感器触发的状态对应关系以A0触发为例:

  A0传感器触发-->二进制:00000110-->十六进制:0x06-->对应case 0x06;

  所以这样做的好处就是当传感器的状态发生改变时程序可以快速的到达指定的处理方式

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

void loop()

{

value = 0;

for(int i=0; i<3; i++){//通过循环检测,读取传感器的状态值

    value |= (digitalRead(pin[i]) << i);//通过位处理得到结果值,digitalRead()用于读取数字值

}


switch (value) {//根据结果值进行相应的事件处理

case 0x00://全部触发

LedOn(0);//点亮相应的点阵

    LedOn(1);

    LedOn(2);

break;

case 0x01://触发右边两个

    LedOn(1);

    LedOn(2);

      break;

    case 0x03://触发右边一个

      LedOn(2);

      break;

    case 0x04://触发左边两个

    LedOn(0);

    LedOn(1);

      break;

    case 0x05://触发中间一个

    LedOn(1);

      break;

    case 0x06://触发左边一个

    LedOn(0);

      break;

    default:

      ;

}

}


void LedOn(int key)//根据参数点亮相应的点阵LED

{

lc.clearDisplay(0);

for(int i=0; i<2; i++){

    for(int j=3*key; j<3*key+2; j++){

        lc.setLed(0, i, j, true);

    }

}

}


void LedInit() //点阵初始化函数

{

lc.shutdown(0,false);

lc.setIntensity(0,8);

lc.clearDisplay(0);

}

④ 下面提供一个小车行走直线的参考例程(Test4.ino):

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2023-07-14 https://www.robotway.com/

  ------------------------------*/

#include <Servo.h>


#define middle1 88

#define middle2 88


Servo myservo[2];

int pin[3] = {A0, A4, A3};

byte value;

byte value_his = 0;//记录上一次的传感器值


void setup()

{

myservo[0].attach(4);

myservo[1].attach(3);

}


void loop()

{

value = 0;

for(int i=0; i<3; i++){

    value |= (digitalRead(pin[i]) << i);

}


if(value == 0x07){//当传感器都没有触发时默认为上一次的值

    value = value_his;

}


switch (value) {

case 0x00://全部触发

    Forwards();

break;

case 0x01://触发右边两个

    while(digitalRead(pin[1])){//通过while循环使小车回到跑道中间

        Right();

    }

      break;

    case 0x03://触发右边一个

      while(digitalRead(pin[1])){

        Right();

    }

      break;

    case 0x04://触发左边两个

    while(digitalRead(pin[1])){

        Left();

    }

      break;

    case 0x05://触发中间一个

    Forwards();

      break;

    case 0x06://触发左边一个

    while(digitalRead(pin[1])){

        Left();

    }

      break;

    default:

      stop();

}

value_his = value;

}


void Left()

{

myservo[0].write(middle1);

myservo[1].write(middle2 + 20);

}


void Right()

{

myservo[0].write(middle1 - 20);

myservo[1].write(middle2);

}


void Forwards()

{

myservo[0].write(middle1 - 20);

myservo[1].write(middle2 + 20);

}


void stop()

{

myservo[0].write(middle1);

myservo[1].write(middle2);

}

⑤ 下面提供一个小车识别十字路口的参考例程(Test5.ino):

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2023-07-14 https://www.robotway.com/

  ------------------------------*/

#include <Servo.h>

#include <LedControl.h>


#define middle1 88

#define middle2 88


Servo myservo[2];

LedControl lc=LedControl(12,11,13,1);


int pin[3] = {A0, A4, A3};

byte value;

byte value_his = 0;

int time[3];//用于记录传感器的触发时间


void setup()

{

LedInit();

myservo[0].attach(4);

myservo[1].attach(3);

}


void loop()

{

value = 0;

for(int i=0; i<3; i++){

    value |= (digitalRead(pin[i]) << i);

    if(!digitalRead(pin[i])){

        time[i] = millis();//调用mills函数可以得到此时单片机的运行时间

    }

}


if(TimeDeal())

{

if(millis() > 1000){//用于排除刚开机时的误判

    LedDis();//十字路口显示

}

}


if(value == 0x07){//当传感器都没有触发时默认为上一次的值

    value = value_his;

}


switch (value) {

case 0x00://全部触发

    Forwards();

break;

case 0x01://触发右边两个

    while(digitalRead(pin[1])){

        Right();

    }

      break;

    case 0x03://触发右边一个

      while(digitalRead(pin[1])){

        Right();

    }

      break;

    case 0x04://触发左边两个

    while(digitalRead(pin[1])){

        Left();

    }

      break;

    case 0x05://触发中间一个

    Forwards();

      break;

    case 0x06://触发左边一个

    while(digitalRead(pin[1])){

        Left();

    }

      break;

    default:

      stop();

}

value_his = value;

lc.clearDisplay(0);

}


void Left()

{

myservo[0].write(middle1);

myservo[1].write(middle2 + 20);

}


void Right()

{

myservo[0].write(middle1 - 20);

myservo[1].write(middle2);

}


void Forwards()

{

myservo[0].write(middle1 - 20);

myservo[1].write(middle2 + 20);

}


void stop()

{

myservo[0].write(middle1);

myservo[1].write(middle2);

}


bool TimeDeal()//十字路口识别函数

{

if(millis() > 500){

if((abs(time[1] - time[0]) < 100) && (abs(time[1] - time[2]) < 100)){//当中间传感器与另外两个传感器触发的时间小于100毫秒时判定为十字路口

    return true;

}

else

return false;   

}

}


void LedDis()//十字路口显示函数

{

for(int i=3; i<5; i++){

    for(int j=0; j<8; j++){

        lc.setLed(0, i, j, true);

    }

}

for(int i=3; i<5; i++){

    for(int j=0; j<8; j++){

        lc.setLed(0, j, i, true);

    }

}

}


void LedInit()

{

lc.shutdown(0,false); //start the 8*8 led

lc.setIntensity(0,8);

lc.clearDisplay(0);

}

 ⑥ 下面提供一个小车实现寻迹的完整程序(TrackingCar.ino):

/*------------------------------------------------------------------------------------

  版权说明:Copyright 2023 Robottime(Beijing) Technology Co., Ltd. All Rights Reserved.

           Distributed under MIT license.See file LICENSE for detail or copy at

           https://opensource.org/licenses/MIT

           by 机器谱 2023-07-14 https://www.robotway.com/

  ------------------------------*/

#include <LedControl.h>

#include <Servo.h>


#define middle1 88

#define middle2 88


LedControl lc=LedControl(12,11,13,1);

Servo myservo[2];

int pin[3] = {A0, A4, A3};

int time[3];

byte value;

byte value_his = 0;

int flag = 0;

int times = 0;


void setup()

{

LedInit();

Serial.begin(9600);//串口,用于调试

myservo[0].attach(4);

myservo[1].attach(3);

}


void loop()

{

value = 0;

for(int i=0; i<3; i++){

    value |= (digitalRead(pin[i]) << i);

    if(!digitalRead(pin[i])){

        time[i] = millis();

    }

}


if(TimeDeal())

{

times++;

Serial.print(times);

}

else

{

if(times > 1){

Serial.println();

    flag += 1;

    Serial.println(flag);

}

times = 0;

}


if(flag == 3){

    while(1){

        stop();

    }

}


if(value == 0x07){

    value = value_his;

}


switch (value) {

case 0x00://全部触发

LedOn(0);

    LedOn(1);

    LedOn(2);

    Forwards();

    //delay(500);

break;

case 0x01://触发右边两个

    LedOn(1);

    LedOn(2);

    while(digitalRead(pin[1])){

        Right();

    }

      break;

    case 0x03://触发右边一个

      LedOn(2);

      while(digitalRead(pin[1])){

        Right();

    }

      break;

    case 0x04://触发左边两个

    LedOn(0);

    LedOn(1);

    while(digitalRead(pin[1])){

        Left();

    }

      break;

    case 0x05://触发中间一个

    LedOn(1);

    Forwards();

      break;

    case 0x06://触发左边一个

    LedOn(0);

    while(digitalRead(pin[1])){

        Left();

    }

      break;

    default:

      stop();

}

value_his = value;

lc.clearDisplay(0);

}


bool TimeDeal()

{

if(millis() > 500){

if((abs(time[1] - time[0]) < 100) && (abs(time[1] - time[2]) < 100)){

    return true;

}

else

return false;   

}

}


void Left()

{

myservo[0].write(middle1);

myservo[1].write(middle2 + 20);

}


void Right()

{

myservo[0].write(middle1 - 20);

myservo[1].write(middle2);

}


void Forwards()

{

myservo[0].write(middle1 - 20);

myservo[1].write(middle2 + 20);

}


void stop()

{

myservo[0].write(middle1);

myservo[1].write(middle2);

}


void LedOn(int key)

{

for(int i=0; i<2; i++){

    for(int j=3*key; j<3*key+2; j++){

        lc.setLed(0, i, j, true);

    }

}

}


void LedInit()

{

lc.shutdown(0,false); //start the 8*8 led

lc.setIntensity(0,8);

lc.clearDisplay(0);

}

 程序源代码资料内容详见 小型双轮差速底盘-寻迹与路口

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

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

相关文章

Vue3和TypeScript项目-移动端兼容

1 全局安装typescript 2 检测安装成功 3 写的是ts代码&#xff0c;但是最后一定要变成js代码&#xff0c;才能在浏览器使用 这样就会多一个js文件 3 ts语法 数组语法 对象语法 安装vue3项目 成功后进入app。安装依赖。因为我们用的是脚手架&#xff0c;要引入东西的时候不需要…

网格简化(QEM)学习笔记

文章目录 网格简化(QEM)1 概述与原理1.1 网格简化的应用1.2 常见的简化操作1.3 二次误差度量 2 算法流程2.1 逐步分析 3 Python代码实现3.1 测试结果 4 总结参考 网格简化(QEM) 1 概述与原理 网格简化&#xff0c;通过减少复杂网格数据的顶点、边和面的数量简化模型的表达&am…

MIT 6.S081 Lab Ten -- mmap

MIT 6.S081 Lab Ten -- mmap 引言mmap(hard)代码解析 引言 本文为 MIT 6.S081 2020 操作系统 实验十解析。 MIT 6.S081课程前置基础参考: 基于RISC-V搭建操作系统系列 mmap(hard) map和munmap系统调用允许UNIX程序对其地址空间进行详细控制。它们可用于在进程之间共享内存&a…

SOLIDWORKS Flow Simulation可以便捷的进行降压分析

导读&#xff1a;现如今压降等应用的工程分析一直是由分析部门的专家执行&#xff0c;这些部门独立于主流设计和开发部门&#xff0c;或者仅为一些重要产品制造昂贵的物理原型。要测试或验证其设计&#xff0c;机械工程师必须依赖于创建物理原型并在工作台或测试台上对其进行测…

【Maven】Nexus3上传maven依赖jar

后端依赖 上次说到前端的批量tgz文件上传私服&#xff0c;其实服务端也有类似情况&#xff0c;我们有个私服也需要进行上传到私服&#xff0c;这里做个记录。因为上次有个小细节没注意白白传错了一遍&#xff0c;这里重新记录总结一下。 # 查看一下结构 $ tree -L 2 . |-- re…

春秋云镜 CVE-2021-32305

春秋云镜 CVE-2021-32305 WebSVN RCE 靶标介绍 WebSVN是一个基于Web的Subversion Repository浏览器&#xff0c;可以查看文件或文件夹的日志&#xff0c;查看文件的变化列表等。其search.php?search 参数下过滤不严谨导致RCE。 启动场景 漏洞利用 EXP PAYLOAD "/bi…

快速消除视频的原声的技巧分享

网络上下载的视频都会有视频原声或者背景音乐&#xff0c;如果不喜欢并且想更换新的BGM要怎么操作呢&#xff1f;今天小编就来教你如何快速给多个视频更换新的BGM&#xff0c;很简单&#xff0c;只需要将原视频的原声快速消音同时添加新的背景音频就行&#xff0c;一起来看看详…

express学习笔记3 - 三大件

便于统一管理router&#xff0c;创建 router 文件夹&#xff0c;创建 router/index.js&#xff1a; const express require(express)// 注册路由 const router express.Router() router.get(/,function(req,res){res.send(让我们开始express之旅) }) /*** 集中处理404请求的…

2023 全国大学生电子设计竞赛题目

2021 全国大学生电子设计竞赛题目 目录 1&#xff08;A题&#xff09;单相逆变器并联运行系统2 &#xff08;B题&#xff09;同轴电缆长度与终端负载检测装置3 &#xff08;C题&#xff09;电感电容测量装置4 &#xff08;D题&#xff09;信号调制方式识别与参数估计装置5 &am…

数据库访问中间件--springdata-jpa的基本使用

二、单表SQL操作-使用关键字拼凑方法 回顾 public interface UserRepository extends JpaRepository<User,Integer> {User findByUsernameLike(String username); }GetMapping("/user/username/{username}")public Object findUserByUsername(PathVariable S…

软考A计划-系统集成项目管理工程师-项目沟通管理-下

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

C语言进阶——文件的读写(文件使用方式、文件的顺序读写、常用函数、fprintf、fscanf)

目录 文件使用方式 文件的顺序读写 常用函数 用法示例 fprintf fscanf 文件使用方式 在fopen函数中详细的各种使用方式&#xff1a; 文件使用方式含义如果指定文件不存在“r”&#xff08;只读&#xff09;为了输入数据&#xff0c;打开一个已经存在的文本文件出错“w”…

2023牛客暑期多校训练营5-I The Yakumo Family

2023牛客暑期多校训练营5-I The Yakumo Family https://ac.nowcoder.com/acm/contest/57359/I 文章目录 2023牛客暑期多校训练营5-I The Yakumo Family题意解题思路代码 题意 解题思路 考虑将序列拆位计算。 先考虑一个简化版本&#xff0c;求&#xff1a; ∑ 1 ≤ l 1 ≤…

新闻标题文本分类任务

目录 知识回顾使用debug调试 知识回顾 预处理内容 文本主要进行清洗、分词/分字 ID替换(不希望计算机看到文字&#xff0c;而是ID)&#xff0c;通过语料表来表示&#xff0c;根据频率高低来分配ID号 文本的ID映射到文本的一个特征向量&#xff0c;进行词嵌入(Embedding)&…

【docker】Windows11系统下安装并配置阿里云镜像加速

【docker】Windows11系统下安装并配置阿里云镜像加速 提示:博主取舍了很多大佬的博文并亲测有效,分享笔记邀大家共同学习讨论 文章目录 【docker】Windows11系统下安装并配置阿里云镜像加速一、查看Windows环境是否支持docker二、 启动Hyper-V三、 官网下载安装Docker应用和数据…

VIOOVI分享:什么是丰田精益生产方式?丰田精益生产方式有哪些?

作为全球知名的汽车生产企业&#xff0c;日本丰田在行业的影响力巨大。而其企业运营模式&#xff0c;也广为经营领域热议&#xff0c;其中&#xff0c;大家对丰田精益生产方式一直都是津津乐道。那么什么是丰田精益生产方式&#xff1f;以下内容为您全面解析。 丰田精益化生产模…

数据结构 | 线性数据结构——双端队列

目录 一、何谓双端队列 二、双端队列抽象数据类型 三、用Python实现双端队列 四、回文检测器 一、何谓双端队列 双端队列是与队列类似的有序集合。它有一前、一后两端&#xff0c;元素在其中保持自己的位置。与队列不同的是&#xff0c;双端队列对在哪一端添加和移除元素没…

Linux - 进程控制(进程等待)

进程等待必要性 之前讲过&#xff0c;子进程退出&#xff0c;父进程如果不管不顾&#xff0c;就可能造成‘僵尸进程’的问题&#xff0c;进而造成内存泄漏。 另外&#xff0c;进程一旦变成僵尸状态&#xff0c;那就刀枪不入&#xff0c;“杀人不眨眼”的kill -9 也无能为力&…

Activiti 深入理解:Activiti 流程引擎的 25 张数据库表都存储了什么?ing

1. Activiti 数据库表名说明&#xff08;分类与说明&#xff09; https://www.activiti.org/userguide/#database.tables.explained \qquad Activiti 的数据库表名称都以 ACT_ 开头&#xff0c;而第二部分是表 use case 的双字符标识&#xff0c;use case 也大致与 Activiti 服…

Vue3中使用事件总线Bus的两种方式(mitt)

首先咱们得先下载mitt指令如下&#xff1a; npm i mitt --save 接下来介绍Vue3中通过mitt使用事件总线的两种方式 方式一&#xff1a; 该方式为全局挂载mitt,个人感觉有点繁琐&#xff0c;不太推荐 创建&#xff1a; 1.首先找到你的main.js或main.ts文件 2.引入mitt 3.…