Context
I’m getting started with AirSDK and adapting the “Hello”/“Roadrunner” examples. As a first step I want the drone to climb to 13 m on takeoff. I tried to replace/override the ascent, but FSUP always executes the default takeoff.normal.ascent
instead of my custom ascent.
Platform
- Drone: ANAFI Ai
- Controller: Olympe (desktop)
- AirSDK mission: custom, based on default mission states
Stage Definition stage.py
from fsup.missions.default.takeoff.stage import Takeoff as DefaultTakeoff
from .normal import NORMAL_STATE
from .changed_ascent import CUSTOM_ASCENT_STATE
from ..uid import UID
_GROUND_MODE_NAME= "com.mycompany.missions.firstmission"
TAKEOFF_STAGE = {
"name": "takeoff",
"class": DefaultTakeoff,
"initial": "normal",
"children": [CUSTOM_ASCENT_STATE, NORMAL_STATE],
}
Normal State definition normal.py
from fsup.missions.default.takeoff.normal import (
NORMAL_STATE as DEF_NORMAL_STATE,
)
_STATES_TO_REMOVE = ["ascent"]
NORMAL_STATE = {
"name": "normal",
"initial": "reset",
"children": [
child
for child in DEF_NORMAL_STATE["children"]
if not child["name"] in _STATES_TO_REMOVE
],
}
Own Ascent Configs changed_ascent.py
from fsup.genstate import guidance_modes
from fsup.missions.default.takeoff.normal import (
Ascent as DefaultAscent,
)
from fsup.missions.default.uid import UID
import os
import cfgreader
import colibrylite.estimation_mode_pb2 as cbry_est
import guidance.ascent_pb2 as gdnc_ascent_msgs
from ..uid import UID
_STATES_TO_REMOVE = ["ascent"]
TARGET_ALTITUDE_M = 13.0
@guidance_modes(UID + ".ascent")
class Ascent(DefaultAscent):
def enter(self, msg):
self.gdnc_asc_svc = self.mc.attach_client_service_pair(
self.mc.gdnc_channel, gdnc_ascent_msgs, forward_events=True
)
self.mc.dctl.cmd.sender.set_estimation_mode(cbry_est.TAKEOFF)
self.log.info(f"[custom_ascent] target altitude = {TARGET_ALTITUDE_M} m")
self.set_guidance_mode(
"com.parrot.missions.default.ascent",
gdnc_ascent_msgs.Config(
type=gdnc_ascent_msgs.TYPE_DEFAULT,
altitude=TARGET_ALTITUDE_M
),
)
CUSTOM_ASCENT_STATE = {
"name": "changed_ascent",
"initial": "ascent",
"children": [
{
"name": "ascent",
"class": Ascent,
},
],
}
mission.py
# only code is shown that is different to the init standard mission
from .takeoff.stage import TAKEOFF_STAGE
def states(self):
# Add your custom stages or override the default stages here
return [
DEF_GROUND_STAGE,
TAKEOFF_STAGE,
DEF_HOVERING_STAGE,
FLYING_STAGE,
DEF_LANDING_STAGE,
DEF_CRITICAL_STAGE,
]
def transitions(self):
transitions = TRANSITIONS + DEF_TRANSITIONS
return transitions
GdncAscent = lambda evt: msg_id(gdnc_ascent_msgs.Event, evt) # noqa: E731
TRANSITIONS = [
[
Dctl("motors_ramping_done"),
"takeoff.normal.wait_ascent",
"takeoff.changed_ascent.ascent",
],
[
Dctl("motors_ramping_done"),
"takeoff.normal.wait_motor_ramping",
"takeoff.changed_ascent.ascent",
],
[GdncAscent("done"), "takeoff.changed_ascent.ascent", "flying.manual"],
]
Mission builds and runs; logs show motors_ramping_done
, but the next state is still takeoff.normal.ascent
.