走钢丝+扫雷的巡迹小车

2016-09-202万
精华项目
AI 快速预览详细 收起
走钢丝+扫雷的巡迹小车是一个基于Arduino的创新项目,使用LDC1000传感器和Romeo BLE控制器。通过检测细铁丝并报警来模拟扫雷游戏。项目包括详细的代码和硬件配置,适合有一定技术基础的Arduino爱好者。


巡迹车估计是入门Arduino的同学们玩厌了的项目了吧

只要在地上贴个黑线,借助光学传感器和一些控制手段,就可以让小车(或者高大上一些的,Vortex)一路沿着黑线行驶。

简单点的做法,只要用红外反射传感器,电压比较器和几片逻辑器件就可以晃晃悠悠起来
复杂点的做法,可以用一大堆光学传感器,在高性能的单片机上运行PID算法,做出可以竞速的智能车




这回我们来做个不一样的巡迹车~~




这个小车用到了DFRobot的Romeo BLE控制器,板载蓝牙和电机驱动,做这种项目灰常合适

小车启动后,沿着一根直径1mm的细铁丝慢慢前进。轨道的随机位置会出现一枚硬币,小车巡迹途中如果遇到硬币,会发出报警声。
(参加过某比赛的同学们别笑我:lol)
走钢丝+扫雷的巡迹小车图7走钢丝+扫雷的巡迹小车图5



因为要检测的是细铁丝,可以使用TI公司的LDC1000数字电感传感器。

粗略地说,原理大概就是,
让一个空心的线圈靠近导体,这时导体就充当了线圈的铁芯,线圈的电感就会略微增加。LDC传感器以很高的频率扫描线圈,当电感特性发生了变化,就说明有导体靠近线圈,这就测出了我们需要的信息。

然后找个舵机,把线圈固定在一个杆上,让舵机来回扫动,同时不断读取LDC传感器的数据。这样,哪里测得的信号最强,就是细铁丝最有可能出现的方向,根据这个数值调整车的方向就可以了

走钢丝+扫雷的巡迹小车图8

这是舵机扫描过程中用串口绘图器画出的波形,上方峰值的位置就是最可能出现铁丝的位置。





走钢丝+扫雷的巡迹小车图6

Romeo上的电机驱动跳线使用了一种不同寻常的接法,将右上方的跳线交叉相连,将PWM信号从L298N的DIR引脚送入,经过测试,这种接法可以显著提升直流电机的低转速性能,但是同时会增加驱动芯片和电机的发热量。



走钢丝+扫雷的巡迹小车图1走钢丝+扫雷的巡迹小车图2走钢丝+扫雷的巡迹小车图3走钢丝+扫雷的巡迹小车图4






