当前位置:   article > 正文

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

自动跟随小车

跟随原理

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

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

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

 效果展示

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

提取码:vddg

代码

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

  1. void loop() {
  2. if (millis() - Run_Time > 200) {
  3. Run_Time = millis();
  4. LeftSpeed = 0; RightSpeed = 0;
  5. PineMotorRun(0);
  6. }
  7. GetIRData(&Angle, &Distance);
  8. if (Distance > 30 && Angle > 0 && Angle < 180) {
  9. LeftSpeed = 50 - (Angle - 90) * 0.5;
  10. RightSpeed = 50 + (Angle - 90) * 0.5;
  11. PineMotor_Run(LeftSpeed, RightSpeed);
  12. Run_Time = millis();
  13. OutStr = "L/R speed:" + String(LeftSpeed) + "," + String(RightSpeed);
  14. }
  15. }

 红外感应代码 "IRSensor.h"

  1. #ifndef __IRSENSOR_H__
  2. #define __IRSENSOR_H__
  3. #define IRPINCOUNT 7
  4. #define MaxIRShotCount 300
  5. #define MaxHistoryCount 10
  6. #define InValidAngle -360.0
  7. struct IRSENSOR_DATA{
  8. uint16_t IRCount[IRPINCOUNT];//红外感应器接受的脉冲数
  9. uint16_t IRValidCount[IRPINCOUNT];//红外感应器接受的有效脉冲数(脉冲间隔时间满足一定的范围)
  10. int IRTimeHistory[IRPINCOUNT][MaxIRShotCount];//红外感应器接受到脉冲时的时刻,单位为微秒
  11. long FirstTime[IRPINCOUNT];//单个感应器的首次脉冲时刻,单位为微秒
  12. long LastTime[IRPINCOUNT]; //单个感应器的最后脉冲时刻,单位为微秒
  13. long IRFirstTime, IREndTime;//整组感应器的首次,最后脉冲时刻,单位为微秒
  14. uint8_t ValidPinCount;//接受到有效信息的红外感应器个数
  15. double IRZone;
  16. double IRDistance;
  17. };
  18. //红外感应器初始化
  19. void iniIR();
  20. static void IRPinCallBack(uint8_t i);
  21. void DisableIRInterrupt();
  22. void ResetIRSensorData();
  23. void ResetIRData();
  24. void ResetIRSensorData();
  25. long GetFirstIRTime_ms();
  26. long GetFinalIRTime_ms();
  27. double CalculateAngleMovingMean(uint8_t LastCount);
  28. void SumIRDataLoop();
  29. void GetIRDataFromPin();//NotConfirmTime
  30. double GetIRAngle();
  31. void GetIRData(double *angle, int *distance);
  32. String GetIRSignalStatus();
  33. uint8_t GetHighLevelPinCount(uint16_t IRCount[], uint16_t LL);
  34. void AnalysisIRValues_OneSide(IRSENSOR_DATA* mData, uint8_t Angle[]);
  35. void RecordIRData(double Currentdata);
  36. void GetValidCount(IRSENSOR_DATA* mData);
  37. void Record_DoubleArray(double DArray[], double mydata, uint8_t * index, uint8_t Length);
  38. void Push_DoubleArray(double History[], int Length);
  39. void Reset_Array16(uint16_t IRCount[], int Length);
  40. #endif

 红外感应代码 “IRSensor.cpp”

  1. #include <Arduino.h>
  2. #include "IRSensor.h"
  3. struct IRSENSOR_DATA mIRData;
  4. int LineCount, Count2 = 0;
  5. bool IsIniIRPin = false, IsReceivedIRData = false;
  6. char strIR[140], Head[] = {"Angle:"};
  7. //IR, Pin从左到右,角度从大到小
  8. uint8_t IRPin[] = {33, 32, 34, 35, 23, 21, 19}; //
  9. uint8_t Angle_Pin[] = {165, 140, 115, 90, 65, 40, 15};
  10. bool IsHaveIRData = false, IsInNearArea = false;
  11. double IRAngleHistory[MaxHistoryCount];
  12. uint8_t IRRecordIndex = MaxHistoryCount - 1;
  13. char IRs[256];
  14. String IRStatus, AngleStatus, IRResponseTime_String, IRTimeStatus;
  15. long LastTime_AnalysisData;
  16. bool IsFinishAnalysis;
  17. long SimulatedTime;
  18. //
  19. static void IRPinCallBack(uint8_t i) {
  20. long CurrentTime = micros();
  21. if (!IsHaveIRData) {
  22. mIRData.IRFirstTime = CurrentTime;
  23. }
  24. if (IsFinishAnalysis) {
  25. IsFinishAnalysis = false;
  26. }
  27. mIRData.IREndTime = CurrentTime;
  28. if (mIRData.IRCount[i] < MaxIRShotCount) {
  29. if (mIRData.IRCount[i] == 0) {
  30. mIRData.FirstTime[i] = CurrentTime;
  31. mIRData.IRTimeHistory[i][mIRData.IRCount[i]] = 0;
  32. mIRData.LastTime[i] = CurrentTime;
  33. } else {
  34. mIRData.IRTimeHistory[i][mIRData.IRCount[i]] = CurrentTime - mIRData.LastTime[i];
  35. }
  36. mIRData.LastTime[i] = CurrentTime;
  37. mIRData.IRCount[i]++;
  38. } else {
  39. mIRData.IRCount[i] = 0;
  40. }
  41. IsHaveIRData = true;
  42. }
  43. //
  44. ///
  45. void IRAM_ATTR IntCallbackIR0() {
  46. uint8_t i = 0;
  47. IRPinCallBack(i);
  48. }
  49. void IRAM_ATTR IntCallbackIR1() {
  50. uint8_t i = 1;
  51. IRPinCallBack(i);
  52. }
  53. void IRAM_ATTR IntCallbackIR2() {
  54. uint8_t i = 2;
  55. IRPinCallBack(i);
  56. }
  57. void IRAM_ATTR IntCallbackIR3() {
  58. uint8_t i = 3;
  59. IRPinCallBack(i);
  60. }
  61. void IRAM_ATTR IntCallbackIR4() {
  62. uint8_t i = 4;
  63. IRPinCallBack(i);
  64. }
  65. void IRAM_ATTR IntCallbackIR5() {
  66. uint8_t i = 5;
  67. IRPinCallBack(i);
  68. }
  69. void IRAM_ATTR IntCallbackIR6() {
  70. uint8_t i = 6;
  71. IRPinCallBack(i);
  72. }
  73. ///
  74. void SumIRDataLoop() {
  75. if (IsHaveIRData && !IsFinishAnalysis && (millis() - GetFirstIRTime_ms() > 30)) {
  76. //一个发射周期是200毫秒,一个脉冲105*2微妙,共25个脉冲,共5.26毫秒。 在30毫秒后分析数据
  77. LastTime_AnalysisData = millis();
  78. GetIRDataFromPin();
  79. IsFinishAnalysis = true;
  80. }
  81. if (millis() - LastTime_AnalysisData > 200) {
  82. ResetIRData();
  83. }
  84. }
  85. void GetIRDataFromPin() {
  86. String IRstr = "";
  87. IRStatus = "";
  88. //SimulateIRData();
  89. if ( GetHighLevelPinCount(mIRData.IRCount, MaxHistoryCount) >= 6) {
  90. IsInNearArea = true;
  91. //Serial.printf("近距离\n");
  92. }
  93. AnalysisIRValues_OneSide(&mIRData, Angle_Pin);
  94. char SS[256];
  95. sprintf(SS, "Angle:%.2f\r\n", mIRData.IRZone);
  96. IRstr = String(SS);
  97. if (IsInNearArea) {
  98. IRstr += "IRD:30\r\n";
  99. mIRData.IRDistance = 30;
  100. } else {
  101. IRstr += "IRD:500\r\n";
  102. mIRData.IRDistance = 500;
  103. }
  104. IRstr += IRStatus;
  105. //Serial.print(IRstr);
  106. ResetIRSensorData();
  107. }
  108. double CalculateAngleMovingMean(uint8_t LastCount) {
  109. double sum = 0, validcount = 0;
  110. uint8_t i;
  111. if (LastCount > MaxHistoryCount) LastCount = MaxHistoryCount;
  112. for (i = 1; i <= LastCount; i++) {
  113. if (0 <= IRAngleHistory[MaxHistoryCount - i] && IRAngleHistory[MaxHistoryCount - i] <= 180 ) {
  114. sum += IRAngleHistory[MaxHistoryCount - i];
  115. validcount++;
  116. }
  117. }
  118. if (validcount > 1) {
  119. return sum / validcount;
  120. } else {
  121. return -1;
  122. }
  123. }
  124. String GetIRSignalStatus() {
  125. return IRStatus;
  126. }
  127. String GetIRResponseTime() {
  128. return IRResponseTime_String;
  129. }
  130. double GetIRAngle() {
  131. return mIRData.IRZone;
  132. }
  133. void GetIRData(double *angle, int *distance) {
  134. *angle = mIRData.IRZone;
  135. *distance = mIRData.IRDistance;
  136. }
  137. void ResetIRData() {
  138. mIRData.IRZone = InValidAngle;
  139. mIRData.IRDistance = 0;
  140. IRStatus = "";
  141. IRResponseTime_String = "";
  142. IsReceivedIRData = false;
  143. }
  144. void ResetIRSensorData() {
  145. uint8_t i;
  146. for (i = 0; i < IRPINCOUNT; i++) {
  147. mIRData.IRCount[i] = 0;
  148. }
  149. long TempTime;
  150. TempTime = micros();
  151. for (i = 0; i < IRPINCOUNT; i++) {
  152. mIRData.IRTimeHistory[i][0] = 0;
  153. uint16_t j;
  154. for (j = 0; j < MaxIRShotCount; j++) {
  155. mIRData.IRTimeHistory[i][j] = 0;
  156. }
  157. mIRData.LastTime[i] = TempTime + 20 * 1000;
  158. mIRData.FirstTime[i] = TempTime + 20 * 1000;
  159. }
  160. mIRData.ValidPinCount = 0;
  161. IsHaveIRData = false;
  162. IsInNearArea = false;
  163. }
  164. uint8_t GetHighLevelPinCount(uint16_t IRCount[], uint16_t LL) {
  165. uint8_t i, Count = 0;
  166. for (i = 0; i < IRPINCOUNT; i++) {
  167. if (IRCount[i] > LL) {
  168. Count++;
  169. }
  170. }
  171. return Count;
  172. }
  173. void AnalysisIRValues_OneSide(IRSENSOR_DATA *mData, uint8_t Angle[]) {
  174. uint16_t i, j;
  175. boolean IsAbnormal = false;
  176. double AngleMean = InValidAngle, AngleSum = 0.0; //arduino无float型数据
  177. int CountSum = 0, ValidCountSum = 0;
  178. String IRStr = "", SideStr = "";
  179. SideStr = "IR:";
  180. IRStr = SideStr;
  181. for (i = 0; i < IRPINCOUNT; i++) {
  182. IRStr += String(mData->IRCount[i]) + ",";
  183. CountSum += mData->IRCount[i];
  184. }
  185. IRStr += "Valid:";
  186. if (CountSum <= 4) { //偶尔几个脉冲,无需理会
  187. for (i = 0; i < IRPINCOUNT; i++) {
  188. mData->IRValidCount[i] = 0;
  189. IRStr += "0,";
  190. }
  191. IRStr += "\r\n";
  192. IRStatus += IRStr;
  193. //Serial.println(IRStatus);
  194. mData->IRZone = InValidAngle;
  195. //Serial.println("No IR Signal.");
  196. return;
  197. }
  198. // Serial.printf("Signal Count: %d \n", CountSum);
  199. boolean isCheckPulseTime = false;
  200. String strOut;
  201. if (isCheckPulseTime) {
  202. GetValidCount(mData);
  203. } else {
  204. for (i = 0; i < IRPINCOUNT; i++) {
  205. mData->IRValidCount[i] = mData->IRCount[i];
  206. }
  207. }
  208. ValidCountSum = 1;//相除时,不会是0
  209. for (i = 0; i < IRPINCOUNT; i++) {
  210. IRStr += String(mData->IRValidCount[i]) + ",";
  211. ValidCountSum += mData->IRValidCount[i];
  212. }
  213. IRStr += "\r\n";
  214. IRStatus += IRStr;
  215. // Serial.println(IRStr);
  216. for (i = 0; i < IRPINCOUNT; i++) {
  217. if ( mData->IRCount[i] > 100) { //脉冲太多,是干扰信号
  218. IsAbnormal = true;
  219. mData->IRZone = InValidAngle;
  220. break;
  221. }
  222. }
  223. if (CountSum / ValidCountSum >= 3) { //少于33%的有效脉冲,极有可能是干扰信号
  224. IsAbnormal = true;
  225. mData->IRZone = InValidAngle;
  226. }
  227. if (IsAbnormal) {
  228. Reset_Array16(mData->IRCount, IRPINCOUNT);
  229. //Serial.printf("abnormal\n");
  230. return;
  231. }
  232. //加权平均
  233. // Serial.println("");
  234. AngleMean = 0; AngleSum = 0;
  235. mData->ValidPinCount = 0;
  236. ValidCountSum = 0;
  237. for (i = 0; i < IRPINCOUNT; i++) {
  238. if (mData->IRValidCount[i] < 1) { //脉冲数量少,认为是干扰,就置0
  239. mData->IRValidCount[i] = 0;
  240. } else {
  241. (mData->ValidPinCount)++;
  242. }
  243. AngleSum += Angle[i] * mData->IRValidCount[i];
  244. ValidCountSum += mData->IRValidCount[i];
  245. }
  246. if (ValidCountSum > 0) {
  247. AngleMean = AngleSum / ValidCountSum;
  248. } else {
  249. AngleMean = InValidAngle;
  250. }
  251. sprintf(IRs, " Zone: %.2f Valid Signal Count: %d \n", AngleMean, ValidCountSum);
  252. strOut = SideStr + String(IRs);
  253. AngleStatus += strOut;
  254. mData->IRZone = AngleMean;
  255. Record_DoubleArray(IRAngleHistory, AngleMean, &IRRecordIndex, MaxHistoryCount);
  256. }
  257. void GetValidCount(IRSENSOR_DATA *mData) {
  258. int CL = 500; uint8_t Error = 50;
  259. int IRTime;
  260. uint16_t i, j;
  261. //用于调试程序
  262. String IRStr = "", strOut;
  263. for (j = 0; j < IRPINCOUNT; j++) {
  264. IRStr = String(j) + ":";
  265. for (i = 0; i < mData->IRCount[j]; i++) {
  266. IRStr += String(mData->IRTimeHistory[j][i]) + " ";
  267. }
  268. // Serial.println(IRStr);
  269. }
  270. for (j = 0; j < IRPINCOUNT; j++) {
  271. mData->IRValidCount[j] = 0;
  272. for (i = 0; i < MaxIRShotCount; i++) {
  273. IRTime = (mData->IRTimeHistory[j][i] + CL / 2) % CL + CL / 2;
  274. if (CL - Error < mData->IRTimeHistory[j][i] && CL - Error < IRTime && IRTime < CL + Error) {
  275. mData->IRValidCount[i]++;
  276. }
  277. }
  278. }
  279. }
  280. long GetFirstIRTime_ms() {
  281. return mIRData.IRFirstTime / 1000;
  282. }
  283. long GetFirstIRTime_us() {
  284. return mIRData.IRFirstTime;
  285. }
  286. long GetFinalIRTime_ms() {
  287. return mIRData.IREndTime / 1000;
  288. }
  289. //
  290. //
  291. void iniIR() {
  292. uint8_t i;
  293. uint8_t Model = FALLING; //CHANGE
  294. if (!IsIniIRPin) {
  295. for (i = 0; i < IRPINCOUNT; i++) {
  296. pinMode(IRPin[i], INPUT_PULLUP);
  297. }
  298. i = 0;
  299. attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR0, Model);//FALLING
  300. i++;
  301. attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR1, Model);
  302. i++;
  303. attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR2, Model);
  304. i++;
  305. attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR3, Model);
  306. i++;
  307. attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR4, Model);
  308. i++;
  309. attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR5, Model);
  310. i++;
  311. attachInterrupt(digitalPinToInterrupt(IRPin[i]), IntCallbackIR6, Model);
  312. ResetIRSensorData();
  313. IRStatus = "";
  314. AngleStatus = "";
  315. IRResponseTime_String = "";
  316. IRTimeStatus = "";
  317. IsIniIRPin = true;
  318. }
  319. }
  320. void DisableIRInterrupt() {
  321. uint8_t i;
  322. for (i = 0; i < IRPINCOUNT; i++) {
  323. detachInterrupt(digitalPinToInterrupt(IRPin[i]));
  324. }
  325. IsIniIRPin = false;
  326. }
  327. //
  328. void Reset_Array16(uint16_t IRCount[], int Length) {
  329. int i;
  330. for (i = 0; i < Length; i++) {
  331. IRCount[i] = 0;
  332. }
  333. }
  334. void Record_DoubleArray(double DArray[], double mydata, uint8_t * index, uint8_t Length) {
  335. DArray[*index] = mydata;
  336. (*index)++;
  337. if ((*index) >= Length) {
  338. Push_DoubleArray(DArray, Length);
  339. *index = Length - 1;
  340. }
  341. }
  342. void Push_DoubleArray(double History[], int Length) {
  343. int i;
  344. for (i = 0; i < Length - 1; i++) {
  345. History[i] = History[i + 1];
  346. }
  347. }
  348. //

 采购清单

序号

物品

数量

备注

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/658992
推荐阅读
相关标签
  

闽ICP备14008679号