• Facile

Ce cours est visible gratuitement en ligne.

Vous pouvez être accompagné et mentoré par un professeur particulier par visioconférence sur ce cours.

J'ai tout compris !

Mis à jour le 29/04/2014

L'accéléromètre

Connectez-vous ou inscrivez-vous gratuitement pour bénéficier de toutes les fonctionnalités de ce cours !

Chaque Windows Phone est équipé d’un accéléromètre. Il s’agit d’un capteur qui permet de mesurer l’accélération linéaire suivant les trois axes dans l’espace. Ainsi, à tout moment, on peut connaitre la direction et l’accélération de la pesanteur qui s’exerce sur le téléphone.
Ceci peut nous permettre de réaliser des petites applications sympathiques en nous servant de l’orientation du téléphone comme d’une interface avec l’utilisateur.
D'autres capteurs facultatifs existent sur un Windows Phone, comme le gyroscope ou le compas.

Voyons comment nous en servir.

Utiliser l'accéléromètre

Si le téléphone est posé sur la table et que la table est bien horizontale, nous allons pouvoir détecter une accélération d’1g sur l’axe des Z, qui correspond à la force de l’apesanteur. Si l’écran est tourné vers le haut, alors l’accélération sera de (0, 0, -1) - voir la figure suivante.

Accélération sur l'axe des Z
Accélération sur l'axe des Z

Bien sûr, l’accéléromètre n’est pas figé dans cette position, la valeur oscille sans arrêt et vous aurez plus vraisemblablement une valeur comme (0,02519062, -0,05639198, -0,994348)…

Pour utiliser l’accéléromètre, vous allez devoir importer l’espace de nom :

using Microsoft.Devices.Sensors;

Étant donné que l’accéléromètre fourni un objet de Type Vector3, qui fait partie de la bibliothèque XNA, nous aurons besoin d’ajouter une référence à l’assembly Microsoft.Xna.Framework.dll (uniquement dans les versions 7.X car pour le SDK 8, la référence est déjà ajoutée).

Il suffit ensuite de s’abonner à l’événement de changement de valeur et de démarrer l’accéléromètre :

public partial class MainPage : PhoneApplicationPage
{
    private Accelerometer accelerometre;
    public MainPage()
    {
        InitializeComponent();
        accelerometre = new Accelerometer();
        accelerometre.CurrentValueChanged += accelerometre_CurrentValueChanged;
        accelerometre.Start();
    }
    
    private void accelerometre_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
    {
        Dispatcher.BeginInvoke(() => Valeurs.Text = e.SensorReading.Acceleration.X + ", " + e.SensorReading.Acceleration.Y + ", " + e.SensorReading.Acceleration.Z);
    }
}

On utilisera le Dispatcher pour pouvoir mettre à jour notre contrôle dans le thread dédié à l’interface :

<TextBlock x:Name="Valeurs" />

Cet événement nous fournit les 3 valeurs des différents axes. Par contre, à partir du moment où nous démarrons l’accéléromètre, nous recevrons un paquet de positions très régulièrement. Il est important de pouvoir faire du tri là-dedans. Ne vous inquiétez pas si vos valeurs oscillent dans une petite fourchette, c’est normal. Même si vous êtes le plus immobile possible. :) Suivant vos besoins, vous aurez peut-être besoin de lisser les informations obtenus, par exemple en faisant une moyenne sur les X dernières valeurs. Une autre solution est d’utiliser une formule un peu plus compliqué, issue du traitement du signal. Je ne rentrerai pas dans ces détails car nous n’en aurons pas besoin mais vous pouvez retrouver quelques informations en anglais à cette adresse.

Utiliser l'accéléromètre avec l'émulateur

Alors, l’accéléromètre, c’est très bien avec un téléphone, mais comment faire lorsque nous n’avons pas encore appris à déployer une application sur notre téléphone ?
Cela semble difficile d’orienter notre PC pour faire croire à l’émulateur que nous sommes en train de bouger… Heureusement, il existe une autre solution : les outils de l’émulateur. On peut les démarrer en cliquant sur le dernier bouton de sa barre à droite, comme indiqué sur la figure suivante.

