跟随原理

下面介绍红外感应的跟随小车的自动跟随部分。

基本原理:在小车上安装一个红外接收器(能测量红外入射角的感应器),人手持一个红外发射模块。

根据不同的入射角,调整小车前进方向。若入射角在右边,就控制小车右转,若入射角在左边,就控制小车左转。

 效果展示

百度网盘链接:https://pan.baidu.com/s/1PGTwDxvUbdksDmyfcXAMOw?pwd=vddg

提取码:vddg

代码

以ESP32芯片(Arduino开发环境)为例。

void loop() {if (millis() - Run_Time > 200) {Run_Time = millis();LeftSpeed = 0; RightSpeed = 0;PineMotorRun(0);}GetIRData(&Angle, &Distance);if (Distance > 30 && Angle > 0 && Angle < 180) {LeftSpeed = 50 - (Angle - 90) * 0.5;RightSpeed = 50 + (Angle - 90) * 0.5;PineMotor_Run(LeftSpeed, RightSpeed);Run_Time = millis();OutStr = "L/R speed:" + String(LeftSpeed) + "," + String(RightSpeed);}}

红外感应代码 "IRSensor.h"

#ifndef __IRSENSOR_H__
#define __IRSENSOR_H__#define IRPINCOUNT 7
#define MaxIRShotCount  300
#define MaxHistoryCount 10
#define InValidAngle -360.0struct IRSENSOR_DATA{uint16_t IRCount[IRPINCOUNT];//红外感应器接受的脉冲数uint16_t IRValidCount[IRPINCOUNT];//红外感应器接受的有效脉冲数(脉冲间隔时间满足一定的范围)int IRTimeHistory[IRPINCOUNT][MaxIRShotCount];//红外感应器接受到脉冲时的时刻,单位为微秒long FirstTime[IRPINCOUNT];//单个感应器的首次脉冲时刻,单位为微秒long LastTime[IRPINCOUNT];  //单个感应器的最后脉冲时刻,单位为微秒long IRFirstTime, IREndTime;//整组感应器的首次,最后脉冲时刻,单位为微秒uint8_t ValidPinCount;//接受到有效信息的红外感应器个数double IRZone;double IRDistance;
};//红外感应器初始化
void iniIR();
static void IRPinCallBack(uint8_t i);
void DisableIRInterrupt();
void ResetIRSensorData();
void ResetIRData();void ResetIRSensorData();
long GetFirstIRTime_ms();
long GetFinalIRTime_ms();
double CalculateAngleMovingMean(uint8_t LastCount);void SumIRDataLoop();
void GetIRDataFromPin();//NotConfirmTime
double GetIRAngle();
void GetIRData(double *angle, int *distance);
String GetIRSignalStatus();uint8_t GetHighLevelPinCount(uint16_t IRCount[], uint16_t LL);
void AnalysisIRValues_OneSide(IRSENSOR_DATA* mData, uint8_t Angle[]);void RecordIRData(double Currentdata);void GetValidCount(IRSENSOR_DATA* mData);void Record_DoubleArray(double DArray[], double mydata, uint8_t * index, uint8_t Length);
void Push_DoubleArray(double History[], int Length);
void Reset_Array16(uint16_t IRCount[], int Length);#endif

红外感应代码 “IRSensor.cpp”

