Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi robot launch in gazebo and multi robots control with namespace. #224

Merged
merged 12 commits into from
Feb 7, 2025
58 changes: 30 additions & 28 deletions .github/workflows/ros-ci.yml
Original file line number Diff line number Diff line change
@@ -1,58 +1,60 @@
name: humble-devel
# The name of the workflow
name: CI

# Controls when the action will run. Triggers the workflow on push or pull request
# Specifies the events that trigger the workflow
on:
push:
branches: [ humble-devel ]
branches: [ main, humble ]
pull_request:
branches: [ humble-devel ]
branches: [ main, humble ]

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
# Defines a set of jobs to be run as part of the workflow
jobs:
humble-devel:
runs-on: ubuntu-latest
# The name of the job
ROS_CI:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
ros_distribution:
#- foxy
# - galactic
- humble
# - jazzy
# - rolling
include:
# Foxy Fitzroy (June 2020 - May 2023)
# - docker_image: ubuntu:focal
# ros_distribution: foxy
# ros_version: 2
# # Galactic Geochelone (May 2021)
# - docker_image: ubuntu:focal
# ros_distribution: galactic
# ros_version: 2
# Humble Hawksbill (May 2027)
# ROS 2 Humble Hawksbill
- docker_image: ubuntu:jammy
ros_distribution: humble
ros_version: 2
# # Rolling
# - docker_image: ubuntu:jammy
# ROS 2 Jazzy Jalisco
# - docker_image: ubuntu:noble
# ros_distribution: jazzy
# ros_version: 2
# ROS 2 Rolling Ridley
# - docker_image: ubuntu:noble
# ros_distribution: rolling
# ros_version: 2
container:
image: ${{ matrix.docker_image }}
steps:
- name: Setup directories
- name: Setup workspace
run: mkdir -p ros_ws/src
- name: checkout
uses: actions/checkout@v3

- name: Checkout code
uses: actions/checkout@v4
with:
path: ros_ws/src

- name: Setup ROS environment
uses: ros-tooling/setup-ros@0.3.3
uses: ros-tooling/setup-ros@v0.7
with:
required-ros-distributions: ${{ matrix.ros_distribution }}

- name: Build and Test
uses: ros-tooling/action-ros-ci@0.2.5
uses: ros-tooling/action-ros-ci@v0.3
with:
package-name: turtlebot3_simulations
target-ros2-distro: ${{ matrix.ros_distribution }}
vcs-repo-file-url: "https://raw.githubusercontent.com/ROBOTIS-GIT/turtlebot3_simulations/ros2-devel/turtlebot3_simulations_ci.repos"

vcs-repo-file-url: "https://raw.githubusercontent.com/ROBOTIS-GIT/turtlebot3/main/turtlebot3_ci.repos"
package-name: |
turtlebot3_fake_node
turtlebot3_gazebo
turtlebot3_simulations
32 changes: 32 additions & 0 deletions .github/workflows/ros-lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# The name of the workflow
name: Lint

# Specifies the events that trigger the workflow
on:
pull_request:

# Defines a set of jobs to be run as part of the workflow
jobs:
ament_lint:
runs-on: ubuntu-latest
container:
image: rostooling/setup-ros-docker:ubuntu-noble-ros-rolling-ros-base-latest
strategy:
fail-fast: false
matrix:
linter: [cppcheck, cpplint, flake8, pep257, lint_cmake, xmllint, copyright]
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Setup ROS environment
uses: ros-tooling/[email protected]

- name: Run Linter
env:
AMENT_CPPCHECK_ALLOW_SLOW_VERSIONS: 1
uses: ros-tooling/action-ros-lint@master
with:
linter: ${{ matrix.linter }}
distribution: rolling
package-name: "*"
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
#define TURTLEBOT3_FAKE_NODE__TURTLEBOT3_FAKE_NODE_HPP_