Accéder aux outils de l'émulateur
Accéder aux outils de l'émulateur

Dans le premier onglet des outils, nous avons de quoi simuler l’accéléromètre (voir la figure suivante).

En déplaçant le rond orange, nous simulons une accélération du téléphone
En déplaçant le rond orange, nous simulons une accélération du téléphone

Il suffit de sélectionner le petit point orange pour simuler une accélération du téléphone. Vous pouvez utiliser la liste déroulante en bas à gauche pour changer l’orientation du téléphone afin de faciliter l’utilisation de l’accéléromètre. De même, la liste en bas à droite permet de simuler un secouage de téléphone… :)

Exploiter l’accéléromètre

Maintenant que nous savons titiller l’accéléromètre de l’émulateur, utilisons dès à présent les valeurs brutes de l’accéléromètre pour réaliser une petite application où nous allons faire bouger une balle en bougeant notre téléphone.
Nous avons dit que lorsqu’on tient le téléphone à plat, écran vers le haut, nous avons une accélération de (0,0,-1).
Lorsqu’on incline le téléphone vers la gauche, on tend vers l’accélération suivante (-1,0,0). Lorsqu’on incline vers la droite, on tend vers l’accélération (1,0,0).
De la même façon, lorsqu’on incline le téléphone vers l’avant, on tend vers (0,1,0) et lorsqu’on incline vers nous, on tend vers (0,-1,0).

On peut donc utiliser la force des composantes du vecteur pour faire bouger notre balle. Utilisons un Canvas et ajoutons un cercle dedans :

<phone:PhoneApplicationPage
    x:Class="DemoAccelerometre.MainPage"
    …>

    <Canvas x:Name="LayoutRoot" Background="Transparent" Width="480" Height="800" >
        <Ellipse x:Name="Balle" Fill="Blue" Width="50" Height="50" />
    </Canvas>

</phone:PhoneApplicationPage>

Dans le code-behind, nous prendrons gare à démarrer et à arrêter l’accéléromètre, puis il suffira de récupérer les coordonnées de la balle et de les modifier en fonction des composantes du vecteur :

public partial class MainPage : PhoneApplicationPage
{
    private Accelerometer accelerometre;

    public MainPage()
    {
        InitializeComponent();

        Balle.SetValue(Canvas.LeftProperty, LayoutRoot.Width / 2);
        Balle.SetValue(Canvas.TopProperty, LayoutRoot.Height / 2);

        accelerometre = new Accelerometer();
        accelerometre.CurrentValueChanged += accelerometre_CurrentValueChanged;
    }

    private void accelerometre_CurrentValueChanged(object sender, SensorReadingEventArgs<AccelerometerReading> e)
    {
        Dispatcher.BeginInvoke(() =>
            {
                double x = (double)Balle.GetValue(Canvas.LeftProperty) + e.SensorReading.Acceleration.X;
                double y = (double)Balle.GetValue(Canvas.TopProperty) - e.SensorReading.Acceleration.Y;

                if (x <= 0)
                    x = 0;
                if (y <= 0)
                    y = 0;
                if (x >= LayoutRoot.Width - Balle.Width)
                    x = LayoutRoot.Width - Balle.Width;
                if (y >= LayoutRoot.Height - Balle.Height)
                    y = LayoutRoot.Height - Balle.Height;

                Balle.SetValue(Canvas.LeftProperty, x);
                Balle.SetValue(Canvas.TopProperty, y);
            });
    }

    protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
    {
        accelerometre.Stop(); 
        base.OnNavigatedFrom(e);
    }

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
        accelerometre.Start();
        base.OnNavigatedTo(e);
    }
}

Et voilà, notre cercle bouge en fonction de l’orientation du téléphone. Remarquez que pour avoir un mouvement naturel, nous avons inversé l’axe des Y en réalisant une soustraction.
Bon, c’est bien mais la balle n’avance pas très vitre. Il pourrait être judicieux d’appliquer un facteur de vitesse, par exemple :

