We choose the Moon

Android + Mathematica: Sensor de orientación

Jorge García Tíscar| January 29, 2012

Abrimos un nuevo frente! Tenemos intención esta vez de trastear con sensores de movimiento y posición, pero desgraciadamente no contamos con los recursos económicos para costearnos una placa de sensores 9DoF. La solución es intentar emplear los sensores que ya poseemos dentro de nuestros teléfonos móviles.

Vamos a empezar con el ejemplo más sencillo posible: intentar grabar los datos que lee el sensor de orientación del teléfono y “visualizarlos” matemáticamente, en este caso con la inestimable ayuda de Mathematica. Básicamente, moveremos el teléfono mientras una aplicación graba los datos en un archivo de texto, que leeremos a posteriori e intentaremos transformar en una visualización. El resultado ha sido el siguiente:

¿Que cómo se hace? Pues bien, con paciencia, como todo. Vamos paso a paso.

Android: grabación de datos

En primer lugar necesitamos una aplicación que lea los datos del sensor y los grabe en un archivo de texto que podamos leer. Como en este caso sólo vamos a leer de un sensor, hemos elegido Sensor Dump. Una vez instalada, ya podemos seleccionar cualquier sensor disponible en el teléfono, y desde el menú de cada sensor, registrar los datos en un fichero .CSV, tal y como se ve en la imagen de la derecha.

A continuación, movemos un poco el trasto, detenemos la grabación, y conectamos el teléfono al ordenador y nos hacemos con el fichero de datos, que posteriormente importaremos desde Mathematica.

Mathematica: tratamiento de datos

En primer lugar, vamos a importar los datos desde el fichero, que en nuestro caso, en una alarde de detallismo, se llama “a.csv”. Usamos el siguiente código, teniendo en cuenta que nos saltamos las tres primeras lineas, por ser encabezados:

1
data = Import[NotebookDirectory[] <> "/a.csv"][[4 ;; All, All]]

Con lo que obtenemos una serie de valores de esta forma:

1
2
3
4
5
28690439382000 1327788139149382000 153. 1. 3.
28690595845000 1327788139305845000 153. 2. 2.
28690675100000 1327788139385100000 153. 2. 3.
28690753682000 1327788139463682000 153. 1. 3.
28690833364000 1327788139543364000 153. 2. 2.

Los dos primeros representan el tiempo, y los tres últimos la orientación, siendo los ángulos el de acimut (azimuth), el de cabeceo (pitch) y el de guiñada (roll).

Suavizado (media móvil)

Dado que los sensores son muy ruidosos, es conveniente hacer un poco de suavizado de la señal. Simplemente, realizamos una media móvil de cada dos valores. Se podría promediar a más valores, pero dado que se pasa de 360º a 0º, la media móvil introduce valores intermedios que desgracian el asunto desvirtúan la visualización, con lo que nos limitamos a una media de cada dos elementos:

1
2
3
4
5
smooth=2;

azimuth=MovingAverage[data[[All,3]],smooth];
pitch=-MovingAverage[data[[All,4]],smooth];
roll=-MovingAverage[data[[All,5]],smooth];

Representación

A continuación, representamos las series de datos que hemos obtenido, con la ayuda del comando ListPlot, y habiendo empleado el paquete PlotLegends:

1
2
3
4
5
6
7
8
9
10
ListPlot[{azimuth, pitch, roll},
    Joined -> True, LabelStyle -> 12,
    AxesLabel -> {"Dato [n]", "Grados [º]"},
    PlotStyle -> Thickness[0.005],
    PlotLegend ->
    Table[Style[i, 12], {i, {"Acimut", "Cabeceo", "Guiñada"}}],
    LegendPosition -> {1, -0.1}, LegendSize -> {0.6, 0.5},
    LegendShadow -> None, LegendBorder -> None, LegendTextSpace -> 4,
    ImageSize -> 520
]

Con lo que obtenemos la siguiente figura:

Animación

Para obtener una animación que simule matemáticamente cómo hemos movido el teléfono, definimos primero un paralepípedo que hará de teléfono:

1
movil = Cuboid[{-0.5, -0.25, -0.05}, {0.5, 0.25, 0.05}];

Ahora definimos, para cada instante \(t\), la “frame” que va a definir nuestra visualización. Vamos a combinar un espacio 3D en el que rotaremos el objeto según los 3 ejes ZYX, con una visualización del gráfico anterior que nos indique en qué punto de la simulación nos encontramos:

1
2
3
4
5
6
7
8
9
10
11
12
13
frame[\[Alpha]_, \[Beta]_, \[Gamma]_, t_] := GraphicsRow[{
  Graphics3D[Rotate[Rotate[Rotate[movil, \[Alpha] \[Degree],
  {0, 0, 1}], \[Beta] \[Degree], {0, 1, 0}], \[Gamma] \[Degree], {1,
  0, 0}], Axes -> True, LabelStyle -> 12,
  PlotRange -> {{-0.6, 0.6}, {-0.6, 0.6}, {-0.6, 0.6}},
  ViewPoint -> {-3.8, 2, 4}],
 Show[
  ListPlot[{azimut, pitch, roll},
   Joined -> True, LabelStyle -> 12,
   AxesLabel -> {"n", "Grados [º]"}],
  ListPlot[{{{t, azimut[[t]]}}, {{t, pitch[[t]]}}, {{t,
   roll[[t]]}}}, PlotMarkers -> {Automatic, 10}]]
 }, ImageSize -> 590];

Ahora ya podemos emplear el comando Animate[] para recorrer toda la lista de datos, consiguiendo la siguiente visualización:

Por último, hemos exportado la visualización como .gif mediante:

1
2
3
4
Export[NotebookDirectory[] <> "anim.gif",
 Table[frame[azimuth[[t]], pitch[[t]], roll[[t]], t],
 {t, 1,Length[azimuth]}],
 ImageSize -> 590]

En cuanto a integrar la animación con el vídeo, eso escapa a nuestras limitadas competencias, y de ello se ha encargado una mano amiga que prefiere permanecer en el anonimato y no ser relacionada con We choose the Moon de ninguna manera. Quizá porque trabaje para los rusos y quiera llegar a la Luna antes que nosotros!

Conclusiones

Este ha sido el ejemplo más sencillo de cómo leer datos de movimiento de los sensores de un teléfono Android, pero por algo hay que empezar! Como se observa, la señal es muy ruidosa, muy limitada, y no representa muy exactamente el movimiento real. Sin embargo, sí que se aprecia que reproduce los movimientos del teléfono, aunque sea de mala manera, lo cual ya no parece un comienzo aceptable.

Esperamos profundizar más en este tema, ya sea mejorando la toma de datos, leyendo más sensores que nos permitan representar la posición además de la orientación, tomando datos en tiempo real, o incluso, para ser fieles a nuestra tradición aeronáutica, montar el teléfono en un avión RC y darle un paseo, esperemos que no demasiado mortal. Os mantendremos informados!

avatar Thanks for reading! To share this post, use this permalink

Comments