Skip to content

Commit

Permalink
Simplified and tidied up implementation of Specification (#92)
Browse files Browse the repository at this point in the history
* updated Specification implementation

* updated arm schemas

* updated goto schema

* updated parachute spec

* updated setmode

* updated takeoff specs

* removed GoToNormally from common module

* updated GoTo for rover

* updated goto specs

* fixed bad imports
  • Loading branch information
ChrisTimperley committed Sep 18, 2018
1 parent 81861b8 commit 5c5043a
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 413 deletions.
1 change: 0 additions & 1 deletion example/hello_world.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
from houston.generator.resources import ResourceLimits
from houston.mission import Mission
from houston.runner import MissionRunnerPool
from houston.ardu.common.goto import CircleBasedGotoGenerator
from houston.ardu.copter.state import State as CopterState
from houston.ardu.configuration import Configuration as ArduConfig

Expand Down
2 changes: 1 addition & 1 deletion houston/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class ActionSchema(object):
def __init__(self,
name: str,
parameters: List[Parameter],
specifications: List[Specification]
specs: List[Specification]
) -> None:
"""
Constructs an ActionSchema object.
Expand Down
50 changes: 10 additions & 40 deletions houston/ardu/common/arm.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,47 +51,17 @@ def dispatch(self,

class ArmNormally(Specification):
def __init__(self) -> None:
super().__init__('arm-normal')

def precondition(self, action, state, environment, config):
return action['arm'] \
and self.is_satisfiable(state, environment, config)

def postcondition(self,
action,
state_before,
state_after,
environment,
config):
return state_after.armed

def timeout(self, action, state, environment, config):
return config.constant_timeout_offset + 1.0

def is_satisfiable(self, state, environment, config):
return state.armable and state.mode in ['GUIDED', 'LOITER']
pre = lambda a, s, e, c:\
a['arm'] and s.armable and s.mode in ['GUIDED', 'LOITER']
post = lambda a, s0, s1, e, c: s1.armed
timeout = lambda a, s, e, c: c.constant_timeout_offset + 1.0
super().__init__('arm-normal', pre, post, timeout)


class DisarmNormally(Specification):
def __init__(self) -> None:
super().__init__('disarm-normal')

def precondition(self, action, state, environment, config):
return not action['arm'] \
and self.is_satisfiable(state, environment, config)

def postcondition(self,
action,
state_before,
state_after,
environment,
config):
return not state_after.armed

def timeout(self, action, state, environment, config):
return config.constant_timeout_offset + 1

# TODO
def is_satisfiable(self, state, environment, config):
# and state['mode'] in ['GUIDED', 'LOITER']
return state.armed
pre = lambda a, s, e, c:\
a['arm'] and s.armed
post = lambda a, s0, s1, e, c: not s1.armed
timeout = lambda a, s, e, c: c.constant_timeout_offset + 1.0
super().__init__('disarm-normal', pre, post, timeout)
93 changes: 16 additions & 77 deletions houston/ardu/common/goto.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__all__ = ['GotoNormally', 'GotoLoiter']
__all__ = ['GotoLoiter']

from typing import Tuple

Expand All @@ -12,85 +12,24 @@
from ...specification import Specification, Idle


class GotoNormally(Specification):
"""
If the robot is armed and not in its `LOITER` mode, GoTo actions should
cause the robot to move to the desired location. For certain Ardu systems,
the precondition on this normal behaviour is stronger; for more
information, refer to the system-specific subclasses of GotoNormally.
"""
def __init__(self) -> None:
super().__init__('normal')

def timeout(self, action, state, environment, config):
from_loc = (state.latitude, state.longitude)
to_loc = (action['latitude'], action['longitude'])
dist = geopy.distance.great_circle(from_loc, to_loc).meters
timeout = dist * config.time_per_metre_travelled
timeout += config.constant_timeout_offset
return timeout

def precondition(self, action, state, environment, config):
"""
This behaviour will occur for Goto actions when the system is armed and
not in its `LOITER` mode.
"""
return state.armed and state.mode != 'LOITER'

def postcondition(self,
action,
state_before,
state_after,
environment,
config):
"""
Upon completion of the action, the robot should be at the longitude and
latitude specified by the action parameters.
"""
err_lon = 0.1
err_lat = 0.1
sat_lon = \
abs(state_after.longitude - action['longitude']) <= err_lon
sat_lat = \
abs(state_after.latitude - action['latitude']) <= err_lat
return sat_lon and sat_lat

def is_satisfiable(self, state, environment, config):
return self.precondition(None, state, environment, config)


class GotoLoiter(Specification):
"""
If the robot is armed and in its `LOITER` mode, GoTo actions should have no
effect upon the robot. (Why isn't this covered by Idle?)
"""
def __init__(self) -> None:
super().__init__('loiter')

def timeout(self, action, state, environment, config):
return config.constant_timeout_offset

def precondition(self, action, state, environment, config):
return state.armed and state.mode == 'LOITER'

def postcondition(self,
action,
state_before,
state_after,
environment,
config):
sat_mode = state_after.mode == 'LOITER'
# FIXME 82
err_lon = 0.1
err_lat = 0.1
err_alt = 0.5
sat_lon = \
abs(state_after.longitude - state_before.longitude) <= err_lon
sat_lat = \
abs(state_after.latitude - state_before.latitude) <= err_lat
sat_alt = \
abs(state_after.altitude - state_before.altitude) <= err_alt
return sat_mode and sat_lon and sat_lat and sat_alt

def is_satisfiable(self, state, environment, config):
return self.precondition(None, state, environment, config)
pre = lambda a, s, e, c: s.armed and s.mode == 'LOITER'
timeout = lambda a, s, e, c: c.constant_timeout_offset

def post(a, s0, s1, e, c) -> bool:
sat_mode = s1.mode == 'LOITER'
# FIXME 82
err_lon = 0.1
err_lat = 0.1
err_alt = 0.5
sat_lon = abs(s1.longitude - s0.longitude) <= err_lon
sat_lat = abs(s1.latitude - s0.latitude) <= err_lat
sat_alt = abs(s1.altitude - s0.altitude) <= err_alt
return sat_mode and sat_lon and sat_lat and sat_alt

super().__init__('loiter', pre, post, timeout)
76 changes: 40 additions & 36 deletions houston/ardu/copter/goto.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
__all__ = ['GoToSchema', 'GotoNormally']
__all__ = ['GoToSchema']

import dronekit
import geopy.distance

from ..common.goto import GotoNormally as GotoNormallyBase
from ..common.goto import GotoLoiter
from ...specification import Specification
from ...configuration import Configuration
from ...state import State
from ...environment import Environment
Expand All @@ -12,6 +13,43 @@
from ...valueRange import ContinuousValueRange, DiscreteValueRange


class GotoNormally(Specification):
"""
If the robot is armed and not in its `LOITER` mode, GoTo actions should
cause the robot to move to the desired location. For certain Ardu systems,
the precondition on this normal behaviour is stronger; for more
information, refer to the system-specific subclasses of GotoNormally.
"""
def __init__(self) -> None:
def pre(a, s, e, c) -> bool:
# FIXME 82
sat_armed = s.armed
sat_mode = s.mode != 'LOITER'
err_alt = 0.1
sat_alt = s.altitude + err_alt > 0.3
return sat_armed and sat_mode and sat_alt

def post(a, s0, s1, e, c) -> bool:
# FIXME 82
err_lon = 0.1
err_lat = 0.1
err_alt = 0.1
sat_lon = abs(s1.longitude - a['longitude']) <= err_lon
sat_lat = abs(s1.latitude - a['latitude']) <= err_lat
sat_alt = abs(s1.altitude - a['altitude']) <= err_alt
return sat_lon and sat_lat and sat_alt

def timeout(a, s, e, c) -> float:
from_loc = (s.latitude, s.longitude)
to_loc = (a['latitude'], a['longitude'])
dist = geopy.distance.great_circle(from_loc, to_loc).meters
timeout = dist * c.time_per_metre_travelled
timeout += c.constant_timeout_offset
return timeout

super().__init__('normal', pre, post, timeout)


class GoToSchema(ActionSchema):
def __init__(self) -> None:
name = 'goto'
Expand All @@ -38,37 +76,3 @@ def dispatch(self,
action['longitude'],
action['altitude'])
sandbox.connection.simple_goto(loc)


class GotoNormally(GotoNormallyBase):
def precondition(self, action, state, environment, config) -> bool:
"""
For GoTo actions within the ArduCopter to exhibit a "normal" behaviour,
the robot must be at an altitude greater than 0.3 metres.
"""
base = super().precondition(action, state, environment, config)
if not base:
return False
# FIXME 82
err_alt = 0.1
sat_alt = state.altitude + err_alt > 0.3
return sat_alt

def postcondition(self,
action,
state_before,
state_after,
environment,
config) -> bool:
"""
Upon completion of the action, the robot should be at the longitude,
latitude, and altitude specified by the action.
"""
base = super().postcondition(action, state_before, state_after,
environment, config)
if not base:
return False
# FIXME 82
err_alt = 0.1
sat_alt = abs(state_after.altitude - action['altitude']) <= err_alt
return sat_alt
50 changes: 21 additions & 29 deletions houston/ardu/copter/parachute.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,35 +40,27 @@ def dispatch(self,

class ParachuteNormally(Specification):
def __init__(self):
super().__init__("normal")
def pre(a, s, e, c) -> bool:
# FIXME #82
sat_action = a['parachute_action'] == 2
noise_alt = 0.1
sat_armed = s.armed
sat_mode = s.mode == 'GUIDED'
sat_alt = s.alt > c.min_parachute_alt - noise_alt
return sat_action and sat_armed and sat_mode and sat_alt

def timeout(self, action, state, environment, config):
timeout = state.altitude * config.time_per_metre_travelled
timeout += config.constant_timeout_offset
return timeout
def post(a, s0, s1, e, c) -> bool:
# FIXME #82
noise_alt = 0.1
noise_vz = 0.05
sat_armed = not s1.armed
sat_alt = s1.altitude < 0.3 + noise_alt
sat_vz = 0.0 <= s1.vz <= noise_vz
return sat_armed and sat_alt and sat_vz

def postcondition(self,
action,
state_before,
state_after,
environment,
config):
# FIXME #82
noise_alt = 0.1
noise_vz = 0.05
sat_armed = not state_after.armed
sat_alt = state_after.altitude < 0.3 + noise_alt
sat_vz = 0.0 <= state_after.vz <= noise_vz
return sat_armed and sat_alt and sat_vz
def timeout(a, s, e, c) -> float:
timeout = s.altitude * c.time_per_metre_travelled
timeout += c.constant_timeout_offset
return timeout

def precondition(self, action, state, environment, config):
return action['parachute_action'] == 2 and \
self.is_satisfiable(state, environment, config)

def is_satisfiable(self, state, environment, config):
# FIXME #82
noise_alt = 0.1
sat_armed = state.armed
sat_mode = state.mode == 'GUIDED'
sat_alt = state.alt > config.min_parachute_alt - noise_alt
return sat_armed and sat_mode and sat_alt
super().__init__("normal", pre, post, timeout)

0 comments on commit 5c5043a

Please sign in to comment.