Les capteurs

Ce contenu est obsolète. Il peut contenir des informations intéressantes mais soyez prudent avec celles-ci.

La majorité des appareils modernes sont bien plus que de simples outils pour communiquer ou naviguer sur internet. Ils ont des capacités sensorielles, matérialisées par leurs capteurs. Ces capteurs nous fournissent des informations brutes avec une grande précision, qu'il est possible d’interpréter pour comprendre les transitions d'état que vit le terminal. On trouve par exemple des accéléromètres, des gyroscopes, des capteurs de champ magnétique, etc. Tous ces capteurs nous permettent d'explorer de nouvelles voies, d'offrir de nouvelles possibilités aux utilisateurs.

On va donc voir dans ce chapitre comment surveiller ces capteurs et comment les manipuler. On verra ainsi les informations que donnent les capteurs et comment en déduire ce que fait faire l'utilisateur à l'appareil.

Les différents capteurs

On peut répartir les capteurs en trois catégories :

  • Les capteurs de mouvements : en mesurant les forces d'accélération et de rotation sur les trois axes, ces capteurs sont capables de déterminer dans quelle direction se dirige l'appareil. On y trouve l'accéléromètre, les capteurs de gravité, les gyroscopes et les capteurs de vecteurs de rotation.
  • Les capteurs de position : évidemment, ils déterminent la position de l'appareil. On trouve ainsi les capteurs d’orientation et le magnétomètre.
  • Les capteurs environnementaux : ce sont trois capteurs (baromètre, photomètre et thermomètre) qui mesurent la pression atmosphérique, l'illumination et la température ambiante.

D'un point de vue technique, on trouve deux types de capteurs. Certains sont des composants matériels, c'est-à-dire qu'il y a un composant physique présent sur le terminal. Ils fournissent des données en prenant des mesures. Certains autres capteurs sont uniquement présents d'une manière logicielle. Ils se basent sur des données fournies par des capteurs physiques pour calculer des données nouvelles.

Il n'est pas rare qu'un terminal n'ait pas tous les capteurs, mais seulement une sélection. Par exemple, la grande majorité des appareils ont un accéléromètre ou un magnétomètre, mais peu ont un thermomètre. De plus, il arrive qu'un terminal ait plusieurs exemplaires d'un capteur, mais calibrés d'une manière différente de façon à avoir des résultats différents.

Ces différents capteurs sont représentés par une valeur dans la classe Sensor. On trouve ainsi :

Nom du capteur

Valeur système

Type

Description

Utilisation typique

Accéléromètre

TYPE_ACCELEROMETER

Matériel

Mesure la force d'accélération appliquée au terminal sur les trois axes (x, y et z), donc la force de gravitation (m/s²).

Détecter les mouvements.

Tous les capteurs

TYPE_ALL

Matériel et logiciel

Représente tous les capteurs qui existent.

Gyroscope

TYPE_GYROSCOPE

Matériel

Mesure le taux de rotation sur chacun des trois axes en radian par seconde (rad/s).

Détecter l'orientation de l'appareil.

Photomètre

TYPE_LIGHT

Matériel

Mesure le niveau de lumière ambiante en lux (lx).

Détecter la luminosité pour adapter celle de l'écran de l'appareil.

Magnétomètre

TYPE_MAGNETIC_FIELD

Matériel

Mesure le champ géomagnétique sur les trois axes en microtesla (μT).

Créer un compas.

Orientation

TYPE_ORIENTATION

Logiciel

Mesure le degré de rotation que l'appareil effectue sur les trois axes.

Déterminer la position de l'appareil.

Baromètre

TYPE_PRESSURE

Matériel

Mesure la pression ambiante en hectopascal (hPa) ou millibar (mbar).

Surveiller les changements de pression de l'air ambiant.

Capteur de proximité

TYPE_PROXIMITY

Matériel

Mesure la proximité d'un objet en centimètres (cm).

Détecter si l'utilisateur porte le téléphone à son oreille pendant un appel.

Thermomètre

TYPE_TEMPERATURE

Matériel

Mesure la température de l'appareil en degrés Celsius (°C).

Surveiller la température.

Les lignes en italique correspondent aux valeurs qui existent dans l'API 7 mais qui ne sont pas utilisables avant l'API 9.

Opérations génériques

Demander la présence d'un capteur