#include <tf2/LinearMath/Quaternion.h>
#include <rclcpp/rclcpp.hpp>
#include <chrono>

#include <rclcpp/rclcpp.hpp>
#include "geometry_msgs/msg/transform_stamped.hpp"
#include "geometry_msgs/msg/twist.hpp"
#include "nav_msgs/msg/odometry.hpp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
#ifndef TURTLEBOT3_GAZEBO__TURTLEBOT3_DRIVE_HPP_
#define TURTLEBOT3_GAZEBO__TURTLEBOT3_DRIVE_HPP_

#include <tf2/LinearMath/Matrix3x3.h>
#include <tf2/LinearMath/Quaternion.h>
#include <geometry_msgs/msg/twist.hpp>
#include <nav_msgs/msg/odometry.hpp>
#include <rclcpp/rclcpp.hpp>
#include <sensor_msgs/msg/laser_scan.hpp>
#include <tf2/LinearMath/Matrix3x3.h>
#include <tf2/LinearMath/Quaternion.h>

#define DEG2RAD (M_PI / 180.0)
#define RAD2DEG (180.0 / M_PI)
Expand Down
137 changes: 137 additions & 0 deletions turtlebot3_gazebo/launch/multi_robot.launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
#
# Copyright 2019 ROBOTIS CO., LTD.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Authors: Joep Tool, HyunGyu

import os
import xml.etree.ElementTree as ET

from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import GroupAction
from launch.actions import IncludeLaunchDescription
from launch.actions import RegisterEventHandler
from launch.event_handlers import OnShutdown
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import PushRosNamespace


def generate_launch_description():
TURTLEBOT3_MODEL = os.environ['TURTLEBOT3_MODEL']

number_of_robots = 4
namespace = 'TB3'
pose = [[-2, -0.5], [0.5, -2], [2, 0.5], [-0.5, 2]]
model_folder = 'turtlebot3_' + TURTLEBOT3_MODEL
urdf_path = os.path.join(
get_package_share_directory('turtlebot3_gazebo'),
'models',
model_folder,
'model.sdf'
)
save_path = os.path.join(
get_package_share_directory('turtlebot3_gazebo'),
'models',
model_folder,
'tmp'
)
launch_file_dir = os.path.join(get_package_share_directory('turtlebot3_gazebo'), 'launch')
pkg_gazebo_ros = get_package_share_directory('gazebo_ros')

use_sim_time = LaunchConfiguration('use_sim_time', default='false')

world = os.path.join(
get_package_share_directory('turtlebot3_gazebo'),
'worlds',
'turtlebot3_world.world'
)

gzserver_cmd = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(pkg_gazebo_ros, 'launch', 'gzserver.launch.py')
),
launch_arguments={'world': world}.items()
)

gzclient_cmd = IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(pkg_gazebo_ros, 'launch', 'gzclient.launch.py')
)
)

robot_state_publisher_cmd_list = []

for count in range(number_of_robots):
robot_state_publisher_cmd_list.append(
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, 'robot_state_publisher.launch.py')
),
launch_arguments={
'use_sim_time': use_sim_time,
'frame_prefix': f'{namespace}_{count+1}'
}.items()
)
)

spawn_turtlebot_cmd_list = []

for count in range(number_of_robots):
tree = ET.parse(urdf_path)
root = tree.getroot()
for odom_frame_tag in root.iter('odometry_frame'):
odom_frame_tag.text = f'{namespace}_{count+1}/odom'
for base_frame_tag in root.iter('robot_base_frame'):
base_frame_tag.text = f'{namespace}_{count+1}/base_footprint'
for scan_frame_tag in root.iter('frame_name'):
scan_frame_tag.text = f'{namespace}_{count+1}/base_scan'
urdf_modified = ET.tostring(tree.getroot(), encoding='unicode')
urdf_modified = '<?xml version="1.0" ?>\n'+urdf_modified
with open(f'{save_path}{count+1}.sdf', 'w') as file:
file.write(urdf_modified)

