def setUp(self, plane: aircraft.Aircraft = aircraft.cessna172P, task_type: Type[ tasks.HeadingControlTask] = tasks.TurnHeadingControlTask): self.env = None self.env = JsbSimEnv(aircraft=plane, task_type=task_type) self.env.reset()
def init_and_reset_env(self, env: JsbSimEnv): self.assertIsInstance(env.task, HeadingControlTask) # we interact at 5 Hz, so we expect the sim to run 12 timesteps per # interaction since it runs at 120 Hz self.assertEqual(12, env.sim_steps_per_agent_step) # we init a random agent with a seed agent = RandomAgent(action_space=env.action_space) self.assertEqual(env.action_space, agent.action_space) # this task has an action space of three controls: aileron, elevator, rudder expected_num_actions = 3 self.assertEqual(expected_num_actions, len(agent.action_space.low)) # we see that the action space has the correct low and high range of +-1.0 expect_low = np.array([-1.0] * expected_num_actions) expect_high = np.array([1.0] * expected_num_actions) np.testing.assert_array_almost_equal(expect_high, env.action_space.high) np.testing.assert_array_almost_equal(expect_low, env.action_space.low) # we reset the env and receive the first state; the env is now ready state = env.reset() self.assertEqual(len(env.observation_space.low), len(state)) # we close the env and JSBSim closes with it env.close() self.assertIsNone(env.sim.jsbsim)
def setUp(self): if self.env: self.env.close() if self.sim: self.sim.close() self.task = BasicFlightTask() self.env = JsbSimEnv(task_type=BasicFlightTask) self.env.reset() self.sim = self.env.sim self.flightgear = None
def setUp(self, plane: aircraft.Aircraft = aircraft.cessna172P, task_type: Type[ tasks.HeadingControlTask] = tasks.TurnHeadingControlTask, shaping: tasks.Shaping = tasks.Shaping.STANDARD): self.env = None self.env = JsbSimEnv(aircraft=plane, task_type=task_type, shaping=shaping) self.env.reset()
class TurnHeadingControlTest(unittest.TestCase): def setUp(self, plane: aircraft.Aircraft = aircraft.cessna172P, task_type: Type[ tasks.HeadingControlTask] = tasks.TurnHeadingControlTask, shaping: tasks.Shaping = tasks.Shaping.STANDARD): self.env = None self.env = JsbSimEnv(aircraft=plane, task_type=task_type, shaping=shaping) self.env.reset() def tearDown(self): self.env.close() def test_render_heading_control(self): self.setUp(plane=aircraft.a320, task_type=tasks.TurnHeadingControlTask, shaping=tasks.Shaping.EXTRA_SEQUENTIAL) agent = RandomAgent(self.env.action_space) render_every = 5 report_every = 20 EPISODES = 50 for _ in range(EPISODES): ep_reward = 0 done = False state = self.env.reset() self.env.render(mode='flightgear') step_number = 0 while not done: action = agent.act(state) state, reward, done, info = self.env.step(action) ep_reward += reward if step_number % render_every == 0: self.env.render(mode='flightgear') if step_number % report_every == 0: heading_target = tasks.HeadingControlTask.target_track_deg print(f'time:\t{self.env.sim.get_sim_time()} s') print(f'last reward:\t{reward}') print(f'episode reward:\t{ep_reward}') print(f'gear status:\t{self.env.sim[prp.gear]}') print( f'thrust eng0:\t{self.env.sim[prp.engine_thrust_lbs]}') print( f'thrust eng1:\t {self.env.sim[prp.Property("propulsion/engine[1]/thrust-lbs", "")]}' ) print(f'heading:\t{self.env.sim[prp.heading_deg]}') print(f'target heading:\t{self.env.sim[heading_target]}') print('\n') step_number += 1 print(f'***\n' f'EPISODE REWARD: {ep_reward}\n' f'***\n')
class FlightGearRenderTest(unittest.TestCase): def setUp(self, plane: aircraft.Aircraft = aircraft.cessna172P, task_type: Type[ tasks.HeadingControlTask] = tasks.TurnHeadingControlTask): self.env = None self.env = JsbSimEnv(aircraft=plane, task_type=task_type) self.env.reset() def tearDown(self): self.env.close() def test_render_steady_level_flight(self): self.setUp(plane=aircraft.cessna172P, task_type=tasks.HeadingControlTask) agent = ConstantAgent(self.env.action_space) render_every = 5 report_every = 20 EPISODES = 999 for _ in range(EPISODES): ep_reward = 0 done = False state = self.env.reset() self.env.render(mode='flightgear') step_number = 0 while not done: action = agent.act(state) state, reward, done, info = self.env.step(action) ep_reward += reward if step_number % render_every == 0: self.env.render(mode='flightgear') if step_number % report_every == 0: print(f'time:\t{self.env.sim.get_sim_time()} s') print(f'last reward:\t{reward}') print(f'episode reward:\t{ep_reward}') print(f'thrust:\t{self.env.sim[prp.engine_thrust_lbs]}') print( f'engine running:\t{self.env.sim[prp.engine_running]}') step_number += 1 print(f'***\n' f'EPISODE REWARD: {ep_reward}\n' f'***')
def take_step_with_random_agent(self, env: JsbSimEnv): agent = RandomAgent(action_space=env.action_space) # we set up for a loop through one episode first_state = env.reset() # we take a single step action = agent.act(first_state) state, reward, done, info = env.step(action) # we see the state has changed self.assertEqual(first_state.shape, state.shape) self.assertTrue(np.any(np.not_equal(first_state, state)), msg='state should have changed after simulation step') expected_time_step_size = env.sim_steps_per_agent_step / env.JSBSIM_DT_HZ self.assertAlmostEqual(expected_time_step_size, env.sim.get_sim_time()) self.assertFalse(done, msg='episode is terminal after only a single step') # the aircraft engines are running, as per initial conditions self.assertNotAlmostEqual(env.sim[prp.engine_thrust_lbs], 0) env.close()
class TestFlightGearVisualiser(unittest.TestCase): env = None sim = None flightgear = None def setUp(self): if self.env: self.env.close() if self.sim: self.sim.close() self.task = BasicFlightTask() self.env = JsbSimEnv(task_type=BasicFlightTask) self.env.reset() self.sim = self.env.sim self.flightgear = None # individual test methods should init as needed: # self.flightgear = FlightGearVisualiser(self.sim) def tearDown(self): if self.env: self.env.close() if self.sim: self.sim.close() if self.flightgear: self.flightgear.close() def test_init_creates_figure(self): self.flightgear = FlightGearVisualiser(self.sim, self.task.get_props_to_output(), block_until_loaded=False) self.assertIsInstance(self.flightgear.figure, FigureVisualiser) def test_launch_flightgear(self): self.flightgear = FlightGearVisualiser(self.sim, self.task.get_props_to_output(), block_until_loaded=False) time.sleep(0.5) # check FlightGear has launched by looking at stdout self.assertIn( 'FlightGear', self.flightgear.flightgear_process.stdout.readline().decode()) self.flightgear.close() def test_close_closes_flightgear(self): self.flightgear = FlightGearVisualiser(self.sim, self.task.get_props_to_output(), block_until_loaded=False) self.flightgear.close() timeout_seconds = 2.0 return_code = self.flightgear.flightgear_process.wait( timeout=timeout_seconds) # a non-None return code indicates termination self.assertIsNotNone(return_code) def test_plot_displays_actions(self): self.setUp() self.flightgear = FlightGearVisualiser(self.sim, self.task.get_props_to_output(), block_until_loaded=False) self.flightgear.plot(self.sim) # the figure should have plotted a Lines object each axis for axis in ['axes_stick', 'axes_rudder', 'axes_throttle']: axis_data_plots = getattr(self.flightgear.figure.axes, axis) is_empty_plot = len(axis_data_plots.axes.lines) == 0 self.assertFalse(is_empty_plot, msg=f'no data plotted on axis {axis}')
def setUp(self, task_type: Type[tasks.HeadingControlTask] = BasicFlightTask): self.env = None self.env = JsbSimEnv(task_type) self.env.reset()
class TestJsbSimInstance(unittest.TestCase): def setUp(self, task_type: Type[tasks.HeadingControlTask] = BasicFlightTask): self.env = None self.env = JsbSimEnv(task_type) self.env.reset() def tearDown(self): self.env.close() def test_long_episode_random_actions(self): self.setUp() tic = time.time() self.env.reset() for i in range(2000): self.env.step(action=self.env.action_space.sample()) print(f'jsbsim {i / 10} s\n') toc = time.time() wall_time = (toc - tic) sim_time = self.env.sim.get_sim_time() print(f'Simulated {sim_time} s of flight in {wall_time} s') def test_render_episode(self): self.setUp() render_every = 5 self.env.reset() for i in range(1000): action = self.env.action_space.sample() obs, _, _, _ = self.env.step(action=action) if i % render_every == 0: self.env.render(mode='human') def test_render_steady_level_flight_random(self): """ Runs steady level flight task with a random agent. """ self.setUp(task_type=tasks.HeadingControlTask) agent = RandomAgent(self.env.action_space) render_every = 5 ep_reward = 0 done = False state = self.env.reset() step_number = 0 while not done: action = agent.act(state) state, reward, done, info = self.env.step(action) ep_reward += reward if step_number % render_every == 0: self.env.render(mode='human') step_number += 1 def test_run_episode_steady_level_flight_no_render(self): self.setUp(task_type=tasks.HeadingControlTask) agent = RandomAgent(self.env.action_space) report_every = 20 EPISODES = 10 for _ in range(EPISODES): ep_reward = 0 done = False state = self.env.reset() step_number = 0 while not done: action = agent.act(state) state, reward, done, info = self.env.step(action) ep_reward += reward if step_number % report_every == 0: print(f'time:\t{self.env.sim.get_sim_time()} s') print(f'last reward:\t{reward}') print(f'episode reward:\t{ep_reward}') step_number += 1
def init_env(self, agent_interaction_freq): self.env = JsbSimEnv(task_type=BasicFlightTask, agent_interaction_freq=agent_interaction_freq)
class TestJsbSimEnv(unittest.TestCase): def setUp(self, agent_interaction_freq: int = 10): gym.logger.set_level(gym.logger.DEBUG) self.env = None self.init_env(agent_interaction_freq) self.env.reset() def init_env(self, agent_interaction_freq): self.env = JsbSimEnv(task_type=BasicFlightTask, agent_interaction_freq=agent_interaction_freq) def tearDown(self): self.env.close() def assertValidObservation(self, obs: np.array): """ Helper; checks shape and values of an observation. """ self.assertEqual(self.env.observation_space.shape, obs.shape, msg='observation has wrong size') self.assert_in_box_space(obs, self.env.observation_space) def assertValidAction(self, action: np.array): """ Helper; checks shape and values of an action. """ self.assertEqual(self.env.action_space.shape, action.shape, msg='action has wrong size') self.assert_in_box_space(action, self.env.action_space) def assert_in_box_space(self, sample: np.array, space: gym.spaces.Box) -> None: if space.contains(sample): return else: is_too_low = sample < space.low is_too_high = sample > space.high msg = 'Sample is not in space:' for i in range(len(sample)): if is_too_low[i]: msg += f'\nelement {i} too low: {sample[i]} < {space.low[i]}' if is_too_high[i]: msg += f'\nelement {i} too high: {sample[i]} > {space.high[i]}' raise AssertionError(msg) def validate_action_made(self, action: np.array): """ Helper; confirms action was correctly input to simulation. """ self.assertValidAction(action) for prop, command in zip(self.env.task.action_variables, action): actual = self.env.sim[prop] self.assertAlmostEqual( command, actual, msg='simulation commanded value does not match action') def test_init_spaces(self): # check correct types for obs and action space self.assertIsInstance(self.env.observation_space, gym.Space, msg='observation_space is not a Space object') self.assertIsInstance(self.env.action_space, gym.Space, msg='action_space is not a Space object') # check low and high values are as expected obs_lows = self.env.observation_space.low obs_highs = self.env.observation_space.high act_lows = self.env.action_space.low act_highs = self.env.action_space.high places_tol = 3 for prop, lo, hi in zip(self.env.task.state_variables, obs_lows, obs_highs): self.assertAlmostEqual(lo, prop.min, msg=f'{prop} min of {prop.min} does not' f'match space low of {lo}') self.assertAlmostEqual(hi, prop.max, msg=f'{prop} max of {prop.max} does not' f'match space high of {hi}') def test_reset_env(self): self.setUp() obs = self.env.reset() self.assertValidObservation(obs) def test_do_action(self): self.setUp() action1 = np.array([0.0] * len(self.env.task.action_variables)) action2 = np.linspace(-0.5, .5, num=len(self.env.task.action_variables)) # do an action and check results obs, _, _, _ = self.env.step(action1) self.assertValidObservation(obs) self.validate_action_made(action1) # repeat action several times for _ in range(10): obs, _, _, _ = self.env.step(action1) self.assertValidObservation(obs) self.validate_action_made(action1) # repeat new action for _ in range(10): obs, _, _, _ = self.env.step(action2) self.assertValidObservation(obs) self.validate_action_made(action2) def test_figure_created_closed(self): self.env.render(mode='human') self.assertIsInstance(self.env.figure_visualiser.figure, plt.Figure) self.env.close() self.assertIsNone(self.env.figure_visualiser.figure) def test_plot_state(self): # note: this checks that plot works without throwing exception # correctness of plot must be checked in appropriate manual_test self.setUp() self.env.render(mode='human') action = np.array([0.0] * len(self.env.task.action_variables)) # repeat action several times for _ in range(3): obs, _, _, _ = self.env.step(action) self.env.render(mode='human') def test_plot_actions(self): # note: this checks that plot works without throwing exception # correctness of plot must be checked in appropriate manual_test self.env.render(mode='human') # repeat action several times for _ in range(3): action = self.env.action_space.sample() _, _, _, _ = self.env.step(action) self.env.render(mode='human') def test_asl_agl_elevations_equal(self): # we want the height above sea level to equal ground elevation at all times self.setUp(agent_interaction_freq=1) for i in range(25): self.env.step(action=self.env.action_space.sample()) alt_sl = self.env.sim[prp.altitude_sl_ft] alt_gl = self.env.sim[prp.BoundedProperty('position/h-agl-ft', '', 0, 0)] self.assertAlmostEqual(alt_sl, alt_gl) def test_render_flightgear_mode(self): self.setUp() self.env.render(mode='flightgear', flightgear_blocking=False) self.assertIsInstance(self.env.flightgear_visualiser, FlightGearVisualiser) self.env.close()