Normalized Difference Vegetation Index (NDVI) sensor?

Does anyone know where I could buy or how I could build a NDVI sensor to detect between grass and sidewalk or soil for a robot mower? I know NASA uses NDVI sensors on Landsat satellites but these might be a little too expensive for me :wink: .

This is the best basic definition of NVDI that you will easily find NVDI WIKI you need to be able to measure the amount of light in the visible spectrum and then in the near IR spectrum so in general you need tow calibrated cameras at the minimum. You can probably find better ways to do what you need.

If I measure (with a photo transistor) the reflectance from grass under a RED LED and then do the same with a IR LED and do some math I should be able to get NVDI. This probably means I would need to mow in the dark at night to exclude sunlight. That is OK. I could quickly (say every 100mS) switch between RED and IR LEDs and capture the readings to get a quick response. Mow at night and recharge the batteries from photocells during the day. Sounds doable. At sunup it quits mowing, goes to a 'home' position, and recharges until sundown.

You would have a ratio of red reflectance over IR reflectance and that might very well work and is a great way to approach the problem if it does, but you don’t have the NVDI. You would not have the NVDI because it is the reflectance from the full visible spectrum not just the red portion.

You will need to make sure the sensors and emitters are looking at the same place in both wavebands otherwise the ratio won’t help you because you will be taking the red reflectance from one patch and the IR from another. The optics of having them sample the same patch is easy if you know the height of the grass, for this application you definitely don’t know the grass height if you did you would not need to mow.

I don't think you need the full visible spectrum to calculate NDVI = (Near IR - RED) / (Near IR + RED) therefore if I have a RED LED and a Near IR LED and a photo transistor which covers the spectrum from RED to near IR then in theory I should be able to calculate NDVI. Anyway I will experiment with it and see how far I get. Optics and beam splitters? I will try without if I can. I will pulse the RED, get a reading, pulse the near-IR, get another reading, then do the NDVI calculation and compare the results with a fixed value.

I don't need an accurate NDVI reading, I only need to sense the difference between grass and sidewalk or mulch.

If you get something working, make sure to post back here - this sounds like an interesting experiment, certainly.

warren631:
I don't think you need the full visible spectrum to calculate NDVI = (Near IR - RED) / (Near IR + RED)

Yes you are correct, apparently the NVDI definition goes back the imagers on AHVRRand it onlt used the 5 bands it had in the red and called this the VIS. When I think of the VIS light spectrum I think about the sum of light through the whole visible (.4 - ,7 micrometers).

I got it working on the bench. I use two 10mm red LED's and two 10mm IR LED's and a LDR sensor mounted in a circle with the LDR (also about 10mm in diameter) in the center in a black tube so it only sees the reflected light. I tried a photo transistor but it seemed too sensitive and too narrow field of view. The LDR seems to have a fast enough reaction time. I use a 1.0 meg-ohm for the LDR and two 22 ohm resistors for the LED's. I will need two sensors for my mower and I will connect both to one NANO dedicated to the sensors. Now I will mount them on my mower and see how they work in the real world. Here is my final code after bench testing:

int r=0, ir=0;
float NDVI=0.0;

void setup()
{
 Serial.begin(115200);
 pinMode(3, OUTPUT);      //IR LED
 pinMode(2, OUTPUT);      //RED LED
        pinMode(13, OUTPUT);     //indicator LED
}

void loop()
{
// get reading for RED:
 digitalWrite(3, HIGH);
 delay(25);            //wait for LDR sensor to stabalize
        r = analogRead(A0);   //read the LDR sensor  
//        sendPlotData("1-ir", (ir));
 digitalWrite(3, LOW);
 
// get reading for IR:
        digitalWrite(2, HIGH);
 delay(25);            //wait for LDR sensor to stabalize
        ir = analogRead(A0);  //read the LDR sensor 
//        sendPlotData("2-r", (r));
 digitalWrite(2, LOW);

// do NDVI calculation:
//        NDVI = float((float(ir) - float(r)) / (float(ir) + float(r)));
        NDVI = float(float(ir) - float(r)); //not true NDVI - just a ratio
        sendPlotData("3-NDVI", (NDVI));
        digitalWrite(13, LOW);   // turn the indicator LED off
        if ((NDVI) > 2)   digitalWrite(13, HIGH);   // GRASS FOUND!
}
//*********format data for MegunoLink charting *****************************
// for testing only:
void sendPlotData(String seriesName, float data) {
  Serial.print("{");  
  Serial.print(seriesName);
  Serial.print(",T,");
  Serial.print(data);
  Serial.println("}");
 
}

