Skip to content

Commit b1e436d

Browse files
LucasJSchaurebidart
authored andcommitted
Integrate JSBSim flight dynamics into Project AirSim
This commit introduces JSBSim as a supported flight dynamics engine in Project AirSim. It includes all necessary CMake integration, external dependency setup, and a sample fixed-wing aircraft configuration using JSBSim. Additionally, it adds example Python scripts (`hello_fixed_wing.py`, `hello_helicopter.py`) that demonstrate how to load a JSBSim-based aircraft and interact with it using the simulation API. Note: ✔️ Fully functional on Windows. ⚠️ Not yet supported on Linux.
1 parent 00c7afb commit b1e436d

File tree

65 files changed

+4273
-646
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

65 files changed

+4273
-646
lines changed

.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,3 +376,24 @@ env/
376376
# Simulink cache
377377
**/slprj
378378
*.slxc
379+
380+
# Jsbsim repository files
381+
core_sim/jsbsim/
382+
383+
# temporary for Jonathan
384+
physics/test/CMakeLists.txt
385+
core_sim/test/CMakeLists.txt
386+
unreal/Blocks/Plugins/InternalDevUseContent/
387+
unreal/Blocks/Plugins/ProjectAirSim/SDK/
388+
unreal/Blocks/Samples/
389+
390+
# Compilation with Visual Studio
391+
# TODO: Make this cleaner. This is a temporary fix.
392+
CMakeFiles/
393+
CMakeCache.txt
394+
CTestTestfile.cmake
395+
*.vcxproj
396+
*.filters
397+
*.cmake
398+
DartConfiguration.tcl
399+
ProjectAirSimLibs.sln

CMakeLists.txt

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ set(SIMLIBS_TEST_DIR ${CMAKE_BINARY_DIR}/unit_tests)
4545
set(UE_PLUGIN_SIMLIBS_DIR ${CMAKE_SOURCE_DIR}/unreal/Blocks/Plugins/ProjectAirSim/SimLibs)
4646
set(UNITY_WRAPPER_DLL_DIR ${CMAKE_SOURCE_DIR}/unity/BlocksUnity/Assets/Plugins)
4747
set(MATLAB_PHYSICS_DIR ${CMAKE_SOURCE_DIR}/physics/matlab_sfunc)
48+
set(JSBSIM_CORESIM_DIR ${CMAKE_SOURCE_DIR}/core_sim/jsbsim)
4849
set(MATLAB_CONTROL_DIR ${CMAKE_SOURCE_DIR}/vehicle_apis/multirotor_api/matlab_sfunc)
4950
# set(UE_PLUGIN_CESIUM_NATIVE_DIR ${CMAKE_SOURCE_DIR}/unreal/Blocks/Plugins/ProjectAirSim/SimLibs)
5051
# set(UE_CESIUM_PLUGIN_DIR ${CMAKE_SOURCE_DIR}/unreal/Blocks/Plugins/CesiumForUnreal/)
@@ -72,6 +73,66 @@ elseif(UNIX)
7273
# set(CESIUM_LINUX_TOOLCHAIN ${UE_CESIUM_PLUGIN_DIR}/extern/unreal-linux-toolchain.cmake)
7374
endif()
7475

