ARES - Ares Mission Script

Si solo te quedas con una idea de esta guía, que sea esta:

AMS es una máquina de estados.

No se ejecuta un programa de arriba abajo como si fuera C o Python: se procesa en qué fase de la misión está el vehículo y actúa en consecuencia. En vez de escribir directamente firmware completo para tu aviónica para una misión, puedes escribir un AMS y ARES lo interpreta y ejecuta. Así es fácil definir la lógica de misiones muy distintas, personalizadas según tus requisitos. 

Una vez tienes tu script, solo lo tienes que subir al cohete con ARES-MC (Ares File system), y activar la misión mediante un API command. En caso de que haya algún error, podrás verlo mediante la conexión serial al activar la misión.

¿Qué es un estado?

Un estado representa una fase de la misión, por ejemplo:

WAIT
 ↓
FLIGHT
 ↓
DESCENT
 ↓
LANDED

Mientras el sistema esté en WAIT, solo hará lo que hayas definido dentro de WAIT. Cuando cambie a FLIGHT, dejará de ejecutar WAIT y empezará a ejecutar FLIGHT No hay magia. Solo cambia de «modo de trabajo».

¿Cuándo cambia de estado?

Cuando una transición se cumple, por ejemplo:

state WAIT:
    transition to FLIGHT when TC.command == LAUNCH

Aquí el sistema permanecerá esperando hasta recibir el comando LAUNCH.

En ese momento saltará automáticamente a FLIGHT.

También puedes usar sensores:

transition to DESCENT when BARO.alt falling for 500ms

O combinar varias condiciones:

transition to SAFE when GPS.speed < 1 and BARO.alt < 20

Piensa en las transiciones como un «si pasa esto, cambia de fase».

¿Qué ocurre dentro de un estado?

Normalmente cuatro cosas:

1. Se ejecuta on_enter

Solo ocurre una vez, justo al entrar. Perfecto para enviar un evento o inicializar variables.

on_enter:
    EVENT.info "Despegando"

Si ves el mismo mensaje repetido cien veces, algo has hecho mal.

2. Lógica de la fase de la misión

Por ejemplo, cada cierto tiempo puedes enviar datos por radio.

every 1000ms:
    HK.report {
        alt: BARO.alt
    }

Aquí se enviará la altura una vez por segundo.

Si quieres registrar información en memoria:

log_every 200ms:
    LOG.report {
        alt: BARO.alt
    }

Piensa en HK.report como «lo mando a tierra» y en LOG.report como «me lo guardo para luego».

3. Se comprueban las transiciones

En cada ciclo AMS pregunta:

«¿Sigo aquí o tengo que cambiar de estado?»

Si alguna transición se cumple, cambia inmediatamente.

Un ejemplo

state WAIT:
    on_enter:
        EVENT.info "Esperando lanzamiento"

    transition to FLIGHT when TC.command == LAUNCH

state FLIGHT:
    on_enter:
        EVENT.info "¡Motor encendido!"

    every 1000ms:
        HK.report {
            alt: BARO.alt
        }

    transition to DESCENT when BARO.alt falling for 500ms

state DESCENT:
    on_enter:
        EVENT.info "Descendiendo"

    transition to LANDED when GPS.speed < 1

Leyéndolo casi parece castellano:

  • Espera el lanzamiento.
  • Cuando llegue LAUNCH, entra en vuelo.
  • Durante el vuelo envía telemetría.
  • Cuando detecte que la altura empieza a bajar, pasa a descenso.
  • Cuando prácticamente no haya velocidad, considera que ha aterrizado.

Variables

Las variables permiten guardar información para utilizarla más adelante. Por ejemplo, al inicio de la misión puedes medir la altura del suelo y almacenarla:

var ground_alt = 0

state WAIT:
    on_enter:
        set ground_alt = CALIBRATE(BARO.alt, 10)

Más adelante podrás utilizar ese valor para tomar decisiones:

