Calibrate an EV3 Color Sensor with Lejos/Java

Hi everybody,

Context

I’m currently working a somewhat complex Mindstorms project (shh…), and, after choosing to develop the software part with Lejos and Java, I’ve been confronted to a classical issue concerning the Color Sensor. By default, as everybody knows, the Color Sensor is able to capture 7 different colors predefined by Lego, and another color, undefined, returned by the sensor when it detects nothing. You’re going to tell me that 7 colors is sufficient for the vast majority of the projects, and… you’re right ! But, reality is totally different and it appears that the mesures made by the sensor are more than approximative, this being even more heterogenous between the sensors. To be noticed, this article does not only concerns Lejos and Java, the same phenomenoms appear with the official Mindstorms softare by Lego (under Labview) or any other programing language you may use to develop your codes (python, C, etc. with ev3dev for example).

Several reasons explain those errors :

  • The sensor, when it comes to generate the measure, emits the 3 base colors with the help of its 3 diodes, red, green and blue (RVB). Those 3 colors generate lighting on the surface to be analyzed et reflect themselves into the color sensor. So, if the surface is not flat, not perfectly aligned in front of the sensor, or not at the perfect distance, there’s a big probability that any of the colors emited by the diodes is captured instead of the global reflected light. The red seems to predominate and is returned more often than it should.
  • Capture conditions have a huge impact on the measure quality. Under a strong light or in the dark, the lighting of the diodes won’t give the same result, obviously changing the measures.
  • The surface to be sampled also greatly impacts the quality of the measure. A shiny surface will be much harder to evaluate than a matte one. By the way, Lego recommends, like many others on the web, to use matte surfaces for their measures… Althought, there’s nothing more shiny than a Lego part !
  • Returning only 7 colors don’t allow the full spectre of the possible measures. Obviously, a very large number of real colors exist (I don’t speak of the 16 millions of your screen’s colors). Between 2 reds, one being darker, obtaining an error and possible and a brown return sent by the sensor may often happen. Blue and green under a strong lighting are confusing too.

How to fix that issue ?

Only 2 possibilities may help you to break those limitations :

  • Keep the color sensor into perfect conditions will permit you to obtain good results. For example, you’ll find lots of robots able to follow lines. They’re operating on a white surface, following a black line. Here are good conditions, there’s an heavy contrast between the 2 colors, the position of the sensor is quite stable, he’s pointing at the bottom at roughly 1 cm away of the surface to measure, and moves parallel to the surface.
  • Change your approach and use the sensor differently. this second possibility is not as easy to implement but will give much better results. The idea is to forget the ability of the sensor to identify correctly a color. Instead, we’ll use the raw values of the 3 colors red, green and blue measured by the sensor. After obtaining those values, we’ll only have to test them and operate when our tests will succeed (it will be more clear with the example below if I lost you !). This method is drastically more efficient and will give you very good results, it will ask you more work but is also much more rewarding. This method will use calibration technics.

Calibration

Calibration is an action which aim is to parameter the sensor, or the software that exploits it. In the preset case, the sensor is not parameterable, we then have to adapt the software. Functionnally, calibrating consists in several operations. I will illustrate them with Lejos and Java right after the explanations because you can realize those manipulations with every programming language able to work with EV3 hardwares.

  • Completely build the model, and position the sensor at its right place. If you change the colors to analyze after you made the calibration, if you move the sensor, or even change the sensor by another one, you’ll have to start again from the beginning. If the sensor is hidden from exterior lightings, inside a body or under panels, place them and dont touch them anymore.
  • Configure the sensor in RGB mode. This mode will return the 3 RGB values of the captured color. Upon the programming language chosen, it will be values in the 0..1024 interval, a float value smaller than 1, etc…
  • For every surface to analyze, realize a few measures and write the results (the 3 values of the Red, Blue and Green captures). The idea is to position the surface to analyse exactly like you would like the sensor to detect the expected color, usually, this should be when the surface is perfectly in front of the sensor. Make at least 4 or 5 measures, then change to the next surface (color) and proceed again with measures. go back to the first color and check if the measures are close of the ones you made yet. For each color, you should then have 4 or 5 series of 3 values, close to each other. For example, for a Lego red panel, you may obtain something like 0.80 / 0.01 / 0.02.
  • Once one with measures, the hardest is done. Just implement tests in your software, one for each color to measure. Check if the values return by your sensor is close of the measures you did before and you’ll know what color is in front of the captor ! For example, for the previous red, ou may do a test that would look like 0.75<Red value<0,85, Green value<0.03, Blue value<0.03. Just be carefull to check alos the green and the blue, even if the value are very low, if you ignore them, your tests may be wrong !

Using this method, you can easily and correctly detect 7 or 8 colors and obtain 100% positive results. Try that with the color mode…

Let’s code…

