Seiten

Montag, 17. März 2014

Lego Mindstorms EV3 Components: Color Sensor - Part 1

In this and in our next postings on our Lego Mindstorms series we will have a closer look at the EV3 color sensor. Today we will use the sensor's ability of detecting and distinguishing different colors.

Meanwhile version 0.7.0 of the lejos api was released. But in this tutorial we will still use the 0.6.0-release as 0.7.0s changes weren't dramatic. So you should be able to run the same program with 0.7.0, too.

The robot

The only task the robot will do in this tutorial is: Detecting colors and tell us which color was detected. So in fact we only need the sensor. No motors or something like that is needed. But as we already have a robot from our last articles on the infrared sensor we will take that one and only switch the infrared sensor with the new color sensor.



The parcours

We built a stick with different colors: red, green, yellow and blue. We will hold the different colors in front of the robot's sensor and the robot should give feedback on the detected color by letting the background-LED of the brick's buttons illuminate in the same color. One exception: If blue is detected the program should exit.


The color sensor API

Like the infrared sensor the color sensor has different modes for different tasks. The infrared sensor for example had modes to detect signals of an infrared source or to detect objects and their distance to the robot.
There is a mode for every task a sensor can execute. To detect different colors we need the color sensor's colorIDMode. Once the sensor is in the right mode we only need to call colorSensor.getColorID to get the detected color.

Sounds easy, IS easy. Here comes the code.

The code

We have two classes this time: The Main-class and the ColorRecognizerThread-class. First, let's look at the (really small) main-class:

public class Main {


    public static void main(String[] args) throws InterruptedException {
        final EV3ColorSensor colorSensor = new EV3ColorSensor(SensorPort.S2);
        final SensorMode mode = colorSensor.getColorIDMode();
        final ColorRecognizerThread colorRecognizerThread = new ColorRecognizerThread(colorSensor);

        colorRecognizerThread.start();

        Button.waitForAnyPress();
    }

}

In contrast to our earlier postings we don't need a DifferentialPilot this time because the robot doesn't have to move. So the only things we do is instantiating a new ColorSensor-Instance and set it to the colorIDMode. Then we instantiate a ColorRecognizerThread (which extends Thread) and give it the color sensor object as a constructor parameter. In that thread we will implement the logic to detect colors and let the LED illuminate in the same color.
After starting the thread we wait for any button press as always so that our program won't just exit after starting the thread.

The ColorRecognizerThread looks like the following:

public class ColorRecognizerThread extends Thread {

    private EV3ColorSensor colorSensor;

    public ColorRecognizerThread(final EV3ColorSensor colorSensor) {
        this.colorSensor = colorSensor;
    }

    @Override
    public void run() {
        while(true){
            final int colorId = colorSensor.getColorID();
            switch (colorId){
                //RED
                case 0:
                    Button.LEDPattern(2);
                    break;
                //GREEN
                case 1:
                    Button.LEDPattern(1);
                    break;
                //BLUE
                case 2:
                    Button.LEDPattern(4);
                    threadSleep(2000);
                    Button.LEDPattern(0);
                    System.exit(0);
                //YELLOW
                case 3:
                    Button.LEDPattern(3);
                    break;
                default:
                    Button.LEDPattern(0);
            }
        }
    }

    private void threadSleep(final int ms) {
        try {
            Thread.sleep(ms);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

The constructor is easy as it only saves the color sensor object into a private variable so that we can use it in the run-method.
The really interesting part is the run-method of course, which is called when starting the thread in the main class. We have a deadloop as we want the robot to detect colors continuously. In that loop we get the color found by the color sensor by calling the getColorID()-Method. As you can see it returns an integer. By having a look at the sources (Color.java) we find out which integer value stands for which color:

    public static final int RED = 0;
    public static final int GREEN = 1;
    public static final int BLUE = 2;
    public static final int YELLOW = 3;
    public static final int MAGENTA = 4;
    public static final int ORANGE = 5;
    public static final int WHITE = 6;
    public static final int BLACK = 7;
    public static final int PINK = 8;
    public static final int GRAY = 9;
    public static final int LIGHT_GRAY = 10;
    public static final int DARK_GRAY = 11;
    public static final int CYAN = 12;
    public static final int BROWN = 13;
    public static final int NONE = -1;

So, after getting the id we have a "switch case"-statement for that id. If the found color is red, green or yellow we just want the background-LED of the brick to illuminate in the same color (case 0, 1 and 3). Maybe you are curious why the integer parameter for the Button.LEDPattern-method doesn't match with the integer values from the Color-class. Well, the LED doesn't have a pattern for all of the colors. It has 9 different patterns in total (thanks to tigger from the forum for that information!)

0: turn off button lights
1/2/3: static light green/red/yellow
4/5/6: normal blinking light green/red/yellow
7/8/9: fast blinking light green/red/yellow
>9: same as 9.
 If the color blue is found (case 2) I wanted the program to do the following: Turn the light of the color sensor of and exit the program. The api says that we can use the setFloodlight(int color)-method for that by calling it with Color.NONE:

/**
* Used to turn on or off the floodlight by color. If the sensor has multiple light colors,
* you can control which color is turned on or off. If the color does not exist, it does
* nothing and returns false. You can turn the floodlight off by using Color.NONE.
But this throws an exception if you look at the sources of EV3ColorSensor (the default case of the switch-statement):

public boolean setFloodlight(int color)
    {
        int mode;
        switch (color)
        {
        case Color.BLUE:
            mode = COL_AMBIENT;
            break;
        case Color.WHITE:
            mode = COL_COLOR;
            break;
        case Color.RED:
            mode = COL_REFLECT;
            break;
        default:
            // TODO: Should we ignore a wrong color or throw an exception?
            throw new IllegalArgumentException("Invalid color specified");
        }
        switchMode(mode, SWITCH_DELAY);
        // TODO Auto-generated method stub
        return true;
    }

But I never the less wanted to give some optic signal to show that the program is exiting. Turning off the LED wasn't enough because we already turn it of in our default case of our switch statement in the ColorRecognizerThread. In the end I decided to let the LED display the pattern 4 (blinking green) for two seconds before exiting the program. The threadSleep-method was just created to have the try-catch out of our run-method (because it looks stupid).

That's all.

The result

And here is the tiny result (watch on youtube for better quality):