feat: add Core URDF with basic Gazebo sim

This commit is contained in:
David
2025-09-30 17:49:01 -05:00
parent 8c5287158d
commit e5af28af3a
28 changed files with 6347 additions and 10 deletions

View File

@@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 3.8)
project(core_gazebo)
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic)
endif()
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(controller_manager REQUIRED)
find_package(gripper_controllers REQUIRED)
find_package(rclcpp REQUIRED)
find_package(ros2_control REQUIRED)
find_package(ros2_controllers REQUIRED)
find_package(trajectory_msgs REQUIRED)
find_package(xacro REQUIRED)
# Copy necessary files to designated locations in the project
install (
DIRECTORY config launch models worlds
DESTINATION share/${PROJECT_NAME}
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
# the following line skips the linter which checks for copyrights
# comment the line when a copyright and license is added to all source files
set(ament_cmake_copyright_FOUND TRUE)
# the following line skips cpplint (only works in a git repo)
# comment the line when this package is in a git repo and when
# a copyright and license is added to all source files
set(ament_cmake_cpplint_FOUND TRUE)
ament_lint_auto_find_test_dependencies()
endif()
ament_package()

25
src/core_gazebo/LICENSE Normal file
View File

@@ -0,0 +1,25 @@
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,25 @@
# gz topic published by Sensors plugin
- ros_topic_name: "camera_head/depth/camera_info"
gz_topic_name: "camera_head/camera_info"
ros_type_name: "sensor_msgs/msg/CameraInfo"
gz_type_name: "gz.msgs.CameraInfo"
direction: GZ_TO_ROS
lazy: true # Determines whether connections are created immediately at startup (when false) or only when data is actually requested by a subscriber (when true), helping to conserve system resources at the cost of potential initial delays in data flow.
# gz topic published by Sensors plugin
- ros_topic_name: "camera_head/depth/color/points"
gz_topic_name: "camera_head/points"
ros_type_name: "sensor_msgs/msg/PointCloud2"
gz_type_name: "gz.msgs.PointCloudPacked"
direction: GZ_TO_ROS
lazy: true
# Clock configuration
- ros_topic_name: "clock"
gz_topic_name: "clock"
ros_type_name: "rosgraph_msgs/msg/Clock"
gz_type_name: "gz.msgs.Clock"
direction: GZ_TO_ROS
lazy: false

View File

@@ -0,0 +1,286 @@
#!/usr/bin/env python3
import os
from launch import LaunchDescription
from launch.actions import (
AppendEnvironmentVariable,
DeclareLaunchArgument,
IncludeLaunchDescription
)
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
def generate_launch_description():
"""
Generate a launch description for the Gazebo simulation.
This function sets up all necessary parameters, paths, and nodes required to launch
the Gazebo simulation with a robot. It handles:
1. Setting up package paths and constants
2. Declaring launch arguments for robot configuration
3. Setting up the Gazebo environment
4. Spawning the robot in simulation
Returns:
LaunchDescription: A complete launch description for the simulation
"""
# Constants for paths to different files and folders
package_name_gazebo = 'core_gazebo'
package_name_description = 'core_rover_description'
# package_name_moveit = 'mycobot_moveit_config'
default_robot_name = 'core_rover'
gazebo_models_path = 'models'
default_world_file = 'pick_and_place_demo.world'
gazebo_worlds_path = 'worlds'
ros_gz_bridge_config_file_path = 'config/ros_gz_bridge.yaml'
# Set the path to different files and folders
pkg_ros_gz_sim = FindPackageShare(package='ros_gz_sim').find('ros_gz_sim')
pkg_share_gazebo = FindPackageShare(package=package_name_gazebo).find(package_name_gazebo)
pkg_share_description = FindPackageShare(
package=package_name_description).find(package_name_description)
# pkg_share_moveit = FindPackageShare(package=package_name_moveit).find(package_name_moveit)
gazebo_models_path = os.path.join(pkg_share_gazebo, gazebo_models_path)
default_ros_gz_bridge_config_file_path = os.path.join(
pkg_share_gazebo, ros_gz_bridge_config_file_path)
# Get the parent directory of the package share to access all ROS packages
ros_packages_path = os.path.dirname(pkg_share_description)
# Launch configuration variables
jsp_gui = LaunchConfiguration('jsp_gui')
load_controllers = LaunchConfiguration('load_controllers')
robot_name = LaunchConfiguration('robot_name')
use_rviz = LaunchConfiguration('use_rviz')
use_camera = LaunchConfiguration('use_camera')
use_gazebo = LaunchConfiguration('use_gazebo')
use_robot_state_pub = LaunchConfiguration('use_robot_state_pub')
use_sim_time = LaunchConfiguration('use_sim_time')
world_file = LaunchConfiguration('world_file')
world_path = PathJoinSubstitution([
pkg_share_gazebo,
gazebo_worlds_path,
world_file
])
# Set the pose configuration variables
x = LaunchConfiguration('x')
y = LaunchConfiguration('y')
z = LaunchConfiguration('z')
roll = LaunchConfiguration('roll')
pitch = LaunchConfiguration('pitch')
yaw = LaunchConfiguration('yaw')
################################################################################################
# Declare the launch arguments
declare_robot_name_cmd = DeclareLaunchArgument(
name='robot_name',
default_value=default_robot_name,
description='The name for the robot')
declare_load_controllers_cmd = DeclareLaunchArgument(
name='load_controllers',
default_value='true',
description='Flag to enable loading of ROS 2 controllers')
declare_use_robot_state_pub_cmd = DeclareLaunchArgument(
name='use_robot_state_pub',
default_value='true',
description='Flag to enable robot state publisher')
# GUI and visualization arguments
declare_jsp_gui_cmd = DeclareLaunchArgument(
name='jsp_gui',
default_value='false',
description='Flag to enable joint_state_publisher_gui')
declare_use_camera_cmd = DeclareLaunchArgument(
name='use_camera',
default_value='false',
description='Flag to enable the RGBD camera for Gazebo point cloud simulation')
declare_use_gazebo_cmd = DeclareLaunchArgument(
name='use_gazebo',
default_value='true',
description='Flag to enable Gazebo')
declare_use_rviz_cmd = DeclareLaunchArgument(
name='use_rviz',
default_value='true',
description='Flag to enable RViz')
declare_use_sim_time_cmd = DeclareLaunchArgument(
name='use_sim_time',
default_value='true',
description='Use simulation (Gazebo) clock if true')
declare_world_cmd = DeclareLaunchArgument(
name='world_file',
default_value=default_world_file,
description='World file name (e.g., simple_demo.world, pick_and_place_demo.world)')
# Pose arguments
declare_x_cmd = DeclareLaunchArgument(
name='x',
default_value='0.0',
description='x component of initial position, meters')
declare_y_cmd = DeclareLaunchArgument(
name='y',
default_value='0.0',
description='y component of initial position, meters')
declare_z_cmd = DeclareLaunchArgument(
name='z',
default_value='0.75',
description='z component of initial position, meters')
declare_roll_cmd = DeclareLaunchArgument(
name='roll',
default_value='0.0',
description='roll angle of initial orientation, radians')
declare_pitch_cmd = DeclareLaunchArgument(
name='pitch',
default_value='0.0',
description='pitch angle of initial orientation, radians')
declare_yaw_cmd = DeclareLaunchArgument(
name='yaw',
default_value='0.0',
description='yaw angle of initial orientation, radians')
################################################################################################
# Launch stuff
# Include Robot State Publisher launch file if enabled
robot_state_publisher_cmd = IncludeLaunchDescription(
PythonLaunchDescriptionSource([
os.path.join(pkg_share_description, 'launch', 'robot_state_publisher.launch.py')
]),
launch_arguments={
'jsp_gui': jsp_gui,
'use_camera': use_camera,
'use_gazebo': use_gazebo,
'use_rviz': use_rviz,
'use_sim_time': use_sim_time
}.items(),
condition=IfCondition(use_robot_state_pub)
)
# # Include ROS 2 Controllers launch file if enabled
# load_controllers_cmd = IncludeLaunchDescription(
# PythonLaunchDescriptionSource([
# os.path.join(pkg_share_moveit, 'launch', 'load_ros2_controllers.launch.py')
# ]),
# launch_arguments={
# 'use_sim_time': use_sim_time
# }.items(),
# condition=IfCondition(load_controllers)
# )
# Set Gazebo model path - include both models directory and ROS packages
set_env_vars_resources = AppendEnvironmentVariable(
'GZ_SIM_RESOURCE_PATH',
gazebo_models_path)
# Add ROS packages path so Gazebo can resolve package:// URIs
set_env_vars_packages = AppendEnvironmentVariable(
'GZ_SIM_RESOURCE_PATH',
os.path.dirname(pkg_share_description))
# Start Gazebo with optimized arguments
start_gazebo_cmd = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(pkg_ros_gz_sim, 'launch', 'gz_sim.launch.py')),
launch_arguments=[('gz_args', [' -r -v 3 --render-engine ogre2 ', world_path])])
# Bridge ROS topics and Gazebo messages for establishing communication
start_gazebo_ros_bridge_cmd = Node(
package='ros_gz_bridge',
executable='parameter_bridge',
parameters=[{
'config_file': default_ros_gz_bridge_config_file_path,
}],
output='screen'
)
# Includes optimizations to minimize latency and bandwidth when streaming image data
start_gazebo_ros_image_bridge_cmd = Node(
package='ros_gz_image',
executable='image_bridge',
arguments=[
'/camera_head/depth_image',
'/camera_head/image',
],
remappings=[
('/camera_head/depth_image', '/camera_head/depth/image_rect_raw'),
('/camera_head/image', '/camera_head/color/image_raw'),
],
condition=IfCondition(use_camera)
)
# Spawn the robot
start_gazebo_ros_spawner_cmd = Node(
package='ros_gz_sim',
executable='create',
output='screen',
arguments=[
'-topic', '/robot_description',
'-name', robot_name,
'-allow_renaming', 'true',
'-x', x,
'-y', y,
'-z', z,
'-R', roll,
'-P', pitch,
'-Y', yaw
])
################################################################################################
# Launch description
ld = LaunchDescription()
# Declare the launch options
ld.add_action(declare_robot_name_cmd)
ld.add_action(declare_jsp_gui_cmd)
ld.add_action(declare_load_controllers_cmd)
ld.add_action(declare_use_camera_cmd)
ld.add_action(declare_use_gazebo_cmd)
ld.add_action(declare_use_rviz_cmd)
ld.add_action(declare_use_robot_state_pub_cmd)
ld.add_action(declare_use_sim_time_cmd)
ld.add_action(declare_world_cmd)
# Add pose arguments
ld.add_action(declare_x_cmd)
ld.add_action(declare_y_cmd)
ld.add_action(declare_z_cmd)
ld.add_action(declare_roll_cmd)
ld.add_action(declare_pitch_cmd)
ld.add_action(declare_yaw_cmd)
# Add the actions to the launch description
ld.add_action(set_env_vars_resources)
ld.add_action(set_env_vars_packages)
ld.add_action(robot_state_publisher_cmd)
# ld.add_action(load_controllers_cmd)
ld.add_action(start_gazebo_cmd)
ld.add_action(start_gazebo_ros_bridge_cmd)
ld.add_action(start_gazebo_ros_image_bridge_cmd)
ld.add_action(start_gazebo_ros_spawner_cmd)
return ld