It’s time for something more concrete, simple, but that will be enough to understand the mechanisms described before. It’s time for something more concrete, simple, but that will be enough to understand the mechanisms described before. I will consider that you have a working EV3 Brick under Lejos, a Java programming environment (Eclipse !). We will use the Remote mode that allows to run software on the computer instead of the brick, so to have concrete results on the screen.

Let’s start by something simple. Declare a Brick in remote mode, and instead of creating an useless Sensor, we will just declare a RMISampleProvider to get our datas from the sensor. Declaring a Sensor would not help us much and in remote mode, it does not always work very well. The RMISampleProvider will do all the job. On my brick, it’s connected on port 1 (S1). As parameters, it needs the sensor type and the capture mode (RGB). Obviously we dont chose Color, remember than we want to obtain the raw values measured, not an estimated color.
After, we only need to use fetchSample on the RMISampleProvider to obtain a measure, 3 floats are returned, the 3 values of the red, green and blue colors measured.

RMISampleProvider sampleProvider=null;
try {
	RemoteEV3 ev3=new RemoteEV3(“10.0.1.1”);
	sampleProvider=ev3.createSampleProvider(“S1”, “lejos.hardware.sensor.EV3ColorSensor”, “RGB”);
	float[] samples=new float[3];
	samples=sampleProvider.fetchSample();
	
	System.out.println(“RGB=”+samples[0]+” / “+samples[1]+” / “+samples[2]);

} catch (Exception e) {
	e.printStackTrace();
} finally {
	try {
		sampleProvider.close();
	} catch (RemoteException e) {
		e.printStackTrace();
	}
}

If we execute that code while a red Lego is in front of the sensor, we’ll obtain some values near 0.18137255 / 0.04509804 / 0.03137255, we will round them to 0.181 / 0.045 / 0.031. Obviously, you WON’T obtain those values, but something close.

If you read the article carefully, you yet know that the next step consists in writing a test to check those values. We add a little bit of margin to the test because the sensor will never return exactly the same measure. For our values, the test could be :

0.175 < 0.181 < 0.19
0.04 < 0.045 < 0.05
0.025 < 0.031 < 0.04

I add it to the previous code to check if it works…

RMISampleProvider sampleProvider=null;
try {
	RemoteEV3 ev3=new RemoteEV3(“10.0.1.1”);
	sampleProvider=ev3.createSampleProvider(“S1”, “lejos.hardware.sensor.EV3ColorSensor”, “RGB”);
	float[] samples=new float[3];
	samples=sampleProvider.fetchSample();
	System.out.println(“RGB=”+samples[0]+” / “+samples[1]+” / “+samples[2]);

	if ( (samples[0]>0.175f) && (samples[0]<0.190f) && (samples[1]>0.040f) && (samples[1]<0.055f) && (samples[2]>0.025f) && (samples[2]<0.045f)) {
		System.out.println(“Red”);
	}

} catch (Exception e) {
	e.printStackTrace();
} finally {
	try {
		sampleProvider.close();
	} catch (RemoteException e) {
		e.printStackTrace();
	}
}

Now, if you start the program again and put the red part you used previously, the word Red should appear in the console. This means that the test is verified and that everything is working well. Remove the red part, and start the program again, nothing will happen.

Now, we can add more tests to check new colors, in a no-end loop and we’re done. The final code is below and is able to recognize red and white parts (with my sensor, with my lighting, obviously…)

RMISampleProvider sampleProvider=null;
try {
	RemoteEV3 ev3=new RemoteEV3(“10.0.1.1”);
	sampleProvider=ev3.createSampleProvider(“S1”, “lejos.hardware.sensor.EV3ColorSensor”, “RGB”);
	float[] samples=new float[3];
	samples=sampleProvider.fetchSample();
	System.out.println(“RGB=”+samples[0]+” / “+samples[1]+” / “+samples[2]);

	while (System.in.available() == 0) {
		samples=sampleProvider.fetchSample();

		if ((samples[0]>0.255f) && (samples[0]<0.265f) && (samples[1]>0.260f) && (samples[1]<0.270f) && (samples[2]>0.195f) && (samples[2]<0.205f)) { 
                       System.out.println(“White”); 
                } 

                if ((samples[0]>0.175f) && (samples[0]<0.190f) && (samples[1]>0.040f) && (samples[1]<0.055f) && (samples[2]>0.025f) && (samples[2]<0.045f)) {
			System.out.println(“Red”);
		}
		Delay.msDelay(50);
	}

} catch (Exception e) {
	e.printStackTrace();
} finally {
	try {
		sampleProvider.close();
	} catch (RemoteException e) {
		e.printStackTrace();
	}
}

You may obviously ameliorate that code and make it more effective. The best thing to do would be to create a class that monitors the sensor and launches events when the sensor recognizes calibrated colors.

Click here to download the LXF file from the Youtube demo :  EV3ColorSensorRGBCalibration.lxf (476 downloads )

Share