Skip to content

Commit

Permalink
Fleet navigation ROS 2 package and assets (#450)
Browse files Browse the repository at this point in the history
Fleet navigation ROS2 package and assets

---------

Signed-off-by: Piotr Jaroszek <[email protected]>
  • Loading branch information
pijaro authored Aug 24, 2023
1 parent c5b7c01 commit 02ff6f8
Show file tree
Hide file tree
Showing 27 changed files with 2,813 additions and 35 deletions.
2 changes: 1 addition & 1 deletion .gitattributes
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

#
# Git LFS (see https://git-lfs.github.com/)
#
Expand Down Expand Up @@ -130,3 +129,4 @@ Templates/Ros2FleetRobotTemplate/docs/images/*.png filter= diff= merge= -text
Gems/ROS2/docs/**/*.png -filter -diff -merge
Templates/Ros2ProjectTemplate/Screenshots/*.png filter= diff= merge= -text
Templates/Ros2FleetRobotTemplate/Screenshots/*.png filter= diff= merge= -text
*.pgm filter=lfs diff=lfs merge=lfs -text
133 changes: 126 additions & 7 deletions Templates/Ros2FleetRobotTemplate/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ to install all required dependencies and create your project with a template (ma
## Spawning robots

The level contains spawn points configured to easily add more Proteus robots through ROS 2 calls.

This is done with the [Spawner Component](https://development--o3deorg.netlify.app/docs/user-guide/interactivity/robotics/concepts-and-components-overview/#spawner).
There are 4 spawn points already added in the level. You can use them all with the following service calls:

Expand All @@ -28,17 +29,135 @@ ros2 service call /spawn_entity gazebo_msgs/srv/SpawnEntity '{name: 'proteus', x
ros2 service call /spawn_entity gazebo_msgs/srv/SpawnEntity '{name: 'proteus', xml: 'spawnPoint4'}'
```

## Topics and frames
## Fleet navigation

This template comes with the example fleet navigation ROS 2 package called `o3de_fleet_nav`. You can find a prepared ROS 2 workspace in the `Examples` directory.

This package contains a modified code from `nav2_bringup` package (https://github.com/ros-planning/navigation2).

In this example, a fleet of robots is automatically spawned and each individual robot can be controlled via the Rviz2. An AMCL localization is used for robot localization.

> Notice: Before running an automated fleet example, please make sure your level doesn't contain any robots in it (they will be spawned).
### Fleet configuration

You can configure the fleet by modifying `Example/ros2_ws/src/o3de_fleet_nav/config/fleet_config.yaml` file:

```
fleet:
- robot_name: proteus
robot_namespace: robot1
position:
x: -6.0
y: 0.5
z: 0.2
- robot_name: proteus
robot_namespace: robot2
position:
x: -6.0
y: 7.5
z: 0.2
- robot_name: proteus
robot_namespace: robot3
position:
x: -6.0
y: -6.0
z: 0.2
```

This configuration file contains the data about each robot in a fleet:
- name (a type of the robot to spawn),
- namespace (must be unique per spawned robot),
- spawning position (spawn position is also used as an AMCL initial estimation).

In this example, only the `proteus` robot is supported.

You can modify contents of this file to add/remove robots or change their initial positions.

Every spawned robot will have its own namespace for all topics. For the first robot, these will be:
> Notice: You have to rebuild the ROS 2 workspace for changes to update.
- `/proteus_1/cmd_vel`: The topic to [control the robot](https://development--o3deorg.netlify.app/docs/user-guide/interactivity/robotics/concepts-and-components-overview/#robot-control).
- `/proteus_1/pc` - The topic of simulated LIDAR point cloud.
### Navigation configuration

You can configure navigation parameters by modifying `Examples/ros2_ws/src/o3de_fleet_nav/params/<ROS_DISTRO>/nav2_*.yaml` files.

Please visit the [nav2 configuration guide](https://navigation.ros.org/configuration/index.html) for a detailed description of the navigation parameters.

> Notice: You have to rebuild the ROS 2 workspace for changes to update.
### Topics and frames

Every spawned robot will have its own namespace for all topics. For the first robot ('robot1' namespace), these will be:

- `/robot1/cmd_vel`: The topic to [control the robot](https://development--o3deorg.netlify.app/docs/user-guide/interactivity/robotics/concepts-and-components-overview/#robot-control).
- `/robot1/scan` - The topic of simulated 2D laser scanner sensor.

The first spawned robot also provides the following transformations:

- `/proteus_1/odom`
- `/proteus_1/base_link`
- `/proteus_1/lidar`
- `/robot1/odom`
- `/robot1/base_link`
- `/robot1/lidar`

To understand more about transformations, see ROS 2 navigation [documentation](https://navigation.ros.org/setup_guides/transformation/setup_transforms.html).

## Using your robots in the simulation

You can also use your robots in the simulation. To do so, you need to:
- [import a robot](https://docs.o3de.org/docs/user-guide/interactivity/robotics/importing-robot/) from existing URDF file,
- or create a robot from scratch in a O3DE editor using ROS 2 gem components (see [Frames](https://docs.o3de.org/docs/user-guide/interactivity/robotics/concepts-and-components-overview)),
- make sure that your robot has a 2D scanner attached
- and publishes scans on `scan` topic (see [Sensors](https://docs.o3de.org/docs/user-guide/interactivity/robotics/concepts-and-components-overview/#robot-control)),
- is controlled via the `cmd_vel` topic (see [Robot Control](https://docs.o3de.org/docs/user-guide/interactivity/robotics/concepts-and-components-overview/#robot-control)).

When you have your robot set up:
- create a prefab out of it (skip if URDF importer did that for you),
- load the `Warehouse` level,
- assign the prefab to the `RobotSpawner` entity inside `ROS2 Spawner Component` with a preferred name.

Then you can alter `fleet_config.yaml` file to change the robot name to the assigned one, and start the fleet simulation with your robot!

### Building

- Source ROS 2:
```
. /opt/ros/humble/setup.bash
```

- Go to the ROS 2 workspace:
```
cd Examples/ros2_ws
```

- Install ROS 2 dependencies:
```
rosdep update
rosdep install --from-paths src -y --ignore-src
```

- Build workspace:
```
colcon build --symlink-install
```

### Running

- Run the `Warehouse` level in O3DE editor.

- Source ROS 2 and the `o3de_fleet_nav` workspace:
```
. /opt/ros/humble/setup.bash
```

- Source the `o3de_fleet_nav` workspace:
```
cd Examples/ros2_ws
. ./install/setup.bash
```

- Run the fleet example:
```
ros2 launch o3de_fleet_nav o3de_fleet_nav_launch.py
```

Few RViz2 windows should appear. You can use the "Nav2 Goal" button to send goal to the robot.

![RViz2](Screenshots/fleet_rviz.png)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
This package contains a modified code from "nav2_bringup" package (https://github.com/ros-planning/navigation2).
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
fleet:
- robot_name: proteus
robot_namespace: robot1
position:
x: -6.0
y: 0.5
z: 0.2
- robot_name: proteus
robot_namespace: robot2
position:
x: -6.0
y: 7.5
z: 0.2
- robot_name: proteus
robot_namespace: robot3
position:
x: -6.0
y: -6.0
z: 0.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Copyright (c) 2018 Intel Corporation
# Copyright (c) Contributors to the Open 3D Engine Project.
#
# 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.

import os
import yaml

from ament_index_python.packages import get_package_share_directory

from launch import LaunchDescription
from launch.actions import (DeclareLaunchArgument, ExecuteProcess, GroupAction,
IncludeLaunchDescription, LogInfo)
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, TextSubstitution
from nav2_common.launch import ReplaceString


def generate_launch_description():
# Get the launch directory
o3de_fleet_nav_dir = get_package_share_directory('o3de_fleet_nav')
o3de_launch_dir = os.path.join(o3de_fleet_nav_dir, 'launch')

# Load fleet configuration
fleet_config_file = os.path.join(o3de_fleet_nav_dir, 'config', 'fleet_config.yaml')
robots = []
with open(fleet_config_file, 'r') as f:
configuration = yaml.safe_load(f)
for robot in configuration["fleet"]:
robots.append(
{
"name": robot["robot_name"],
"namespace": robot["robot_namespace"],
"x_pose": robot["position"]["x"],
"y_pose": robot["position"]["y"],
"z_pose": robot["position"]["z"]
}
)

map_yaml_file = LaunchConfiguration('map')
autostart = LaunchConfiguration('autostart')
rviz_config_file = LaunchConfiguration('rviz_config')
use_rviz = LaunchConfiguration('use_rviz')
log_settings = LaunchConfiguration('log_settings', default='true')

distro = os.getenv('ROS_DISTRO')

# Declare the launch arguments
declare_map_yaml_cmd = DeclareLaunchArgument(
'map',
default_value=os.path.join(o3de_fleet_nav_dir, 'maps', 'map_warehouse.yaml'),
description='Full path to map file to load')

declare_robot_params_file_cmd = DeclareLaunchArgument(
'robot_params_file',
default_value=os.path.join(o3de_fleet_nav_dir, 'params', distro, 'nav2_multirobot_params.yaml'),
description='Full path to the ROS2 parameters file to use for all robot launched nodes')

declare_autostart_cmd = DeclareLaunchArgument(
'autostart', default_value='True',
description='Automatically startup the stacks')

declare_rviz_config_file_cmd = DeclareLaunchArgument(
'rviz_config',
default_value=os.path.join(o3de_fleet_nav_dir, 'rviz', 'nav2_namespaced_view.rviz'),
description='Full path to the RVIZ config file to use.')

declare_use_rviz_cmd = DeclareLaunchArgument(
'use_rviz',
default_value='True',
description='Whether to start RVIZ')

# Launch navigation for each robot in fleet configuration
nav_instances_cmds = []
for robot in robots:
params_file = LaunchConfiguration("robot_params_file")

configured_params = ReplaceString(
source_file=params_file,
replacements={
'<robot_name>': robot['name'],
'<robot_namespace>': robot['namespace'],
'<robot_initial_pose_x>': str(robot['x_pose']),
'<robot_initial_pose_y>': str(robot['y_pose']),
'<robot_initial_pose_z>': str(robot['z_pose'])
})


group = GroupAction([
IncludeLaunchDescription(
PythonLaunchDescriptionSource(
os.path.join(o3de_launch_dir, 'o3de_rviz_launch.py')),
condition=IfCondition(use_rviz),
launch_arguments={
'namespace': TextSubstitution(text=robot['namespace']),
'use_namespace': 'True',
'rviz_config': rviz_config_file}.items()),

IncludeLaunchDescription(
PythonLaunchDescriptionSource(os.path.join(o3de_launch_dir,
'o3de_nav_launch.py')),
launch_arguments={'namespace': robot['namespace'],
'use_namespace': 'True',
'map': map_yaml_file,
'use_sim_time': 'True',
'params_file': configured_params,
'autostart': autostart}.items()),

LogInfo(
condition=IfCondition(log_settings),
msg=['Launching ', robot['namespace']]),
LogInfo(
condition=IfCondition(log_settings),
msg=[robot['namespace'], ' map yaml: ', map_yaml_file]),
LogInfo(
condition=IfCondition(log_settings),
msg=[robot['namespace'], ' params yaml: ', params_file]),
LogInfo(
condition=IfCondition(log_settings),
msg=[robot['namespace'], ' rviz config file: ', rviz_config_file]),
LogInfo(
condition=IfCondition(log_settings),
msg=[robot['namespace'], ' autostart: ', autostart])
])

nav_instances_cmds.append(group)

# Create the launch description and populate
ld = LaunchDescription()

# Declare the launch options
ld.add_action(declare_map_yaml_cmd)
ld.add_action(declare_robot_params_file_cmd)
ld.add_action(declare_use_rviz_cmd)
ld.add_action(declare_autostart_cmd)
ld.add_action(declare_rviz_config_file_cmd)

# Launch robots
for simulation_instance_cmd in nav_instances_cmds:
ld.add_action(simulation_instance_cmd)

return ld
Loading

0 comments on commit 02ff6f8

Please sign in to comment.