Calibrage du capteur de couleur EV3 avec Lejos

Bonjour à tous,

La situation

Je suis en train de travailler sur un projet Mindstorms assez complexe (chut…) et, après avoir fait le choix de basculer ma Brick EV3 sous Lejos afin de pouvoir développer en Java, je suis confronté à un problème classique concernant le capteur de couleur EV3. Par défaut, le capteur de couleur sait, lorsqu’il est en mode couleur, capturer 7 couleurs prédéfinies par LEGO ainsi qu’une « non-couleur » représentant la valeur retournée par le capteur lorsqu’il ne capte rien. Vous allez me dire que 7 couleurs c’est suffisant pour la majorité des projets, et… vous avez raison. Sauf que dans les faits, la réalité est toute autre et il s’avère que les mesures faites par ce capteur sont plus qu’approximatives, en plus hétérogènes d’un capteur à l’autre. A savoir, cet article ne concerne pas uniquement Lejos, les mêmes phénomènes se produisent avec l’interface Mindstorm de LEGO (sous Labview) ou n’importe quel autre outil tiers sur lequel vous aurez fait démarrer votre brick (python sous EV3dev, etc…).

Différentes raisons expliquent ces écarts de mesure :

  • Le capteur, pour générer sa mesure, émet 3 couleurs de bases à l’aide de 3 diodes, rouge, verte et bleue. Ces trois couleurs génèrent un éclairage sur la surface à mesurer et c’est cet éclairage qui permet ensuite d’obtenir la nature de la couleur située en face du capteur. Ainsi, si la surface n’est pas plate, pas pile alignée à la hauteur du capteur, ou à la bonne distance, il y a une forte probabilité que l’une ou l’autre des couleurs émise par les diodes soit capturée au lieu de l’éclairage global, faussant ainsi la mesure. Le rouge semble être prédominant et est retourné bien plus souvent qu’il ne le devrait.
  • Les conditions de capture influent sur la qualité de la mesure. En pleine lumière ou dans le noir, l’éclairage des diodes ne donne pas les mêmes résultats.
  • La nature de surface échantillonnée a elle aussi un impact très fort sur la qualité de la mesure. Ainsi, une surface brillante sera beaucoup plus difficile à évaluer qu’une surface mate. D’ailleurs, il est préconisé par LEGO et par beaucoup d’autres sources sur le web de mesurer des couleurs apposées sur des surfaces mates… Sauf qu’il n’y a pas plus brillant que le plastique LEGO !
  • Un retour sur 7 couleurs ne permet pas de couvrir un spectre correct de mesures. En effet, il existe, comme vous le savez tous beaucoup plus de couleurs réelles (je ne parle pas des 16 millions de couleurs que peut prendre votre écran bien sur). Aussi, entre 2 rouges, l’un franc et l’autre sombre, il est facile d’obtenir une mesure rouge et une marron ! Le bleu et le vert, avec un fort éclairage se ressemblent, etc…

Comment régler le problème ?

Il n’existe que 2 solutions pour régler ce problème de limitation du capteur de couleurs :

  • Le maintenir dans les conditions qui permettent d’obtenir de bons résultats. Par exemple, on voit beaucoup de robots qui savent suivre des lignes. Ils sont posés sur une surface blanche et suivent une ligne noire. Voilà de bonnes conditions. Il y a un très fort contraste entre les 2 couleurs, la position du capteur est stable, il pointe vers le bas à environ 1 cm de la surface ou moins, et son déplacement latéral, donc parallèle à la surface maintient ces bonnes conditions.
  • Changer d’approche et utiliser le capteur différemment. Ce second cas n’est pas aussi simple à mettre en oeuvre mais il donne de bien meilleurs résultats. L’idée est d’oublier la capacité du capteur à donner comme résultat une couleur prédéfinie en usine. A la place, nous allons récupérer les composantes de couleur qu’ils mesurent, c’est à dire le volume des couleurs rouge, verte et bleue qui est renvoyé dans la capteur. Il suffit ensuite de mettre en place des tests qui renvoient un résultat en fonction de ces valeurs. Bien sur, cette méthode demande plus de travail mais les résultats obtenus sont sans conteste bien meilleurs. Cette méthode s’appuie sur une technique de calibrage (le mot est lâché !) que je vais détailler ensuite.

Le calibrage