送上代码~~据说写代码要规范,别人看到才会舒服。
  1. <font face="Verdana">
  2. //3rd version
  3. #include <Servo.h>
  4. #include "SPI.h"
  5. #include <LiquidCrystal_I2C.h>
  6. #include <Wire.h>
  7. #define GPIO_MOT_LEFT_EN 5
  8. #define GPIO_MOT_RIGHT_EN 6
  9. #define GPIO_MOT_LEFT_DIR 4
  10. #define GPIO_MOT_RIGHT_DIR 7
  11. #define GPIO_SENSOR_SERVO 8
  12. #define GPIO_BUZZER 3
  13. #define GPIO_KEY A7
  14. #define GPIO_LED 13
  15. #define NAVI_DISTANCE_K 0.04
  16. #define NAVI_MOT_PULSEWIDTH 100
  17. #define IDLE_L 127
  18. #define IDLE_R 127
  19. #define SCAN_STARTPOS 50
  20. #define SCAN_ENDPOS 130
  21. #define SCAN_CENTER 90
  22. #define CONTROL_KP 3
  23. #define CONTROL_RUNSPEED 60
  24. #define CONTROL_TURNSPEED 50
  25. #define NAVI_TURNTHRE 6
  26. #define SIGNAL_COIN 20000
  27. #define SERVO_SLPTIME 3
  28. #define SCAN_SLPTIME 50
  29. const int CSB = 10;
  30. //Hardware driver
  31. Servo ScannerArm;
  32. LiquidCrystal_I2C lcd(0x27,16,2);
  33. void SetMotorDuty(byte L,byte R) {
  34.         analogWrite(GPIO_MOT_LEFT_EN,L);
  35.         analogWrite(GPIO_MOT_RIGHT_EN,R);
  36. }
  37. void Motor_Crank() {
  38.         digitalWrite(GPIO_MOT_LEFT_DIR,HIGH);
  39.         digitalWrite(GPIO_MOT_RIGHT_DIR,HIGH);
  40. }
  41. void Motor_Standby() {
  42.         digitalWrite(GPIO_MOT_LEFT_DIR,LOW);
  43.         digitalWrite(GPIO_MOT_RIGHT_DIR,LOW);
  44. }
  45. void GPIO_Init() {
  46.         pinMode(GPIO_MOT_LEFT_EN,OUTPUT);
  47.         pinMode(GPIO_MOT_RIGHT_EN,OUTPUT);
  48.         pinMode(GPIO_MOT_LEFT_DIR,OUTPUT);
  49.         pinMode(GPIO_MOT_RIGHT_DIR,OUTPUT);
  50.         pinMode(GPIO_BUZZER,OUTPUT);
  51.         pinMode(GPIO_LED,OUTPUT);
  52.         SetMotorDuty(0,0);
  53. }
  54. void Serial_LDCSensor_Init() {
  55.         unsigned int data = 0;
  56.   Serial.begin(9600);
  57.   // start SPI library/ activate BUS
  58.   SPI.begin();
  59.   pinMode(CSB, OUTPUT);
  60. SPI.setBitOrder(MSBFIRST);
  61.   SPI.setDataMode(SPI_MODE0); // CPOL = 0 and CPH = 0 mode 3 also works
  62.   SPI.setClockDivider(SPI_CLOCK_DIV4); // set SCLK @ 4MHz, LDC1000 max is 4MHz DIV2 also works
  63.   
  64.     // set power mode to idle to configure stuff
  65.   digitalWrite(CSB, LOW);
  66.   SPI.transfer(0x0B);
  67.   SPI.transfer(0x00);
  68.   digitalWrite(CSB, HIGH);
  69.   delay(100);
  70. // Set RpMax
  71.   digitalWrite(CSB, LOW);
  72.   SPI.transfer(0x01);
  73.   SPI.transfer(0x0E);
  74.   digitalWrite(CSB, HIGH);
  75.   delay(100);
  76.   // Set RpMin
  77.   digitalWrite(CSB, LOW);
  78.   SPI.transfer(0x02);
  79.   SPI.transfer(0x3B);
  80.   digitalWrite(CSB, HIGH);
  81.   delay(100);
  82.   
  83.    // Set Sensor frequency
  84.   digitalWrite(CSB, LOW);
  85.   SPI.transfer(0x03);
  86.   SPI.transfer(0x94);
  87.   digitalWrite(CSB, HIGH);
  88.   delay(100);
  89.   
  90.    // Set LDC configurationn
  91.   digitalWrite(CSB, LOW);
  92.   SPI.transfer(0x04);
  93.   SPI.transfer(0x17);
  94.   digitalWrite(CSB, HIGH);
  95.   delay(100);
  96.   
  97.    // Set clock configuration
  98.   digitalWrite(CSB, LOW);
  99.   SPI.transfer(0x05);
  100.   SPI.transfer(0x00);
  101.   digitalWrite(CSB, HIGH);
  102.   delay(100);
  103.   
  104.   
  105.   // disable all interrupt modes
  106.   digitalWrite(CSB, LOW);
  107.   SPI.transfer(0x0A);
  108.   SPI.transfer(0x00);
  109.   digitalWrite(CSB, HIGH);
  110.   // set thresh HiLSB value
  111.   digitalWrite(CSB, LOW);
  112.   SPI.transfer(0x06);
  113.   SPI.transfer(0x50);
  114.   digitalWrite(CSB, HIGH);
  115.   delay(100);
  116.   // set thresh HiMSB value
  117.   digitalWrite(CSB, LOW);
  118.   SPI.transfer(0x07);
  119.   SPI.transfer(0x14);
  120.   digitalWrite(CSB, HIGH);
  121.   delay(100);
  122.   // set thresh LoLSB value
  123.   digitalWrite(CSB, LOW);
  124.   SPI.transfer(0x08);
  125.   SPI.transfer(0xC0);
  126.   digitalWrite(CSB, HIGH);
  127.   delay(100);
  128.   // set thresh LoMSB value
  129.   digitalWrite(CSB, LOW);
  130.   SPI.transfer(0x09);
  131.   SPI.transfer(0x12);
  132.   digitalWrite(CSB, HIGH);
  133.   delay(100);
  134.   
  135.   // set power mode to active mode
  136.   digitalWrite(CSB, LOW);
  137.   SPI.transfer(0x0B);
  138.   SPI.transfer(0x01);
  139.   digitalWrite(CSB, HIGH);
  140.   delay(100);
  141.         
  142. }
  143. void Alarm(byte type) {
  144.         switch(type)
  145.         {
  146.         case 1:
  147.         tone(GPIO_BUZZER,1109,220);
  148.         delay(220);
  149.         tone(GPIO_BUZZER,1245,220);
  150.         delay(220);
  151.         tone(GPIO_BUZZER,1397,220);
  152.         delay(220);
  153.         noTone(GPIO_BUZZER);
  154.         break;
  155.         
  156.         case 2:
  157.         tone(GPIO_BUZZER,1397,220);
  158.         delay(220);
  159.         tone(GPIO_BUZZER,1109,220);
  160.         delay(220);
  161.         tone(GPIO_BUZZER,1661,220);
  162.         delay(220);
  163.         noTone(GPIO_BUZZER);
  164.         break;
  165.         
  166.         case 3:
  167.         tone(GPIO_BUZZER,1109,50);
  168.         delay(50);
  169.         noTone(GPIO_BUZZER);
  170.         break;
  171.         
  172.         case 4:
  173.         tone(GPIO_BUZZER,1397,150);
  174.         delay(150);
  175.         tone(GPIO_BUZZER,1661,150);
  176.         delay(150);
  177.         tone(GPIO_BUZZER,2794,150);
  178.         delay(150);
  179.         tone(GPIO_BUZZER,2217,150);
  180.         delay(150);
  181.         tone(GPIO_BUZZER,2489,150);
  182.         delay(150);
  183.         tone(GPIO_BUZZER,3322,150);
  184.         delay(150);
  185.         }
  186. }
  187. void Servo_Init() {
  188.         ScannerArm.attach(GPIO_SENSOR_SERVO);
  189.         ScannerArm.write(90);
  190. }
  191. void Display_Init(){
  192.         lcd.init();
  193.         lcd.backlight();
  194.         //lcd.blink();
  195. }
  196. bool CoinFoundFlag = false;
  197. int LDC_SingleScan() {
  198.     unsigned int val = 0;
  199.   unsigned int dataLSB = 0;
  200.   unsigned int dataMSB = 0;
  201.   unsigned int proximitydata = 0;
  202.   
  203.   // Read proximity data LSB register
  204.   digitalWrite(CSB, LOW);
  205.   SPI.transfer(0xA1); // 0x80 + 0x21
  206.   dataLSB = SPI.transfer(0x00);
  207.   digitalWrite(CSB, HIGH);
  208.   delay(SERVO_SLPTIME);
  209.   
  210.   // Read proximity data MSB register
  211.   digitalWrite(CSB, LOW);
  212.   SPI.transfer(0xA2); // 0x80 + 0x22
  213.   dataMSB = SPI.transfer(0x00);
  214.   digitalWrite(CSB, HIGH);
  215.   delay(SERVO_SLPTIME);
  216.   
  217.   proximitydata = ((unsigned int)dataMSB << 8) | (dataLSB);// combine two registers to form 16bit resolution proximity data
  218.   if(proximitydata == 0) Alarm(3);
  219.   Serial.println(proximitydata);
  220. // Serial.print("\t");
  221.   return proximitydata;
  222. }
  223. byte LDC_AutoScan() {
  224.         static signed char ScanDirection = 1;
  225.         int MaxSignalOnPos = 0;
  226.         int MaxSignal = 0;
  227.         int LDCReadBuf = 0;
  228.         int Start;
  229.         int End;
  230.         if(ScanDirection == 1)
  231.         {
  232.         Start = SCAN_STARTPOS;
  233.         End = SCAN_ENDPOS;
  234.         }
  235.         else
  236.         {
  237.         End = SCAN_STARTPOS;
  238.         Start = SCAN_ENDPOS;
  239.         }
  240.         for(int CurrentPos = Start ; CurrentPos != End ; CurrentPos += ScanDirection)
  241.         {
  242.                 LDCReadBuf = LDC_SingleScan();
  243.                 ScannerArm.write(CurrentPos);
  244.                 if(LDCReadBuf > MaxSignal)
  245.                 {
  246.                         MaxSignal = LDCReadBuf;
  247.                         MaxSignalOnPos = CurrentPos;
  248.                 }
  249.         }
  250.         ScanDirection = -ScanDirection;
  251.         if(MaxSignal >= SIGNAL_COIN) {
  252.         Alarm(4);
  253.         CoinFoundFlag = true;
  254.         }
  255.         return MaxSignalOnPos;
  256. }
  257. int LDCValueMax = 1;
  258. int LDCValueMin = 0;
  259. void LDC_StartCalibration()
  260. {        
  261.         
  262. }
  263. void LCD_Format() {
  264.         lcd.setCursor(0,0);
  265.         lcd.print("STATUS  Dir=    ");
  266.         lcd.setCursor(0,1);
  267.         lcd.print("Dst=    Tim=    ");
  268. }
  269. void LCD_ShowInfo(int dir,int dst,int time) {
  270.         lcd.setCursor(12,0);
  271.         lcd.print(dir);
  272.         lcd.setCursor(4,1);
  273.         lcd.print(dst);
  274.         lcd.setCursor(12,1);
  275.         lcd.print(time);
  276. }
  277. byte SpeedWidthLimit(int orig){
  278.         byte dest = orig;
  279.         if(orig >= 255) dest = 254;
  280.         if(orig <= 0 ) dest = 1;
  281.         return dest;
  282. }
  283. int Run_Time;
  284. void setup() {
  285.         Serial_LDCSensor_Init();
  286.         GPIO_Init();
  287.         Servo_Init();
  288.         Display_Init();
  289.         lcd.setCursor(0,0);
  290.         lcd.print("Press any key");
  291.         lcd.setCursor(0,1);
  292.         lcd.print("   [PLL MODE]");
  293.         Alarm(1);
  294.         while(analogRead(GPIO_KEY) > 900);
  295.         Alarm(2);
  296.         delay(1000);
  297.         SetMotorDuty(IDLE_L,IDLE_R);
  298.         delay(1000);
  299.         Run_Time = millis() / 1000;
  300. //        while(1) LDC_SingleScan();
  301. }
  302. int Distance = 0;
  303. void loop() {
  304.         int Duty_Left;
  305.         int Duty_Right;
  306.         int PeakAngle = LDC_AutoScan() - 90;
  307.         if(PeakAngle <= NAVI_TURNTHRE && PeakAngle >= -NAVI_TURNTHRE)
  308.         {
  309.         Duty_Left = IDLE_L + CONTROL_RUNSPEED + (CONTROL_KP * PeakAngle);
  310.         Duty_Right = IDLE_R + CONTROL_RUNSPEED - (CONTROL_KP * PeakAngle);
  311.         }
  312.         else
  313.         {
  314.         Duty_Left = IDLE_L + CONTROL_TURNSPEED + (CONTROL_KP * PeakAngle);
  315.         Duty_Right = IDLE_R + CONTROL_TURNSPEED - (CONTROL_KP * PeakAngle);
  316.         }
  317.         Motor_Crank();
  318.         if(CoinFoundFlag)
  319.         {
  320.         SetMotorDuty(IDLE_L + CONTROL_RUNSPEED + 20,IDLE_R + CONTROL_RUNSPEED + 20);
  321.         CoinFoundFlag = false;
  322.         }
  323.         else
  324.         {
  325.         SetMotorDuty(SpeedWidthLimit(Duty_Left),SpeedWidthLimit(Duty_Right));
  326.         }
  327.         
  328.         Distance += NAVI_DISTANCE_K * CONTROL_RUNSPEED;
  329.         
  330.         delay(NAVI_MOT_PULSEWIDTH);
  331.         
  332.         SetMotorDuty(IDLE_L,IDLE_R);
  333.         LCD_Format();
  334.         LCD_ShowInfo(PeakAngle , Distance , millis() / 1000 - Run_Time);
  335.         Motor_Standby();
  336.         //Serial.println(LDC_AutoScan());
  337. }
  338. </font>