Il se peut que votre application n'ait aucun sens sans un certain capteur. Si c'est un jeu qui exploite la détection de mouvements par exemple, vous feriez mieux d'interdire aux gens qui n'ont pas un accéléromètre de pouvoir télécharger votre application sur le Play Store. Pour indiquer qu'on ne veut pas qu'un utilisateur sans accéléromètre puisse télécharger votre application, il vous faudra ajouter une ligne de type <uses-feature> dans votre Manifest :

1
2
<uses-feature android:name="android.hardware.sensor.accelerometer"
  android:required="true" />

N'oubliez pas que android:required="true" sert à préciser que la présence de l'accéléromètre est absolument indispensable. S'il est possible d'utiliser votre application sans l'accéléromètre mais qu'il est fortement recommandé d'en posséder un, alors il vous suffit de mettre à la place android:required="false".

Identifier les capteurs

La classe qui permet d'accéder aux capteurs est SensorManager. Pour en obtenir une instance, il suffit de faire :

1
SensorManager sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);

Comme je l'ai déjà dit, les capteurs sont représentés par la classe Sensor. Si vous voulez connaître la liste de tous les capteurs existants sur l'appareil, il vous faudra utiliser la méthode List<Sensor> getSensorList(int type) avec type qui vaut Sensor.TYPE_ALL. De même, pour connaître tous les capteurs qui correspondent à une catégorie de capteurs, utilisez l'une des valeurs vues précédemment dans cette même méthode. Par exemple, pour connaître la liste de tous les magnétomètres :

1
ArrayList<Sensor> liste = sensorManager.getSensorList(Sensor.TYPE_MAGNETIC_FIELD);

Il est aussi possible d'obtenir une instance d'un capteur. Il suffit d'utiliser la méthode Sensor getDefaultSensor(int type) avec type un identifiant présenté dans le tableau précédent. Comme je vous l'ai déjà dit, il peut y avoir plusieurs capteurs qui ont le même objectif dans un appareil, c'est pourquoi cette méthode ne donnera que l'appareil par défaut, celui qui correspondra aux besoins les plus génériques.

Si le capteur demandé n'existe pas dans l'appareil, la méthode getDefaultSensor renverra null.

1
2
3
4
5
Sensor accelerometre = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELETOMETER);
if(accelerometre != null)
  // Il y a au moins un accéléromètre
else
  // Il n'y en a pas

Il est ensuite possible de récupérer des informations sur le capteur, comme par exemple sa consommation électrique avec float getPower() et sa portée avec float getMaximumRange().

Vérifiez toujours qu'un capteur existe, même s'il est très populaire. Par exemple, il est peu probable qu'un accéléromètre soit absent, mais c'est possible !

Détection des changements des capteurs

L'interface SensorEventListener permet de détecter deux types de changement dans les capteurs :

  • Un changement de précision du capteur avec la méthode de callback void onAccuracyChanged(Sensor sensor, int accuracy) avec sensor le capteur dont la précision a changé et accuracy la nouvelle précision. accuracy peut valoir SensorManager.SENSOR_STATUS_ACCURACY_LOW pour une faible précision, SensorManager.SENSOR_STATUS_ACCURACY_MEDIUM pour une précision moyenne, SensorManager.SENSOR_STATUS_ACCURACY_HIGH pour une précision maximale et SensorManager.SENSOR_STATUS_ACCURACY_UNRELIABLE s'il ne faut pas faire confiance à ce capteur.
  • Le capteur a calculé une nouvelle valeur, auquel cas se lancera la méthode de callback void onSensorChanged(SensorEvent event). Un SensorEvent indique à chaque fois quatre informations contenues dans quatre attributs : l'attribut accuracy indique la précision de cette mesure (il peut avoir les mêmes valeurs que précédemment), l'attribut sensor contient une référence au capteur qui a fait la mesure, l'attribut timestamp est l'instant en nanosecondes où la valeur a été prise, et enfin les valeurs sont contenues dans l'attribut values.

values est un tableau d'entiers. Si dans le tableau précédent j'ai dit que les valeurs correspondaient aux trois axes, alors le tableau a trois valeurs : values[0] est la valeur sur l'axe x, values[1] la valeur sur l'axe y et values[2] la valeur sur l'axe z. Si le calcul ne se fait pas sur trois axes, alors il n'y aura que values[0] qui contiendra la valeur. Attention, cette méthode sera appelée très souvent, il est donc de votre devoir de ne pas effectuer d'opérations bloquantes à l'intérieur. Si vous effectuez des opérations longues à résoudre, alors il se peut que la méthode soit à nouveau lancée alors que l'ancienne exécution n'avait pas fini ses calculs, ce qui va encombrer le processeur au fur et à mesure.