double vitesse = 3.5;
double x = (double)Balle.GetValue(Canvas.LeftProperty) + e.SensorReading.Acceleration.X * vitesse;
double y = (double)Balle.GetValue(Canvas.TopProperty) - e.SensorReading.Acceleration.Y * vitesse;

La balle va cette fois-ci un peu plus vite. Mais elle pourrait avoir un peu plus d’inertie, et notamment freiner lorsqu’on redresse le téléphone. N’oubliez pas que plus le téléphone est plat et plus Z tend vers -1.
On peut donc trouver une formule où le fait que le téléphone soit plat ralentisse la balle, en divisant par exemple par la valeur absolue de Z :

double facteur = e.SensorReading.Acceleration.Z == 0 ? 0.00001 : Math.Abs(e.SensorReading.Acceleration.Z);
double vitesse = 3.5;
double x = (double)Balle.GetValue(Canvas.LeftProperty) + e.SensorReading.Acceleration.X * vitesse / facteur;
double y = (double)Balle.GetValue(Canvas.TopProperty) - e.SensorReading.Acceleration.Y * vitesse / facteur;

Bon, on est loin d’un vrai moteur physique simulant l’accélération, mais c’est déjà un peu mieux. ;)

Attention, n’oubliez pas d’arrêter l’accéléromètre quand vous avez fini avec lui grâce à la méthode Stop() afin d’économiser la batterie. C’est ce que je fais dans la méthode OnNavigatedFrom().

Les autres capteurs facultatifs

L’accéléromètre est le seul capteur obligatoire dans les spécifications d’un Windows Phone. Mais il y a d’autres capteurs facultatifs qui peuvent faire partie d’un Windows Phone. Il y a notamment le compas (ou le magnétomètre). Il permet de connaitre l’emplacement du pôle nord magnétique et peut servir à faire une boussole par exemple. Étant facultatif, il faudra penser à vérifier s’il est présent avec :

if (!Compass.IsSupported)
{
    MessageBox.Show("Votre téléphone ne possède pas de compas");
}

De même, le gyroscope est facultatif sur les Windows Phone. Il sert à mesurer la vitesse de rotation suivant les trois axes. Il est différent de l’accéléromètre. Pour voir la différence entre les deux, imaginez-vous debout avec le téléphone en position portrait devant vous, face au nord. Si vous faites un quart de tour vers la droite, vous vous retrouvez face à l’est, le téléphone n’a pas changé de position dans vos mains, mais il a subi une rotation suivant un axe. Cette rotation, c’est le gyroscope qui va être en mesure de vous la fournir. Pour l’accéléromètre, pensez à notre petite application de balle réalisée plus haut. Pour tester sa présence, vous pourrez faire :

if (!Gyroscope.IsSupported)
{
    MessageBox.Show("Votre téléphone ne possède pas de gyroscope");
}

Il vous faudra peut-être adapter le comportement de votre application en conséquence, ou prévenir l’utilisateur qu’elle est inutilisable sans gyroscope.

La motion API

Travailler avec tous ces capteurs en même temps est plutôt complexe. De plus, si jamais il manque au téléphone un capteur que vous souhaitez utiliser, il vous faut revoir vos calculs pour adapter votre application.
C’est là qu’intervient la motion API, que l’on peut traduire en API de mouvement. Elle permet de gérer toute la logique mathématique associée à ces trois capteurs afin de fournir des valeurs facilement exploitables. Au final, on arrive très facilement à déterminer comment le téléphone est orienté dans l’espace. Cela permet par exemple de transposer le téléphone dans un monde 3D virtuel, permettant les applications de réalité augmentée, les jeux et pourquoi pas des applications auxquelles nous n’avons pas encore pensé…

La motion API s’utilise grosso modo comme l’accéléromètre. Elle nous fournit plusieurs valeurs avec notamment une propriété Attitude que l’on peut traduire en « assiette », dans le langage de l’aviation, qui permet d’obtenir l’orientation de l’avion dans l’espace. Cette propriété nous fournit plusieurs valeurs intéressantes qui, toujours dans le domaine de l’aviation, sont :

  • Yaw : qui indique la direction par rapport au nez de l’avion, afin de savoir si l’avion tourne à droite ou à gauche ;

  • Pitch : qui indique si le nez de l’avion monte ou descend ;

  • Roll : qui permet de savoir si l’avion bascule sur la droite ou la gauche.