#include <Arduino.h>#include "IRSensor.h"struct IRSENSOR_DATA mIRData;int  LineCount, Count2 = 0;
bool IsIniIRPin = false, IsReceivedIRData = false;char strIR[140], Head[] = {"Angle:"};//IR, Pin从左到右,角度从大到小
uint8_t IRPin[] = {33, 32, 34, 35, 23, 21, 19}; //
uint8_t Angle_Pin[] = {165, 140, 115, 90, 65, 40, 15};bool IsHaveIRData = false, IsInNearArea = false;double IRAngleHistory[MaxHistoryCount];
uint8_t IRRecordIndex = MaxHistoryCount - 1;char IRs[256];
String IRStatus, AngleStatus, IRResponseTime_String, IRTimeStatus;long LastTime_AnalysisData;
bool IsFinishAnalysis;
long SimulatedTime;//static void IRPinCallBack(uint8_t i) {long CurrentTime = micros();if (!IsHaveIRData) {mIRData.IRFirstTime = CurrentTime;}if (IsFinishAnalysis) {IsFinishAnalysis = false;}mIRData.IREndTime = CurrentTime;if (mIRData.IRCount[i] < MaxIRShotCount) {if (mIRData.IRCount[i] == 0) {mIRData.FirstTime[i] = CurrentTime;mIRData.IRTimeHistory[i][mIRData.IRCount[i]] = 0;mIRData.LastTime[i] = CurrentTime;} else {mIRData.IRTimeHistory[i][mIRData.IRCount[i]] = CurrentTime - mIRData.LastTime[i];}mIRData.LastTime[i] = CurrentTime;mIRData.IRCount[i]++;} else {mIRData.IRCount[i] = 0;}IsHaveIRData = true;
}
/////
void IRAM_ATTR IntCallbackIR0() {uint8_t i = 0;IRPinCallBack(i);
}void IRAM_ATTR IntCallbackIR1() {uint8_t i = 1;IRPinCallBack(i);
}void IRAM_ATTR IntCallbackIR2() {uint8_t i = 2;IRPinCallBack(i);
}void IRAM_ATTR IntCallbackIR3() {uint8_t i = 3;IRPinCallBack(i);
}void IRAM_ATTR IntCallbackIR4() {uint8_t i = 4;IRPinCallBack(i);
}void IRAM_ATTR IntCallbackIR5() {uint8_t i = 5;IRPinCallBack(i);
}void IRAM_ATTR IntCallbackIR6() {uint8_t i = 6;IRPinCallBack(i);
}
///void SumIRDataLoop() {if (IsHaveIRData && !IsFinishAnalysis && (millis() - GetFirstIRTime_ms() > 30)) {//一个发射周期是200毫秒,一个脉冲105*2微妙,共25个脉冲,共5.26毫秒。 在30毫秒后分析数据LastTime_AnalysisData = millis();GetIRDataFromPin();IsFinishAnalysis = true;}if (millis() - LastTime_AnalysisData > 200) {ResetIRData();}}void GetIRDataFromPin() {String IRstr = "";IRStatus = "";//SimulateIRData();if ( GetHighLevelPinCount(mIRData.IRCount, MaxHistoryCount) >= 6) {IsInNearArea = true;//Serial.printf("近距离\n");}AnalysisIRValues_OneSide(&mIRData, Angle_Pin);char SS[256];sprintf(SS, "Angle:%.2f\r\n", mIRData.IRZone);IRstr = String(SS);if (IsInNearArea) {IRstr += "IRD:30\r\n";mIRData.IRDistance = 30;} else {IRstr += "IRD:500\r\n";mIRData.IRDistance = 500;}IRstr += IRStatus;//Serial.print(IRstr);ResetIRSensorData();
}double CalculateAngleMovingMean(uint8_t LastCount) {double sum = 0, validcount = 0;uint8_t i;if (LastCount > MaxHistoryCount) LastCount = MaxHistoryCount;for (i = 1; i <= LastCount; i++) {if (0 <= IRAngleHistory[MaxHistoryCount - i] && IRAngleHistory[MaxHistoryCount - i] <= 180 ) {sum += IRAngleHistory[MaxHistoryCount - i];validcount++;}}if (validcount > 1) {return sum / validcount;} else {return -1;}}String GetIRSignalStatus() {return IRStatus;
}String GetIRResponseTime() {return IRResponseTime_String;
}double GetIRAngle() {return mIRData.IRZone;
}void GetIRData(double *angle, int *distance) {*angle =  mIRData.IRZone;*distance =  mIRData.IRDistance;
}void ResetIRData() {mIRData.IRZone = InValidAngle;mIRData.IRDistance = 0;IRStatus = "";IRResponseTime_String = "";IsReceivedIRData = false;
}void ResetIRSensorData() {uint8_t i;for (i = 0; i < IRPINCOUNT; i++) {mIRData.IRCount[i] = 0;}long TempTime;TempTime = micros();for (i = 0; i < IRPINCOUNT; i++) {mIRData.IRTimeHistory[i][0] = 0;uint16_t j;for (j = 0; j < MaxIRShotCount; j++) {mIRData.IRTimeHistory[i][j] = 0;}mIRData.LastTime[i] = TempTime + 20 * 1000;mIRData.FirstTime[i] = TempTime + 20 * 1000;}mIRData.ValidPinCount = 0;IsHaveIRData = false;IsInNearArea = false;
}uint8_t GetHighLevelPinCount(uint16_t IRCount[], uint16_t LL) {uint8_t i, Count = 0;for (i = 0; i < IRPINCOUNT; i++) {if (IRCount[i] > LL) {Count++;}}return Count;
}void AnalysisIRValues_OneSide(IRSENSOR_DATA *mData, uint8_t Angle[]) {uint16_t i, j;boolean IsAbnormal = false;double AngleMean = InValidAngle, AngleSum = 0.0;  //arduino无float型数据int CountSum = 0, ValidCountSum = 0;String IRStr = "", SideStr = "";SideStr = "IR:";IRStr = SideStr;for (i = 0; i < IRPINCOUNT; i++) {IRStr += String(mData->IRCount[i]) + ",";CountSum += mData->IRCount[i];}IRStr += "Valid:";if (CountSum <= 4) {  //偶尔几个脉冲,无需理会for (i = 0; i < IRPINCOUNT; i++) {mData->IRValidCount[i] = 0;IRStr +=  "0,";}IRStr += "\r\n";IRStatus += IRStr;//Serial.println(IRStatus);mData->IRZone = InValidAngle;//Serial.println("No IR Signal.");return;}//  Serial.printf("Signal Count: %d \n", CountSum);boolean isCheckPulseTime = false;String strOut;if (isCheckPulseTime) {GetValidCount(mData);} else {for (i = 0; i < IRPINCOUNT; i++) {mData->IRValidCount[i] = mData->IRCount[i];}}ValidCountSum = 1;//相除时,不会是0for (i = 0; i < IRPINCOUNT; i++) {IRStr += String(mData->IRValidCount[i]) + ",";ValidCountSum += mData->IRValidCount[i];}IRStr += "\r\n";IRStatus += IRStr;//  Serial.println(IRStr);for (i = 0; i < IRPINCOUNT; i++) {if ( mData->IRCount[i] > 100) {  //脉冲太多,是干扰信号IsAbnormal = true;mData->IRZone = InValidAngle;break;}}if (CountSum / ValidCountSum >= 3) {  //少于33%的有效脉冲,极有可能是干扰信号IsAbnormal = true;mData->IRZone = InValidAngle;}if (IsAbnormal) {Reset_Array16(mData->IRCount, IRPINCOUNT);//Serial.printf("abnormal\n");return;}//加权平均//  Serial.println("");AngleMean = 0;    AngleSum = 0;mData->ValidPinCount = 0;ValidCountSum = 0;for (i = 0; i < IRPINCOUNT; i++) {if (mData->IRValidCount[i] < 1) {    //脉冲数量少,认为是干扰,就置0mData->IRValidCount[i] = 0;} else {(mData->ValidPinCount)++;}AngleSum += Angle[i]  * mData->IRValidCount[i];ValidCountSum += mData->IRValidCount[i];}if (ValidCountSum > 0) {AngleMean = AngleSum / ValidCountSum;} else {AngleMean = InValidAngle;}sprintf(IRs, " Zone: %.2f Valid Signal Count: %d \n", AngleMean, ValidCountSum);strOut = SideStr + String(IRs);AngleStatus += strOut;mData->IRZone = AngleMean;Record_DoubleArray(IRAngleHistory, AngleMean, &IRRecordIndex, MaxHistoryCount);}void GetValidCount(IRSENSOR_DATA *mData) {int CL = 500; uint8_t Error = 50;int IRTime;uint16_t i, j;//用于调试程序String IRStr = "", strOut;for (j = 0; j < IRPINCOUNT; j++) {IRStr = String(j) + ":";for (i = 0; i < mData->IRCount[j]; i++) {IRStr += String(mData->IRTimeHistory[j][i]) + " ";}//    Serial.println(IRStr);}for (j = 0; j < IRPINCOUNT; j++) {mData->IRValidCount[j] = 0;for (i = 0; i < MaxIRShotCount; i++) {IRTime = (mData->IRTimeHistory[j][i] + CL / 2) % CL + CL / 2;if (CL - Error < mData->IRTimeHistory[j][i] && CL - Error < IRTime  &&  IRTime < CL + Error) {mData->IRValidCount[i]++;}}}}long GetFirstIRTime_ms() {return mIRData.IRFirstTime / 1000;
}long GetFirstIRTime_us() {return mIRData.IRFirstTime;
}long GetFinalIRTime_ms() {return mIRData.IREndTime / 1000;
}////
void iniIR() {uint8_t i;uint8_t Model = FALLING; //CHANGEif (!IsIniIRPin) {for (i = 0; i < IRPINCOUNT; i++) {pinMode(IRPin[i], INPUT_PULLUP);}i = 0;attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR0, Model);//FALLINGi++;attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR1, Model);i++;attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR2, Model);i++;attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR3, Model);i++;attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR4, Model);i++;attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR5, Model);i++;attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR6, Model);ResetIRSensorData();IRStatus = "";AngleStatus = "";IRResponseTime_String = "";IRTimeStatus = "";IsIniIRPin = true;}}void DisableIRInterrupt() {uint8_t i;for (i = 0; i < IRPINCOUNT; i++) {detachInterrupt(digitalPinToInterrupt(IRPin[i]));}IsIniIRPin = false;
}//
void Reset_Array16(uint16_t IRCount[], int Length) {int i;for (i = 0; i < Length; i++) {IRCount[i] = 0;}}void Record_DoubleArray(double DArray[], double mydata, uint8_t * index, uint8_t Length) {DArray[*index] = mydata;(*index)++;if ((*index) >= Length) {Push_DoubleArray(DArray, Length);*index = Length - 1;}}void Push_DoubleArray(double History[], int Length) {int i;for (i = 0; i < Length - 1; i++) {History[i] = History[i + 1];}}//

