diff --git a/gazebo/launch/drone_landing.launch.py b/gazebo/launch/drone_landing.launch.py
index 3ec89de..516de09 100644
--- a/gazebo/launch/drone_landing.launch.py
+++ b/gazebo/launch/drone_landing.launch.py
@@ -1,110 +1,62 @@
#!/usr/bin/env python3
"""
Gazebo Launch File - Drone Landing Simulation
-Launches Gazebo with the world and spawns the drone.
-Supports both 'gz' (newer) and 'ign' (Fortress) commands.
+Works with Ignition Fortress (ROS 2 Humble)
"""
import os
import shutil
-from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, ExecuteProcess, IncludeLaunchDescription
-from launch.conditions import IfCondition
-from launch.launch_description_sources import PythonLaunchDescriptionSource
-from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
+from launch.actions import DeclareLaunchArgument, ExecuteProcess
+from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
-def get_gazebo_command():
- """Determine whether to use 'gz' or 'ign' command."""
- if shutil.which('gz'):
- return 'gz'
- elif shutil.which('ign'):
- return 'ign'
- else:
- # Default to gz, will fail with helpful error
- return 'gz'
-
-
def generate_launch_description():
"""Generate the launch description."""
+ # Get paths
script_dir = os.path.dirname(os.path.abspath(__file__))
gazebo_dir = os.path.dirname(script_dir)
-
world_file = os.path.join(gazebo_dir, 'worlds', 'drone_landing.sdf')
- model_path = os.path.join(gazebo_dir, 'models')
- drone_model = os.path.join(model_path, 'drone', 'model.sdf')
- gz_resource_path = os.environ.get('GZ_SIM_RESOURCE_PATH', '')
- ign_resource_path = os.environ.get('IGN_GAZEBO_RESOURCE_PATH', '')
- combined_path = f"{model_path}:{gz_resource_path}:{ign_resource_path}"
-
- os.environ['GZ_SIM_RESOURCE_PATH'] = combined_path
- os.environ['IGN_GAZEBO_RESOURCE_PATH'] = combined_path
+ # Determine which gazebo command is available
+ if shutil.which('ign'):
+ gz_cmd = ['ign', 'gazebo', '-r', world_file]
+ elif shutil.which('gz'):
+ gz_cmd = ['gz', 'sim', '-r', world_file]
+ else:
+ gz_cmd = ['ign', 'gazebo', '-r', world_file]
use_sim_time = LaunchConfiguration('use_sim_time', default='true')
- headless = LaunchConfiguration('headless', default='false')
-
- # Detect which Gazebo command is available
- gz_cmd = get_gazebo_command()
-
- # For ign command, use 'gazebo' subcommand; for gz, use 'sim'
- if gz_cmd == 'ign':
- sim_cmd = [gz_cmd, 'gazebo', '-r', world_file]
- spawn_cmd = [
- gz_cmd, 'service', '-s', '/world/drone_landing_world/create',
- '--reqtype', 'ignition.msgs.EntityFactory',
- '--reptype', 'ignition.msgs.Boolean',
- '--timeout', '5000',
- '--req', f'sdf_filename: "{drone_model}", name: "drone"'
- ]
- else:
- sim_cmd = [gz_cmd, 'sim', '-r', world_file]
- spawn_cmd = [
- gz_cmd, 'service', '-s', '/world/drone_landing_world/create',
- '--reqtype', 'gz.msgs.EntityFactory',
- '--reptype', 'gz.msgs.Boolean',
- '--timeout', '5000',
- '--req', f'sdf_filename: "{drone_model}", name: "drone"'
- ]
return LaunchDescription([
DeclareLaunchArgument(
'use_sim_time',
default_value='true',
- description='Use simulation (Gazebo) clock'
- ),
- DeclareLaunchArgument(
- 'headless',
- default_value='false',
- description='Run Gazebo in headless mode (no GUI)'
+ description='Use simulation clock'
),
+ # Start Gazebo
ExecuteProcess(
- cmd=sim_cmd,
- output='screen',
- additional_env={
- 'GZ_SIM_RESOURCE_PATH': combined_path,
- 'IGN_GAZEBO_RESOURCE_PATH': combined_path
- }
- ),
-
- ExecuteProcess(
- cmd=spawn_cmd,
+ cmd=gz_cmd,
output='screen'
),
+ # ROS-Gazebo Bridge
Node(
package='ros_gz_bridge',
executable='parameter_bridge',
name='gz_bridge',
arguments=[
- '/drone/cmd_vel@geometry_msgs/msg/Twist@gz.msgs.Twist',
- '/model/drone/odometry@nav_msgs/msg/Odometry@gz.msgs.Odometry',
- '/drone/imu@sensor_msgs/msg/Imu@gz.msgs.IMU',
- '/clock@rosgraph_msgs/msg/Clock@gz.msgs.Clock',
+ # Velocity commands (bidirectional)
+ '/drone/cmd_vel@geometry_msgs/msg/Twist]ignition.msgs.Twist',
+ # Odometry (from Gazebo to ROS)
+ '/model/drone/odometry@nav_msgs/msg/Odometry[ignition.msgs.Odometry',
+ # IMU (from Gazebo to ROS)
+ '/drone/imu@sensor_msgs/msg/Imu[ignition.msgs.IMU',
+ # Clock
+ '/clock@rosgraph_msgs/msg/Clock[ignition.msgs.Clock',
],
parameters=[{'use_sim_time': use_sim_time}],
output='screen'
@@ -119,12 +71,9 @@ if __name__ == '__main__':
print(" ros2 launch gazebo/launch/drone_landing.launch.py")
print()
print("Or start Gazebo manually:")
-
- gz_cmd = get_gazebo_command()
- if gz_cmd == 'ign':
+ if shutil.which('ign'):
print(" ign gazebo gazebo/worlds/drone_landing.sdf")
else:
print(" gz sim gazebo/worlds/drone_landing.sdf")
-
print()
print("Then run: python run_gazebo.py")
diff --git a/gazebo/models/drone/model_fortress.sdf b/gazebo/models/drone/model_fortress.sdf
new file mode 100644
index 0000000..6085065
--- /dev/null
+++ b/gazebo/models/drone/model_fortress.sdf
@@ -0,0 +1,161 @@
+
+
+
+
+ 0 0 5 0 0 0
+
+
+
+
+ 1.0
+
+ 0.029125
+ 0
+ 0
+ 0.029125
+ 0
+ 0.055225
+
+
+
+
+
+
+ 0.3 0.3 0.1
+
+
+
+
+
+
+
+ 0.3 0.3 0.1
+
+
+
+ 0.8 0.1 0.1 1
+ 0.9 0.2 0.2 1
+ 0.2 0.2 0.2 1
+
+
+
+
+
+ 0.12 0.12 0 0 0 0.785
+
+ 0.2 0.02 0.02
+
+
+ 0.2 0.2 0.2 1
+ 0.3 0.3 0.3 1
+
+
+
+ 0.12 -0.12 0 0 0 -0.785
+
+ 0.2 0.02 0.02
+
+
+ 0.2 0.2 0.2 1
+ 0.3 0.3 0.3 1
+
+
+
+ -0.12 0.12 0 0 0 -0.785
+
+ 0.2 0.02 0.02
+
+
+ 0.2 0.2 0.2 1
+ 0.3 0.3 0.3 1
+
+
+
+ -0.12 -0.12 0 0 0 0.785
+
+ 0.2 0.02 0.02
+
+
+ 0.2 0.2 0.2 1
+ 0.3 0.3 0.3 1
+
+
+
+
+
+ 0.17 0.17 0.05 0 0 0
+
+ 0.080.01
+
+
+ 0.1 0.1 0.1 1
+ 0.2 0.2 0.2 1
+
+
+
+ 0.17 -0.17 0.05 0 0 0
+
+ 0.080.01
+
+
+ 0.1 0.1 0.1 1
+ 0.2 0.2 0.2 1
+
+
+
+ -0.17 0.17 0.05 0 0 0
+
+ 0.080.01
+
+
+ 0.1 0.1 0.1 1
+ 0.2 0.2 0.2 1
+
+
+
+ -0.17 -0.17 0.05 0 0 0
+
+ 0.080.01
+
+
+ 0.1 0.1 0.1 1
+ 0.2 0.2 0.2 1
+
+
+
+
+
+ true
+ 100
+ /drone/imu
+
+
+
+
+
+
+
+
+
+
+ odom
+ base_link
+ /model/drone/odometry
+ 50
+
+
+
+
+ true
+ false
+ 50
+
+
+
+
diff --git a/gazebo/worlds/drone_landing.sdf b/gazebo/worlds/drone_landing.sdf
index a80a200..14e0886 100644
--- a/gazebo/worlds/drone_landing.sdf
+++ b/gazebo/worlds/drone_landing.sdf
@@ -1,20 +1,32 @@
-
+
+
0.001
1.0
-
-
-
-
+
+
+
+
+
+ ogre2
+
+
+
true
0 0 10 0 0 0
@@ -23,7 +35,7 @@
-0.5 0.1 -0.9
-
+
true
@@ -50,16 +62,14 @@
-
+
true
0 0 0.15 0 0 0
-
- 1.0 1.0 0.3
-
+ 1.0 1.0 0.3
0.1 0.5 0.1 1
@@ -68,13 +78,125 @@
-
- 1.0 1.0 0.3
-
+ 1.0 1.0 0.3
+
+
+ 0 0 0.151 0 0 0
+ 0.6 0.1 0.001
+
+ 1 1 1 1
+ 1 1 1 1
+
+
+
+ -0.25 0 0.151 0 0 0
+ 0.1 0.5 0.001
+
+ 1 1 1 1
+ 1 1 1 1
+
+
+
+ 0.25 0 0.151 0 0 0
+ 0.1 0.5 0.001
+
+ 1 1 1 1
+ 1 1 1 1
+
+
+
+
+ 0 0 5 0 0 0
+
+
+
+ 1.0
+
+ 0.02900
+ 0.0290
+ 0.055
+
+
+
+
+ 0.3 0.3 0.1
+
+
+
+ 0.3 0.3 0.1
+
+ 0.8 0.1 0.1 1
+ 0.9 0.2 0.2 1
+
+
+
+
+
+ 0.12 0.12 0 0 0 0.785
+ 0.2 0.02 0.02
+ 0.3 0.3 0.3 1
+
+
+ 0.12 -0.12 0 0 0 -0.785
+ 0.2 0.02 0.02
+ 0.3 0.3 0.3 1
+
+
+ -0.12 0.12 0 0 0 -0.785
+ 0.2 0.02 0.02
+ 0.3 0.3 0.3 1
+
+
+ -0.12 -0.12 0 0 0 0.785
+ 0.2 0.02 0.02
+ 0.3 0.3 0.3 1
+
+
+
+
+ 0.17 0.17 0.05 0 0 0
+ 0.080.01
+ 0.2 0.2 0.2 1
+
+
+ 0.17 -0.17 0.05 0 0 0
+ 0.080.01
+ 0.2 0.2 0.2 1
+
+
+ -0.17 0.17 0.05 0 0 0
+ 0.080.01
+ 0.2 0.2 0.2 1
+
+
+ -0.17 -0.17 0.05 0 0 0
+ 0.080.01
+ 0.2 0.2 0.2 1
+
+
+
+
+ true
+ 100
+ /drone/imu
+
+
+
+
+
+ odom
+ base_link
+ /model/drone/odometry
+ 50
+
+
+
+