Grosso modo, imaginons que vous ayez le téléphone posé sur la table devant vous, avec les boutons proches de vous :

  • Si vous lui faite faire une rotation sur la table, tout en le conservant posé sur la table, alors vous changez le Yaw.

  • Si vous levez le téléphone pour l’avoir en position verticale, alors vous changez le pitch.

  • Si vous basculez le téléphone pour le mettre sur sa tranche, alors vous changez le roll.

On peut le représenter ainsi (voir la figure suivante).

Assiette d'un téléphone
Assiette d'un téléphone

Même si je ne doute pas de la qualité de mon dessin ( :-° ), le mieux pour comprendre est de l’expérimenter sur un vrai téléphone.
Pour obtenir ces valeurs, il vous suffit de déclarer un objet Motion et de vous abonner à l’événement CurrentValueChanged. Notez que vous pouvez utiliser un petit Helper venant du framework XNA afin d’obtenir un angle à partir de cette valeur.

Utilisez donc le XAML suivant :

<TextBlock x:Name="Yaw" />
<TextBlock x:Name="Pitch" />
<TextBlock x:Name="Roll" />

Avec le code-behind :

public partial class MainPage : PhoneApplicationPage
{
    Motion motion;

    public MainPage()
    {
        InitializeComponent();
    }

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
        if (!Motion.IsSupported)
        {
            MessageBox.Show("L'API Motion n'est pas supportée sur ce téléphone.");
            return;
        }

        if (motion == null)
        {
            motion = new Motion { TimeBetweenUpdates = TimeSpan.FromMilliseconds(20) };
            motion.CurrentValueChanged += motion_CurrentValueChanged;
        }

        try
        {
            motion.Start();
        }
        catch (Exception ex)
        {
            MessageBox.Show("Impossible de démarrer l'API.");
        }
    }

    private void motion_CurrentValueChanged(object sender, SensorReadingEventArgs<MotionReading> e)
    {
        Dispatcher.BeginInvoke(() => 
            {
                if (motion.IsDataValid)
                {
                    float yaw = MathHelper.ToDegrees(e.SensorReading.Attitude.Yaw);
                    float pitch = MathHelper.ToDegrees(e.SensorReading.Attitude.Pitch);
                    float roll = MathHelper.ToDegrees(e.SensorReading.Attitude.Roll);

                    Yaw.Text = "Yaw : " + yaw + " °";
                    Pitch.Text = "Pitch : " + pitch + " °";
                    Roll.Text = "Roll : " + roll + " °";
                }
            });
    }
}

Pour utiliser le MathHelper, il faudra inclure l’espace de nom suivant :

using Microsoft.Xna.Framework;

L’objet Motion est quant à lui disponible avec :

using Microsoft.Devices.Sensors;

En plus de l’assiette, l’API motion fourni d’autres valeurs :

  • DeviceAcceleration, qui est la même chose que ce que l’on obtient avec l’accéléromètre.

  • DeviceRotationRate, qui est la même chose que ce que l’on obtient avec le gyroscope.

  • Gravity, qui renvoie un vecteur en direction du centre de la terre, pour représenter la gravité, comme avec l’accéléromètre.

Vous avez compris que ces valeurs peuvent être obtenues avec les méthodes propres aux capteurs et que ce qui est vraiment intéressant, ce sont les valeurs calculées de l’assiette.

  • L’accéléromètre est un capteur obligatoire sur tout téléphone Windows Phone qui nous offre de nouvelles façons d’interagir avec notre utilisateur.

  • Il est facile à émuler grâce aux outils de l’émulateur Windows Phone.

  • D’autres capteurs facultatifs existent, comme le gyroscope ou le compas.

  • La motion API nous simplifie grandement la détection de l’orientation du téléphone dans l’espace et nous ouvre les portes de la réalité augmentée.

Exemple de certificat de réussite
Exemple de certificat de réussite