76+
# Set up dependency: jsbsim
77+
message("Setting up [jsbsim] dependency as an external project...")
78+
# CMake external projects don't adopt the parent's CMAKE_MSVC_RUNTIME_LIBRARY setting,
79+
# so to force /MD non-debug CRT to match UE, build Debug config as Relwithdebinfo and
80+
# build Release as Release.
81+
set(JSBSIM_BUILD_TYPE $<IF:$<CONFIG:Debug>,Relwithdebinfo,Release>)
82+
set(JSBSIM_SRC_DIR ${CMAKE_BINARY_DIR}/_deps/jsbsim/src/jsbsim-repo)
83+
set(JSBSIM_SHARED_LIB_DIR ${CMAKE_BINARY_DIR}/_deps/jsbsim-install/bin)
84+
set(JSBSIM_STATIC_LIB_DIR ${CMAKE_BINARY_DIR}/_deps/jsbsim-install/lib)
85+
set(JSBSIM_INCLUDE_DIR ${CMAKE_BINARY_DIR}/_deps/jsbsim-install/include/JSBSim ${CMAKE_BINARY_DIR}/_deps/jsbsim-install/include/JSBSim/math)
86+
ExternalProject_Add(jsbsim-repo
87+
GIT_REPOSITORY https://github.com/JSBSim-Team/jsbsim
88+
GIT_TAG "v1.1.12"
89+
GIT_CONFIG "advice.detachedHead=false"
90+
PREFIX ${CMAKE_BINARY_DIR}/_deps/jsbsim
91+
BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config ${JSBSIM_BUILD_TYPE}
92+
INSTALL_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --config ${JSBSIM_BUILD_TYPE} --target install
93+
UPDATE_COMMAND "" # disable update step
94+
CMAKE_ARGS
95+
# -DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}
96+
-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}
97+
-DCMAKE_POSITION_INDEPENDENT_CODE=True
98+
-DCMAKE_BUILD_TYPE:STRING=${JSBSIM_BUILD_TYPE}
99+
-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR}/_deps/jsbsim-install
100+
-DBUILD_SHARED_LIBS=ON
101+
-DBUILD_PYTHON_MODULE=OFF
102+
-DBUILD_DOCS=OFF
103+
TEST_COMMAND "" # disable test step
104+
BUILD_BYPRODUCTS ${JSBSIM_SHARED_LIB_DIR}/JSBSim.dll ${JSBSIM_STATIC_LIB_DIR}/JSBSim.lib
105+
BUILD_ALWAYS 1
106+
)
107+
ExternalProject_Add_Step(jsbsim-repo post-install
108+
COMMAND ${CMAKE_COMMAND} -E echo "Copying [jsbsim] library to ${JSBSIM_CORESIM_DIR}"
109+
COMMAND ${CMAKE_COMMAND} -E copy "${JSBSIM_SHARED_LIB_DIR}/JSBSim.dll" "${JSBSIM_CORESIM_DIR}/lib/$<IF:$<CONFIG:Release>,Release,Debug>/JSBSim.dll"
110+
COMMAND ${CMAKE_COMMAND} -E copy "${JSBSIM_STATIC_LIB_DIR}/JSBSim.lib" "${JSBSIM_CORESIM_DIR}/lib/$<IF:$<CONFIG:Release>,Release,Debug>/JSBSim.lib"
111+
#COMMAND ${CMAKE_COMMAND} -E copy "${JSBSIM_LIB_DIR}/JSBSim.pdb" "${JSBSIM_PHYSICS_DIR}/lib/$<IF:$<CONFIG:Release>,Release,Debug>/JSBSim.pdb"
112+
COMMAND ${CMAKE_COMMAND} -E copy_directory "${JSBSIM_INCLUDE_DIR}" "${JSBSIM_CORESIM_DIR}/include"
113+
# Copy JSBSim models
114+
COMMAND ${CMAKE_COMMAND} -E copy_directory "${JSBSIM_SRC_DIR}/aircraft" "${JSBSIM_CORESIM_DIR}/models/aircraft"
115+
COMMAND ${CMAKE_COMMAND} -E copy_directory "${JSBSIM_SRC_DIR}/engine" "${JSBSIM_CORESIM_DIR}/models/engine"
116+
COMMAND ${CMAKE_COMMAND} -E copy_directory "${JSBSIM_SRC_DIR}/systems" "${JSBSIM_CORESIM_DIR}/models/systems"
117+
# TODO: take out if we are not going to use JSBSim scripts
118+
COMMAND ${CMAKE_COMMAND} -E copy_directory "${JSBSIM_SRC_DIR}/scripts" "${JSBSIM_CORESIM_DIR}/models/scripts"
119+
DEPENDEES install
120+
)
121+
# add jsbsim shared library
122+
add_library(jsbsim SHARED IMPORTED)
123+
if(WIN32)
124+
set_target_properties(jsbsim PROPERTIES
125+
IMPORTED_LOCATION ${JSBSIM_SHARED_LIB_DIR}/JSBSim.dll
126+
IMPORTED_IMPLIB ${JSBSIM_STATIC_LIB_DIR}/JSBSim.lib
127+
)
128+
else()
129+
set_target_properties(jsbsim PROPERTIES
130+
IMPORTED_LOCATION ${JSBSIM_SHARED_LIB_DIR}/libjsbsim.so
131+
IMPORTED_IMPLIB ${JSBSIM_STATIC_LIB_DIR}/libjsbsim.a
132+
)
133+
endif()
134+
135+
75136