Here are photos of the sensor and connection diagram. I used a plastic electrical fitting and a wine cork. I pushed the LEDs and the LDR into the shortened wine cork and pressed the wine cork into the fitting. The photo shows the connections at the back.

Sorry - can't upload photos to this site - it asks for the image URL and I don't have time to upload to another site, etc.

warren631:
...and I don't have time to upload to another site, etc.

Yes you do - use Imgur:

Imgur

It will take you all of 5 minutes to drag and drop and build a simple gallery or get the links and post them here.

Seriously. Use Imgur. Love Imgur.

:smiley:

PS - thanks for the update!

Or just 'Attachments and other options' and post them directly here.
imgur and dropbox and other sites are not accessible to everyone. Attached pictures are.

Here are the photos:



It works up to about 12 inches from the grass. Remember - this sensor only works at night in the dark (robomow at night and scare the neighbors!). Maybe someone will show me how to modulate the light so ambient light doesn’t affect it.

Can't see the photos so here they are:

Imgur
Imgur
Imgur

Now I have to drink another bottle of wine so I can have the cork to make another sensor.....
Also remember you can't see IR except with a digital camera.
I don't remember the components - I just selected the cheapest I could find on Ebay. The LED's and LDR are all about 10mm in diameter.

I made two sensors and mounted on my robot lawnmower and they work great. I used a separate NANO just to do the sensors so it does not bog down the mower routines. A MEGA does the rest of the mower logic. I will let the mower run at night and automatically shutdown at daybreak. Less kids and dogs and curious neighbors at night anyway. Each finished sensor was a little different so each needed different values. A part of my lawn joins a neighbors lawn so I'm not sure what to do about that. Maybe I could lay a strip of plastic between lawns for the sensors to detect. I'm glad I can dump the invisible fence. Now the mower stops and turns at the edges of the lawn. Here is my code for the two sensors (probably could be improved but it works):

float r=0.0, ir=0.0;
float NDVI=0.0;
float rr=0.0, irr=0.0;
float NDVIr=0.0;

void setup()
{
	Serial.begin(115200);
	pinMode(2, OUTPUT);      //IR LED left
 	pinMode(3, OUTPUT);      //RED LED left
        pinMode(7, OUTPUT);      //not grass LED left
	pinMode(4, OUTPUT);      //IR LED right
 	pinMode(5, OUTPUT);      //RED LED right
        pinMode(8, OUTPUT);      //not grass LED right

        pinMode(13, OUTPUT);     //LED
}

void loop()
{

// get reading for IR:
	digitalWrite(2, HIGH);
	digitalWrite(4, HIGH);
	delay(25);            //wait for LDR sensor to stabalize
        ir = analogRead(A0)*1.0;  //read the left LDR sensor  
        irr = analogRead(A1)*1.0; //read the right LDR sensor  
//        sendPlotData("ir", (ir));
//        sendPlotData("irr", (irr));
	digitalWrite(2, LOW);
	digitalWrite(4, LOW);
	
// get reading for RED:
        digitalWrite(3, HIGH);
        digitalWrite(5, HIGH);
	delay(25);           //wait for LDR sensor to stabalize
        r = analogRead(A0)*1.0;  //read the LDR sensor 
        rr = analogRead(A1)*1.0; //read the LDR sensor 
//        sendPlotData("r", (r));
//        sendPlotData("rr", (rr));
	digitalWrite(3, LOW);
	digitalWrite(5, LOW);

// do NDVI calculation:
//        NDVI = float((float(ir) - float(r)) / (float(ir) + float(r)));
        NDVI = float(float(ir) - float(r)); //not true NDVI - just a ratio
        NDVIr = float(float(irr) - float(rr)); //not true NDVI - just a ratio
        sendPlotData("NDVI", (NDVI));
        sendPlotData("NDVIr", (NDVIr));
        if ((NDVI) > 60) digitalWrite(7, HIGH); else digitalWrite(7, LOW);   // NOT GRASS left!
        if ((NDVIr) > 3) digitalWrite(8, HIGH); else digitalWrite(8, LOW);   // NOT GRASS right!

}
//*********format data for MegunoLink charting *****************************
void sendPlotData(String seriesName, float data) {
  Serial.print("{");  
  Serial.print(seriesName);
  Serial.print(",T,");
  Serial.print(data);
  Serial.println("}");
}
1 Like