设为首页收藏本站

走钢丝+扫雷的巡迹小车

virtualwiz 发表于 2016-9-20 20:38:49 | 显示全部楼层 [复制链接]
8 4040
本帖最后由 virtualwiz 于 2016-9-20 20:48 编辑

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

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

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




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




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

小车启动后,沿着一根直径1mm的细铁丝慢慢前进。轨道的随机位置会出现一枚硬币,小车巡迹途中如果遇到硬币,会发出报警声。
(参加过某比赛的同学们别笑我
IMG_20160726_011122.jpg IMG_4375.jpg



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

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

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

1.png

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





IMG_4379.jpg

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



IMG_4364.jpg IMG_4365.jpg IMG_4367.jpg IMG_4370.jpg






送上代码~~据说写代码要规范,别人看到才会舒服。
  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
IMG_4361.jpg
IMG_20160727_110441.jpg
发表于 2016-9-21 09:47:33 | 显示全部楼层
关于代码规范啊,我觉得做的还是不错的嘛,哈哈。缩进后看起来很舒服。
关于巡线,其实真的可以学习很多东西的。
回复 支持 反对

使用道具 举报

发表于 2016-9-21 16:59:25 | 显示全部楼层
V神代码好工整!!
回复 支持 反对

使用道具 举报

发表于 2016-9-22 14:15:13 | 显示全部楼层
感谢分享
回复 支持 反对

使用道具 举报

发表于 2016-10-1 20:24:01 | 显示全部楼层
只见到一长串的Arduino代码
回复 支持 反对

使用道具 举报

发表于 2016-10-21 19:21:46 | 显示全部楼层
V神值得学习,还是那个问题,电源用啥尼?
回复 支持 反对

使用道具 举报

发表于 2016-11-8 14:35:46 | 显示全部楼层
看视频更有意思!想法挺不错的。。。脑洞很大
回复 支持 反对

使用道具 举报

发表于 2016-12-4 22:08:59 | 显示全部楼层
16年湖北大学生电子设计竞赛的题目。我们仪器组做的是电子秤,用的是28335,控制组做的就是这个题
回复 支持 反对

使用道具 举报

发表于 2017-3-6 22:35:21 | 显示全部楼层
第一次看见如此画风清奇的寻线方式····感觉打开了新世界的大门。。。
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册  

本版积分规则 允许回帖同步到新浪微博  

  • 版主
  • 3204
  • 54
  • 蘑菇人勋章

    蘑菇人勋章

  • 活跃会员

    活跃会员

  • 版主限定

    版主限定

推荐阅读

精华导读




公司简介| 联系我们| 小黑屋| 加入我们| 微博| 优酷| 英文网站| DF创客社区 ( 沪ICP备09038501号-4  
友情链接| 硬创邦| 花生壳社区| 模友之吧| 电子发烧友社区| 创客星球| 云汉电子社区| 电子工程网| 与非网| Arduino中文社区| 南极熊3D打印网| OneNET|

上海智位机器人有限公司  沪ICP备09038501号-4   

Powered by Discuz! X3.1

Licensed Comsenz Inc.

返回顶部 返回列表