spawn_turtlebot_cmd_list.append(
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(launch_file_dir, 'multi_spawn_turtlebot3.launch.py')
),
launch_arguments={
'x_pose': str(pose[count][0]),
'y_pose': str(pose[count][1]),
'robot_name': f'{TURTLEBOT3_MODEL}_{count+1}',
'namespace': f'{namespace}_{count+1}',
'sdf_path': f'{save_path}{count+1}.sdf'
}.items()
)
)

ld = LaunchDescription()
# Add the commands to the launch description
ld.add_action(gzserver_cmd)
ld.add_action(gzclient_cmd)
ld.add_action(RegisterEventHandler(
OnShutdown(
on_shutdown=lambda event,
context: [os.remove(f'{save_path}{count+1}.sdf') for count in range(number_of_robots)]
)
))
for count, spawn_turtlebot_cmd in enumerate(spawn_turtlebot_cmd_list, start=1):
ld.add_action(GroupAction([PushRosNamespace(f'{namespace}_{count}'),
robot_state_publisher_cmd_list[count-1],
spawn_turtlebot_cmd]))

return ld
60 changes: 60 additions & 0 deletions turtlebot3_gazebo/launch/multi_spawn_turtlebot3.launch.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
# Copyright 2019 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Authors: HyunGyu

import os

from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node


def generate_launch_description():
TURTLEBOT3_MODEL = os.environ['TURTLEBOT3_MODEL']

x_pose = LaunchConfiguration('x_pose', default='0.0')
y_pose = LaunchConfiguration('y_pose', default='0.0')
robot_name = LaunchConfiguration('robot_name', default=TURTLEBOT3_MODEL)
namespace = LaunchConfiguration('namespace', default='')
sdf_path = LaunchConfiguration('sdf_path', default='')

declare_x_position_cmd = DeclareLaunchArgument(
'x_pose', default_value='0.0',
description='Specify namespace of the robot')

declare_y_position_cmd = DeclareLaunchArgument(
'y_pose', default_value='0.0',
description='Specify namespace of the robot')
start_gazebo_ros_spawner_cmd = Node(
package='gazebo_ros',
executable='spawn_entity.py',
arguments=[
'-entity', robot_name,
'-file', sdf_path,
'-x', x_pose,
'-y', y_pose,
'-z', '0.01',
'-robot_namespace', namespace
],
output='screen',
)

ld = LaunchDescription()
ld.add_action(declare_x_position_cmd)
ld.add_action(declare_y_position_cmd)
ld.add_action(start_gazebo_ros_spawner_cmd)
return ld
9 changes: 5 additions & 4 deletions turtlebot3_gazebo/launch/robot_state_publisher.launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,16 @@
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument
from launch.substitutions import LaunchConfiguration
from launch.substitutions import LaunchConfiguration, PythonExpression
from launch_ros.actions import Node


def generate_launch_description():
TURTLEBOT3_MODEL = os.environ['TURTLEBOT3_MODEL']

use_sim_time = LaunchConfiguration('use_sim_time', default='false')
use_sim_time = LaunchConfiguration('use_sim_time', default='true')
urdf_file_name = 'turtlebot3_' + TURTLEBOT3_MODEL + '.urdf'
frame_prefix = LaunchConfiguration('frame_prefix', default='')

print('urdf_file_name : {}'.format(urdf_file_name))

Expand All @@ -46,15 +47,15 @@ def generate_launch_description():
'use_sim_time',
default_value='false',
description='Use simulation (Gazebo) clock if true'),

Node(
package='robot_state_publisher',
executable='robot_state_publisher',
name='robot_state_publisher',
output='screen',
parameters=[{
'use_sim_time': use_sim_time,
'robot_description': robot_desc
'robot_description': robot_desc,
'frame_prefix': PythonExpression(["'", frame_prefix, "/'"])
}],
),
])
Loading
Loading