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.

#define MIN_SPEED 50
#define CRAWL_SPEED 85
#define MAX_SPEED 125
 
#include <LiquidCrystal.h>
LiquidCrystal lcd(30, 31, 40, 41, 42, 43);
 
#define STROBE_PIN 50
#define INHIBIT_PIN 51
 
#define BIT1_PIN 21
#define BIT2_PIN 23
#define BIT3_PIN 25
#define BIT4_PIN 27
 
#define DIRECTION1_PIN 52
#define DIRECTION2_PIN 53
 
void initMultiplexer() {
  //set the output mode.
  pinMode(INHIBIT_PIN,OUTPUT);
  pinMode(STROBE_PIN,OUTPUT);
  pinMode(BIT1_PIN,OUTPUT);
  pinMode(BIT2_PIN,OUTPUT);
  pinMode(BIT3_PIN,OUTPUT);
  pinMode(BIT4_PIN,OUTPUT);
  pinMode(DIRECTION1_PIN,OUTPUT);
  pinMode(DIRECTION2_PIN,OUTPUT);
  //initial state
  digitalWrite(INHIBIT_PIN, HIGH); //high = off.
  digitalWrite(STROBE_PIN, LOW); //toggle low -> high -> low to set output.
  digitalWrite(BIT1_PIN, LOW);
  digitalWrite(BIT2_PIN, LOW);
  digitalWrite(BIT3_PIN, LOW);
  digitalWrite(BIT4_PIN, LOW);
}
 
void change(int out) {
  //work out bits
  Serial.print((out >> 3) & 0x01);
  if ((out >> 3) & 0x01) digitalWrite(BIT4_PIN, HIGH);
  else digitalWrite(BIT4_PIN, LOW);
  if ((out >> 2) & 0x01) digitalWrite(BIT3_PIN, HIGH);
  else digitalWrite(BIT3_PIN, LOW);
  Serial.print((out >> 2) & 0x01);
  if ((out >> 1) & 0x01) digitalWrite(BIT2_PIN, HIGH);
  else digitalWrite(BIT2_PIN, LOW);
  Serial.print((out >> 1) & 0x01);
  if ((out) & 0x01) digitalWrite(BIT1_PIN, HIGH);
  else digitalWrite(BIT1_PIN, LOW);
  Serial.println((out >> 0) & 0x01);
  //toggle strobe
  digitalWrite(STROBE_PIN, HIGH);
  delay(50);
  digitalWrite(STROBE_PIN, LOW);
}
 
void setup() {
  initMultiplexer();
  Serial.begin(9600);
  lcd.begin(16, 2);
}
 
void updateSensorValue(int& tgt, int latest)
{
   int threshold = 25;
   if ((latest < (tgt - threshold)) || (latest > (tgt + threshold))) tgt = latest;
}
 
void pulseMultiplexer() {
  //toggle inhibit to low to actually output power
  digitalWrite(INHIBIT_PIN,LOW); 
  delay(125); //25ms is long enough.
  digitalWrite(INHIBIT_PIN,HIGH); 
  delay(1500); //now delay before going to next point.
}
 
int thresholds[4] = {9999,0,9999,0};
 
int t = millis();
int oldT = millis(), dirT = oldT;
int a1;
int a2;
int spd = 0;
int dir = 0;
 
int train_status = 0;
int sensor = 0;
int point = 0;
 
void loop() {
  t = millis();
  
  if ((t - oldT) > 50) {
    updateSensorValue(a1, analogRead(0));
    updateSensorValue(a2, analogRead(1));
    
    sensor = a1;
    if (point == 1) sensor = a2;
 
    switch(train_status) {
      case 0: //accelerating
        if (spd < MIN_SPEED) spd = MIN_SPEED;
        spd += 2;
        if (spd > MAX_SPEED) {
          train_status = 1;
          spd = MAX_SPEED;
        }
        break; 
      case 1: //travelling
        break;
      case 2: //braking
        spd -= 10;
        if (spd < CRAWL_SPEED) spd = CRAWL_SPEED;
        break;
      case 3: //paused
        spd = 0;
        break; 
    }
    
    if (dir == 1) {
      if ((t - dirT > 1250) && (train_status == 0 || train_status == 1)) {
        train_status = 2;
        dirT = t;
      }
      else if ((t - dirT > 2000) && train_status == 2 && spd == CRAWL_SPEED) {
        train_status = 3;
        dirT = t;        
      }
      else if ((t - dirT > 500) && train_status == 3)
      {
        train_status = 0;
        dir = !dir;
        dirT = t;
        //switch point
        change(3);
        if (point == 0) {
          digitalWrite(DIRECTION1_PIN, HIGH);
          digitalWrite(DIRECTION2_PIN, LOW);
          Serial.println(0);
        } else if (point == 1) {
          digitalWrite(DIRECTION1_PIN, LOW);
          digitalWrite(DIRECTION2_PIN, HIGH);      
          Serial.println(1);
        }
        pulseMultiplexer();
        point = !point;
      }
    } 
    else if (dir == 0) 
    {    
      //we should be checking which point we're about to hit first?
      /*if (a1 > 400 && dir == 0) {
        spd = 0;
        dir = !dir;
        train_status = 0;
      } else if (a1 > 300 && dir == 0) spd = 70;
      else if (a1 > 200 && dir == 0) spd = 90;
      else if (a1 > 150 && dir == 0) spd = 110;
      else spd = 125;*/
      if (train_status != 3) {
        if (sensor > 400) {
          train_status = 3;
          dirT = t; 
        } else if (sensor > 300) {
          train_status = 2;
        }
      } else if (t - dirT > 500 && train_status == 3) {
        train_status = 0;
        dir = !dir;
        dirT = t;
      }
    }
    
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("");
    lcd.setCursor(0, 0);
    lcd.print(a1);
    lcd.setCursor(0, 1);
    lcd.print("");  
    lcd.setCursor(0, 1);
    lcd.print(a2);
 
    lcd.setCursor(5, 0);
    lcd.print(thresholds[0]);
    lcd.setCursor(7, 0);
    lcd.print(thresholds[1]);
  
    lcd.setCursor(5, 1);
    lcd.print(thresholds[2]);
    lcd.setCursor(7, 1);
    lcd.print(thresholds[3]);
  
    lcd.setCursor(11, 0);
    lcd.print(spd);
    lcd.setCursor(11, 1);
    lcd.print(dir);
    lcd.setCursor(15, 1);
    lcd.print(train_status);
 
    oldT = t;
  }
 
  if (a1 < thresholds[0]) thresholds[0] = a1;
  if (a1 > thresholds[1]) thresholds[1] = a1;
  if (a2 < thresholds[2]) thresholds[2] = a2;
  if (a2 > thresholds[3]) thresholds[3] = a2;
  
  if (dir == 0) {
    digitalWrite(3, HIGH);
    digitalWrite(4, LOW);
  } else {
    digitalWrite(4, HIGH);
    digitalWrite(3, LOW);
  }  
  
  analogWrite(2, spd);
}

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 :)


Leave a comment


*

No trackbacks yet.