Subscribe via RSS
31Mar/102

InfraRed + Arduino revisited

Ok, after failing (more-or-less miserably) with the previous attempt at IR distance detection, I went out and purchased 4 of the Sharp GP2D12 from an eBay Store and put them to work. Finally I had a detection system working, but before this I had also attempted another method using larger IR emitter/detectors that I'd bought from Dip Micro.

Attempt 1: QRD1114, and other smaller IR detectors

See the previous blog post here

Attempt 2: IR Emitter/Detector pairing

Since my previous attempt had failed, I'd decided that if the smaller emitters could not produce enough light to avoid sunlight/roomlight interference, then boosting both the emitter and detector should help. I'd accidently purchased a collection of 10x emitter/collector pairs of IR diodes and so I put these to work in the typical setup and tested them out. These were both 3mm in diameter and, depending on the resistors used, could emit a lot more light (tested via my digital camera.)




The setup was the same as per usual... mounted horizontally at the end of the tracks to ensure that the light would reflect (as much as possible) off the approaching train.

Working with IR at night-time has it's benefits. Your room is usually lit under artificial light and so the amount of IR in the 'air' is low. I therefore had some pretty good results with this setup, but of course, come daylight, everything went out the window (or in the window, as the case may be.) Since this detector was to be in the back of an engine shed, I'd thought that I could block out the windows and make a little dark room, but my detectors were still too unreliable.

This experiment was, in the end, functional, but not to the degree that I'd wanted and so I therefore opted for the off-the-shelf Sharp detector.

Note: The goal here was to use the pair at the end of the track to sense distance. It now seems that the best method will be to detect 'occupation' and I will again test this method with a series of emitter/detector pairs along the track to sense when a train is approaching.

Attempt 3: Sharp GP2D12

After being concerned about sizing and the minimum distance that these detectors would work from, I decided I'd just bite the bullet and try them out.



I ended up purchasing 4 quite cheaply on eBay and they arrived from the UK in a short amount of time. I then realised that the versions I'd bought were optimised for detection between 10cm and 70cm. This really sucks, as I'd want the range to be much closer to the detector, as 10cm is a long way for a no-detection zone. I then looked at the graphs showing the voltage compared to distance:

It turns out that this datasheet shows you the comparison of the various detectors that Sharp makes. There seems to be one detector better suited for my project, but.. as they say... hindsight is a bitch. Even worse is that Toys Downunder currently has them in stock!

Although the 'optimum' detecting distance for unit is around 10cm, the detector still gives valid voltage results right down to around 3cm. The only issue here is that the voltage difference is not always increasing! I therefore have to take the value and use it in different scenarios (i.e. when cruising, braking, stopped, etc...)

The setup was the same as usual:

So, to deal with this, I needed to work on the voltage change... in steps of around 0.25v. If you happen to implement this yourself, you'll notice straight away that the voltage returned by this unit is not constant when the vehicle is stopped or approaching... it's perpetually flitting around +-0.5v and this can be a real nightmare. Due to this I only choose to detect larger changes which means only reading the sensor if it is +-25 of the current read value.

The basic idea is to determine the deceleration of the vehicle dependent on it's distance from the wall. The final testing code is listed next. Note that this also contains my multiplexer, thottle and also an off-the-shelf LCD for which you can find tutorials here.