transition to LANDED when BARO.alt < (ground_alt + 10)

En otras palabras, las variables sirven para que el script tenga memoria.

Condiciones

Las transiciones indican cuándo salir de un estado.

Las condiciones (conditions:) indican qué debe seguir siendo cierto mientras permanezcas en él.

conditions:
    BARO.temp > -40
    BARO.temp < 85

on_error:
    EVENT.error "Temperatura fuera de rango"

Si alguna condición deja de cumplirse, AMS considera que el estado ya no es seguro y ejecutará el bloque on_error (o la transición de recuperación correspondiente).

Piensa en ellas como las reglas que nunca deberían romperse durante esa fase de la misión.

El inicio de un script

Antes de definir los estados de la misión, AMS necesita saber con qué hardware va a trabajar y qué servicios estarán disponibles.

Por eso, todos los scripts comienzan con una pequeña sección de configuración. Piensa en ella como la «lista de ingredientes» de la misión: aquí se declara qué dispositivos existen y cómo se llamarán dentro del script.

Declarando los periféricos

Los periféricos se añaden mediante la directiva include, por ejemplo:

include BN220 as GPS
include BMP280 as BARO
include DXLR03 as COM

Cada línea indica tres cosas:

  • el driver que utilizará AMS (BN220, BMP280, DXLR03);
  • el alias con el que accederás a ese dispositivo (GPS, BARO, COM);
  • opcionalmente, parámetros de configuración como el número de reintentos (retry).

A partir de ese momento podrás acceder a sus datos utilizando el alias:

GPS.alt
GPS.speed

BARO.alt
BARO.temp
BARO.pressure

El alias es simplemente un nombre cómodo para referirse al dispositivo. Incluso podrías utilizar otro distinto:

include BMP280 as ALTIMETER

y acceder después mediante:

ALTIMETER.alt

Aunque normalmente se recomienda utilizar nombres descriptivos como GPS, BARO, IMU o COM para que el código sea más fácil de leer.

Configuración de PUS

Después de declarar el hardware, normalmente se configuran los servicios PUS que utilizará la misión.

Un ejemplo típico es:

pus.apid = 1

pus.service 1 as TC
pus.service 3 as HK
pus.service 5 as EVENT

Esto indica a AMS qué servicios estarán disponibles dentro del script.

Los más habituales son:

  • TC (Service 1): recepción de telemandos.
  • HK (Service 3): envío de telemetría (Housekeeping).
  • EVENT (Service 5): generación de eventos para informar al operador.

En la mayoría de las misiones utilizarás estos tres servicios y rara vez tendrás que modificarlos.

Prioridades

Durante cada ciclo de ejecución pueden coincidir varias acciones: enviar un evento, transmitir telemetría, escribir un registro en memoria, ejecutar una tarea, etc.

Como el tiempo de CPU es limitado, AMS utiliza un sistema de prioridades para decidir qué hacer primero.

La prioridad se configura mediante la directiva:

priorities event=4 hk=3 log=1 budget=2

Cada número indica la importancia relativa de un tipo de acción: cuanto mayor sea el valor, antes se ejecutará.

Los parámetros disponibles son:

  • event: prioridad de los eventos (EVENT.*).
  • hk: prioridad de la telemetría (HK.report).
  • log: prioridad del registro local (LOG.report y SERIAL.report).
  • budget: número máximo de grupos de acciones que pueden ejecutarse en un mismo ciclo.

¿Qué significa el budget?

El budget suele ser el parámetro que más dudas genera.

No limita el número de mensajes enviados, sino el número de grupos de acciones que AMS puede ejecutar en un ciclo.

Por ejemplo, con esta configuración:

priorities event=4 hk=3 log=1 budget=2

si en un mismo instante coinciden:

  • un EVENT.info,
  • un HK.report,
  • un LOG.report,

AMS ejecutará primero el evento y después la telemetría. El registro local tendrá que esperar al siguiente ciclo.

