minimal code; add requirements.txt; download binaries script
This commit is contained in:
Родитель
da3ee52874
Коммит
3e937498a3
|
@ -0,0 +1,2 @@
|
|||
baselines/__pycache__
|
||||
*pyc
|
|
@ -0,0 +1,14 @@
|
|||
# Contributing
|
||||
|
||||
This project welcomes contributions and suggestions. Most contributions require you to
|
||||
agree to a Contributor License Agreement (CLA) declaring that you have the right to,
|
||||
and actually do, grant us the rights to use your contribution. For details, visit
|
||||
https://cla.microsoft.com.
|
||||
|
||||
When you submit a pull request, a CLA-bot will automatically determine whether you need
|
||||
to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the
|
||||
instructions provided by the bot. You will only need to do this once across all repositories using our CLA.
|
||||
|
||||
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/).
|
||||
For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/)
|
||||
or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
## Quickstart
|
||||
- [Website](https://microsoft.github.io/AirSim-Drone-Racing-Lab/)
|
||||
- [Linux and Windows Binaries](https://github.com/microsoft/AirSim-NeurIPS2019-Drone-Racing/releases)
|
||||
- [Linux and Windows Binaries](https://github.com/microsoft/AirSim-Drone-Racing-Lab/releases)
|
||||
- [Python API Doc](https://microsoft.github.io/AirSim-Drone-Racing-Lab/api.html), [airsimdroneracinglab PyPI package](https://pypi.org/project/airsimdroneracinglab/)
|
||||
|
||||
<img src="https://github.com/madratman/airsim_neurips_gifs/blob/master/imgs/neurips_b99_3_drones.gif?raw=true" width="285"> <img src="https://github.com/madratman/airsim_neurips_gifs/blob/master/imgs/neurips_soccer_field_8_drones.gif?raw=true" width="285"> <img src="https://github.com/madratman/airsim_neurips_gifs/blob/master/imgs/neurips_zhangjiajie_4_drones.gif?raw=true" width="285">
|
||||
|
@ -44,7 +44,7 @@ Notes:
|
|||
```
|
||||
./AirSimDroneRacingLab.sh -windowed -NoVSync -BENCHMARK
|
||||
```
|
||||
You can also use the Unreal console commands `Stat FPS`, `Stat UnitGraph`, `r.VSync`, `t.maxFPS`. See [Issue #111](https://github.com/microsoft/AirSim-NeurIPS2019-Drone-Racing/issues/111) for more details.
|
||||
You can also use the Unreal console commands `Stat FPS`, `Stat UnitGraph`, `r.VSync`, `t.maxFPS`. See [Issue #111](https://github.com/microsoft/AirSim-Drone-Racing-Lab/issues/111) for more details.
|
||||
|
||||
- Windows
|
||||
- Navigate to the `AirSim/` directory, and double-click `run.bat` (or `AirSimDroneRacingLab.exe -windowed`)
|
||||
|
@ -89,7 +89,7 @@ Notes:
|
|||
`$ ./run_docker_image.sh DOCKER_IMAGE_NAME:TAG headless`
|
||||
|
||||
## AirSim Drone Racing Lab Features and API
|
||||
- The API is documented at [airsimneurips API doc](https://microsoft.github.io/AirSim-NeurIPS2019-Drone-Racing/api.html)
|
||||
- The API is documented at [airsimdroneracinglab API doc](https://microsoft.github.io/AirSim-Drone-Racing-Lab/api.html)
|
||||
|
||||
-
|
||||
|
||||
|
|
|
@ -0,0 +1,286 @@
|
|||
from argparse import ArgumentParser
|
||||
import airsimdroneracinglab as airsim
|
||||
import cv2
|
||||
import threading
|
||||
import time
|
||||
import utils
|
||||
import numpy as np
|
||||
import math
|
||||
|
||||
# drone_name should match the name in ~/Document/AirSim/settings.json
|
||||
class BaselineRacer(object):
|
||||
def __init__(self, drone_name = "drone_1", viz_traj=True, viz_traj_color_rgba=[1.0, 0.0, 0.0, 1.0], viz_image_cv2=True):
|
||||
self.drone_name = drone_name
|
||||
self.gate_poses_ground_truth = None
|
||||
self.viz_image_cv2 = viz_image_cv2
|
||||
self.viz_traj = viz_traj
|
||||
self.viz_traj_color_rgba = viz_traj_color_rgba
|
||||
|
||||
self.airsim_client = airsim.MultirotorClient()
|
||||
self.airsim_client.confirmConnection()
|
||||
# we need two airsim MultirotorClient objects because the comm lib we use (rpclib) is not thread safe
|
||||
# so we poll images in a thread using one airsim MultirotorClient object
|
||||
# and use another airsim MultirotorClient for querying state commands
|
||||
self.airsim_client_images = airsim.MultirotorClient()
|
||||
self.airsim_client_images.confirmConnection()
|
||||
self.airsim_client_odom = airsim.MultirotorClient()
|
||||
self.airsim_client_odom.confirmConnection()
|
||||
self.level_name = None
|
||||
|
||||
self.image_callback_thread = threading.Thread(target=self.repeat_timer_image_callback, args=(self.image_callback, 0.03))
|
||||
self.odometry_callback_thread = threading.Thread(target=self.repeat_timer_odometry_callback, args=(self.odometry_callback, 0.02))
|
||||
self.is_image_thread_active = False
|
||||
self.is_odometry_thread_active = False
|
||||
|
||||
self.MAX_NUMBER_OF_GETOBJECTPOSE_TRIALS = 10 # see https://github.com/microsoft/AirSim-Drone-Racing-Lab/issues/38
|
||||
|
||||
# loads desired level
|
||||
def load_level(self, level_name, sleep_sec = 2.0):
|
||||
self.level_name = level_name
|
||||
self.airsim_client.simLoadLevel(self.level_name)
|
||||
self.airsim_client.confirmConnection() # failsafe
|
||||
time.sleep(sleep_sec) # let the environment load completely
|
||||
|
||||
# Starts an instance of a race in your given level, if valid
|
||||
def start_race(self, tier=3):
|
||||
self.airsim_client.simStartRace(tier)
|
||||
|
||||
# Resets a current race: moves players to start positions, timer and penalties reset
|
||||
def reset_race(self):
|
||||
self.airsim_client.simResetRace()
|
||||
|
||||
# arms drone, enable APIs, set default traj tracker gains
|
||||
def initialize_drone(self):
|
||||
self.airsim_client.enableApiControl(vehicle_name=self.drone_name)
|
||||
self.airsim_client.arm(vehicle_name=self.drone_name)
|
||||
|
||||
# set default values for trajectory tracker gains
|
||||
traj_tracker_gains = airsim.TrajectoryTrackerGains(kp_cross_track = 5.0, kd_cross_track = 0.0,
|
||||
kp_vel_cross_track = 3.0, kd_vel_cross_track = 0.0,
|
||||
kp_along_track = 0.4, kd_along_track = 0.0,
|
||||
kp_vel_along_track = 0.04, kd_vel_along_track = 0.0,
|
||||
kp_z_track = 2.0, kd_z_track = 0.0,
|
||||
kp_vel_z = 0.4, kd_vel_z = 0.0,
|
||||
kp_yaw = 3.0, kd_yaw = 0.1)
|
||||
|
||||
self.airsim_client.setTrajectoryTrackerGains(traj_tracker_gains, vehicle_name=self.drone_name)
|
||||
time.sleep(0.2)
|
||||
|
||||
def takeoffAsync(self):
|
||||
self.airsim_client.takeoffAsync().join()
|
||||
|
||||
# like takeoffAsync(), but with moveOnSpline()
|
||||
def takeoff_with_moveOnSpline(self, takeoff_height = 1.0):
|
||||
start_position = self.airsim_client.simGetVehiclePose(vehicle_name=self.drone_name).position
|
||||
takeoff_waypoint = airsim.Vector3r(start_position.x_val, start_position.y_val, start_position.z_val-takeoff_height)
|
||||
|
||||
self.airsim_client.moveOnSplineAsync([takeoff_waypoint], vel_max=15.0, acc_max=5.0, add_position_constraint=True, add_velocity_constraint=False,
|
||||
add_acceleration_constraint=False, viz_traj=self.viz_traj, viz_traj_color_rgba=self.viz_traj_color_rgba, vehicle_name=self.drone_name).join()
|
||||
|
||||
# stores gate ground truth poses as a list of airsim.Pose() objects in self.gate_poses_ground_truth
|
||||
def get_ground_truth_gate_poses(self):
|
||||
gate_names_sorted_bad = sorted(self.airsim_client.simListSceneObjects("Gate.*"))
|
||||
# gate_names_sorted_bad is of the form `GateN_GARBAGE`. for example:
|
||||
# ['Gate0', 'Gate10_21', 'Gate11_23', 'Gate1_3', 'Gate2_5', 'Gate3_7', 'Gate4_9', 'Gate5_11', 'Gate6_13', 'Gate7_15', 'Gate8_17', 'Gate9_19']
|
||||
# we sort them by their ibdex of occurence along the race track(N), and ignore the unreal garbage number after the underscore(GARBAGE)
|
||||
gate_indices_bad = [int(gate_name.split('_')[0][4:]) for gate_name in gate_names_sorted_bad]
|
||||
gate_indices_correct = sorted(range(len(gate_indices_bad)), key=lambda k: gate_indices_bad[k])
|
||||
gate_names_sorted = [gate_names_sorted_bad[gate_idx] for gate_idx in gate_indices_correct]
|
||||
self.gate_poses_ground_truth = []
|
||||
for gate_name in gate_names_sorted:
|
||||
curr_pose = self.airsim_client.simGetObjectPose(gate_name)
|
||||
counter = 0
|
||||
while (math.isnan(curr_pose.position.x_val) or math.isnan(curr_pose.position.y_val) or math.isnan(curr_pose.position.z_val)) and (counter < self.MAX_NUMBER_OF_GETOBJECTPOSE_TRIALS):
|
||||
print(f"DEBUG: {gate_name} position is nan, retrying...")
|
||||
counter += 1
|
||||
curr_pose = self.airsim_client.simGetObjectPose(gate_name)
|
||||
assert not math.isnan(curr_pose.position.x_val), f"ERROR: {gate_name} curr_pose.position.x_val is still {curr_pose.position.x_val} after {counter} trials"
|
||||
assert not math.isnan(curr_pose.position.y_val), f"ERROR: {gate_name} curr_pose.position.y_val is still {curr_pose.position.y_val} after {counter} trials"
|
||||
assert not math.isnan(curr_pose.position.z_val), f"ERROR: {gate_name} curr_pose.position.z_val is still {curr_pose.position.z_val} after {counter} trials"
|
||||
self.gate_poses_ground_truth.append(curr_pose)
|
||||
|
||||
# this is utility function to get a velocity constraint which can be passed to moveOnSplineVelConstraints()
|
||||
# the "scale" parameter scales the gate facing vector accordingly, thereby dictating the speed of the velocity constraint
|
||||
def get_gate_facing_vector_from_quaternion(self, airsim_quat, scale = 1.0):
|
||||
import numpy as np
|
||||
# convert gate quaternion to rotation matrix.
|
||||
# ref: https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion; https://www.lfd.uci.edu/~gohlke/code/transformations.py.html
|
||||
q = np.array([airsim_quat.w_val, airsim_quat.x_val, airsim_quat.y_val, airsim_quat.z_val], dtype=np.float64)
|
||||
n = np.dot(q, q)
|
||||
if n < np.finfo(float).eps:
|
||||
return airsim.Vector3r(0.0, 1.0, 0.0)
|
||||
q *= np.sqrt(2.0 / n)
|
||||
q = np.outer(q, q)
|
||||
rotation_matrix = np.array([[1.0-q[2, 2]-q[3, 3], q[1, 2]-q[3, 0], q[1, 3]+q[2, 0]],
|
||||
[ q[1, 2]+q[3, 0], 1.0-q[1, 1]-q[3, 3], q[2, 3]-q[1, 0]],
|
||||
[ q[1, 3]-q[2, 0], q[2, 3]+q[1, 0], 1.0-q[1, 1]-q[2, 2]]])
|
||||
gate_facing_vector = rotation_matrix[:,1]
|
||||
return airsim.Vector3r(scale * gate_facing_vector[0], scale * gate_facing_vector[1], scale * gate_facing_vector[2])
|
||||
|
||||
def fly_through_all_gates_one_by_one_with_moveOnSpline(self):
|
||||
if self.level_name == "Building99_Hard":
|
||||
vel_max = 5.0
|
||||
acc_max = 2.0
|
||||
|
||||
if self.level_name in ["Soccer_Field_Medium", "Soccer_Field_Easy", "ZhangJiaJie_Medium"] :
|
||||
vel_max = 10.0
|
||||
acc_max = 5.0
|
||||
|
||||
return self.airsim_client.moveOnSplineAsync([gate_pose.position], vel_max=vel_max, acc_max=acc_max,
|
||||
add_position_constraint=True, add_velocity_constraint=False, add_acceleration_constraint=False, viz_traj=self.viz_traj, viz_traj_color_rgba=self.viz_traj_color_rgba, vehicle_name=self.drone_name)
|
||||
|
||||
def fly_through_all_gates_at_once_with_moveOnSpline(self):
|
||||
if self.level_name in ["Soccer_Field_Medium", "Soccer_Field_Easy", "ZhangJiaJie_Medium", "Qualifier_Tier_1", "Qualifier_Tier_2", "Qualifier_Tier_3", "Final_Tier_1", "Final_Tier_2", "Final_Tier_3"] :
|
||||
vel_max = 30.0
|
||||
acc_max = 15.0
|
||||
|
||||
if self.level_name == "Building99_Hard":
|
||||
vel_max = 4.0
|
||||
acc_max = 1.0
|
||||
|
||||
return self.airsim_client.moveOnSplineAsync([gate_pose.position for gate_pose in self.gate_poses_ground_truth], vel_max=vel_max, acc_max=acc_max,
|
||||
add_position_constraint=True, add_velocity_constraint=False, add_acceleration_constraint=False, viz_traj=self.viz_traj, viz_traj_color_rgba=self.viz_traj_color_rgba, vehicle_name=self.drone_name)
|
||||
|
||||
def fly_through_all_gates_one_by_one_with_moveOnSplineVelConstraints(self):
|
||||
add_velocity_constraint = True
|
||||
add_acceleration_constraint = False
|
||||
|
||||
if self.level_name in ["Soccer_Field_Medium", "Soccer_Field_Easy"] :
|
||||
vel_max = 15.0
|
||||
acc_max = 3.0
|
||||
speed_through_gate = 2.5
|
||||
|
||||
if self.level_name == "ZhangJiaJie_Medium":
|
||||
vel_max = 10.0
|
||||
acc_max = 3.0
|
||||
speed_through_gate = 1.0
|
||||
|
||||
if self.level_name == "Building99_Hard":
|
||||
vel_max = 2.0
|
||||
acc_max = 0.5
|
||||
speed_through_gate = 0.5
|
||||
add_velocity_constraint = False
|
||||
|
||||
# scale param scales the gate facing vector by desired speed.
|
||||
return self.airsim_client.moveOnSplineVelConstraintsAsync([gate_pose.position],
|
||||
[self.get_gate_facing_vector_from_quaternion(gate_pose.orientation, scale = speed_through_gate)],
|
||||
vel_max=vel_max, acc_max=acc_max,
|
||||
add_position_constraint=True, add_velocity_constraint=add_velocity_constraint, add_acceleration_constraint=add_acceleration_constraint,
|
||||
viz_traj=self.viz_traj, viz_traj_color_rgba=self.viz_traj_color_rgba, vehicle_name=self.drone_name)
|
||||
|
||||
def fly_through_all_gates_at_once_with_moveOnSplineVelConstraints(self):
|
||||
if self.level_name in ["Soccer_Field_Easy", "Soccer_Field_Medium", "ZhangJiaJie_Medium"]:
|
||||
vel_max = 15.0
|
||||
acc_max = 7.5
|
||||
speed_through_gate = 2.5
|
||||
|
||||
if self.level_name == "Building99_Hard":
|
||||
vel_max = 5.0
|
||||
acc_max = 2.0
|
||||
speed_through_gate = 1.0
|
||||
|
||||
return self.airsim_client.moveOnSplineVelConstraintsAsync([gate_pose.position for gate_pose in self.gate_poses_ground_truth],
|
||||
[self.get_gate_facing_vector_from_quaternion(gate_pose.orientation, scale = speed_through_gate) for gate_pose in self.gate_poses_ground_truth],
|
||||
vel_max=vel_max, acc_max=acc_max,
|
||||
add_position_constraint=True, add_velocity_constraint=True, add_acceleration_constraint=False,
|
||||
viz_traj=self.viz_traj, viz_traj_color_rgba=self.viz_traj_color_rgba, vehicle_name=self.drone_name)
|
||||
|
||||
def image_callback(self):
|
||||
# get uncompressed fpv cam image
|
||||
request = [airsim.ImageRequest("fpv_cam", airsim.ImageType.Scene, False, False)]
|
||||
response = self.airsim_client_images.simGetImages(request)
|
||||
img_rgb_1d = np.fromstring(response[0].image_data_uint8, dtype=np.uint8)
|
||||
img_rgb = img_rgb_1d.reshape(response[0].height, response[0].width, 3)
|
||||
if self.viz_image_cv2:
|
||||
cv2.imshow("img_rgb", img_rgb)
|
||||
cv2.waitKey(1)
|
||||
|
||||
def odometry_callback(self):
|
||||
# get uncompressed fpv cam image
|
||||
drone_state = self.airsim_client_odom.getMultirotorState()
|
||||
# in world frame:
|
||||
position = drone_state.kinematics_estimated.position
|
||||
orientation = drone_state.kinematics_estimated.orientation
|
||||
linear_velocity = drone_state.kinematics_estimated.linear_velocity
|
||||
angular_velocity = drone_state.kinematics_estimated.angular_velocity
|
||||
|
||||
# call task() method every "period" seconds.
|
||||
def repeat_timer_image_callback(self, task, period):
|
||||
while self.is_image_thread_active:
|
||||
task()
|
||||
time.sleep(period)
|
||||
|
||||
def repeat_timer_odometry_callback(self, task, period):
|
||||
while self.is_odometry_thread_active:
|
||||
task()
|
||||
time.sleep(period)
|
||||
|
||||
def start_image_callback_thread(self):
|
||||
if not self.is_image_thread_active:
|
||||
self.is_image_thread_active = True
|
||||
self.image_callback_thread.start()
|
||||
print("Started image callback thread")
|
||||
|
||||
def stop_image_callback_thread(self):
|
||||
if self.is_image_thread_active:
|
||||
self.is_image_thread_active = False
|
||||
self.image_callback_thread.join()
|
||||
print("Stopped image callback thread.")
|
||||
|
||||
def start_odometry_callback_thread(self):
|
||||
if not self.is_odometry_thread_active:
|
||||
self.is_odometry_thread_active = True
|
||||
self.odometry_callback_thread.start()
|
||||
print("Started odometry callback thread")
|
||||
|
||||
def stop_odometry_callback_thread(self):
|
||||
if self.is_odometry_thread_active:
|
||||
self.is_odometry_thread_active = False
|
||||
self.odometry_callback_thread.join()
|
||||
print("Stopped odometry callback thread.")
|
||||
|
||||
def main(args):
|
||||
# ensure you have generated the neurips planning settings file by running python generate_settings_file.py
|
||||
baseline_racer = BaselineRacer(drone_name="drone_1", viz_traj=args.viz_traj, viz_traj_color_rgba=[1.0, 1.0, 0.0, 1.0], viz_image_cv2=args.viz_image_cv2)
|
||||
baseline_racer.load_level(args.level_name)
|
||||
if args.level_name == "Qualifier_Tier_1":
|
||||
args.race_tier = 1
|
||||
if args.level_name == "Qualifier_Tier_2":
|
||||
args.race_tier = 2
|
||||
if args.level_name == "Qualifier_Tier_3":
|
||||
args.race_tier = 3
|
||||
baseline_racer.start_race(args.race_tier)
|
||||
baseline_racer.initialize_drone()
|
||||
baseline_racer.takeoff_with_moveOnSpline()
|
||||
baseline_racer.get_ground_truth_gate_poses()
|
||||
baseline_racer.start_image_callback_thread()
|
||||
baseline_racer.start_odometry_callback_thread()
|
||||
|
||||
if args.planning_baseline_type == "all_gates_at_once" :
|
||||
if args.planning_and_control_api == "moveOnSpline":
|
||||
baseline_racer.fly_through_all_gates_at_once_with_moveOnSpline().join()
|
||||
if args.planning_and_control_api == "moveOnSplineVelConstraints":
|
||||
baseline_racer.fly_through_all_gates_at_once_with_moveOnSplineVelConstraints().join()
|
||||
|
||||
if args.planning_baseline_type == "all_gates_one_by_one":
|
||||
if args.planning_and_control_api == "moveOnSpline":
|
||||
baseline_racer.fly_through_all_gates_one_by_one_with_moveOnSpline().join()
|
||||
if args.planning_and_control_api == "moveOnSplineVelConstraints":
|
||||
baseline_racer.fly_through_all_gates_one_by_one_with_moveOnSplineVelConstraints().join()
|
||||
|
||||
# Comment out the following if you observe the python script exiting prematurely, and resetting the race
|
||||
baseline_racer.stop_image_callback_thread()
|
||||
baseline_racer.stop_odometry_callback_thread()
|
||||
baseline_racer.reset_race()
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('--level_name', type=str, choices=["Soccer_Field_Easy", "Soccer_Field_Medium", "ZhangJiaJie_Medium", "Building99_Hard",
|
||||
"Qualifier_Tier_1", "Qualifier_Tier_2", "Qualifier_Tier_3", "Final_Tier_1", "Final_Tier_2", "Final_Tier_3"], default="ZhangJiaJie_Medium")
|
||||
parser.add_argument('--planning_baseline_type', type=str, choices=["all_gates_at_once","all_gates_one_by_one"], default="all_gates_at_once")
|
||||
parser.add_argument('--planning_and_control_api', type=str, choices=["moveOnSpline", "moveOnSplineVelConstraints"], default="moveOnSpline")
|
||||
parser.add_argument('--enable_viz_traj', dest='viz_traj', action='store_true', default=False)
|
||||
parser.add_argument('--enable_viz_image_cv2', dest='viz_image_cv2', action='store_true', default=False)
|
||||
parser.add_argument('--race_tier', type=int, choices=[1,2,3], default=1)
|
||||
args = parser.parse_args()
|
||||
main(args)
|
|
@ -0,0 +1,112 @@
|
|||
from argparse import ArgumentParser
|
||||
import airsimdroneracinglab as airsim
|
||||
import time
|
||||
import utils
|
||||
import threading
|
||||
import numpy as np
|
||||
import cv2
|
||||
from baseline_racer import BaselineRacer
|
||||
|
||||
class BaselineRacerImageBenchmarker(BaselineRacer):
|
||||
def __init__(self,
|
||||
img_benchmark_type = 'simGetImage',
|
||||
drone_name = "drone_1",
|
||||
viz_traj = False,
|
||||
viz_traj_color_rgba=[1.0, 1.0, 0.0, 1.0],
|
||||
viz_image_cv2 = False):
|
||||
super().__init__(drone_name=drone_name, viz_traj=viz_traj, viz_image_cv2=viz_image_cv2)
|
||||
|
||||
self.image_benchmark_num_images = 0
|
||||
self.image_benchmark_total_time = 0.0
|
||||
self.image_callback_thread = None
|
||||
if img_benchmark_type == "simGetImage":
|
||||
self.image_callback_thread = threading.Thread(target=self.repeat_timer_img, args=(self.image_callback_benchmark_simGetImage, 0.05))
|
||||
if img_benchmark_type == "simGetImages":
|
||||
self.image_callback_thread = threading.Thread(target=self.repeat_timer_img, args=(self.image_callback_benchmark_simGetImages, 0.05))
|
||||
self.is_image_thread_active = False
|
||||
|
||||
def start_img_benchmark_thread(self):
|
||||
if not self.is_image_thread_active:
|
||||
self.is_image_thread_active = True
|
||||
self.image_callback_thread.start()
|
||||
print("Started img image_callback thread")
|
||||
|
||||
def stop_img_benchmark_thread(self):
|
||||
if self.is_image_thread_active:
|
||||
self.is_image_thread_active = False
|
||||
self.image_callback_thread.join()
|
||||
print("Stopped image callback thread.")
|
||||
|
||||
def repeat_timer_img(self, task, period):
|
||||
while self.is_image_thread_active:
|
||||
task()
|
||||
time.sleep(period)
|
||||
|
||||
def print_benchmark_results(self):
|
||||
avg_fps = 1.0 / ((self.image_benchmark_total_time) / float(self.image_benchmark_num_images))
|
||||
print(self.level_name + ": {} avg_fps for {} num of images".format(avg_fps, self.image_benchmark_num_images))
|
||||
|
||||
def image_callback_benchmark_simGetImage(self):
|
||||
self.image_benchmark_num_images += 1
|
||||
iter_start_time = time.time()
|
||||
response = self.airsim_client_images.simGetImage("fpv_cam", airsim.ImageType.Scene)
|
||||
img_rgb = cv2.imdecode(airsim.string_to_uint8_array(response), cv2.IMREAD_UNCHANGED)
|
||||
self.image_benchmark_total_time += time.time() - iter_start_time
|
||||
avg_fps = 1.0 / ((self.image_benchmark_total_time) / float(self.image_benchmark_num_images))
|
||||
print(self.level_name + ": {} avg_fps for {} num of images".format(avg_fps, self.image_benchmark_num_images))
|
||||
# uncomment following lines to viz image
|
||||
# if self.viz_image_cv2:
|
||||
# cv2.imshow("img_rgb", img_rgb_1d_new)
|
||||
# cv2.waitKey(1)
|
||||
|
||||
def image_callback_benchmark_simGetImages(self):
|
||||
self.image_benchmark_num_images += 1
|
||||
iter_start_time = time.time()
|
||||
request = [airsim.ImageRequest("fpv_cam", airsim.ImageType.Scene, False, False)]
|
||||
response = self.airsim_client_images.simGetImages(request)
|
||||
img_rgb_1d = np.fromstring(response[0].image_data_uint8, dtype=np.uint8)
|
||||
img_rgb = img_rgb_1d.reshape(response[0].height, response[0].width, 3)
|
||||
self.image_benchmark_total_time += time.time() - iter_start_time
|
||||
avg_fps = 1.0 / ((self.image_benchmark_total_time) / float(self.image_benchmark_num_images))
|
||||
print(self.level_name + ": {} avg_fps for {} num of images".format(avg_fps, self.image_benchmark_num_images))
|
||||
# uncomment following lines to viz image
|
||||
# if self.viz_image_cv2:
|
||||
# cv2.imshow("img_rgb", img_rgb_1d_new)
|
||||
# cv2.waitKey(1)
|
||||
|
||||
def main(args):
|
||||
# ensure you have generated the neurips planning settings file by running python generate_settings_file.py
|
||||
baseline_racer = BaselineRacerImageBenchmarker(img_benchmark_type=args.img_benchmark_type, \
|
||||
drone_name="drone_1", \
|
||||
viz_traj=args.viz_traj, \
|
||||
viz_traj_color_rgba=[1.0, 1.0, 0.0, 1.0], \
|
||||
viz_image_cv2=args.viz_image_cv2)
|
||||
|
||||
baseline_racer.load_level(args.level_name)
|
||||
if args.level_name == "Qualifier_Tier_1":
|
||||
args.race_tier = 1
|
||||
if args.level_name == "Qualifier_Tier_2":
|
||||
args.race_tier = 2
|
||||
if args.level_name == "Qualifier_Tier_3":
|
||||
args.race_tier = 3
|
||||
|
||||
baseline_racer.start_race(args.race_tier)
|
||||
baseline_racer.initialize_drone()
|
||||
baseline_racer.takeoff_with_moveOnSpline()
|
||||
baseline_racer.get_ground_truth_gate_poses()
|
||||
baseline_racer.start_img_benchmark_thread()
|
||||
baseline_racer.fly_through_all_gates_at_once_with_moveOnSpline().join()
|
||||
baseline_racer.stop_img_benchmark_thread()
|
||||
baseline_racer.print_benchmark_results()
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = ArgumentParser()
|
||||
parser.add_argument('--level_name', type=str, choices=["Soccer_Field_Easy", "Soccer_Field_Medium", "ZhangJiaJie_Medium", "Building99_Hard",
|
||||
"Qualifier_Tier_1", "Qualifier_Tier_2", "Qualifier_Tier_3"], default="ZhangJiaJie_Medium")
|
||||
parser.add_argument('--enable_viz_traj', dest='viz_traj', action='store_true', default=False)
|
||||
parser.add_argument('--img_benchmark_type', type=str, choices=["simGetImage", "simGetImages"], default="simGetImages")
|
||||
parser.add_argument('--enable_viz_image_cv2', dest='viz_image_cv2', action='store_true', default=False)
|
||||
parser.add_argument('--race_tier', type=int, choices=[1,2,3], default=1)
|
||||
|
||||
args = parser.parse_args()
|
||||
main(args)
|
|
@ -0,0 +1,3 @@
|
|||
from utils import AirSimSettingsCreator
|
||||
|
||||
AirSimSettingsCreator().write_airsim_neurips_baseline_settings_file()
|
|
@ -0,0 +1,95 @@
|
|||
import airsimdroneracinglab as airsim
|
||||
import json
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
def to_airsim_vector(np_arr):
|
||||
assert np.size(np_arr) == 3
|
||||
return airsim.Vector3r(np.float(np_arr[0]), np.float(np_arr[1]), np.float(np_arr[2]))
|
||||
|
||||
def to_airsim_vectors(np_arr):
|
||||
return [to_airsim_vector(np_arr[i, :]) for i in range(np.size(np_arr, 0))]
|
||||
|
||||
# these clases are only meant to be settings generator.
|
||||
# for everything else, there's airsimdroneracinglab.Pose()
|
||||
class Position():
|
||||
def __init__(self, x = 0.0, y = 0.0, z = 0.0):
|
||||
self.x = x
|
||||
self.y = y
|
||||
self.z = z
|
||||
|
||||
class Rotation():
|
||||
def __init__(self, yaw = 0.0, pitch = 0.0, roll = 0.0):
|
||||
self.yaw = yaw
|
||||
self.pitch = pitch
|
||||
self.roll = roll
|
||||
|
||||
class Pose():
|
||||
def __init__(self, position, rotation):
|
||||
self.position = position
|
||||
self.rotation = rotation
|
||||
|
||||
class AirSimSettingsCreator(object):
|
||||
def __init__(self, sim_mode = "Multirotor"):
|
||||
self.sim_mode = sim_mode
|
||||
self.settings_dict = {}
|
||||
|
||||
def add_minimal(self):
|
||||
self.settings_dict["SeeDocsAt"] = "https://github.com/Microsoft/AirSim/blob/master/docs/settings.md"
|
||||
self.settings_dict["SettingsVersion"] = 1.2
|
||||
self.settings_dict["SimMode"] = self.sim_mode
|
||||
self.settings_dict["ClockSpeed"] = 1
|
||||
|
||||
# can be used for camera pose or vehicle pose by passing in the right settings_key
|
||||
def set_pose(self, setting_key, pose):
|
||||
setting_key["X"] = pose.position.x
|
||||
setting_key["Y"] = pose.position.y
|
||||
setting_key["Z"] = pose.position.z
|
||||
setting_key["Pitch"] = pose.rotation.pitch
|
||||
setting_key["Roll"] = pose.rotation.roll
|
||||
setting_key["Yaw"] = pose.rotation.yaw
|
||||
|
||||
def add_multirotor(self, vehicle_name, pose):
|
||||
assert(self.settings_dict["SimMode"] == "Multirotor")
|
||||
if "Vehicles" not in self.settings_dict.keys():
|
||||
self.settings_dict['Vehicles'] = {}
|
||||
|
||||
self.settings_dict['Vehicles'][vehicle_name] = {}
|
||||
self.settings_dict['Vehicles'][vehicle_name]["VehicleType"] = "SimpleFlight"
|
||||
self.set_pose(self.settings_dict['Vehicles'][vehicle_name], pose)
|
||||
|
||||
def add_camera(self, vehicle_name, camera_name, relative_pose, image_type, image_width, image_height, fov_horizontal_degrees):
|
||||
# fetch vehicle setting dict
|
||||
vehicle_setting = self.settings_dict['Vehicles'][vehicle_name]
|
||||
# initialize vehicle's camera setting dict to empty
|
||||
vehicle_setting['Cameras'] = {}
|
||||
vehicle_setting['Cameras'][camera_name] = {}
|
||||
camera_setting = vehicle_setting['Cameras'][camera_name]
|
||||
self.set_pose(camera_setting, relative_pose)
|
||||
capture_setting = {}
|
||||
capture_setting['Width'] = image_width
|
||||
capture_setting['Height'] = image_height
|
||||
capture_setting['ImageType'] = image_type
|
||||
capture_setting['FOV_Degrees'] = fov_horizontal_degrees
|
||||
camera_setting['CaptureSettings'] = [capture_setting]
|
||||
|
||||
# default linux: /home/$USER/Documents/AirSim/settings.json
|
||||
# default windows: C:\\Users\\%USERNAME%\\Documents\\AirSim\\settings.json
|
||||
def write_airsim_settings_file(self, base_filename="settings.json"):
|
||||
user_dir = os.path.expanduser("~")
|
||||
airsim_settings_dir = os.path.join(user_dir, "Documents", "AirSim")
|
||||
if not os.path.exists(airsim_settings_dir):
|
||||
os.makedirs(airsim_settings_dir)
|
||||
airsim_settings_abs_file_path = os.path.join(airsim_settings_dir, base_filename)
|
||||
with open(airsim_settings_abs_file_path, "w") as f:
|
||||
json.dump(self.settings_dict, f, indent=2, sort_keys=True)
|
||||
|
||||
# usage: AirSimSettingsCreator().write_airsim_neurips_baseline_settings_file()
|
||||
def write_airsim_neurips_baseline_settings_file(self):
|
||||
instance = self.__class__()
|
||||
instance.add_minimal()
|
||||
instance.add_multirotor(vehicle_name = "drone_1", pose = Pose(Position(), Rotation()))
|
||||
instance.add_camera(vehicle_name = "drone_1", camera_name = 'fpv_cam', relative_pose=Pose(Position(0.25, 0.0, 0.0), Rotation()),
|
||||
image_type = 0, image_width = 320, image_height = 240, fov_horizontal_degrees = 90)
|
||||
instance.add_multirotor(vehicle_name = "drone_2", pose = Pose(Position(), Rotation()))
|
||||
instance.write_airsim_settings_file()
|
|
@ -0,0 +1,39 @@
|
|||
ARG BASE_IMAGE=nvidia/cudagl:10.0-devel-ubuntu18.04
|
||||
FROM $BASE_IMAGE
|
||||
|
||||
RUN apt-get update
|
||||
RUN apt-get install \
|
||||
git \
|
||||
libglu1-mesa-dev \
|
||||
pulseaudio \
|
||||
python3 \
|
||||
python3-pip \
|
||||
sudo \
|
||||
sudo \
|
||||
wget \
|
||||
x11-xserver-utils \
|
||||
xdg-user-dirs \
|
||||
unzip \
|
||||
-y --no-install-recommends
|
||||
|
||||
RUN pip3 install setuptools wheel
|
||||
RUN pip3 install airsimdroneracinglab
|
||||
|
||||
RUN adduser --force-badname --disabled-password --gecos '' --shell /bin/bash airsim_user && \
|
||||
echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers && \
|
||||
adduser airsim_user sudo && \
|
||||
adduser airsim_user audio && \
|
||||
adduser airsim_user video
|
||||
|
||||
USER airsim_user
|
||||
ENV USER airsim_user
|
||||
WORKDIR /home/airsim_user
|
||||
RUN sudo chown -R airsim_user /home/airsim_user
|
||||
|
||||
RUN git clone https://github.com/microsoft/AirSim-Drone-Racing-Lab && \
|
||||
cd AirSim-Drone-Racing-Lab && \
|
||||
bash download_training_binaries.sh && \
|
||||
bash download_qualification_binaries.sh && \
|
||||
mv AirSim_Training/ ../ && \
|
||||
mv AirSim_Qualification/ ../ && \
|
||||
cd ../
|
|
@ -0,0 +1,36 @@
|
|||
from argparse import ArgumentParser
|
||||
import subprocess
|
||||
|
||||
class DockerImageBuilder():
|
||||
def __init__(self, args):
|
||||
self.args = args
|
||||
|
||||
def build_docker_image(self):
|
||||
# if a base image is not specified, we use the Ubuntu 18, CUDA 10 image from NVIDIA
|
||||
docker_build_command = ['docker', 'build', '--network=host', \
|
||||
'-t', self.args.target_image, \
|
||||
'-f', self.args.dockerfile, \
|
||||
'--build-arg', 'BASE_IMAGE=' + self.args.base_image, \
|
||||
'.']
|
||||
|
||||
print(" ".join(docker_build_command))
|
||||
subprocess.call(docker_build_command)
|
||||
|
||||
def main(args):
|
||||
docker_image_builder = DockerImageBuilder(args)
|
||||
docker_image_builder.build_docker_image()
|
||||
|
||||
if __name__=="__main__":
|
||||
parser = ArgumentParser(description='AirSim Neurips-Game-of-Drones docker image builder')
|
||||
parser.add_argument('--dockerfile', type=str, default='Dockerfile', help='path to docker file')
|
||||
parser.add_argument('--base_image', type=str, default="nvidia/cudagl:10.0-devel-ubuntu18.04", help='base image name AND tag, on top of which the target image is built')
|
||||
parser.add_argument('--target_image', type=str, help='desired name of target image name AND tag')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# if a target image name is not specified, let's call it airsim_neurips:SOURCE_IMAGE_TAG
|
||||
if not args.target_image:
|
||||
target_image_tag = args.base_image.split(":")[1]
|
||||
args.target_image = 'airsim_neurips' + ':' + target_image_tag
|
||||
|
||||
main(args)
|
|
@ -0,0 +1,79 @@
|
|||
# !bin/bash
|
||||
|
||||
# **Usage**
|
||||
# - for running default image, training binaries, in windowed mode:
|
||||
# `$ ./run_docker_image.sh "" training`
|
||||
# - for running default image, qualification binaries, in windowed mode:
|
||||
# `$ ./run_docker_image.sh "" qualification`
|
||||
# - for running default image, training binaries, in headless mode:
|
||||
# `$ ./run_docker_image.sh "" training headless`
|
||||
# - for running default image, qualification binaries, in headless mode:
|
||||
# `$ ./run_docker_image.sh "" qualification headless`
|
||||
# - for running a custom image in windowed mode, pass in you image name and tag:
|
||||
# `# $ ./run_docker_image.sh DOCKER_IMAGE_NAME:TAG`
|
||||
# - for running a custom image in headless mode, pass in you image name and tag, followed by "headless":
|
||||
# # $ ./run_docker_image.sh DOCKER_IMAGE_NAME:TAG headless
|
||||
|
||||
# This script takes three optional arguments
|
||||
|
||||
# 1st argument is the name (and tag) of the dockerfile to run
|
||||
# by default, it is set to "airsim_neurips:10.0-devel-ubuntu18.04"
|
||||
# else user can specify a docker image as follows:
|
||||
# $ ./run_docker_image.sh DOCKER_IMAGE_NAME:TAG
|
||||
DOCKER_IMAGE_NAME=${1:-airsim_neurips:10.0-devel-ubuntu18.04}
|
||||
|
||||
# 2nd argument: can be "training" or "qualification"
|
||||
TRAINING_OR_QUALIFICATION=${2:-training}
|
||||
|
||||
# 3rd argument: if user passes "headless", binary runs in headless mode
|
||||
IS_HEADLESS=${3:-notheadless}
|
||||
|
||||
# this block is for running X apps in docker
|
||||
XAUTH=/tmp/.docker.xauth
|
||||
if [[ ! -f $XAUTH ]]
|
||||
then
|
||||
xauth_list=$(xauth nlist :0 | sed -e 's/^..../ffff/')
|
||||
if [ ! -z "$xauth_list" ]
|
||||
then
|
||||
echo $xauth_list | xauth -f $XAUTH nmerge -
|
||||
else
|
||||
touch $XAUTH
|
||||
fi
|
||||
chmod a+r $XAUTH
|
||||
fi
|
||||
|
||||
# per use the following commented out code for different options
|
||||
if [[ $2 = "training" ]]; then
|
||||
UNREAL_BINARY_COMMAND="bash /home/airsim_user/AirSim_Training/AirSimExe.sh -windowed -opengl"
|
||||
elif [[ $2 = "qualification" ]]; then
|
||||
UNREAL_BINARY_COMMAND="bash /home/airsim_user/AirSim_Qualification/AirSimExe.sh -windowed -opengl"
|
||||
fi
|
||||
|
||||
# eleminate terminal output and run airsim process in the background
|
||||
# UNREAL_BINARY_COMMAND="bash /home/airsim_user/AirSim_Training/AirSimExe.sh -windowed -opengl &>/dev/null &"
|
||||
|
||||
# set window resolution
|
||||
# UNREAL_BINARY_COMMAND="/home/airsim_user/AirSim_Training -windowed -ResX=1080 -ResY=720"
|
||||
|
||||
# now, let's check if we need to run in headless mode or not
|
||||
# set SDL_VIDEODRIVER_VALUE to '' if windowed mode, 'offscreen' if headless mode
|
||||
SDL_VIDEODRIVER_VALUE='';
|
||||
if [[ $3 = "headless" ]]; then
|
||||
SDL_VIDEODRIVER_VALUE='offscreen';
|
||||
fi
|
||||
|
||||
# now, set the environment varible SDL_VIDEODRIVER to SDL_VIDEODRIVER_VALUE
|
||||
# and tell the docker container to execute UNREAL_BINARY_COMMAND
|
||||
nvidia-docker run -it \
|
||||
-e SDL_VIDEODRIVER=$SDL_VIDEODRIVER_VALUE \
|
||||
-e SDL_HINT_CUDA_DEVICE='0' \
|
||||
--net=host \
|
||||
--env="DISPLAY=$DISPLAY" \
|
||||
--env="QT_X11_NO_MITSHM=1" \
|
||||
--volume="/tmp/.X11-unix:/tmp/.X11-unix:rw" \
|
||||
-env="XAUTHORITY=$XAUTH" \
|
||||
--volume="$XAUTH:$XAUTH" \
|
||||
--runtime=nvidia \
|
||||
--rm \
|
||||
$DOCKER_IMAGE_NAME \
|
||||
/bin/bash -c "$UNREAL_BINARY_COMMAND"
|
|
@ -0,0 +1,17 @@
|
|||
#!/bin/bash
|
||||
wget -c https://github.com/microsoft/AirSim-Drone-Racing-Lab/releases/download/v1.0-linux/AirSim-Drone-Racing-Lab.zip;
|
||||
mkdir -p /home/$USER/Documents/AirSim;
|
||||
unzip AirSim-Drone-Racing-Lab.zip;
|
||||
|
||||
cd AirSim-Drone-Racing-Lab/Content/Paks;
|
||||
wget -c https://github.com/microsoft/AirSim-Drone-Racing-Lab/releases/download/v1.0-linux/Building99.pak;
|
||||
wget -c https://github.com/microsoft/AirSim-Drone-Racing-Lab/releases/download/v1.0-linux/Soccer_Field.pak;
|
||||
wget -c https://github.com/microsoft/AirSim-Drone-Racing-Lab/releases/download/v1.0-linux/ZhangJiaJie.pak;
|
||||
wget -c https://github.com/microsoft/AirSim-Drone-Racing-Lab/releases/download/v1.0-linux/Qual_Tier_1_and_Tier_3.pak;
|
||||
wget -c https://github.com/microsoft/AirSim-Drone-Racing-Lab/releases/download/v1.0-linux/Qual_Tier_2.pak;
|
||||
wget -c https://github.com/microsoft/AirSim-Drone-Racing-Lab/releases/download/v1.0-linux/Final_Tier_1_and_Tier_2.pak;
|
||||
wget -c https://github.com/microsoft/AirSim-Drone-Racing-Lab/releases/download/v1.0-linux/Final_Tier_3.pak;
|
||||
cd ../../../;
|
||||
|
||||
wget --directory-prefix=/home/$USER/Documents/AirSim -c https://github.com/microsoft/AirSim-Drone-Racing-Lab/releases/download/v1.0-linux/settings.json;
|
||||
rm AirSim-Drone-Racing-Lab.zip;
|
|
@ -0,0 +1,6 @@
|
|||
airsimdroneracinglab==1.2.0
|
||||
msgpack-python==0.5.6
|
||||
msgpack-rpc-python==0.4.1
|
||||
numpy==1.19.0
|
||||
opencv-python==4.2.0.34
|
||||
tornado==4.5.3
|
|
@ -0,0 +1,61 @@
|
|||
import glob
|
||||
import os
|
||||
import time
|
||||
|
||||
disqualified_racers = set()
|
||||
finished_racers = set()
|
||||
|
||||
def open_file():
|
||||
list_of_files = glob.glob('*.log')
|
||||
latest_file = max(list_of_files, key=os.path.getctime)
|
||||
print("Opened file: " + latest_file)
|
||||
return open(latest_file, "r+")
|
||||
|
||||
def follow(filename):
|
||||
filename.seek(0,2)
|
||||
while True:
|
||||
line = filename.readline()
|
||||
if not line:
|
||||
time.sleep(0.1)
|
||||
continue
|
||||
yield line
|
||||
|
||||
def process(line):
|
||||
tokens = line.split()
|
||||
if len(tokens) != 3 and len(tokens) != 5:
|
||||
print("ERROR Bad line: " + line)
|
||||
print("Tokens: " + str(tokens))
|
||||
return
|
||||
|
||||
if tokens[3] == "disqualified" and tokens[4] == '1' and tokens[0] not in disqualified_racers:
|
||||
disqualified_racers.add(tokens[0])
|
||||
handle_disqualified_racer(tokens[0])
|
||||
return
|
||||
|
||||
if tokens[3] == "finished" and tokens[4] == '1' and tokens[0] not in finished_racers:
|
||||
finished_racers.add(tokens[0])
|
||||
handle_finished_racer(tokens[0])
|
||||
return
|
||||
|
||||
if tokens[3] == "gates_passed":
|
||||
handle_gate_passed(tokens[0], tokens[4])
|
||||
|
||||
def handle_disqualified_racer(racer_name):
|
||||
print(racer_name + " has been disqualified!")
|
||||
#Start a new race.
|
||||
|
||||
def handle_finished_racer(racer_name):
|
||||
print(racer_name + " has finished!")
|
||||
#Start a new race.
|
||||
|
||||
def handle_gate_passed(racer_name, gate_idx_passed):
|
||||
# log file gate indices are 1-indexed, not 0-indexed
|
||||
print("{} passed gate idx {}".format(racer_name, gate_idx_passed - 1))
|
||||
|
||||
def main():
|
||||
f = open_file()
|
||||
for line in follow(f):
|
||||
process(line)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -0,0 +1,85 @@
|
|||
import airsimdroneracinglab
|
||||
import threading
|
||||
import time
|
||||
|
||||
class ReproduceResetRaceCondition():
|
||||
def __init__(self, drone_name = "drone_1"):
|
||||
self.airsim_client = airsimdroneracinglab.MultirotorClient()
|
||||
self.airsim_client_2 = airsimdroneracinglab.MultirotorClient()
|
||||
self.airsim_client_3 = airsimdroneracinglab.MultirotorClient()
|
||||
self.drone_name = drone_name
|
||||
self.is_thread_active = False
|
||||
self.thread_reset = threading.Thread(target=self.repeat_timer, args=(self.reset, 0.05))
|
||||
self.thread_reset_race = threading.Thread(target=self.repeat_timer, args=(self.reset_race, 0.03))
|
||||
self.thread_reset_and_reset_race = threading.Thread(target=self.repeat_timer, args=(self.reset_and_reset_race, 0.09))
|
||||
self.is_thread_active = False
|
||||
|
||||
def repeat_timer(self, callback, period):
|
||||
while self.is_thread_active:
|
||||
callback()
|
||||
time.sleep(period)
|
||||
|
||||
def load_level(self, level_name, sleep_sec = 2.0):
|
||||
self.level_name = level_name
|
||||
self.airsim_client.simLoadLevel(self.level_name)
|
||||
self.airsim_client.confirmConnection() # failsafe
|
||||
time.sleep(sleep_sec) # let the environment load completely
|
||||
|
||||
def reset(self):
|
||||
print(time.time(), 'called reset')
|
||||
self.airsim_client.reset()
|
||||
|
||||
def reset_race(self):
|
||||
print(time.time(), 'called simResetRace')
|
||||
self.airsim_client_2.simResetRace()
|
||||
|
||||
def reset_and_reset_race(self):
|
||||
print(time.time(), 'called reset, followed by simResetRace')
|
||||
self.airsim_client_3.reset()
|
||||
self.airsim_client_3.simResetRace()
|
||||
|
||||
def start_race(self, tier):
|
||||
print(time.time(), 'called start race')
|
||||
self.airsim_client.simStartRace(tier)
|
||||
|
||||
def initialize_drone(self):
|
||||
self.airsim_client.enableApiControl(vehicle_name=self.drone_name)
|
||||
self.airsim_client.arm(vehicle_name=self.drone_name)
|
||||
|
||||
# set default values for trajectory tracker gains
|
||||
traj_tracker_gains = airsimdroneracinglab.TrajectoryTrackerGains(kp_cross_track = 5.0, kd_cross_track = 0.0,
|
||||
kp_vel_cross_track = 3.0, kd_vel_cross_track = 0.0,
|
||||
kp_along_track = 0.4, kd_along_track = 0.0,
|
||||
kp_vel_along_track = 0.04, kd_vel_along_track = 0.0,
|
||||
kp_z_track = 2.0, kd_z_track = 0.0,
|
||||
kp_vel_z = 0.4, kd_vel_z = 0.0,
|
||||
kp_yaw = 3.0, kd_yaw = 0.1)
|
||||
|
||||
self.airsim_client.setTrajectoryTrackerGains(traj_tracker_gains, vehicle_name=self.drone_name)
|
||||
time.sleep(0.2)
|
||||
|
||||
def start_threads(self):
|
||||
if not self.is_thread_active:
|
||||
self.is_thread_active = True
|
||||
self.thread_reset.start()
|
||||
self.thread_reset_race.start()
|
||||
self.thread_reset_and_reset_race.start()
|
||||
print("Started threads")
|
||||
|
||||
def stop_threads(self):
|
||||
if self.is_thread_active:
|
||||
self.is_thread_active = False
|
||||
self.thread_reset.join()
|
||||
self.thread_reset_race.join()
|
||||
self.thread_reset_and_reset_race.join()
|
||||
print("Stopped threads.")
|
||||
|
||||
if __name__ == "__main__":
|
||||
reproducer = ReproduceResetRaceCondition('drone_1')
|
||||
reproducer.load_level('Qualifier_Tier_1')
|
||||
reproducer.initialize_drone()
|
||||
reproducer.start_race(3)
|
||||
time.sleep(5)
|
||||
reproducer.start_threads()
|
||||
time.sleep(3600)
|
||||
reproducer.stop_threads()
|
Загрузка…
Ссылка в новой задаче