1
2
3
4
5
6
7
8
9
final SensorEventListener mSensorEventListener = new SensorEventListener() {
  public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // Que faire en cas de changement de précision ?
  }

  public void onSensorChanged(SensorEvent sensorEvent) {
    // Que faire en cas d'évènements sur le capteur ?
  }
};

Une fois notre interface écrite, il faut déclarer au capteur que nous sommes à son écoute. Pour cela, on va utiliser la méthode boolean registerListener(SensorEventListener listener, Sensor sensor, int rate) de SensorManager, avec le listener, le capteur dans sensor et la fréquence de mise à jour dans rate. Il est possible de donner à rate les valeurs suivantes, de la fréquence la moins élevée à la plus élevée :

  • SensorManager.SENSOR_DELAY_NORMAL (0,2 seconde entre chaque prise) ;
  • SensorManager.SENSOR_DELAY_UI (0,06 seconde entre chaque mise à jour, délai assez lent qui convient aux interfaces graphiques) ;
  • SensorManager.SENSOR_DELAY_GAME (0,02 seconde entre chaque prise, convient aux jeux) ;
  • SensorManager.SENSOR_DELAY_FASTEST (0 seconde entre les prises).

Le délai que vous indiquez n'est qu'une indication, il ne s'agit pas d'un délai très précis. Il se peut que la prise se fasse avant ou après le moment choisi. De manière générale, la meilleure pratique est d'avoir la valeur la plus lente possible, puisque c'est elle qui permet d'économiser le plus le processeur et donc la batterie.

Enfin, on peut désactiver l'écoute d'un capteur avec void unregisterListener(SensorEventListener listener, Sensor sensor). N'oubliez pas de désactiver vos capteurs pendant que l'activité n'est pas au premier plan (donc il faut le désactiver pendant onPause() et le réactiver pendant onResume()), car le système ne le fera pas pour vous. De manière générale, désactivez les capteurs dès que vous ne les utilisez plus.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
private SensorManager mSensorManager = null;
private Sensor mAccelerometer = null;

final SensorEventListener mSensorEventListener = new SensorEventListener() {
  public void onAccuracyChanged(Sensor sensor, int accuracy) {
    // Que faire en cas de changement de précision ?
  }

  public void onSensorChanged(SensorEvent sensorEvent) {
    // Que faire en cas d'évènements sur le capteur ?
  }
};

@Override
public final void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
  mAccelerometer = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
}

@Override
protected void onResume() {
  super.onResume();
  mSensorManager.registerListener(mSensorEventListener, mAccelerometer, SensorManager.SENSOR_DELAY_NORMAL);
}

@Override
protected void onPause() {
  super.onPause();
  mSensorManager.unregisterListener(mSensorEventListener, mAccelerometer);
}

Les capteurs de mouvements