En cambio, si existen varios bloques HK.report que deben ejecutarse al mismo tiempo, todos forman parte del grupo de telemetría y se enviarán juntos.

¿Qué configuración debería usar?

Para la mayoría de las misiones, la configuración recomendada es:

priorities event=4 hk=3 log=1 budget=2

Con esta distribución:

  • Los eventos siempre tienen máxima prioridad, ya que suelen indicar cambios importantes o situaciones anómalas.
  • La telemetría se envía antes que los registros locales para mantener informada a la estación de tierra.
  • Los logs tienen la prioridad más baja, ya que siempre pueden analizarse después del vuelo.

Solo suele ser necesario modificar estos valores en casos muy concretos, como pruebas de laboratorio o misiones con requisitos de telemetría muy específicos.

En general, si no tienes un motivo para cambiar las prioridades, déjalas como están. La configuración por defecto está pensada para ofrecer un buen equilibrio entre rendimiento, uso de CPU y cantidad de información transmitida.

Expresiones

Las expresiones son la forma que tiene AMS de responder a preguntas como:

  • ¿Ha despegado el cohete?
  • ¿Está bajando?
  • ¿Se ha recibido un comando?
  • ¿Es seguro continuar?

Se utilizan principalmente en las transiciones, las condiciones y cualquier lugar donde AMS necesite evaluar si algo es verdadero o falso.

Por ejemplo:

GPS.speed > 10

Esta expresión será verdadera cuando la velocidad medida por el GPS sea superior a 10.

También puedes comparar variables:

BARO.alt > ground_alt + 100

O combinar varias condiciones:

GPS.fix and BARO.alt > 50
GPS.speed < 1 and BARO.alt < ground_alt + 5

Incluso puedes utilizar paréntesis para dejar clara la prioridad de las operaciones:

(GPS.speed > 20 or IMU.acc > 3) and BATTERY.voltage > 7

Operadores de comparación

Los operadores más habituales son:

==   Igual que
!=   Distinto de
>    Mayor que
>=   Mayor o igual que
<    Menor que
<=   Menor o igual que

Operadores lógicos

También puedes combinar expresiones mediante operadores lógicos:

and    Ambas condiciones deben cumplirse.
or     Basta con que se cumpla una.
not    Invierte el resultado.

Por ejemplo:

not GPS.fix

significa simplemente:

«No hay posición GPS válida.»

En general, las expresiones de AMS se leen casi como una frase en castellano, por lo que suelen ser fáciles de entender incluso en scripts largos.

 

Funciones integradas

AMS incluye varias funciones para realizar operaciones habituales sin tener que escribir lógica adicional.

Se utilizan igual que en cualquier otro lenguaje:

FUNCION(argumentos)

CALIBRATE()

La función CALIBRATE() calcula un valor estable a partir de varias muestras consecutivas.

Es especialmente útil durante la inicialización de la misión para obtener una referencia antes del lanzamiento.

Por ejemplo:

var ground_alt = 0

on_enter:
    set ground_alt = CALIBRATE(BARO.alt, 20)

En este caso AMS toma 20 muestras de la altura medida por el barómetro y obtiene un valor calibrado que se utilizará como altura del suelo.

Es una función muy utilizada para compensar pequeñas variaciones de los sensores antes de comenzar la misión.

Otras funciones

Además de CALIBRATE(), AMS incorpora otras funciones para operaciones matemáticas y de tratamiento de datos.

Entre las más habituales se encuentran:

ABS()      Valor absoluto.
MIN()      Menor de varios valores.
MAX()      Mayor de varios valores.
AVG()      Media de varios valores.

Por ejemplo:

ABS(IMU.acc_z)

obtiene el valor absoluto de la aceleración.

MAX(BARO.alt, GPS.alt)

devuelve la mayor de las dos alturas.

MIN(temp1, temp2)

devuelve la menor temperatura.

Estas funciones permiten escribir expresiones más claras y evitar cálculos innecesarios dentro del propio script.