View File

@@ -0,0 +1,18 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
<name>core_gazebo</name>
<version>0.0.0</version>
<description>TODO: Package description</description>
<maintainer email="ds0196@uah.edu">David</maintainer>
<license>BSD-3-Clause</license>
<buildtool_depend>ament_cmake</buildtool_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<export>
<build_type>ament_cmake</build_type>
</export>
</package>

View File

@@ -0,0 +1,89 @@
<?xml version="1.0" ?>
<sdf version="1.6">
<world name="default">
<!-- Plugin for simulating physics -->
<plugin
filename="gz-sim-physics-system"
name="gz::sim::systems::Physics">
<engine>
<filename>libgz-physics-bullet-featherstone-plugin.so</filename>
</engine>
</plugin>
<!-- Plugin for handling user commands -->
<plugin
filename="gz-sim-user-commands-system"
name="gz::sim::systems::UserCommands">
</plugin>
<!-- Plugin for broadcasting scene updates -->
<plugin
filename="gz-sim-scene-broadcaster-system"
name="gz::sim::systems::SceneBroadcaster">
</plugin>
<!-- Plugin for handling sensors like the LIDAR -->
<plugin
filename="gz-sim-sensors-system"
name="gz::sim::systems::Sensors">
<render_engine>ogre2</render_engine>
</plugin>
<!-- Plugin for IMU -->
<plugin filename="gz-sim-imu-system"
name="gz::sim::systems::Imu">
</plugin>
<!-- To add realistic gravity, do: 0.0 0.0 -9.8, otherwise do 0.0 0.0 0.0 -->
<gravity>0.0 0.0 -9.8</gravity>
<!-- Include a model of the Sun from an external URI -->
<include>
<uri>
https://fuel.gazebosim.org/1.0/OpenRobotics/models/Sun
</uri>
</include>
<!-- Local Ground Plane with modified friction -->
<model name="ground_plane">
<static>true</static>
<link name="link">
<collision name="collision">
<geometry>
<plane>
<normal>0 0 1</normal>
</plane>
</geometry>
<surface>
<friction>
<torsional>
<coefficient>0.0</coefficient>
</torsional>
</friction>
</surface>
</collision>
<visual name="visual">
<geometry>
<plane>
<normal>0 0 1</normal>
<size>100 100</size>
</plane>
</geometry>
<material>
<ambient>0.8 0.8 0.8 1</ambient>
<diffuse>0.8 0.8 0.8 1</diffuse>
<specular>0.8 0.8 0.8 1</specular>
</material>
</visual>
</link>
</model>
<!-- Define scene properties -->
<scene>
<shadows>false</shadows>
</scene>
</world>
</sdf>

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" ?>
<sdf version="1.7">
<world name="default">
<!-- Plugin for simulating physics -->
<plugin
filename="gz-sim-physics-system"
name="gz::sim::systems::Physics">
</plugin>
<!-- Plugin for handling user commands -->
<plugin
filename="gz-sim-user-commands-system"
name="gz::sim::systems::UserCommands">
</plugin>
<!-- Plugin for broadcasting scene updates -->
<plugin
filename="gz-sim-scene-broadcaster-system"
name="gz::sim::systems::SceneBroadcaster">
</plugin>
<!-- Plugin for sensor handling -->
<plugin
filename="gz-sim-sensors-system"
name="gz::sim::systems::Sensors">
<render_engine>ogre2</render_engine>
</plugin>
<!-- To add realistic gravity, do: 0.0 0.0 -9.8, otherwise do 0.0 0.0 0.0 -->
<gravity>0.0 0.0 -9.8</gravity>
<!-- Include a model of the Sun from an external URI -->
<include>
<uri>
https://fuel.gazebosim.org/1.0/OpenRobotics/models/Sun
</uri>
</include>
<!-- Include a model of the Ground Plane from an external URI -->
<include>
<uri>
https://fuel.gazebosim.org/1.0/OpenRobotics/models/Ground Plane
</uri>
</include>
<!-- Include the cylinder model -->
<!-- <include>
<uri>model://red_cylinder</uri>
<name>red_cylinder</name>
<pose>0.22 0.12 0.175 0 0 0</pose>
</include> -->
<!-- Include the other objects -->
<!-- <include>
<uri>model://mustard</uri>
<pose>0.7 0.15 0.08 0 0 0</pose>
</include>
<include>
<uri>model://cheezit_big_original</uri>
<pose>0.64 0.23 0.17 1.571 0 0</pose>
</include>
<include>
<uri>model://cardboard_box</uri>
<pose>0.65 0.60 0.3 0 0 0.5</pose>
</include>
<include>
<uri>model://coke_can</uri>
<pose>0.5 0.15 0.15 0 0 2.3</pose>
</include> -->
<!-- Define scene properties -->
<scene>
<shadows>false</shadows>
</scene>
</world>
</sdf>