复制代码


IMG_4358.jpg (63.77 KB, 下载次数: 3519)

走钢丝+扫雷的巡迹小车 · 创意Arduino项目_image_3.webp

IMG_4361.jpg (85.78 KB, 下载次数: 3447)

走钢丝+扫雷的巡迹小车 · 创意Arduino项目_image_6.webp

IMG_20160727_110441.jpg (106.44 KB, 下载次数: 3405)

走钢丝+扫雷的巡迹小车 · 创意Arduino项目_image_9.webp

创作许可协议

本项目采用 None(不开放任何权利,保留所有权利) 进行许可。

评论(8)
DDIsKII
DDIsKII
第一次看见如此画风清奇的寻线方式····感觉打开了新世界的大门。。。
qq819287927434
qq819287927434
16年湖北大学生电子设计竞赛的题目。我们仪器组做的是电子秤,用的是28335,控制组做的就是这个题
swanglei
swanglei
看视频更有意思!想法挺不错的。。。脑洞很大
svw
svw
V神值得学习,还是那个问题,电源用啥尼?
iooops
iooops
只见到一长串的Arduino代码
dsweiliang
dsweiliang
感谢分享
Ash1
Ash1
V神代码好工整!!
微笑的rockets
微笑的rockets
关于代码规范啊,我觉得做的还是不错的嘛,哈哈。缩进后看起来很舒服。
关于巡线,其实真的可以学习很多东西的。
- 没有更多了 -