Le calibrage est une opération qui consiste à paramétrer le capteur, ou le logiciel qui l’exploite, en fonction de plusieurs paramètres. Dans notre cas, le capteur n’est pas modifiable, c’est donc le logiciel qu’il va falloir adapter. De manière générique, afin de mettre en place le calibrage, il va falloir réaliser un certain nombre d’opérations. J’illustrerai avec Lejos et Java ensuite, puisque vous pouvez réaliser ces manipulations à partir de n’importe quel solutions exploitant les EV3 :

  • Construire le modèle et positionner le capteur et les éléments à mesurer définitivement. En effet, si vous changez les couleurs à mesurer après avoir fait le calibrage, que vous déplacez le capteur, ou pire encore, que vous remplacez le capteur par un autre, tout sera à refaire ! Si le capteur doit être sous un capot, ou des panels, positionnez-les, l’impact de la lumière extérieure est important.
  • Placer le capteur en mode RGB, ce mode vous retournera les composantes rouge, verte et bleue de la couleur mesurée. Suivant le langage de programmation utilisé, il s’agira soit d’une valeur entière sur 1024, soit d’une valeur flottante comprise entre 0 et 1, etc…
  • Pour chaque couleur à mesurer, dans votre montage, réaliser une série de mesures et noter les résultats obtenus. L’idée est de positionner le montage dans la position pour laquelle vous souhaitez avoir une mesure idéale du capteur, en principe, c’est lorsque la couleur a mesure est bien en face du capteur. Mesurez plusieurs fois chaque couleur, après les avoir échangées. Pratiquez au moins 5 mesures par couleur. Vous allez obtenir pour chaque couleur à mesurer des trios de valeurs se ressemblant. Par exemple avec Lejos, pour du rouge vif, vous pouvez obtenir des valeurs du type 0,8 / 0,01 / 0,02
  • Une fois ces mesures réalisées, le plus dur est fait. Vous n’avez plus dans votre programme qu’à ajouter des tests sur les valeurs en leur donnant un peu de latitude, pour l’exemple pour l’exemple précédent, je pourrais faire un test ressemblant à 0,75<rouge<0,85, vert<0,03, bleu<0,03. Attention à bien vérifier que vert et bleu sont faibles et à ne pas les ignorer !

En utilisant cette méthode, vous pouvez aisément et correctement mesurer 7 ou 8 couleurs et obtenir des résultats 100% opérationnels. Essayez donc d’en faire autant avec le mode Couleur !

Mise en pratique

Il est temps de passer à un exemple, simple, mais qui vous suffira à comprendre les mécanismes évoqués dans la section calibrage. Je m’appuie bien sur sur une brique EV3 démarrée sous Lejos, un programme en Java, et pour des facilités d’usage, du mode Remote qui permet d’exécuter le programme sur l’ordinateur plutôt que sur la brique, et donc notamment d’avoir un affichage des résultats à l’écran.

Commençons par quelque chose de simple. On déclare une Brick ev3 en remote, et au lieu de créer un Sensor, on crée directement un RMISampleProvider pour récupérer les données. Le Sensor ne nous servirait à rien ou presque et en mode Remote, c’est se compliquer la vie pour rien (et cela ne fonctionne pas très bien en mode Remote). Le RMISampleProvider va faire tout le boulot. Il est connecté sur ma brique sur le port 1 (S1) et on lui passe aussi comme paramètre le type du capteur et le mode de capture, ici, c’est RGB. Si on choisissait Color, on serait dans le mode dans lequel le capteur ne renvoit qu’une couleur qu’il a estimée et bien sur, ce n’est pas ce que nous voulons !

Ensuite, on fait un fetchSample classique sur le RMISampleProvider et on affiche les 3 valeurs retournées qui correspondent aux valeurs R, V et B.

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();
	}
}

Si on exécute ce code avec en face du capteur un LEGO de couleur rouge, on obtient des valeurs ressemblant à 0.18137255 / 0.04509804 / 0.03137255, pour simplifier, j’arrondis à 0.181 / 0.045 / 0.031.

Si vous avez bien suivi, l’étape suivante est d’écrire un test qui va vérifier si nos 3 valeurs sont contenues chacune entre 2 valeurs les encadrants. On se laisse un peu de marge dans le test pour pallier aux variations de mesure. Pour les valeurs précédentes, cela pourrait donner :

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

J’ajoute le test au code existant afin de vérifier s’il fonctionne…

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();
	}
}

Et normalement, sur votre console, vous devriez voir apparaître le mot Red, ce qui signifie que le test se vérifie et que tout fonctionne bien. Retirez la pièce rouge en face du capteur, relancez le programme, et rien ne s’affichera.

Il ne reste plus maintenant qu’à ajouter de nouveaux tests pour de nouvelles couleurs, placer tout cela dans une boucle sans fin, et le tour est joué. Voilà le code final qui teste des pièces blanches et rouges.

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();
	}
}

Vous pouvez bien sur améliorer ce code et le rendre plus efficace, idéalement, construire une classe qui surveille le capteur et déclenche un listener lorsqu’un test se vérifie, c’est à dire qu’une couleur connue est positionnée devant le capteur.

Cliquez ici pour télécharger le fichier LXF de la démonstration Youtube :  EV3ColorSensorRGBCalibration.lxf (413 téléchargements )

 

Share