On va ici étudier les capteurs qui permettent de garder un œil sur les mouvements du terminal. Pour l'API 7, on trouve surtout l'accéléromètre, mais les versions suivantes supportent aussi le gyroscope, ainsi que trois capteurs logiciels (gravitationnel, d'accélération linéaire et de vecteurs de rotation). De nos jours, on trouve presque tout le temps un accéléromètre et un gyroscope. Pour les capteurs logiciels, c'est plus complexe puisqu'ils se basent souvent sur plusieurs capteurs pour déduire et calculer des données, ils nécessitent donc la présence de plusieurs capteurs différents.

On utilise les capteurs de mouvements pour détecter les… mouvements. Je pense en particulier aux inclinaisons, aux secousses, aux rotations ou aux balancements. Typiquement, les capteurs de mouvements ne sont pas utilisés pour détecter la position de l'utilisateur, mais si on les utilise conjointement avec d'autres capteurs, comme par exemple le magnétomètre, ils permettent de mieux évaluer ses déplacements.

Tous ces capteurs retournent un tableau de float de taille 3, chaque élément correspondant à un axe différent (voir figure suivante). La première valeur, values[0], se trouve sur l'axe x, il s'agit de l'axe de l'horizon, quand vous bougez votre téléphone de gauche à droite. Ainsi, la valeur est positive et augmente quand vous déplacez le téléphone vers la droite, alors qu'elle est négative et continue à diminuer plus vous le déplacez vers la gauche.

La deuxième valeur, values[1], correspond à l'axe y, c’est-à-dire l'axe vertical, quand vous déplacez votre téléphone de haut en bas. La valeur est positive quand vous déplacez le téléphone vers le haut et négative quand vous le déplacez vers le bas.

Enfin, la troisième valeur, values[2], correspond à l'axe z, il s'agit de l'axe sur lequel vous pouvez éloigner ou rapprocher le téléphone de vous. Quand vous le rapprochez de vous, la valeur est positive et, quand vous l'éloignez, la valeur est négative.

Vous l'aurez compris, toutes ces valeurs respectent un schéma identique : un 0 signifie pas de mouvement, une valeur positive un déplacement dans le sens de l'axe et une valeur négative un déplacement dans le sens inverse de celui de l'axe.

Les différents axes

Enfin, cela va peut-être vous sembler logique, mais l'accéléromètre ne mesure pas du tout la vitesse, juste le changement de vitesse. Si vous voulez obtenir la vitesse depuis les données de l'accéléromètre, il vous faudra intégrer l'accélération sur le temps (que l'on peut obtenir avec l'attribut timestamp) pour obtenir la vitesse. Et pour obtenir une distance, il vous faudra intégrer la vitesse sur le temps.

Les capteurs de position

On trouve trois (bon, en fait deux et un autre moins puissant) capteurs qui permettent de déterminer la position du terminal : le magnétomètre, le capteur d'orientation et le capteur de proximité (c'est le moins puissant, il est uniquement utilisé pour détecter quand l'utilisateur a le visage collé au téléphone, afin d'afficher le menu uniquement quand l'utilisateur n'a pas le téléphone contre la joue). Le magnétomètre et le capteur de proximité sont matériels, alors que le capteur d'orientation est une combinaison logicielle de l'accéléromètre et du magnétomètre.

Le capteur d'orientation et le magnétomètre renvoient un tableau de taille 3, alors que pour le capteur de proximité c'est plus compliqué. Parfois il renvoie une valeur en centimètres, parfois juste une valeur qui veut dire « proche » et une autre qui veut dire « loin » ; dans ces cas-là, un objet est considéré comme éloigné s'il se trouve à plus de 5 cm.

Cependant, le magnétomètre n'est pas utilisé que pour déterminer la position de l'appareil. Si on l'utilise conjointement avec l'accéléromètre, il est possible de détecter l'inclinaison de l'appareil. Et pour cela, la seule chose dont nous avons besoin, c'est de faire de gros calculs trigonométriques. Enfin… Android va (heureusement !) les faire pour nous.

Dans tous les cas, nous allons utiliser deux capteurs, il nous faudra donc déclarer les deux dans deux listeners différents. Une fois les données récupérées, il est possible de calculer ce qu'on appelle la méthode Rotation avec la méthode statique static boolean SensorManager.getRotationMatrix(float[] R, float[] I, float[] gravity, float[] geomagnetic) avec R le tableau de taille 9 dans lequel seront stockés les résultats, I un tableau d'inclinaison qui peut bien valoir null, gravity les données de l'accéléromètre et geomagnetic les données du magnétomètre.

La matrice rendue s'appelle une matrice de rotation. À partir de celle-ci, vous pouvez obtenir l'orientation de l'appareil avec static float[] SensorManager.getOrientation(float[] R, float[] values) avec R la matrice de rotation et values le tableau de taille 3 qui contiendra la valeur de la rotation pour chaque axe :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
SensorManager sensorManager = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
Sensor accelerometre = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
Sensor magnetometre = sm.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD);

sensorManager.registerListener(accelerometreListener, accelerometre, SensorManager.SENSOR_DELAY_UI);
sensorManager.registerListener(magnetometreListener, magnetometre, SensorManager.SENSOR_DELAY_UI);

// …

float[] values = new float[3];
float[] R = new float[9];
SensorManager.getRotationMatrix(R, null, accelerometreValues, magnetometreValues);
SensorManager.getOrientation(R, values);

Log.d("Sensors", "Rotation sur l'axe z : " + values[0]);
Log.d("Sensors", "Rotation sur l'axe x : " + values[1]);
Log.d("Sensors", "Rotation sur l'axe y : " + values[2]);