采购清单

序号

物品

数量

备注

1

小车底座(含1块亚克力板,2个马达,2个车轮,1个万向轮,1个电池盒)

1

2

电路板

1

3

ESP32芯片

1

4

Mico-USB线

1

5

红外感应器

1

https://item.taobao.com/item.htm?spm=a2oq0.12575281.0.0.50111debsOkWHG&ft=t&id=674576373037

6

马达驱动板(L298N)

1

7

充电板

1

8

18650锂电池

1

9

红外发射器

1

基于红外感应的远距离智能跟随小车,自动跟随小车相关推荐

  1. 【论文】ROS系统的无人小车自动跟随方案研究

    这个专栏是专注于入了职场之后,对写论文能力要求和技巧经验的一些总结. 在职场不同于在学习等科研院所,更多要求的是发出论文,而不是发高水平论文. 文章列表: [程序员读论文]为什么要读论文? [程序员读 ...

  2. 基于stm32单片机的WIFI智能联网天气预报自动校时系统(源码+原理图+全套资料)

    资料编号:071 通过wifi 联网获取任何城市的时间和天气,OLED显示,城市位置可以调整, 具体功能请看演示视频  全套资料齐全: 71-基于stm32单片机的WIFI智能联网天气预报自动校时系统 ...

  3. 自动跟随机器人:一种简易的自动跟随方案,自动跟随小车、自动跟随平衡小车、STM32、基于超声波的自动跟随小车

    目的:一种廉价的跟随方案,让大家都能够参与进来,技术难度不大,一些人也能够DIY一些属于自己的"跟随"机器人!并不是要做工业应用什么的.只是做出来玩玩~ / 1 / 介绍 先看视频 ...

  4. 红外感应灯和人体感应灯两者之间区别在哪?

    感应灯在生活中的应用很普遍,感应灯有很多种,其中较为常见的是红外感应灯和人体感应灯,那么这两者之间有什么区别呢? 1.性质区别: 人体感应灯是基于红外线技术的一种自动控制产品,当有人进入感应范围的时候 ...

  5. python机器人编程——差速AGV机器、基于视觉和预测控制的循迹、自动行驶(上篇)

    目录 一.前言 二.视觉自动循迹的算法流程 (1)图像的获取 (2)图像的预处理 (3)目标轨迹的提取 (4)根据已知曲线进行预测控制 三.核心模块及要点 轨迹图像细化 小车轮距L的测量 预测轨迹变换 ...

  6. 基于stm32f103智能风扇,可以手动开关也可温控感应和红外感应。 有程序及原理图

    基于stm32f103智能风扇,可以手动开关也可温控感应和红外感应. 有程序及原理图,有论文参考. 带有k5,proteus8等软件安装教程. ID:6915648239069102LSAMX

  7. 6、基于51单片机智能台灯坐姿定时+光线+红外感应系统设计(程序+原理图+参考论文+开题报告+任务书等)

    摘要 社会在不断进步,人类在不断追求,市场在不断变化,高科技应用含量决定着产品发展的新趋势和前景,智能化技术在电子产品领域的应用意义深远.随着电子产品的快速发展,家用电器也越来越偏向智能化,已经应用于 ...

  8. 60、基于51单片机1602显示智能台灯坐姿定时+光线+红外+人体感应系统设计

    毕设帮助.开题指导.技术解答(有偿)见文末. 目录 摘要 一.硬件方案 二.设计功能 三.实物图 四.原理图 五.PCB图 六.Protues仿真 七.程序 八.资料包括 摘要 社会在不断进步,人类在 ...

  9. 基于STM32的智能循迹避障小车

    [1]研究背景 随着计算机,微电子技术的快速发展,智能化技术的开发越来越快,智能程度也越来越高,应用的范围也得到了极大的扩展.因此,基于嵌入式技术的智能小车应运而生. 近来两年,智能小车在生活中有着广 ...

最新文章

  1. zookeeper 集群安装
  2. linux snmp磁盘io,cacti利用snmpdiskio 监控服务器磁盘
  3. VS2008 error RC2170: bitmap file xxx.png is not in 3.00 format(转)
  4. 51CTO,什么情况?
  5. NIO 之 FileChannel
  6. python创建脚本文件_python创建文件备份的脚本
  7. 使用 | Java使用WebMagic 爬取网站
  8. attributeerror: __exit___利用__attribute__特性提高 APP 的鲁棒性
  9. 新建SVN Repository
  10. (带手机版数据同步)高等院校学院学校类网站源码 政府单位学院学校网站织梦模板
  11. 计算机车辆识别检测毕业设计,车辆识别论文,关于基于计算机网络技术的车辆识别技术相关参考文献资料-免费论文范文...
  12. 利用PS制作GIF动图
  13. UNIX时间戳和北京时间的相互转换
  14. DirectAdmin教程-初级
  15. 写函数,计算传入函数的字符串中,数字、字母、空格 以及 其他内容的个数,并返回结果
  16. 想成为一名黑客怎么办?
  17. 基于xsh的vbs脚本的使用(条件判断和函数)
  18. org.apache.ibatis.binding.BindingException: Type interface com.chen.dao.PersonDao is not known to th
  19. 怎样通过穴位按摩来减轻脚踝扭伤的疼痛
  20. ant安装配置问题:ANT_HOME is set incorrectly or ant could not be located. Please set ANT_HOME.

热门文章

  1. Android 发布代码到github 并且部署到 JitPack maven 仓库详细步骤
  2. (3)DispatcherServlet与初始化主线
  3. 基于安卓的高校程序设计类课程考核平台的设计与实现
  4. 关于稼穑[jià sè] 的神话传说(稼穑:种植与收割,泛指农业劳动)
  5. mp2551总线收发器芯片作用_高速CAN收发器MCP2551
  6. 多旋翼无人机组合导航系统-多源信息融合算法附Matlab代码
  7. 解决电脑CPU占用率高问题
  8. HTML 直播 RTMP流实时播放
  9. 【图像】搜索相同,或者相似照片
  10. Labview Socket通讯_通讯板卡