76137
# Set up dependency: nlohmann JSON
77138
# Directly download single include file json.hpp
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
"""
2+
Copyright (C) Microsoft Corporation. All rights reserved.
3+
4+
Demonstrates flying a quadrotor drone with camera sensors.
5+
"""
6+
7+
import asyncio
8+
import math
9+
10+
from projectairsim import ProjectAirSimClient, Drone, World
11+
from projectairsim.utils import projectairsim_log
12+
from projectairsim.image_utils import ImageDisplay
13+
14+
15+
# Async main function to wrap async drone commands
16+
async def main():
17+
# Create a Project AirSim client
18+
client = ProjectAirSimClient()
19+
20+
# Initialize an ImageDisplay object to display camera sub-windows
21+
image_display = ImageDisplay()
22+
23+
try:
24+
# Connect to simulation environment
25+
client.connect()
26+
27+
# Create a World object to interact with the sim world and load a scene
28+
world = World(client, "scene_basic_fixed_wing.jsonc", delay_after_load_sec=2)
29+
30+
# Create a Drone object to interact with a drone in the loaded sim world
31+
drone = Drone(client, world, "x8")
32+
#
33+
## ------------------------------------------------------------------------------
34+
#
35+
## Subscribe to chase camera sensor as a client-side pop-up window
36+
#chase_cam_window = "ChaseCam"
37+
#image_display.add_chase_cam(chase_cam_window)
38+
#client.subscribe(
39+
# drone.sensors["DownCamera"]["scene_camera"],
40+
# lambda _, chase: image_display.receive(chase, chase_cam_window),
41+
#)
42+
#
43+
## Subscribe to the downward-facing camera sensor's RGB and Depth images
44+
#rgb_name = "RGB-Image"
45+
#image_display.add_image(rgb_name, subwin_idx=0)
46+
#client.subscribe(
47+
# drone.sensors["DownCamera"]["scene_camera"],
48+
# lambda _, rgb: image_display.receive(rgb, rgb_name),
49+
#)
50+
#
51+
#depth_name = "Depth-Image"
52+
#image_display.add_image(depth_name, subwin_idx=2)
53+
#client.subscribe(
54+
# drone.sensors["DownCamera"]["depth_camera"],
55+
# lambda _, depth: image_display.receive(depth, depth_name),
56+
#)
57+
#
58+
#image_display.start()
59+
#
60+
## ------------------------------------------------------------------------------
61+
#
62+
## Set the drone to be ready to fly
63+
## JSBSim robot currently does not support control the drone at runtime
64+
#drone.enable_api_control()
65+
##set brakes to 1
66+
#drone.set_brakes(1)
67+
#drone.arm()
68+
#
69+
## ------------------------------------------------------------------------------
70+
#
71+
## set takeoff z to 120 meters
72+
#drone.set_take_off_z(-120)
73+
#
74+
## ------------------------------------------------------------------------------
75+
#
76+
## Sleep for two seconds to
77+
#await asyncio.sleep(2)
78+
#
79+
## release brakes
80+
#drone.set_brakes(0)
81+
#
82+
#projectairsim_log().info("takeoff_async: starting")
83+
#takeoff_task = (
84+
# await drone.takeoff_async(timeout_sec=1200)
85+
#) # schedule an async task to start the command
86+
#
87+
#await takeoff_task
88+
#projectairsim_log().info("takeoff_async: completed")
89+
#
90+
#projectairsim_log().info("Waiting to stabilize altitude... (10 seconds)")
91+
#await asyncio.sleep(10)
92+
#
93+
## ------------------------------------------------------------------------------
94+
#
95+
## Command the drone to move to position 1000,1000,-200
96+
#move_up_task = await drone.move_to_position_async(
97+
# north=1000, east=1000, down=-200, velocity=33.0, lookahead=100, timeout_sec=60
98+
#)
99+
#projectairsim_log().info("Move to position 1000,1000,-200 invoked")
100+
#
101+
#await move_up_task
102+
#projectairsim_log().info("Move to position completed")
103+
#
104+
## ------------------------------------------------------------------------------
105+
#
106+
## Command vehicle to fly at a specific heading and speed
107+
#projectairsim_log().info("Heading 90 invoked")
108+
#heading_45_task = await drone.move_by_heading_async(
109+
# heading=math.radians(90.0), speed=20.0, duration=10
110+
#)
111+
#await heading_45_task
112+
#projectairsim_log().info("Heading 90 complete.")
113+
#
114+
## ------------------------------------------------------------------------------
115+
#
116+
## Command the drone to move to position 0,0,-100
117+
#move_up_task = await drone.move_to_position_async(
118+
# north=0, east=0, down=-100, velocity=33.0, lookahead=100, timeout_sec=60
119+
#)
120+
#projectairsim_log().info("Move to position 0,0,-100 invoked")
121+
#
122+
#await move_up_task
123+
#projectairsim_log().info("Move to position completed")
124+
## ------------------------------------------------------------------------------
125+
#
126+
#projectairsim_log().info("land_async: starting")
127+
#land_task = await drone.land_async()
128+
#await land_task
129+
#projectairsim_log().info("land_async: completed")
130+
## set brakes to 50%
131+
#drone.set_brakes(0.5)
132+
133+
# ------------------------------------------------------------------------------
134+
135+
# Shut down the drone
136+
drone.disarm()
137+
drone.disable_api_control()
138+
139+
# logs exception on the console
140+
except Exception as err:
141+
projectairsim_log().error(f"Exception occurred: {err}", exc_info=True)
142+
143+
finally:
144+
# Always disconnect from the simulation environment to allow next connection
145+
client.disconnect()
146+
147+
image_display.stop()
148+
149+
150+
if __name__ == "__main__":
151+
asyncio.run(main()) # Runner for async main function
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
"""
2+
Copyright (C) Microsoft Corporation. All rights reserved.
3+
4+
Demonstrates flying a quadrotor drone with camera sensors.
5+
"""
6+
7+
import asyncio
8+
9+
from projectairsim import ProjectAirSimClient, Drone, World
10+
from projectairsim.utils import projectairsim_log
11+
from projectairsim.image_utils import ImageDisplay
12+
13+
14+
# Async main function to wrap async drone commands
15+
async def main():
16+
# Create a Project AirSim client
17+
client = ProjectAirSimClient()
18+
19+
# Initialize an ImageDisplay object to display camera sub-windows
20+
image_display = ImageDisplay()
21+
22+
try:
23+
# Connect to simulation environment
24+
client.connect()
25+
26+
# Create a World object to interact with the sim world and load a scene
27+
world = World(client, "scene_basic_helicopter.jsonc", delay_after_load_sec=2)
28+
29+
# Create a Drone object to interact with a drone in the loaded sim world
30+
drone = Drone(client, world, "Drone1")
31+
32+
# ------------------------------------------------------------------------------
33+
34+
# Subscribe to chase camera sensor as a client-side pop-up window
35+
chase_cam_window = "ChaseCam"
36+
image_display.add_chase_cam(chase_cam_window)
37+
client.subscribe(
38+
drone.sensors["Chase"]["scene_camera"],
39+
lambda _, chase: image_display.receive(chase, chase_cam_window),
40+
)
41+
42+
# Subscribe to the downward-facing camera sensor's RGB and Depth images
43+
rgb_name = "RGB-Image"
44+
image_display.add_image(rgb_name, subwin_idx=0)
45+
client.subscribe(
46+
drone.sensors["DownCamera"]["scene_camera"],
47+
lambda _, rgb: image_display.receive(rgb, rgb_name),
48+
)
49+
50+
depth_name = "Depth-Image"
51+
image_display.add_image(depth_name, subwin_idx=2)
52+
client.subscribe(
53+
drone.sensors["DownCamera"]["depth_camera"],
54+
lambda _, depth: image_display.receive(depth, depth_name),
55+
)
56+
57+
image_display.start()
58+
59+
# ------------------------------------------------------------------------------
60+
61+
# Set the drone to be ready to fly
62+
# JSBSim robot currently does not support control the drone at runtime
63+
# drone.enable_api_control()
64+
# drone.arm()
65+
66+
# # ------------------------------------------------------------------------------
67+
68+
# projectairsim_log().info("takeoff_async: starting")
69+
# takeoff_task = (
70+
# await drone.takeoff_async()
71+
# ) # schedule an async task to start the command
72+
73+
# # Example 1: Wait on the result of async operation using 'await' keyword
74+
# await takeoff_task
75+
# projectairsim_log().info("takeoff_async: completed")
76+
77+
# # ------------------------------------------------------------------------------
78+
79+
# # Command the drone to move up in NED coordinate system at 1 m/s for 4 seconds
80+
# move_up_task = await drone.move_by_velocity_async(
81+
# v_north=0.0, v_east=0.0, v_down=-1.0, duration=4.0
82+
# )
83+
# projectairsim_log().info("Move-Up invoked")
84+
85+
# await move_up_task
86+
# projectairsim_log().info("Move-Up completed")
87+
88+
# # ------------------------------------------------------------------------------
89+
90+
# # Command the Drone to move down in NED coordinate system at 1 m/s for 4 seconds
91+
# move_down_task = await drone.move_by_velocity_async(
92+
# v_north=0.0, v_east=0.0, v_down=1.0, duration=4.0
93+
# ) # schedule an async task to start the command
94+
# projectairsim_log().info("Move-Down invoked")
95+
96+
# # Example 2: Wait for move_down_task to complete before continuing
97+
# while not move_down_task.done():
98+
# await asyncio.sleep(0.005)
99+
# projectairsim_log().info("Move-Down completed")
100+
101+
# # ------------------------------------------------------------------------------
102+
103+
# projectairsim_log().info("land_async: starting")
104+
# land_task = await drone.land_async()
105+
# await land_task
106+
# projectairsim_log().info("land_async: completed")
107+
108+
# # # ------------------------------------------------------------------------------
109+
110+
111+
112+
# ------------------------------------------------------------------------------
113+
# sleep for 5 minutes to contiue seeing the camera images
114+
await asyncio.sleep(300)
115+
116+
117+
# Shut down the drone
118+
#drone.disarm()
119+
#drone.disable_api_control()
120+
121+
# logs exception on the console
122+
except Exception as err:
123+
projectairsim_log().error(f"Exception occurred: {err}", exc_info=True)
124+
125+
finally:
126+
# Always disconnect from the simulation environment to allow next connection
127+
client.disconnect()
128+
129+
image_display.stop()
130+
131+
132+
if __name__ == "__main__":
133+
asyncio.run(main()) # Runner for async main function

client/python/example_user_scripts/jsbsim_env_actor.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
"""
1717
from projectairsim import ProjectAirSimClient, World, EnvActor
1818
from projectairsim.utils import projectairsim_log
19-
import asyncio
2019

2120

2221
async def env_actor_motion_plan(env_actor: EnvActor, world: World):

0 commit comments

Comments
 (0)