001#define MIN_SPEED 50
002#define CRAWL_SPEED 85
003#define MAX_SPEED 125
004  
005#include <LiquidCrystal.h>
006LiquidCrystal lcd(30, 31, 40, 41, 42, 43);
007  
008#define STROBE_PIN 50
009#define INHIBIT_PIN 51
010  
011#define BIT1_PIN 21
012#define BIT2_PIN 23
013#define BIT3_PIN 25
014#define BIT4_PIN 27
015  
016#define DIRECTION1_PIN 52
017#define DIRECTION2_PIN 53
018  
019void initMultiplexer() {
020  //set the output mode.
021  pinMode(INHIBIT_PIN,OUTPUT);
022  pinMode(STROBE_PIN,OUTPUT);
023  pinMode(BIT1_PIN,OUTPUT);
024  pinMode(BIT2_PIN,OUTPUT);
025  pinMode(BIT3_PIN,OUTPUT);
026  pinMode(BIT4_PIN,OUTPUT);
027  pinMode(DIRECTION1_PIN,OUTPUT);
028  pinMode(DIRECTION2_PIN,OUTPUT);
029  //initial state
030  digitalWrite(INHIBIT_PIN, HIGH); //high = off.
031  digitalWrite(STROBE_PIN, LOW); //toggle low -> high -> low to set output.
032  digitalWrite(BIT1_PIN, LOW);
033  digitalWrite(BIT2_PIN, LOW);
034  digitalWrite(BIT3_PIN, LOW);
035  digitalWrite(BIT4_PIN, LOW);
036}
037  
038void change(int out) {
039  //work out bits
040  Serial.print((out >> 3) & 0x01);
041  if ((out >> 3) & 0x01) digitalWrite(BIT4_PIN, HIGH);
042  else digitalWrite(BIT4_PIN, LOW);
043  if ((out >> 2) & 0x01) digitalWrite(BIT3_PIN, HIGH);
044  else digitalWrite(BIT3_PIN, LOW);
045  Serial.print((out >> 2) & 0x01);
046  if ((out >> 1) & 0x01) digitalWrite(BIT2_PIN, HIGH);
047  else digitalWrite(BIT2_PIN, LOW);
048  Serial.print((out >> 1) & 0x01);
049  if ((out) & 0x01) digitalWrite(BIT1_PIN, HIGH);
050  else digitalWrite(BIT1_PIN, LOW);
051  Serial.println((out >> 0) & 0x01);
052  //toggle strobe
053  digitalWrite(STROBE_PIN, HIGH);
054  delay(50);
055  digitalWrite(STROBE_PIN, LOW);
056}
057  
058void setup() {
059  initMultiplexer();
060  Serial.begin(9600);
061  lcd.begin(16, 2);
062}
063  
064void updateSensorValue(int& tgt, int latest)
065{
066   int threshold = 25;
067   if ((latest < (tgt - threshold)) || (latest > (tgt + threshold))) tgt = latest;
068}
069  
070void pulseMultiplexer() {
071  //toggle inhibit to low to actually output power
072  digitalWrite(INHIBIT_PIN,LOW); 
073  delay(125); //25ms is long enough.
074  digitalWrite(INHIBIT_PIN,HIGH); 
075  delay(1500); //now delay before going to next point.
076}
077  
078int thresholds[4] = {9999,0,9999,0};
079  
080int t = millis();
081int oldT = millis(), dirT = oldT;
082int a1;
083int a2;
084int spd = 0;
085int dir = 0;
086  
087int train_status = 0;
088int sensor = 0;
089int point = 0;
090  
091void loop() {
092  t = millis();
093   
094  if ((t - oldT) > 50) {
095    updateSensorValue(a1, analogRead(0));
096    updateSensorValue(a2, analogRead(1));
097     
098    sensor = a1;
099    if (point == 1) sensor = a2;
100  
101    switch(train_status) {
102      case 0: //accelerating
103        if (spd < MIN_SPEED) spd = MIN_SPEED;
104        spd += 2;
105        if (spd > MAX_SPEED) {
106          train_status = 1;
107          spd = MAX_SPEED;
108        }
109        break
110      case 1: //travelling
111        break;
112      case 2: //braking
113        spd -= 10;
114        if (spd < CRAWL_SPEED) spd = CRAWL_SPEED;
115        break;
116      case 3: //paused
117        spd = 0;
118        break
119    }
120     
121    if (dir == 1) {
122      if ((t - dirT > 1250) && (train_status == 0 || train_status == 1)) {
123        train_status = 2;
124        dirT = t;
125      }
126      else if ((t - dirT > 2000) && train_status == 2 && spd == CRAWL_SPEED) {
127        train_status = 3;
128        dirT = t;        
129      }
130      else if ((t - dirT > 500) && train_status == 3)
131      {
132        train_status = 0;
133        dir = !dir;
134        dirT = t;
135        //switch point
136        change(3);
137        if (point == 0) {
138          digitalWrite(DIRECTION1_PIN, HIGH);
139          digitalWrite(DIRECTION2_PIN, LOW);
140          Serial.println(0);
141        else if (point == 1) {
142          digitalWrite(DIRECTION1_PIN, LOW);
143          digitalWrite(DIRECTION2_PIN, HIGH);      
144          Serial.println(1);
145        }
146        pulseMultiplexer();
147        point = !point;
148      }
149    
150    else if (dir == 0) 
151    {    
152      //we should be checking which point we're about to hit first?
153      /*if (a1 > 400 && dir == 0) {
154        spd = 0;
155        dir = !dir;
156        train_status = 0;
157      } else if (a1 > 300 && dir == 0) spd = 70;
158      else if (a1 > 200 && dir == 0) spd = 90;
159      else if (a1 > 150 && dir == 0) spd = 110;
160      else spd = 125;*/
161      if (train_status != 3) {
162        if (sensor > 400) {
163          train_status = 3;
164          dirT = t; 
165        else if (sensor > 300) {
166          train_status = 2;
167        }
168      else if (t - dirT > 500 && train_status == 3) {
169        train_status = 0;
170        dir = !dir;
171        dirT = t;
172      }
173    }
174     
175    lcd.clear();
176    lcd.setCursor(0, 0);
177    lcd.print("");
178    lcd.setCursor(0, 0);
179    lcd.print(a1);
180    lcd.setCursor(0, 1);
181    lcd.print("");  
182    lcd.setCursor(0, 1);
183    lcd.print(a2);
184  
185    lcd.setCursor(5, 0);
186    lcd.print(thresholds[0]);
187    lcd.setCursor(7, 0);
188    lcd.print(thresholds[1]);
189   
190    lcd.setCursor(5, 1);
191    lcd.print(thresholds[2]);
192    lcd.setCursor(7, 1);
193    lcd.print(thresholds[3]);
194   
195    lcd.setCursor(11, 0);
196    lcd.print(spd);
197    lcd.setCursor(11, 1);
198    lcd.print(dir);
199    lcd.setCursor(15, 1);
200    lcd.print(train_status);
201  
202    oldT = t;
203  }
204  
205  if (a1 < thresholds[0]) thresholds[0] = a1;
206  if (a1 > thresholds[1]) thresholds[1] = a1;
207  if (a2 < thresholds[2]) thresholds[2] = a2;
208  if (a2 > thresholds[3]) thresholds[3] = a2;
209   
210  if (dir == 0) {
211    digitalWrite(3, HIGH);
212    digitalWrite(4, LOW);
213  else {
214    digitalWrite(4, HIGH);
215    digitalWrite(3, LOW);
216  }  
217   
218  analogWrite(2, spd);
219}

From the above, we get the following action... note that the whole process here is automated (throttle, detection, point switching):

Conclusion

This sensor worked much better than the previous attempts... but it's still not the best. I think I might now just grab one of the GP2D120s (as it would simply be plug-and-play) and see what happens. Also lighting plays an affect here too, it might just be easier in the end to have a strip of LDRs to work out where the train is... but everything has it's good and bad side!

The other option will be to have a spaced strip of detectors down one side of the track and emitters on the other. This will be like your tandy/radio-shack store that beep-beeps when you trip the beam... we'll see how well it works.

Comments (2) Trackbacks (0)
  1. Wow, those Sharp sensors are way larger than I’d expected. I’m beginning to think that a row of IR beams is going to end being the way to go with this, complex as it might be…

    • They are huge… they do the job though.
      I’m currently fine-tuning a library to manage their output and it’s functioning ok. Time isn’t on my side though.

      In the end just tripping IR beams would’ve been the easiest method and you could either accumulate the resistance or multiplex around 4 or 5 beams so as to not waste Arduino pins; but… this is more fun :)


Re: stevenh 's comment

*

( Cancel )

No trackbacks yet.