No necesitas memorizar todas las funciones disponibles. Conocer CALIBRATE() y saber que existen funciones matemáticas básicas suele ser suficiente para empezar a desarrollar la mayoría de las misiones.

Documentación completa

Algunos ejemplos

include BMP280 as BARO
include DXLR03 as COM
pus.apid = 1
 
pus.service 3 as HK
pus.service 5 as EVENT
pus.service 1 as TC
 
state PING:
  on_enter:
    EVENT.info «LINK_TEST: HK cada 5s»
  priorities event=4 hk=4 log=1 budget=2
  every 5000ms:
    HK.report {
      baro_alt:   BARO.alt
      baro_temp:  BARO.temp
      baro_press: BARO.pressure
    }
  log_every 1000ms:
    LOG.report {
      baro_alt:  BARO.alt
      baro_temp: BARO.temp
    }
  transition to DONE when TIME.elapsed > 60000
 
state DONE:
  on_enter:
    EVENT.info «LINK_TEST: completado»
  priorities event=4 hk=1 log=1 budget=1
  every 10000ms:
    HK.report {
      baro_alt:   BARO.alt
      baro_temp:  BARO.temp
      baro_press: BARO.pressure
    }

 

include BMP280 as BARO
 
pus.apid = 1
pus.service 5 as EVENT
 
var zero_alt = 0.0
var up_threshold = 0.0
var down_threshold = 0.0
 
state CALIBRATE:
  on_enter:
    EVENT.info «CALIBRATING»
    set zero_alt = CALIBRATE(BARO.alt, 10)
  transition to WAIT_UP when TIME.elapsed > 5000
 
state WAIT_UP:
  on_enter:
    EVENT.info «LIFT_BAROMETER»
    set up_threshold = zero_alt + 0.5
  log_every 250ms:
    SERIAL.report {
      altitude: BARO.alt
      reference: zero_alt
    }
  transition to UP_DETECTED when BARO.alt > up_threshold
 
state UP_DETECTED:
  on_enter:
    EVENT.info «HEIGHT_DETECTED»
  transition to WAIT_DOWN when TIME.elapsed > 1000
state WAIT_DOWN:
  on_enter:
    EVENT.info «LOWER_BAROMETER»
    set down_threshold = zero_alt + 0.2
  log_every 250ms:
    SERIAL.report {
      altitude: BARO.alt
      reference: zero_alt
    }
  transition to FINISHED when BARO.alt < down_threshold
 
state FINISHED:
  on_enter:
    EVENT.info «TEST_OK»

 

include BMP280 as BARO
include ADXL375 as IMU

pus.apid = 1
pus.service 3 as HK
pus.service 5 as EVENT

var zero_alt = 0.0
var imu_ax0 = 0.0
var imu_ay0 = 0.0
var imu_az0 = 0.0

var accel_x_corr = 0.0
var accel_y_corr = 0.0
var accel_z_corr = 0.0

state CALIBRATE:
  on_enter:
    EVENT.info «Calibrado de sensores: 10 tomas»
    set zero_alt = CALIBRATE(BARO.alt, 10)
    set imu_ax0 = CALIBRATE(IMU.accel_x, 10)
    set imu_ay0 = CALIBRATE(IMU.accel_y, 10)
    set imu_az0 = CALIBRATE(IMU.accel_z, 10)

  on_timeout 5000ms:
    transition to GRABANDO

state GRABANDO:
  wifi.disable
  api.disable

  priorities event=4 hk=1 log=3 budget=3

  on_enter:
    EVENT.info «Grabando ACCEL en memoria flash»

  log_every 10ms:
    LOG.report {
      accel_x:   IMU.accel_x
      accel_y:   IMU.accel_y
      accel_z:   IMU.accel_z
    }

  on_timeout 10000ms:
    transition to FINALIZADO

state FINALIZADO:
  wifi.enable
  api.enable
  on_enter:
    EVENT.info «Grabacion completada exitosamente»