Je vais vous expliquer maintenant à quoi correspondent ces chiffres, cependant avant toute chose, vous devez imaginez votre téléphone portable posé sur une table, le haut qui pointe vers vous, l'écran vers le sol. Ainsi, l'axe z pointe de bas en haut, l'axe x de droite à gauche et l'axe y de "loin devant vous" à "loin derrière vous" (en gros c'est l'axe qui vous traverse) :

  • La rotation sur l'axe z est le mouvement que vous faites pour ouvrir ou fermer une bouteille de jus d'orange posée verticalement sur une table. C'est donc comme si vous englobiez le téléphone dans votre main et que vous le faisiez tourner sur l'écran. Il s'agit de l'angle entre le nord magnétique et l'angle de l'axe y. Il vaut 0 quand l'axe y pointe vers le nord magnétique, 180 si l'axe y pointe vers le sud, 90 quand il pointe vers l'est et 270 quand il pointe vers l'ouest.
  • La rotation autour de l'axe x est le mouvement que vous faites quand vous ouvrez la bouteille de jus d'orange posée sur une table mais que le bouchon pointe vers votre droite. Enfin, ce n'est pas malin parce que vous allez tout renverser par terre. Elle vaut -90 quand vous tournez vers vous (ouvrir la bouteille) et 90 quand vous tournez dans l'autre sens (fermer la bouteille).
  • La rotation sur l'axe y est le mouvement que vous faites quand vous ouvrez une bouteille de jus d'orange couchée sur la table alors que le bouchon pointe vers vous. Elle vaut 180 quand vous tournez vers la gauche (fermer la bouteille) et -180 quand vous tournez vers la droite (ouvrir la bouteille).

Enfin, il vous est possible de changer le système de coordonnées pour qu'il corresponde à vos besoins. C'est utile si votre application est censée être utilisée en mode paysage plutôt qu'en mode portrait par exemple. On va utiliser la méthode static boolean SensorManager.remapCoordinateSystem(float[] inR, int X, int Y, float[] outR) avec inR la matrice de rotation à transformer (celle qu'on obtient avec getRotationMatrix), X désigne la nouvelle orientation de l'axe x, Y la nouvelle orientation de l'axe y et outR la nouvelle matrice de rotation (ne mettez pas inR dedans). Vous pouvez mettre dans X et Y des valeurs telles que SensorManager.AXIS_X qui représente l'axe x et SensorManager.AXIS_MINUS_X son orientation inverse. Vous trouverez de même les valeurs SensorManager.AXIS_Y, SensorManager.AXIS_MINUS_Y, SensorManager.AXIS_Z et SensorManager.AXIS_MINUS_Z.

Les capteurs environnementaux

Pour être franc, il n'y a pas tellement à dire sur les capteurs environnementaux. On en trouve trois dans l'API 7 : le baromètre, le photomètre et le thermomètre. Ce sont tous des capteurs matériels, mais il est bien possible qu'ils ne soient pas présents dans un appareil. Tout dépend du type d'appareil, pour être exact. Sur un téléphone et sur une tablette, on en trouve rarement (à l'exception du photomètre qui permet de détecter automatiquement la meilleure luminosité pour l'écran), mais sur une station météo ils sont souvent présents. Il faut donc redoubler de prudence quand vous essayez de les utiliser, vérifiez à l'avance leur présence.

Tous ces capteurs rendent des valeurs uniques, pas de tableaux à plusieurs dimensions.


  • La majorité des appareils sous Android utilisent des capteurs pour créer un lien supplémentaire entre l'utilisateur et son appareil. On rencontre trois types de capteurs : les capteurs de mouvements, les capteurs de position et les capteurs environnementaux.
  • La présence d'un capteur dépend énormément des appareils, il faut donc faire attention à bien déclarer son utilisation dans le Manifest et à vérifier sa présence au moment de l'utilisation.
  • Les capteurs de mouvements permettent de détecter les déplacements que l'utilisateur fait faire à son appareil. Il arrive qu'ils soient influencés par des facteurs extérieurs comme la gravité, il faut donc réfléchir à des solutions basées sur des calculs physiques quand on désire avoir une valeur précise d'une mesure.
  • Les capteurs de position sont capables de repérer la position de l'appareil par rapport à un référentiel. Par exemple, le capteur de proximité peut donner la distance entre l'utilisateur et l’appareil. En pratique, le magnétomètre est surtout utilisé conjointement avec des capteurs de mouvements pour détecter d'autres types de mouvements, comme les rotations.
  • Les capteurs environnementaux sont vraiment beaucoup plus rares sur un téléphone ou une tablette, mais il existe d'autres terminaux spécifiques, comme des stations météorologiques, qui sont bardés de ce type de capteurs.