add humble-navigation2
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
# Nav2 Simple (Python3) Commander
|
||||
|
||||
## Overview
|
||||
|
||||
The goal of this package is to provide a "navigation as a library" capability to Python3 users. We provide an API that handles all the ROS2-y and Action Server-y things for you such that you can focus on building an application leveraging the capabilities of Nav2. We also provide you with demos and examples of API usage to build common basic capabilities in autonomous mobile robotics.
|
||||
|
||||
This was built by [Steve Macenski](https://www.linkedin.com/in/steve-macenski-41a985101/) at [Samsung Research](https://www.sra.samsung.com/), with initial prototypes being prepared for the Keynote at the [2021 ROS Developers Day](https://www.theconstructsim.com/ros-developers-day-2021/) conference (code can be found [here](https://github.com/SteveMacenski/nav2_rosdevday_2021)).
|
||||
|
||||

|
||||
|
||||
## API
|
||||
|
||||
See its [API Guide Page](https://navigation.ros.org/commander_api/index.html) for additional parameter descriptions.
|
||||
|
||||
The methods provided by the basic navigator are shown below, with inputs and expected returns. If a server fails, it may throw an exception or return a `None` object, so please be sure to properly wrap your navigation calls in try/catch and check results for `None` type.
|
||||
|
||||
New as of September 2023: the simple navigator constructor will accept a `namespace` field to support multi-robot applications or namespaced Nav2 launches.
|
||||
|
||||
| Robot Navigator Method | Description |
|
||||
| --------------------------------- | -------------------------------------------------------------------------- |
|
||||
| setInitialPose(initial_pose) | Sets the initial pose (`PoseStamped`) of the robot to localization. |
|
||||
| goThroughPoses(poses, behavior_tree='') | Requests the robot to drive through a set of poses (list of `PoseStamped`).|
|
||||
| goToPose(pose, behavior_tree='') | Requests the robot to drive to a pose (`PoseStamped`). |
|
||||
| followWaypoints(poses) | Requests the robot to follow a set of waypoints (list of `PoseStamped`). This will execute the specific `TaskExecutor` at each pose. |
|
||||
| followPath(path, controller_id='', goal_checker_id='') | Requests the robot to follow a path from a starting to a goal `PoseStamped`, `nav_msgs/Path`. |
|
||||
| spin(spin_dist=1.57, time_allowance=10) | Requests the robot to performs an in-place rotation by a given angle. |
|
||||
| backup(backup_dist=0.15, backup_speed=0.025, time_allowance=10) | Requests the robot to back up by a given distance. |
|
||||
| cancelTask() | Cancel an ongoing task request.|
|
||||
| isTaskComplete() | Checks if task is complete yet, times out at `100ms`. Returns `True` if completed and `False` if still going. |
|
||||
| getFeedback() | Gets feedback from task, returns action server feedback object. |
|
||||
| getResult() | Gets final result of task, to be called after `isTaskComplete` returns `True`. Returns action server result object. |
|
||||
| getPath(start, goal, planner_id='', use_start=False) | Gets a path from a starting to a goal `PoseStamped`, `nav_msgs/Path`. |
|
||||
| getPathThroughPoses(start, goals, planner_id='', use_start=False) | Gets a path through a starting to a set of goals, a list of `PoseStamped`, `nav_msgs/Path`. |
|
||||
| smoothPath(path, smoother_id='', max_duration=2.0, check_for_collision=False) | Smooths a given `nav_msgs/msg/Path` path. |
|
||||
| changeMap(map_filepath) | Requests a change from the current map to `map_filepath`'s yaml. |
|
||||
| clearAllCostmaps() | Clears both the global and local costmaps. |
|
||||
| clearLocalCostmap() | Clears the local costmap. |
|
||||
| clearGlobalCostmap() | Clears the global costmap. |
|
||||
| getGlobalCostmap() | Returns the global costmap, `nav2_msgs/Costmap` |
|
||||
| getLocalCostmap() | Returns the local costmap, `nav2_msgs/Costmap` |
|
||||
| waitUntilNav2Active(navigator='bt_navigator, localizer='amcl') | Blocks until Nav2 is completely online and lifecycle nodes are in the active state. To be used in conjunction with autostart or external lifecycle bringup. Custom navigator and localizer nodes can be specified |
|
||||
| lifecycleStartup() | Sends a request to all lifecycle management servers to bring them into the active state, to be used if autostart is `false` and you want this program to control Nav2's lifecycle. |
|
||||
| lifecycleShutdown() | Sends a request to all lifecycle management servers to shut them down. |
|
||||
| destroyNode() | Releases the resources used by the object. |
|
||||
|
||||
A general template for building applications is as follows:
|
||||
|
||||
``` python3
|
||||
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator
|
||||
import rclpy
|
||||
|
||||
rclpy.init()
|
||||
|
||||
nav = BasicNavigator()
|
||||
...
|
||||
nav.setInitialPose(init_pose)
|
||||
nav.waitUntilNav2Active() # if autostarted, else use `lifecycleStartup()`
|
||||
...
|
||||
path = nav.getPath(init_pose, goal_pose)
|
||||
smoothed_path = nav.smoothPath(path)
|
||||
...
|
||||
nav.goToPose(goal_pose)
|
||||
while not nav.isTaskComplete():
|
||||
feedback = nav.getFeedback()
|
||||
if feedback.navigation_duration > 600:
|
||||
nav.cancelTask()
|
||||
...
|
||||
result = nav.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
print('Goal succeeded!')
|
||||
elif result == TaskResult.CANCELED:
|
||||
print('Goal was canceled!')
|
||||
elif result == TaskResult.FAILED:
|
||||
print('Goal failed!')
|
||||
```
|
||||
|
||||
## Usage of Demos and Examples
|
||||
|
||||
Make sure to install the `aws_robomaker_small_warehouse_world` package or build it in your local workspace alongside Nav2. It can be found [here](https://github.com/aws-robotics/aws-robomaker-small-warehouse-world). The demonstrations, examples, and launch files assume you're working with this gazebo world (such that the hard-programmed shelf locations and routes highlighting the API are meaningful).
|
||||
|
||||
Make sure you have set the model directory of turtlebot3 simulation and aws warehouse world to the `GAZEBO_MODEL_PATH`. There are 2 main ways to run the demos of the `nav2_simple_commander` API.
|
||||
|
||||
### Automatically
|
||||
|
||||
The main benefit of this is automatically showing the above demonstrations in a single command for the default robot model and world. This will make use of Nav2's default robot and parameters set out in the main simulation launch file in `nav2_bringup`.
|
||||
|
||||
``` bash
|
||||
# Launch the launch file for the demo / example
|
||||
ros2 launch nav2_simple_commander security_demo_launch.py
|
||||
```
|
||||
|
||||
This will bring up the robot in the AWS Warehouse in a reasonable position, launch the autonomy script, and complete some task to demonstrate the `nav2_simple_commander` API.
|
||||
|
||||
### Manually
|
||||
|
||||
The main benefit of this is to be able to launch alternative robot models or different navigation configurations than the default for a specific technology demonstration. As long as Nav2 and the simulation (or physical robot) is running, the simple python commander examples / demos don't care what the robot is or how it got there. Since the examples / demos do contain hard-programmed item locations or routes, you should still utilize the AWS Warehouse. Obviously these are easy to update if you wish to adapt these examples / demos to another environment.
|
||||
|
||||
``` bash
|
||||
# Terminal 1: launch your robot navigation and simulation (or physical robot). For example
|
||||
ros2 launch nav2_bringup tb3_simulation_launch.py world:=/path/to/aws_robomaker_small_warehouse_world/.world map:=/path/to/aws_robomaker_small_warehouse_world/.yaml
|
||||
|
||||
# Terminal 2: launch your autonomy / application demo or example. For example
|
||||
ros2 run nav2_simple_commander demo_security
|
||||
```
|
||||
|
||||
Then you should see the autonomy application running!
|
||||
|
||||
## Examples
|
||||
|
||||
The `nav2_simple_commander` has a few examples to highlight the API functions available to you as a user:
|
||||
|
||||
- `example_nav_to_pose.py` - Demonstrates the navigate to pose capabilities of the navigator, as well as a number of auxiliary methods.
|
||||
- `example_nav_through_poses.py` - Demonstrates the navigate through poses capabilities of the navigator, as well as a number of auxiliary methods.
|
||||
- `example_waypoint_follower.py` - Demonstrates the waypoint following capabilities of the navigator, as well as a number of auxiliary methods.
|
||||
- `example_follow_path.py` - Demonstrates the path following capabilities of the navigator, as well as a number of auxiliary methods such as path smoothing.
|
||||
## Demos
|
||||
|
||||
The `nav2_simple_commander` has a few demonstrations to highlight a couple of simple autonomy applications you can build using the `nav2_simple_commander` API:
|
||||
|
||||
- `demo_security.py` - A simple security robot application, showing how to have a robot follow a security route using Navigate Through Poses to do a patrol route, indefinitely.
|
||||
- `demo_picking.py` - A simple item picking application, showing how to have a robot drive to a specific shelf in a warehouse to either pick an item or have a person place an item into a basket and deliver it to a destination for shipping using Navigate To Pose.
|
||||
- `demo_inspection.py` - A simple shelf inspection application, showing how to use the Waypoint Follower and task executors to take pictures, RFID scans, etc of shelves to analyze the current shelf statuses and locate items in the warehouse.
|
||||
@@ -0,0 +1,99 @@
|
||||
# Copyright (c) 2021 Samsung Research America
|
||||
# Copyright (c) 2022 Joshua Wallace
|
||||
#
|
||||
# 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
|
||||
|
||||
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, PythonExpression
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
warehouse_dir = get_package_share_directory('aws_robomaker_small_warehouse_world')
|
||||
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
|
||||
python_commander_dir = get_package_share_directory('nav2_simple_commander')
|
||||
|
||||
map_yaml_file = os.path.join(warehouse_dir, 'maps', '005', 'map.yaml')
|
||||
world = os.path.join(python_commander_dir, 'warehouse.world')
|
||||
|
||||
# Launch configuration variables
|
||||
use_rviz = LaunchConfiguration('use_rviz')
|
||||
headless = LaunchConfiguration('headless')
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_use_rviz_cmd = DeclareLaunchArgument(
|
||||
'use_rviz',
|
||||
default_value='True',
|
||||
description='Whether to start RVIZ')
|
||||
|
||||
declare_simulator_cmd = DeclareLaunchArgument(
|
||||
'headless',
|
||||
default_value='False',
|
||||
description='Whether to execute gzclient)')
|
||||
|
||||
# start the simulation
|
||||
start_gazebo_server_cmd = ExecuteProcess(
|
||||
cmd=['gzserver', '-s', 'libgazebo_ros_factory.so', world],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
start_gazebo_client_cmd = ExecuteProcess(
|
||||
condition=IfCondition(PythonExpression(['not ', headless])),
|
||||
cmd=['gzclient'],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
urdf = os.path.join(nav2_bringup_dir, 'urdf', 'turtlebot3_waffle.urdf')
|
||||
start_robot_state_publisher_cmd = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
arguments=[urdf])
|
||||
|
||||
# start the visualization
|
||||
rviz_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'rviz_launch.py')),
|
||||
condition=IfCondition(use_rviz),
|
||||
launch_arguments={'namespace': '',
|
||||
'use_namespace': 'False'}.items())
|
||||
|
||||
# start navigation
|
||||
bringup_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')),
|
||||
launch_arguments={'map': map_yaml_file}.items())
|
||||
|
||||
# start the demo autonomy task
|
||||
demo_cmd = Node(
|
||||
package='nav2_simple_commander',
|
||||
executable='example_assisted_teleop',
|
||||
emulate_tty=True,
|
||||
output='screen')
|
||||
|
||||
ld = LaunchDescription()
|
||||
ld.add_action(declare_use_rviz_cmd)
|
||||
ld.add_action(declare_simulator_cmd)
|
||||
ld.add_action(start_gazebo_server_cmd)
|
||||
ld.add_action(start_gazebo_client_cmd)
|
||||
ld.add_action(start_robot_state_publisher_cmd)
|
||||
ld.add_action(rviz_cmd)
|
||||
ld.add_action(bringup_cmd)
|
||||
ld.add_action(demo_cmd)
|
||||
return ld
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright (c) 2021 Samsung Research America
|
||||
#
|
||||
# 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
|
||||
|
||||
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, PythonExpression
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
warehouse_dir = get_package_share_directory('aws_robomaker_small_warehouse_world')
|
||||
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
|
||||
python_commander_dir = get_package_share_directory('nav2_simple_commander')
|
||||
|
||||
map_yaml_file = os.path.join(warehouse_dir, 'maps', '005', 'map.yaml')
|
||||
world = os.path.join(python_commander_dir, 'warehouse.world')
|
||||
|
||||
# Launch configuration variables
|
||||
use_rviz = LaunchConfiguration('use_rviz')
|
||||
headless = LaunchConfiguration('headless')
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_use_rviz_cmd = DeclareLaunchArgument(
|
||||
'use_rviz',
|
||||
default_value='True',
|
||||
description='Whether to start RVIZ')
|
||||
|
||||
declare_simulator_cmd = DeclareLaunchArgument(
|
||||
'headless',
|
||||
default_value='False',
|
||||
description='Whether to execute gzclient)')
|
||||
|
||||
# start the simulation
|
||||
start_gazebo_server_cmd = ExecuteProcess(
|
||||
cmd=['gzserver', '-s', 'libgazebo_ros_factory.so', world],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
start_gazebo_client_cmd = ExecuteProcess(
|
||||
condition=IfCondition(PythonExpression(['not ', headless])),
|
||||
cmd=['gzclient'],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
urdf = os.path.join(nav2_bringup_dir, 'urdf', 'turtlebot3_waffle.urdf')
|
||||
start_robot_state_publisher_cmd = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
arguments=[urdf])
|
||||
|
||||
# start the visualization
|
||||
rviz_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'rviz_launch.py')),
|
||||
condition=IfCondition(use_rviz),
|
||||
launch_arguments={'namespace': '',
|
||||
'use_namespace': 'False'}.items())
|
||||
|
||||
# start navigation
|
||||
bringup_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')),
|
||||
launch_arguments={'map': map_yaml_file}.items())
|
||||
|
||||
# start the demo autonomy task
|
||||
demo_cmd = Node(
|
||||
package='nav2_simple_commander',
|
||||
executable='example_follow_path',
|
||||
emulate_tty=True,
|
||||
output='screen')
|
||||
|
||||
ld = LaunchDescription()
|
||||
ld.add_action(declare_use_rviz_cmd)
|
||||
ld.add_action(declare_simulator_cmd)
|
||||
ld.add_action(start_gazebo_server_cmd)
|
||||
ld.add_action(start_gazebo_client_cmd)
|
||||
ld.add_action(start_robot_state_publisher_cmd)
|
||||
ld.add_action(rviz_cmd)
|
||||
ld.add_action(bringup_cmd)
|
||||
ld.add_action(demo_cmd)
|
||||
return ld
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright (c) 2021 Samsung Research America
|
||||
#
|
||||
# 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
|
||||
|
||||
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, PythonExpression
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
warehouse_dir = get_package_share_directory('aws_robomaker_small_warehouse_world')
|
||||
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
|
||||
python_commander_dir = get_package_share_directory('nav2_simple_commander')
|
||||
|
||||
map_yaml_file = os.path.join(warehouse_dir, 'maps', '005', 'map.yaml')
|
||||
world = os.path.join(python_commander_dir, 'warehouse.world')
|
||||
|
||||
# Launch configuration variables
|
||||
use_rviz = LaunchConfiguration('use_rviz')
|
||||
headless = LaunchConfiguration('headless')
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_use_rviz_cmd = DeclareLaunchArgument(
|
||||
'use_rviz',
|
||||
default_value='True',
|
||||
description='Whether to start RVIZ')
|
||||
|
||||
declare_simulator_cmd = DeclareLaunchArgument(
|
||||
'headless',
|
||||
default_value='False',
|
||||
description='Whether to execute gzclient)')
|
||||
|
||||
# start the simulation
|
||||
start_gazebo_server_cmd = ExecuteProcess(
|
||||
cmd=['gzserver', '-s', 'libgazebo_ros_factory.so', world],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
start_gazebo_client_cmd = ExecuteProcess(
|
||||
condition=IfCondition(PythonExpression(['not ', headless])),
|
||||
cmd=['gzclient'],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
urdf = os.path.join(nav2_bringup_dir, 'urdf', 'turtlebot3_waffle.urdf')
|
||||
start_robot_state_publisher_cmd = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
arguments=[urdf])
|
||||
|
||||
# start the visualization
|
||||
rviz_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'rviz_launch.py')),
|
||||
condition=IfCondition(use_rviz),
|
||||
launch_arguments={'namespace': '',
|
||||
'use_namespace': 'False'}.items())
|
||||
|
||||
# start navigation
|
||||
bringup_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')),
|
||||
launch_arguments={'map': map_yaml_file}.items())
|
||||
|
||||
# start the demo autonomy task
|
||||
demo_cmd = Node(
|
||||
package='nav2_simple_commander',
|
||||
executable='demo_inspection',
|
||||
emulate_tty=True,
|
||||
output='screen')
|
||||
|
||||
ld = LaunchDescription()
|
||||
ld.add_action(declare_use_rviz_cmd)
|
||||
ld.add_action(declare_simulator_cmd)
|
||||
ld.add_action(start_gazebo_server_cmd)
|
||||
ld.add_action(start_gazebo_client_cmd)
|
||||
ld.add_action(start_robot_state_publisher_cmd)
|
||||
ld.add_action(rviz_cmd)
|
||||
ld.add_action(bringup_cmd)
|
||||
ld.add_action(demo_cmd)
|
||||
return ld
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright (c) 2021 Samsung Research America
|
||||
#
|
||||
# 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
|
||||
|
||||
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, PythonExpression
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
warehouse_dir = get_package_share_directory('aws_robomaker_small_warehouse_world')
|
||||
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
|
||||
python_commander_dir = get_package_share_directory('nav2_simple_commander')
|
||||
|
||||
map_yaml_file = os.path.join(warehouse_dir, 'maps', '005', 'map.yaml')
|
||||
world = os.path.join(python_commander_dir, 'warehouse.world')
|
||||
|
||||
# Launch configuration variables
|
||||
use_rviz = LaunchConfiguration('use_rviz')
|
||||
headless = LaunchConfiguration('headless')
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_use_rviz_cmd = DeclareLaunchArgument(
|
||||
'use_rviz',
|
||||
default_value='True',
|
||||
description='Whether to start RVIZ')
|
||||
|
||||
declare_simulator_cmd = DeclareLaunchArgument(
|
||||
'headless',
|
||||
default_value='False',
|
||||
description='Whether to execute gzclient)')
|
||||
|
||||
# start the simulation
|
||||
start_gazebo_server_cmd = ExecuteProcess(
|
||||
cmd=['gzserver', '-s', 'libgazebo_ros_factory.so', world],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
start_gazebo_client_cmd = ExecuteProcess(
|
||||
condition=IfCondition(PythonExpression(['not ', headless])),
|
||||
cmd=['gzclient'],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
urdf = os.path.join(nav2_bringup_dir, 'urdf', 'turtlebot3_waffle.urdf')
|
||||
start_robot_state_publisher_cmd = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
arguments=[urdf])
|
||||
|
||||
# start the visualization
|
||||
rviz_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'rviz_launch.py')),
|
||||
condition=IfCondition(use_rviz),
|
||||
launch_arguments={'namespace': '',
|
||||
'use_namespace': 'False'}.items())
|
||||
|
||||
# start navigation
|
||||
bringup_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')),
|
||||
launch_arguments={'map': map_yaml_file}.items())
|
||||
|
||||
# start the demo autonomy task
|
||||
demo_cmd = Node(
|
||||
package='nav2_simple_commander',
|
||||
executable='example_nav_through_poses',
|
||||
emulate_tty=True,
|
||||
output='screen')
|
||||
|
||||
ld = LaunchDescription()
|
||||
ld.add_action(declare_use_rviz_cmd)
|
||||
ld.add_action(declare_simulator_cmd)
|
||||
ld.add_action(start_gazebo_server_cmd)
|
||||
ld.add_action(start_gazebo_client_cmd)
|
||||
ld.add_action(start_robot_state_publisher_cmd)
|
||||
ld.add_action(rviz_cmd)
|
||||
ld.add_action(bringup_cmd)
|
||||
ld.add_action(demo_cmd)
|
||||
return ld
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright (c) 2021 Samsung Research America
|
||||
#
|
||||
# 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
|
||||
|
||||
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, PythonExpression
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
warehouse_dir = get_package_share_directory('aws_robomaker_small_warehouse_world')
|
||||
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
|
||||
python_commander_dir = get_package_share_directory('nav2_simple_commander')
|
||||
|
||||
map_yaml_file = os.path.join(warehouse_dir, 'maps', '005', 'map.yaml')
|
||||
world = os.path.join(python_commander_dir, 'warehouse.world')
|
||||
|
||||
# Launch configuration variables
|
||||
use_rviz = LaunchConfiguration('use_rviz')
|
||||
headless = LaunchConfiguration('headless')
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_use_rviz_cmd = DeclareLaunchArgument(
|
||||
'use_rviz',
|
||||
default_value='True',
|
||||
description='Whether to start RVIZ')
|
||||
|
||||
declare_simulator_cmd = DeclareLaunchArgument(
|
||||
'headless',
|
||||
default_value='False',
|
||||
description='Whether to execute gzclient)')
|
||||
|
||||
# start the simulation
|
||||
start_gazebo_server_cmd = ExecuteProcess(
|
||||
cmd=['gzserver', '-s', 'libgazebo_ros_factory.so', world],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
start_gazebo_client_cmd = ExecuteProcess(
|
||||
condition=IfCondition(PythonExpression(['not ', headless])),
|
||||
cmd=['gzclient'],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
urdf = os.path.join(nav2_bringup_dir, 'urdf', 'turtlebot3_waffle.urdf')
|
||||
start_robot_state_publisher_cmd = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
arguments=[urdf])
|
||||
|
||||
# start the visualization
|
||||
rviz_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'rviz_launch.py')),
|
||||
condition=IfCondition(use_rviz),
|
||||
launch_arguments={'namespace': '',
|
||||
'use_namespace': 'False'}.items())
|
||||
|
||||
# start navigation
|
||||
bringup_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')),
|
||||
launch_arguments={'map': map_yaml_file}.items())
|
||||
|
||||
# start the demo autonomy task
|
||||
demo_cmd = Node(
|
||||
package='nav2_simple_commander',
|
||||
executable='example_nav_to_pose',
|
||||
emulate_tty=True,
|
||||
output='screen')
|
||||
|
||||
ld = LaunchDescription()
|
||||
ld.add_action(declare_use_rviz_cmd)
|
||||
ld.add_action(declare_simulator_cmd)
|
||||
ld.add_action(start_gazebo_server_cmd)
|
||||
ld.add_action(start_gazebo_client_cmd)
|
||||
ld.add_action(start_robot_state_publisher_cmd)
|
||||
ld.add_action(rviz_cmd)
|
||||
ld.add_action(bringup_cmd)
|
||||
ld.add_action(demo_cmd)
|
||||
return ld
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright (c) 2021 Samsung Research America
|
||||
#
|
||||
# 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
|
||||
|
||||
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, PythonExpression
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
warehouse_dir = get_package_share_directory('aws_robomaker_small_warehouse_world')
|
||||
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
|
||||
python_commander_dir = get_package_share_directory('nav2_simple_commander')
|
||||
|
||||
map_yaml_file = os.path.join(warehouse_dir, 'maps', '005', 'map.yaml')
|
||||
world = os.path.join(python_commander_dir, 'warehouse.world')
|
||||
|
||||
# Launch configuration variables
|
||||
use_rviz = LaunchConfiguration('use_rviz')
|
||||
headless = LaunchConfiguration('headless')
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_use_rviz_cmd = DeclareLaunchArgument(
|
||||
'use_rviz',
|
||||
default_value='True',
|
||||
description='Whether to start RVIZ')
|
||||
|
||||
declare_simulator_cmd = DeclareLaunchArgument(
|
||||
'headless',
|
||||
default_value='False',
|
||||
description='Whether to execute gzclient)')
|
||||
|
||||
# start the simulation
|
||||
start_gazebo_server_cmd = ExecuteProcess(
|
||||
cmd=['gzserver', '-s', 'libgazebo_ros_factory.so', world],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
start_gazebo_client_cmd = ExecuteProcess(
|
||||
condition=IfCondition(PythonExpression(['not ', headless])),
|
||||
cmd=['gzclient'],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
urdf = os.path.join(nav2_bringup_dir, 'urdf', 'turtlebot3_waffle.urdf')
|
||||
start_robot_state_publisher_cmd = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
arguments=[urdf])
|
||||
|
||||
# start the visualization
|
||||
rviz_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'rviz_launch.py')),
|
||||
condition=IfCondition(use_rviz),
|
||||
launch_arguments={'namespace': '',
|
||||
'use_namespace': 'False'}.items())
|
||||
|
||||
# start navigation
|
||||
bringup_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')),
|
||||
launch_arguments={'map': map_yaml_file}.items())
|
||||
|
||||
# start the demo autonomy task
|
||||
demo_cmd = Node(
|
||||
package='nav2_simple_commander',
|
||||
executable='demo_picking',
|
||||
emulate_tty=True,
|
||||
output='screen')
|
||||
|
||||
ld = LaunchDescription()
|
||||
ld.add_action(declare_use_rviz_cmd)
|
||||
ld.add_action(declare_simulator_cmd)
|
||||
ld.add_action(start_gazebo_server_cmd)
|
||||
ld.add_action(start_gazebo_client_cmd)
|
||||
ld.add_action(start_robot_state_publisher_cmd)
|
||||
ld.add_action(rviz_cmd)
|
||||
ld.add_action(bringup_cmd)
|
||||
ld.add_action(demo_cmd)
|
||||
return ld
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright (c) 2021 Samsung Research America
|
||||
#
|
||||
# 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
|
||||
|
||||
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, PythonExpression
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
warehouse_dir = get_package_share_directory('aws_robomaker_small_warehouse_world')
|
||||
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
|
||||
python_commander_dir = get_package_share_directory('nav2_simple_commander')
|
||||
|
||||
map_yaml_file = os.path.join(warehouse_dir, 'maps', '005', 'map.yaml')
|
||||
world = os.path.join(python_commander_dir, 'warehouse.world')
|
||||
|
||||
# Launch configuration variables
|
||||
use_rviz = LaunchConfiguration('use_rviz')
|
||||
headless = LaunchConfiguration('headless')
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_use_rviz_cmd = DeclareLaunchArgument(
|
||||
'use_rviz',
|
||||
default_value='True',
|
||||
description='Whether to start RVIZ')
|
||||
|
||||
declare_simulator_cmd = DeclareLaunchArgument(
|
||||
'headless',
|
||||
default_value='False',
|
||||
description='Whether to execute gzclient)')
|
||||
|
||||
# start the simulation
|
||||
start_gazebo_server_cmd = ExecuteProcess(
|
||||
cmd=['gzserver', '-s', 'libgazebo_ros_factory.so', world],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
start_gazebo_client_cmd = ExecuteProcess(
|
||||
condition=IfCondition(PythonExpression(['not ', headless])),
|
||||
cmd=['gzclient'],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
urdf = os.path.join(nav2_bringup_dir, 'urdf', 'turtlebot3_waffle.urdf')
|
||||
start_robot_state_publisher_cmd = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
arguments=[urdf])
|
||||
|
||||
# start the visualization
|
||||
rviz_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'rviz_launch.py')),
|
||||
condition=IfCondition(use_rviz),
|
||||
launch_arguments={'namespace': '',
|
||||
'use_namespace': 'False'}.items())
|
||||
|
||||
# start navigation
|
||||
bringup_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')),
|
||||
launch_arguments={'map': map_yaml_file}.items())
|
||||
|
||||
# start the demo autonomy task
|
||||
demo_cmd = Node(
|
||||
package='nav2_simple_commander',
|
||||
executable='demo_recoveries',
|
||||
emulate_tty=True,
|
||||
output='screen')
|
||||
|
||||
ld = LaunchDescription()
|
||||
ld.add_action(declare_use_rviz_cmd)
|
||||
ld.add_action(declare_simulator_cmd)
|
||||
ld.add_action(start_gazebo_server_cmd)
|
||||
ld.add_action(start_gazebo_client_cmd)
|
||||
ld.add_action(start_robot_state_publisher_cmd)
|
||||
ld.add_action(rviz_cmd)
|
||||
ld.add_action(bringup_cmd)
|
||||
ld.add_action(demo_cmd)
|
||||
return ld
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright (c) 2021 Samsung Research America
|
||||
#
|
||||
# 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
|
||||
|
||||
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, PythonExpression
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
warehouse_dir = get_package_share_directory('aws_robomaker_small_warehouse_world')
|
||||
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
|
||||
python_commander_dir = get_package_share_directory('nav2_simple_commander')
|
||||
|
||||
map_yaml_file = os.path.join(warehouse_dir, 'maps', '005', 'map.yaml')
|
||||
world = os.path.join(python_commander_dir, 'warehouse.world')
|
||||
|
||||
# Launch configuration variables
|
||||
use_rviz = LaunchConfiguration('use_rviz')
|
||||
headless = LaunchConfiguration('headless')
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_use_rviz_cmd = DeclareLaunchArgument(
|
||||
'use_rviz',
|
||||
default_value='True',
|
||||
description='Whether to start RVIZ')
|
||||
|
||||
declare_simulator_cmd = DeclareLaunchArgument(
|
||||
'headless',
|
||||
default_value='False',
|
||||
description='Whether to execute gzclient)')
|
||||
|
||||
# start the simulation
|
||||
start_gazebo_server_cmd = ExecuteProcess(
|
||||
cmd=['gzserver', '-s', 'libgazebo_ros_factory.so', world],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
start_gazebo_client_cmd = ExecuteProcess(
|
||||
condition=IfCondition(PythonExpression(['not ', headless])),
|
||||
cmd=['gzclient'],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
urdf = os.path.join(nav2_bringup_dir, 'urdf', 'turtlebot3_waffle.urdf')
|
||||
start_robot_state_publisher_cmd = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
arguments=[urdf])
|
||||
|
||||
# start the visualization
|
||||
rviz_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'rviz_launch.py')),
|
||||
condition=IfCondition(use_rviz),
|
||||
launch_arguments={'namespace': '',
|
||||
'use_namespace': 'False'}.items())
|
||||
|
||||
# start navigation
|
||||
bringup_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')),
|
||||
launch_arguments={'map': map_yaml_file}.items())
|
||||
|
||||
# start the demo autonomy task
|
||||
demo_cmd = Node(
|
||||
package='nav2_simple_commander',
|
||||
executable='demo_security',
|
||||
emulate_tty=True,
|
||||
output='screen')
|
||||
|
||||
ld = LaunchDescription()
|
||||
ld.add_action(declare_use_rviz_cmd)
|
||||
ld.add_action(declare_simulator_cmd)
|
||||
ld.add_action(start_gazebo_server_cmd)
|
||||
ld.add_action(start_gazebo_client_cmd)
|
||||
ld.add_action(start_robot_state_publisher_cmd)
|
||||
ld.add_action(rviz_cmd)
|
||||
ld.add_action(bringup_cmd)
|
||||
ld.add_action(demo_cmd)
|
||||
return ld
|
||||
@@ -0,0 +1,683 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<sdf version="1.6">
|
||||
<world name="default">
|
||||
|
||||
<gravity>0 0 -9.8</gravity>
|
||||
<physics default="0" name="default_physics" type="ode">
|
||||
<max_step_size>0.001</max_step_size>
|
||||
<real_time_factor>1</real_time_factor>
|
||||
<real_time_update_rate>1000</real_time_update_rate>
|
||||
</physics>
|
||||
|
||||
|
||||
<!--model name="aws_robomaker_warehouse_RoofB_01_001">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_RoofB_01</uri>
|
||||
</include>
|
||||
<pose frame="">0.0 0.0 0 0 0 0</pose>
|
||||
</model-->
|
||||
|
||||
<model name="aws_robomaker_warehouse_ShelfF_01_001">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ShelfF_01</uri>
|
||||
</include>
|
||||
<pose frame="">-5.795143 -0.956635 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_WallB_01_001">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_WallB_01</uri>
|
||||
</include>
|
||||
<pose frame="">0.0 0.0 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ShelfE_01_001">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ShelfE_01</uri>
|
||||
</include>
|
||||
<pose frame="">4.73156 0.57943 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ShelfE_01_002">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ShelfE_01</uri>
|
||||
</include>
|
||||
<pose frame="">4.73156 -4.827049 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ShelfE_01_003">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ShelfE_01</uri>
|
||||
</include>
|
||||
<pose frame="">4.73156 -8.6651 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ShelfD_01_001">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ShelfD_01</uri>
|
||||
</include>
|
||||
<pose frame="">4.73156 -1.242668 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ShelfD_01_002">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ShelfD_01</uri>
|
||||
</include>
|
||||
<pose frame="">4.73156 -3.038551 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ShelfD_01_003">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ShelfD_01</uri>
|
||||
</include>
|
||||
<pose frame="">4.73156 -6.750542 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<!--model name="aws_robomaker_warehouse_DeskC_01_001">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_DeskC_01</uri>
|
||||
</include>
|
||||
<pose frame="">-0.061684 6.135864 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_DeskC_01_002">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_DeskC_01</uri>
|
||||
</include>
|
||||
<pose frame="">-0.061684 3.039 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_DeskC_01_003">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_DeskC_01</uri>
|
||||
</include>
|
||||
<pose frame="">-0.061684 -6.6493 0 0 0 0</pose>
|
||||
</model-->
|
||||
|
||||
<model name="aws_robomaker_warehouse_GroundB_01_001">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_GroundB_01</uri>
|
||||
</include>
|
||||
<pose frame="">0.0 0.0 -0.090092 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_Lamp_01_005">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_Lamp_01</uri>
|
||||
</include>
|
||||
<pose frame="">0 0 -4 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
|
||||
|
||||
<model name="aws_robomaker_warehouse_Bucket_01_020">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_Bucket_01</uri>
|
||||
</include>
|
||||
<pose frame="">0.433449 9.631706 0 0 0 -1.563161</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_Bucket_01_021">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_Bucket_01</uri>
|
||||
</include>
|
||||
<pose frame="">-1.8321 -6.3752 0 0 0 -1.563161</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_Bucket_01_022">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_Bucket_01</uri>
|
||||
</include>
|
||||
<pose frame="">0.433449 8.59 0 0 0 -1.563161</pose>
|
||||
</model>
|
||||
|
||||
<model name='aws_robomaker_warehouse_ClutteringA_01_016'>
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ClutteringA_01</uri>
|
||||
</include>
|
||||
<pose frame=''>5.708138 8.616844 -0.017477 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name='aws_robomaker_warehouse_ClutteringA_01_017'>
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ClutteringA_01</uri>
|
||||
</include>
|
||||
<pose frame=''>3.408638 8.616844 -0.017477 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name='aws_robomaker_warehouse_ClutteringA_01_018'>
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ClutteringA_01</uri>
|
||||
</include>
|
||||
<pose frame=''>-1.491287 5.222435 -0.017477 0 0 -1.583185</pose>
|
||||
</model>
|
||||
|
||||
|
||||
<model name="aws_robomaker_warehouse_ClutteringC_01_027">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ClutteringC_01</uri>
|
||||
</include>
|
||||
<pose frame="">3.324959 3.822449 -0.012064 0 0 1.563871</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ClutteringC_01_028">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ClutteringC_01</uri>
|
||||
</include>
|
||||
<pose frame="">5.54171 3.816475 -0.015663 0 0 -1.583191</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ClutteringC_01_029">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ClutteringC_01</uri>
|
||||
</include>
|
||||
<pose frame="">5.384239 6.137154 0 0 0 3.150000</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ClutteringC_01_030">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ClutteringC_01</uri>
|
||||
</include>
|
||||
<pose frame="">3.236 6.137154 0 0 0 3.150000</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ClutteringC_01_031">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ClutteringC_01</uri>
|
||||
</include>
|
||||
<pose frame="">-1.573677 2.301994 -0.015663 0 0 -3.133191</pose>
|
||||
</model>
|
||||
|
||||
<model name="aws_robomaker_warehouse_ClutteringC_01_032">
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ClutteringC_01</uri>
|
||||
</include>
|
||||
<pose frame="">-1.2196 9.407 -0.015663 0 0 1.563871</pose>
|
||||
</model>
|
||||
|
||||
<model name='aws_robomaker_warehouse_ClutteringD_01_005'>
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_ClutteringD_01</uri>
|
||||
</include>
|
||||
<pose frame=''>-1.634682 -7.811813 -0.319559 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
<model name='aws_robomaker_warehouse_TrashCanC_01_002'>
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_TrashCanC_01</uri>
|
||||
</include>
|
||||
<pose frame=''>-1.592441 7.715420 0 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
|
||||
<model name='aws_robomaker_warehouse_PalletJackB_01_001'>
|
||||
<include>
|
||||
<uri>model://aws_robomaker_warehouse_PalletJackB_01</uri>
|
||||
</include>
|
||||
<pose frame=''>-0.276098 -9.481944 0.023266 0 0 0</pose>
|
||||
</model>
|
||||
|
||||
|
||||
<light name="Warehouse_CeilingLight_003" type="point">
|
||||
<pose frame="">0 0 9 0 0 0</pose>
|
||||
<diffuse>0.5 0.5 0.5 1</diffuse>
|
||||
<specular>0.2 0.2 0.2 1</specular>
|
||||
<attenuation>
|
||||
<range>80</range>
|
||||
<constant>0.3</constant>
|
||||
<linear>0.01</linear>
|
||||
<quadratic>0.001</quadratic>
|
||||
</attenuation>
|
||||
<cast_shadows>1</cast_shadows>
|
||||
<direction>0.1 0.1 -1</direction>
|
||||
</light>
|
||||
|
||||
<gui fullscreen='0'>
|
||||
<camera name='user_camera'>
|
||||
<pose frame=''>-4.70385 10.895 16.2659 -0 0.921795 -1.12701</pose>
|
||||
<view_controller>orbit</view_controller>
|
||||
<projection_type>perspective</projection_type>
|
||||
</camera>
|
||||
</gui>
|
||||
|
||||
<model name="turtlebot3_waffle">
|
||||
<pose>3.45 2.15 0.01 0.0 0.0 3.14</pose>
|
||||
|
||||
<link name="base_footprint"/>
|
||||
|
||||
<link name="base_link">
|
||||
|
||||
<inertial>
|
||||
<pose>-0.064 0 0.048 0 0 0</pose>
|
||||
<inertia>
|
||||
<ixx>0.001</ixx>
|
||||
<ixy>0.000</ixy>
|
||||
<ixz>0.000</ixz>
|
||||
<iyy>0.001</iyy>
|
||||
<iyz>0.000</iyz>
|
||||
<izz>0.001</izz>
|
||||
</inertia>
|
||||
<mass>1.0</mass>
|
||||
</inertial>
|
||||
|
||||
<collision name="base_collision">
|
||||
<pose>-0.064 0 0.048 0 0 0</pose>
|
||||
<geometry>
|
||||
<box>
|
||||
<size>0.265 0.265 0.089</size>
|
||||
</box>
|
||||
</geometry>
|
||||
</collision>
|
||||
|
||||
<visual name="base_visual">
|
||||
<pose>-0.064 0 0 0 0 0</pose>
|
||||
<geometry>
|
||||
<mesh>
|
||||
<uri>model://turtlebot3_common/meshes/waffle_base.dae</uri>
|
||||
<scale>0.001 0.001 0.001</scale>
|
||||
</mesh>
|
||||
</geometry>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<link name="imu_link">
|
||||
<sensor name="tb3_imu" type="imu">
|
||||
<always_on>true</always_on>
|
||||
<update_rate>200</update_rate>
|
||||
<imu>
|
||||
<angular_velocity>
|
||||
<x>
|
||||
<noise type="gaussian">
|
||||
<mean>0.0</mean>
|
||||
<stddev>2e-4</stddev>
|
||||
</noise>
|
||||
</x>
|
||||
<y>
|
||||
<noise type="gaussian">
|
||||
<mean>0.0</mean>
|
||||
<stddev>2e-4</stddev>
|
||||
</noise>
|
||||
</y>
|
||||
<z>
|
||||
<noise type="gaussian">
|
||||
<mean>0.0</mean>
|
||||
<stddev>2e-4</stddev>
|
||||
</noise>
|
||||
</z>
|
||||
</angular_velocity>
|
||||
<linear_acceleration>
|
||||
<x>
|
||||
<noise type="gaussian">
|
||||
<mean>0.0</mean>
|
||||
<stddev>1.7e-2</stddev>
|
||||
</noise>
|
||||
</x>
|
||||
<y>
|
||||
<noise type="gaussian">
|
||||
<mean>0.0</mean>
|
||||
<stddev>1.7e-2</stddev>
|
||||
</noise>
|
||||
</y>
|
||||
<z>
|
||||
<noise type="gaussian">
|
||||
<mean>0.0</mean>
|
||||
<stddev>1.7e-2</stddev>
|
||||
</noise>
|
||||
</z>
|
||||
</linear_acceleration>
|
||||
</imu>
|
||||
<plugin name="turtlebot3_imu" filename="libgazebo_ros_imu_sensor.so">
|
||||
<initial_orientation_as_reference>false</initial_orientation_as_reference>
|
||||
<ros>
|
||||
<!-- <namespace>/tb3</namespace> -->
|
||||
<remapping>~/out:=imu</remapping>
|
||||
</ros>
|
||||
</plugin>
|
||||
</sensor>
|
||||
</link>
|
||||
|
||||
<link name="base_scan">
|
||||
<inertial>
|
||||
<pose>-0.052 0 0.111 0 0 0</pose>
|
||||
<inertia>
|
||||
<ixx>0.001</ixx>
|
||||
<ixy>0.000</ixy>
|
||||
<ixz>0.000</ixz>
|
||||
<iyy>0.001</iyy>
|
||||
<iyz>0.000</iyz>
|
||||
<izz>0.001</izz>
|
||||
</inertia>
|
||||
<mass>0.125</mass>
|
||||
</inertial>
|
||||
|
||||
<collision name="lidar_sensor_collision">
|
||||
<pose>-0.052 0 0.111 0 0 0</pose>
|
||||
<geometry>
|
||||
<cylinder>
|
||||
<radius>0.0508</radius>
|
||||
<length>0.055</length>
|
||||
</cylinder>
|
||||
</geometry>
|
||||
</collision>
|
||||
|
||||
<visual name="lidar_sensor_visual">
|
||||
<pose>-0.064 0 0.121 0 0 0</pose>
|
||||
<geometry>
|
||||
<mesh>
|
||||
<uri>model://turtlebot3_common/meshes/lds.dae</uri>
|
||||
<scale>0.001 0.001 0.001</scale>
|
||||
</mesh>
|
||||
</geometry>
|
||||
</visual>
|
||||
|
||||
<sensor name="hls_lfcd_lds" type="ray">
|
||||
<always_on>true</always_on>
|
||||
<visualize>true</visualize>
|
||||
<pose>-0.064 0 0.121 0 0 0</pose>
|
||||
<update_rate>5</update_rate>
|
||||
<ray>
|
||||
<scan>
|
||||
<horizontal>
|
||||
<samples>360</samples>
|
||||
<resolution>1.000000</resolution>
|
||||
<min_angle>0.000000</min_angle>
|
||||
<max_angle>6.280000</max_angle>
|
||||
</horizontal>
|
||||
</scan>
|
||||
<range>
|
||||
<min>0.120000</min>
|
||||
<max>3.5</max>
|
||||
<resolution>0.015000</resolution>
|
||||
</range>
|
||||
<noise>
|
||||
<type>gaussian</type>
|
||||
<mean>0.0</mean>
|
||||
<stddev>0.01</stddev>
|
||||
</noise>
|
||||
</ray>
|
||||
<plugin name="turtlebot3_laserscan" filename="libgazebo_ros_ray_sensor.so">
|
||||
<ros>
|
||||
<!-- <namespace>/tb3</namespace> -->
|
||||
<remapping>~/out:=scan</remapping>
|
||||
</ros>
|
||||
<output_type>sensor_msgs/LaserScan</output_type>
|
||||
<frame_name>base_scan</frame_name>
|
||||
</plugin>
|
||||
</sensor>
|
||||
</link>
|
||||
|
||||
<link name="wheel_left_link">
|
||||
|
||||
<inertial>
|
||||
<pose>0.0 0.144 0.023 -1.57 0 0</pose>
|
||||
<inertia>
|
||||
<ixx>0.001</ixx>
|
||||
<ixy>0.000</ixy>
|
||||
<ixz>0.000</ixz>
|
||||
<iyy>0.001</iyy>
|
||||
<iyz>0.000</iyz>
|
||||
<izz>0.001</izz>
|
||||
</inertia>
|
||||
<mass>0.1</mass>
|
||||
</inertial>
|
||||
|
||||
<collision name="wheel_left_collision">
|
||||
<pose>0.0 0.144 0.023 -1.57 0 0</pose>
|
||||
<geometry>
|
||||
<cylinder>
|
||||
<radius>0.033</radius>
|
||||
<length>0.018</length>
|
||||
</cylinder>
|
||||
</geometry>
|
||||
<surface>
|
||||
<!-- This friction pamareter don't contain reliable data!! -->
|
||||
<friction>
|
||||
<ode>
|
||||
<mu>100000.0</mu>
|
||||
<mu2>100000.0</mu2>
|
||||
<fdir1>0 0 0</fdir1>
|
||||
<slip1>0.0</slip1>
|
||||
<slip2>0.0</slip2>
|
||||
</ode>
|
||||
</friction>
|
||||
<contact>
|
||||
<ode>
|
||||
<soft_cfm>0</soft_cfm>
|
||||
<soft_erp>0.2</soft_erp>
|
||||
<kp>1e+5</kp>
|
||||
<kd>1</kd>
|
||||
<max_vel>0.01</max_vel>
|
||||
<min_depth>0.001</min_depth>
|
||||
</ode>
|
||||
</contact>
|
||||
</surface>
|
||||
</collision>
|
||||
|
||||
<visual name="wheel_left_visual">
|
||||
<pose>0.0 0.144 0.023 0 0 0</pose>
|
||||
<geometry>
|
||||
<mesh>
|
||||
<uri>model://turtlebot3_common/meshes/tire.dae</uri>
|
||||
<scale>0.001 0.001 0.001</scale>
|
||||
</mesh>
|
||||
</geometry>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<link name="wheel_right_link">
|
||||
|
||||
<inertial>
|
||||
<pose>0.0 -0.144 0.023 -1.57 0 0</pose>
|
||||
<inertia>
|
||||
<ixx>0.001</ixx>
|
||||
<ixy>0.000</ixy>
|
||||
<ixz>0.000</ixz>
|
||||
<iyy>0.001</iyy>
|
||||
<iyz>0.000</iyz>
|
||||
<izz>0.001</izz>
|
||||
</inertia>
|
||||
<mass>0.1</mass>
|
||||
</inertial>
|
||||
|
||||
<collision name="wheel_right_collision">
|
||||
<pose>0.0 -0.144 0.023 -1.57 0 0</pose>
|
||||
<geometry>
|
||||
<cylinder>
|
||||
<radius>0.033</radius>
|
||||
<length>0.018</length>
|
||||
</cylinder>
|
||||
</geometry>
|
||||
<surface>
|
||||
<!-- This friction pamareter don't contain reliable data!! -->
|
||||
<friction>
|
||||
<ode>
|
||||
<mu>100000.0</mu>
|
||||
<mu2>100000.0</mu2>
|
||||
<fdir1>0 0 0</fdir1>
|
||||
<slip1>0.0</slip1>
|
||||
<slip2>0.0</slip2>
|
||||
</ode>
|
||||
</friction>
|
||||
<contact>
|
||||
<ode>
|
||||
<soft_cfm>0</soft_cfm>
|
||||
<soft_erp>0.2</soft_erp>
|
||||
<kp>1e+5</kp>
|
||||
<kd>1</kd>
|
||||
<max_vel>0.01</max_vel>
|
||||
<min_depth>0.001</min_depth>
|
||||
</ode>
|
||||
</contact>
|
||||
</surface>
|
||||
</collision>
|
||||
|
||||
<visual name="wheel_right_visual">
|
||||
<pose>0.0 -0.144 0.023 0 0 0</pose>
|
||||
<geometry>
|
||||
<mesh>
|
||||
<uri>model://turtlebot3_common/meshes/tire.dae</uri>
|
||||
<scale>0.001 0.001 0.001</scale>
|
||||
</mesh>
|
||||
</geometry>
|
||||
</visual>
|
||||
</link>
|
||||
|
||||
<link name='caster_back_right_link'>
|
||||
<pose>-0.177 -0.064 -0.004 0 0 0</pose>
|
||||
<inertial>
|
||||
<mass>0.001</mass>
|
||||
<inertia>
|
||||
<ixx>0.00001</ixx>
|
||||
<ixy>0.000</ixy>
|
||||
<ixz>0.000</ixz>
|
||||
<iyy>0.00001</iyy>
|
||||
<iyz>0.000</iyz>
|
||||
<izz>0.00001</izz>
|
||||
</inertia>
|
||||
</inertial>
|
||||
<collision name='collision'>
|
||||
<geometry>
|
||||
<sphere>
|
||||
<radius>0.005000</radius>
|
||||
</sphere>
|
||||
</geometry>
|
||||
<surface>
|
||||
<contact>
|
||||
<ode>
|
||||
<soft_cfm>0</soft_cfm>
|
||||
<soft_erp>0.2</soft_erp>
|
||||
<kp>1e+5</kp>
|
||||
<kd>1</kd>
|
||||
<max_vel>0.01</max_vel>
|
||||
<min_depth>0.001</min_depth>
|
||||
</ode>
|
||||
</contact>
|
||||
</surface>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<link name='caster_back_left_link'>
|
||||
<pose>-0.177 0.064 -0.004 0 0 0</pose>
|
||||
<inertial>
|
||||
<mass>0.001</mass>
|
||||
<inertia>
|
||||
<ixx>0.00001</ixx>
|
||||
<ixy>0.000</ixy>
|
||||
<ixz>0.000</ixz>
|
||||
<iyy>0.00001</iyy>
|
||||
<iyz>0.000</iyz>
|
||||
<izz>0.00001</izz>
|
||||
</inertia>
|
||||
</inertial>
|
||||
<collision name='collision'>
|
||||
<geometry>
|
||||
<sphere>
|
||||
<radius>0.005000</radius>
|
||||
</sphere>
|
||||
</geometry>
|
||||
<surface>
|
||||
<contact>
|
||||
<ode>
|
||||
<soft_cfm>0</soft_cfm>
|
||||
<soft_erp>0.2</soft_erp>
|
||||
<kp>1e+5</kp>
|
||||
<kd>1</kd>
|
||||
<max_vel>0.01</max_vel>
|
||||
<min_depth>0.001</min_depth>
|
||||
</ode>
|
||||
</contact>
|
||||
</surface>
|
||||
</collision>
|
||||
</link>
|
||||
|
||||
<joint name="base_joint" type="fixed">
|
||||
<parent>base_footprint</parent>
|
||||
<child>base_link</child>
|
||||
<pose>0.0 0.0 0.010 0 0 0</pose>
|
||||
</joint>
|
||||
|
||||
<joint name="wheel_left_joint" type="revolute">
|
||||
<parent>base_link</parent>
|
||||
<child>wheel_left_link</child>
|
||||
<pose>0.0 0.144 0.023 -1.57 0 0</pose>
|
||||
<axis>
|
||||
<xyz>0 0 1</xyz>
|
||||
</axis>
|
||||
</joint>
|
||||
|
||||
<joint name="wheel_right_joint" type="revolute">
|
||||
<parent>base_link</parent>
|
||||
<child>wheel_right_link</child>
|
||||
<pose>0.0 -0.144 0.023 -1.57 0 0</pose>
|
||||
<axis>
|
||||
<xyz>0 0 1</xyz>
|
||||
</axis>
|
||||
</joint>
|
||||
|
||||
<joint name='caster_back_right_joint' type='ball'>
|
||||
<parent>base_link</parent>
|
||||
<child>caster_back_right_link</child>
|
||||
</joint>
|
||||
|
||||
<joint name='caster_back_left_joint' type='ball'>
|
||||
<parent>base_link</parent>
|
||||
<child>caster_back_left_link</child>
|
||||
</joint>
|
||||
|
||||
<joint name="lidar_joint" type="fixed">
|
||||
<parent>base_link</parent>
|
||||
<child>base_scan</child>
|
||||
<pose>-0.064 0 0.121 0 0 0</pose>
|
||||
<axis>
|
||||
<xyz>0 0 1</xyz>
|
||||
</axis>
|
||||
</joint>
|
||||
|
||||
<plugin name="turtlebot3_diff_drive" filename="libgazebo_ros_diff_drive.so">
|
||||
|
||||
<ros>
|
||||
<!-- <namespace>/tb3</namespace> -->
|
||||
<remapping>/tf:=tf</remapping>
|
||||
</ros>
|
||||
|
||||
<update_rate>30</update_rate>
|
||||
|
||||
<!-- wheels -->
|
||||
<left_joint>wheel_left_joint</left_joint>
|
||||
<right_joint>wheel_right_joint</right_joint>
|
||||
|
||||
<!-- kinematics -->
|
||||
<wheel_separation>0.287</wheel_separation>
|
||||
<wheel_diameter>0.066</wheel_diameter>
|
||||
|
||||
<!-- limits -->
|
||||
<max_wheel_torque>20</max_wheel_torque>
|
||||
<max_wheel_acceleration>1.0</max_wheel_acceleration>
|
||||
|
||||
<command_topic>cmd_vel</command_topic>
|
||||
|
||||
<!-- output -->
|
||||
<publish_odom>true</publish_odom>
|
||||
<publish_odom_tf>true</publish_odom_tf>
|
||||
<publish_wheel_tf>false</publish_wheel_tf>
|
||||
|
||||
<odometry_topic>odom</odometry_topic>
|
||||
<odometry_frame>odom</odometry_frame>
|
||||
<robot_base_frame>base_footprint</robot_base_frame>
|
||||
|
||||
</plugin>
|
||||
|
||||
<plugin name="turtlebot3_joint_state" filename="libgazebo_ros_joint_state_publisher.so">
|
||||
<ros>
|
||||
<!-- <namespace>/tb3</namespace> -->
|
||||
<remapping>~/out:=joint_states</remapping>
|
||||
</ros>
|
||||
<update_rate>30</update_rate>
|
||||
<joint_name>wheel_left_joint</joint_name>
|
||||
<joint_name>wheel_right_joint</joint_name>
|
||||
</plugin>
|
||||
|
||||
</model>
|
||||
|
||||
</world>
|
||||
</sdf>
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright (c) 2021 Samsung Research America
|
||||
#
|
||||
# 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
|
||||
|
||||
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, PythonExpression
|
||||
from launch_ros.actions import Node
|
||||
|
||||
|
||||
def generate_launch_description():
|
||||
warehouse_dir = get_package_share_directory('aws_robomaker_small_warehouse_world')
|
||||
nav2_bringup_dir = get_package_share_directory('nav2_bringup')
|
||||
python_commander_dir = get_package_share_directory('nav2_simple_commander')
|
||||
|
||||
map_yaml_file = os.path.join(warehouse_dir, 'maps', '005', 'map.yaml')
|
||||
world = os.path.join(python_commander_dir, 'warehouse.world')
|
||||
|
||||
# Launch configuration variables
|
||||
use_rviz = LaunchConfiguration('use_rviz')
|
||||
headless = LaunchConfiguration('headless')
|
||||
|
||||
# Declare the launch arguments
|
||||
declare_use_rviz_cmd = DeclareLaunchArgument(
|
||||
'use_rviz',
|
||||
default_value='True',
|
||||
description='Whether to start RVIZ')
|
||||
|
||||
declare_simulator_cmd = DeclareLaunchArgument(
|
||||
'headless',
|
||||
default_value='False',
|
||||
description='Whether to execute gzclient)')
|
||||
|
||||
# start the simulation
|
||||
start_gazebo_server_cmd = ExecuteProcess(
|
||||
cmd=['gzserver', '-s', 'libgazebo_ros_factory.so', world],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
start_gazebo_client_cmd = ExecuteProcess(
|
||||
condition=IfCondition(PythonExpression(['not ', headless])),
|
||||
cmd=['gzclient'],
|
||||
cwd=[warehouse_dir], output='screen')
|
||||
|
||||
urdf = os.path.join(nav2_bringup_dir, 'urdf', 'turtlebot3_waffle.urdf')
|
||||
start_robot_state_publisher_cmd = Node(
|
||||
package='robot_state_publisher',
|
||||
executable='robot_state_publisher',
|
||||
name='robot_state_publisher',
|
||||
output='screen',
|
||||
arguments=[urdf])
|
||||
|
||||
# start the visualization
|
||||
rviz_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'rviz_launch.py')),
|
||||
condition=IfCondition(use_rviz),
|
||||
launch_arguments={'namespace': '',
|
||||
'use_namespace': 'False'}.items())
|
||||
|
||||
# start navigation
|
||||
bringup_cmd = IncludeLaunchDescription(
|
||||
PythonLaunchDescriptionSource(
|
||||
os.path.join(nav2_bringup_dir, 'launch', 'bringup_launch.py')),
|
||||
launch_arguments={'map': map_yaml_file}.items())
|
||||
|
||||
# start the demo autonomy task
|
||||
demo_cmd = Node(
|
||||
package='nav2_simple_commander',
|
||||
executable='example_waypoint_follower',
|
||||
emulate_tty=True,
|
||||
output='screen')
|
||||
|
||||
ld = LaunchDescription()
|
||||
ld.add_action(declare_use_rviz_cmd)
|
||||
ld.add_action(declare_simulator_cmd)
|
||||
ld.add_action(start_gazebo_server_cmd)
|
||||
ld.add_action(start_gazebo_client_cmd)
|
||||
ld.add_action(start_robot_state_publisher_cmd)
|
||||
ld.add_action(rviz_cmd)
|
||||
ld.add_action(bringup_cmd)
|
||||
ld.add_action(demo_cmd)
|
||||
return ld
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 22 MiB |
@@ -0,0 +1,192 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
# Copyright 2022 Stevedan Ogochukwu Omodolor
|
||||
# Copyright 2022 Jaehun Jackson Kim
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
This is a Python3 API for costmap 2d messages from the stack.
|
||||
|
||||
It provides the basic conversion, get/set,
|
||||
and handling semantics found in the costmap 2d C++ API.
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
||||
class PyCostmap2D:
|
||||
"""
|
||||
PyCostmap2D.
|
||||
|
||||
Costmap Python3 API for OccupancyGrids to populate from published messages
|
||||
"""
|
||||
|
||||
def __init__(self, occupancy_map):
|
||||
"""
|
||||
Initialize costmap2D.
|
||||
|
||||
Args:
|
||||
----
|
||||
occupancy_map (OccupancyGrid): 2D OccupancyGrid Map
|
||||
|
||||
"""
|
||||
self.size_x = occupancy_map.info.width
|
||||
self.size_y = occupancy_map.info.height
|
||||
self.resolution = occupancy_map.info.resolution
|
||||
self.origin_x = occupancy_map.info.origin.position.x
|
||||
self.origin_y = occupancy_map.info.origin.position.y
|
||||
self.global_frame_id = occupancy_map.header.frame_id
|
||||
self.costmap_timestamp = occupancy_map.header.stamp
|
||||
# Extract costmap
|
||||
self.costmap = np.array(occupancy_map.data, dtype=np.uint8)
|
||||
|
||||
def getSizeInCellsX(self):
|
||||
"""Get map width in cells."""
|
||||
return self.size_x
|
||||
|
||||
def getSizeInCellsY(self):
|
||||
"""Get map height in cells."""
|
||||
return self.size_y
|
||||
|
||||
def getSizeInMetersX(self):
|
||||
"""Get x axis map size in meters."""
|
||||
return (self.size_x - 1 + 0.5) * self.resolution
|
||||
|
||||
def getSizeInMetersY(self):
|
||||
"""Get y axis map size in meters."""
|
||||
return (self.size_y - 1 + 0.5) * self.resolution
|
||||
|
||||
def getOriginX(self):
|
||||
"""Get the origin x axis of the map [m]."""
|
||||
return self.origin_x
|
||||
|
||||
def getOriginY(self):
|
||||
"""Get the origin y axis of the map [m]."""
|
||||
return self.origin_y
|
||||
|
||||
def getResolution(self):
|
||||
"""Get map resolution [m/cell]."""
|
||||
return self.resolution
|
||||
|
||||
def getGlobalFrameID(self):
|
||||
"""Get global frame_id."""
|
||||
return self.global_frame_id
|
||||
|
||||
def getCostmapTimestamp(self):
|
||||
"""Get costmap timestamp."""
|
||||
return self.costmap_timestamp
|
||||
|
||||
def getCostXY(self, mx: int, my: int) -> np.uint8:
|
||||
"""
|
||||
Get the cost of a cell in the costmap using map coordinate XY.
|
||||
|
||||
Args
|
||||
----
|
||||
mx (int): map coordinate X to get cost
|
||||
my (int): map coordinate Y to get cost
|
||||
|
||||
Returns
|
||||
-------
|
||||
np.uint8: cost of a cell
|
||||
|
||||
"""
|
||||
return self.costmap[self.getIndex(mx, my)]
|
||||
|
||||
def getCostIdx(self, index: int) -> np.uint8:
|
||||
"""
|
||||
Get the cost of a cell in the costmap using Index.
|
||||
|
||||
Args
|
||||
----
|
||||
index (int): index of cell to get cost
|
||||
|
||||
Returns
|
||||
-------
|
||||
np.uint8: cost of a cell
|
||||
|
||||
"""
|
||||
return self.costmap[index]
|
||||
|
||||
def setCost(self, mx: int, my: int, cost: np.uint8) -> None:
|
||||
"""
|
||||
Set the cost of a cell in the costmap using map coordinate XY.
|
||||
|
||||
Args
|
||||
----
|
||||
mx (int): map coordinate X to get cost
|
||||
my (int): map coordinate Y to get cost
|
||||
cost (np.uint8): The cost to set the cell
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
"""
|
||||
self.costmap[self.getIndex(mx, my)] = cost
|
||||
|
||||
def mapToWorld(self, mx: int, my: int) -> tuple[float, float]:
|
||||
"""
|
||||
Get the world coordinate XY using map coordinate XY.
|
||||
|
||||
Args
|
||||
----
|
||||
mx (int): map coordinate X to get world coordinate
|
||||
my (int): map coordinate Y to get world coordinate
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple of float: wx, wy
|
||||
wx (float) [m]: world coordinate X
|
||||
wy (float) [m]: world coordinate Y
|
||||
|
||||
"""
|
||||
wx = self.origin_x + (mx + 0.5) * self.resolution
|
||||
wy = self.origin_y + (my + 0.5) * self.resolution
|
||||
return (wx, wy)
|
||||
|
||||
def worldToMap(self, wx: float, wy: float) -> tuple[int, int]:
|
||||
"""
|
||||
Get the map coordinate XY using world coordinate XY.
|
||||
|
||||
Args
|
||||
----
|
||||
wx (float) [m]: world coordinate X to get map coordinate
|
||||
wy (float) [m]: world coordinate Y to get map coordinate
|
||||
|
||||
Returns
|
||||
-------
|
||||
tuple of int: mx, my
|
||||
mx (int): map coordinate X
|
||||
my (int): map coordinate Y
|
||||
|
||||
"""
|
||||
mx = int((wx - self.origin_x) // self.resolution)
|
||||
my = int((wy - self.origin_y) // self.resolution)
|
||||
return (mx, my)
|
||||
|
||||
def getIndex(self, mx: int, my: int) -> int:
|
||||
"""
|
||||
Get the index of the cell using map coordinate XY.
|
||||
|
||||
Args
|
||||
----
|
||||
mx (int): map coordinate X to get Index
|
||||
my (int): map coordinate Y to get Index
|
||||
|
||||
Returns
|
||||
-------
|
||||
int: The index of the cell
|
||||
|
||||
"""
|
||||
return my * self.size_x + mx
|
||||
@@ -0,0 +1,100 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
#
|
||||
# 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.
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
import rclpy
|
||||
|
||||
"""
|
||||
Basic stock inspection demo. In this demonstration, the expectation
|
||||
is that there are cameras or RFID sensors mounted on the robots
|
||||
collecting information about stock quantity and location.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
|
||||
navigator = BasicNavigator()
|
||||
|
||||
# Inspection route, probably read in from a file for a real application
|
||||
# from either a map or drive and repeat.
|
||||
inspection_route = [
|
||||
[3.461, -0.450],
|
||||
[5.531, -0.450],
|
||||
[3.461, -2.200],
|
||||
[5.531, -2.200],
|
||||
[3.661, -4.121],
|
||||
[5.431, -4.121],
|
||||
[3.661, -5.850],
|
||||
[5.431, -5.800]]
|
||||
|
||||
# Set our demo's initial pose
|
||||
initial_pose = PoseStamped()
|
||||
initial_pose.header.frame_id = 'map'
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
initial_pose.pose.position.x = 3.45
|
||||
initial_pose.pose.position.y = 2.15
|
||||
initial_pose.pose.orientation.z = 1.0
|
||||
initial_pose.pose.orientation.w = 0.0
|
||||
navigator.setInitialPose(initial_pose)
|
||||
|
||||
# Wait for navigation to fully activate
|
||||
navigator.waitUntilNav2Active()
|
||||
|
||||
# Send our route
|
||||
inspection_points = []
|
||||
inspection_pose = PoseStamped()
|
||||
inspection_pose.header.frame_id = 'map'
|
||||
inspection_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
inspection_pose.pose.orientation.z = 1.0
|
||||
inspection_pose.pose.orientation.w = 0.0
|
||||
for pt in inspection_route:
|
||||
inspection_pose.pose.position.x = pt[0]
|
||||
inspection_pose.pose.position.y = pt[1]
|
||||
inspection_points.append(deepcopy(inspection_pose))
|
||||
navigator.followWaypoints(inspection_points)
|
||||
|
||||
# Do something during our route (e.x. AI to analyze stock information or upload to the cloud)
|
||||
# Simply the current waypoint ID for the demonstation
|
||||
i = 0
|
||||
while not navigator.isTaskComplete():
|
||||
i += 1
|
||||
feedback = navigator.getFeedback()
|
||||
if feedback and i % 5 == 0:
|
||||
print('Executing current waypoint: ' +
|
||||
str(feedback.current_waypoint + 1) + '/' + str(len(inspection_points)))
|
||||
|
||||
result = navigator.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
print('Inspection of shelves complete! Returning to start...')
|
||||
elif result == TaskResult.CANCELED:
|
||||
print('Inspection of shelving was canceled. Returning to start...')
|
||||
elif result == TaskResult.FAILED:
|
||||
print('Inspection of shelving failed! Returning to start...')
|
||||
|
||||
# go back to start
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
navigator.goToPose(initial_pose)
|
||||
while not navigator.isTaskComplete():
|
||||
pass
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,123 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
#
|
||||
# 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.
|
||||
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
|
||||
import rclpy
|
||||
from rclpy.duration import Duration
|
||||
|
||||
|
||||
# Shelf positions for picking
|
||||
shelf_positions = {
|
||||
'shelf_A': [-3.829, -7.604],
|
||||
'shelf_B': [-3.791, -3.287],
|
||||
'shelf_C': [-3.791, 1.254],
|
||||
'shelf_D': [-3.24, 5.861]}
|
||||
|
||||
# Shipping destination for picked products
|
||||
shipping_destinations = {
|
||||
'recycling': [-0.205, 7.403],
|
||||
'pallet_jack7': [-0.073, -8.497],
|
||||
'conveyer_432': [6.217, 2.153],
|
||||
'frieght_bay_3': [-6.349, 9.147]}
|
||||
|
||||
"""
|
||||
Basic item picking demo. In this demonstration, the expectation
|
||||
is that there is a person at the item shelf to put the item on the robot
|
||||
and at the pallet jack to remove it
|
||||
(probably with some kind of button for 'got item, robot go do next task').
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
# Recieved virtual request for picking item at Shelf A and bring to
|
||||
# worker at the pallet jack 7 for shipping. This request would
|
||||
# contain the shelf ID ("shelf_A") and shipping destination ("pallet_jack7")
|
||||
####################
|
||||
request_item_location = 'shelf_C'
|
||||
request_destination = 'pallet_jack7'
|
||||
####################
|
||||
|
||||
rclpy.init()
|
||||
|
||||
navigator = BasicNavigator()
|
||||
|
||||
# Set our demo's initial pose
|
||||
initial_pose = PoseStamped()
|
||||
initial_pose.header.frame_id = 'map'
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
initial_pose.pose.position.x = 3.45
|
||||
initial_pose.pose.position.y = 2.15
|
||||
initial_pose.pose.orientation.z = 1.0
|
||||
initial_pose.pose.orientation.w = 0.0
|
||||
navigator.setInitialPose(initial_pose)
|
||||
|
||||
# Wait for navigation to fully activate
|
||||
navigator.waitUntilNav2Active()
|
||||
|
||||
shelf_item_pose = PoseStamped()
|
||||
shelf_item_pose.header.frame_id = 'map'
|
||||
shelf_item_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
shelf_item_pose.pose.position.x = shelf_positions[request_item_location][0]
|
||||
shelf_item_pose.pose.position.y = shelf_positions[request_item_location][1]
|
||||
shelf_item_pose.pose.orientation.z = 1.0
|
||||
shelf_item_pose.pose.orientation.w = 0.0
|
||||
print(f'Received request for item picking at {request_item_location}.')
|
||||
navigator.goToPose(shelf_item_pose)
|
||||
|
||||
# Do something during our route
|
||||
# (e.x. queue up future tasks or detect person for fine-tuned positioning)
|
||||
# Simply print information for workers on the robot's ETA for the demonstation
|
||||
i = 0
|
||||
while not navigator.isTaskComplete():
|
||||
i += 1
|
||||
feedback = navigator.getFeedback()
|
||||
if feedback and i % 5 == 0:
|
||||
print('Estimated time of arrival at ' + request_item_location +
|
||||
' for worker: ' + '{0:.0f}'.format(
|
||||
Duration.from_msg(feedback.estimated_time_remaining).nanoseconds / 1e9)
|
||||
+ ' seconds.')
|
||||
|
||||
result = navigator.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
print('Got product from ' + request_item_location +
|
||||
'! Bringing product to shipping destination (' + request_destination + ')...')
|
||||
shipping_destination = PoseStamped()
|
||||
shipping_destination.header.frame_id = 'map'
|
||||
shipping_destination.header.stamp = navigator.get_clock().now().to_msg()
|
||||
shipping_destination.pose.position.x = shipping_destinations[request_destination][0]
|
||||
shipping_destination.pose.position.y = shipping_destinations[request_destination][1]
|
||||
shipping_destination.pose.orientation.z = 1.0
|
||||
shipping_destination.pose.orientation.w = 0.0
|
||||
navigator.goToPose(shipping_destination)
|
||||
|
||||
elif result == TaskResult.CANCELED:
|
||||
print(f'Task at {request_item_location} was canceled. Returning to staging point...')
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
navigator.goToPose(initial_pose)
|
||||
|
||||
elif result == TaskResult.FAILED:
|
||||
print(f'Task at {request_item_location} failed!')
|
||||
exit(-1)
|
||||
|
||||
while not navigator.isTaskComplete():
|
||||
pass
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,105 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
#
|
||||
# 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.
|
||||
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
import rclpy
|
||||
from rclpy.duration import Duration
|
||||
|
||||
|
||||
"""
|
||||
Basic recoveries demo. In this demonstration, the robot navigates
|
||||
to a dead-end where recoveries such as backup and spin are used
|
||||
to get out of it.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
|
||||
navigator = BasicNavigator()
|
||||
|
||||
# Set our demo's initial pose
|
||||
initial_pose = PoseStamped()
|
||||
initial_pose.header.frame_id = 'map'
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
initial_pose.pose.position.x = 3.45
|
||||
initial_pose.pose.position.y = 2.15
|
||||
initial_pose.pose.orientation.z = 1.0
|
||||
initial_pose.pose.orientation.w = 0.0
|
||||
navigator.setInitialPose(initial_pose)
|
||||
|
||||
# Wait for navigation to fully activate
|
||||
navigator.waitUntilNav2Active()
|
||||
|
||||
goal_pose = PoseStamped()
|
||||
goal_pose.header.frame_id = 'map'
|
||||
goal_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose.pose.position.x = 6.13
|
||||
goal_pose.pose.position.y = 1.90
|
||||
goal_pose.pose.orientation.w = 1.0
|
||||
|
||||
navigator.goToPose(goal_pose)
|
||||
|
||||
i = 0
|
||||
while not navigator.isTaskComplete():
|
||||
i += 1
|
||||
feedback = navigator.getFeedback()
|
||||
if feedback and i % 5 == 0:
|
||||
print(
|
||||
f'Estimated time of arrival to destination is: \
|
||||
{Duration.from_msg(feedback.estimated_time_remaining).nanoseconds / 1e9}'
|
||||
)
|
||||
|
||||
# Robot hit a dead end, back it up
|
||||
print('Robot hit a dead end, backing up...')
|
||||
navigator.backup(backup_dist=0.5, backup_speed=0.1)
|
||||
|
||||
i = 0
|
||||
while not navigator.isTaskComplete():
|
||||
i += 1
|
||||
feedback = navigator.getFeedback()
|
||||
if feedback and i % 5 == 0:
|
||||
print(f'Distance traveled: {feedback.distance_traveled}')
|
||||
|
||||
# Turn it around
|
||||
print('Spinning robot around...')
|
||||
navigator.spin(spin_dist=3.14)
|
||||
|
||||
i = 0
|
||||
while not navigator.isTaskComplete():
|
||||
i += 1
|
||||
feedback = navigator.getFeedback()
|
||||
if feedback and i % 5 == 0:
|
||||
print(f'Spin angle traveled: {feedback.angular_distance_traveled}')
|
||||
|
||||
result = navigator.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
print('Dead end confirmed! Returning to start...')
|
||||
elif result == TaskResult.CANCELED:
|
||||
print('Recovery was canceled. Returning to start...')
|
||||
elif result == TaskResult.FAILED:
|
||||
print('Recovering from dead end failed! Returning to start...')
|
||||
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
navigator.goToPose(initial_pose)
|
||||
while not navigator.isTaskComplete():
|
||||
pass
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,107 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
#
|
||||
# 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.
|
||||
|
||||
from copy import deepcopy
|
||||
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
|
||||
import rclpy
|
||||
from rclpy.duration import Duration
|
||||
|
||||
|
||||
"""
|
||||
Basic security route patrol demo. In this demonstration, the expectation
|
||||
is that there are security cameras mounted on the robots recording or being
|
||||
watched live by security staff.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
|
||||
navigator = BasicNavigator()
|
||||
|
||||
# Security route, probably read in from a file for a real application
|
||||
# from either a map or drive and repeat.
|
||||
security_route = [
|
||||
[1.792, 2.144],
|
||||
[1.792, -5.44],
|
||||
[1.792, -9.427],
|
||||
[-3.665, -9.427],
|
||||
[-3.665, -4.303],
|
||||
[-3.665, 2.330],
|
||||
[-3.665, 9.283]]
|
||||
|
||||
# Set our demo's initial pose
|
||||
initial_pose = PoseStamped()
|
||||
initial_pose.header.frame_id = 'map'
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
initial_pose.pose.position.x = 3.45
|
||||
initial_pose.pose.position.y = 2.15
|
||||
initial_pose.pose.orientation.z = 1.0
|
||||
initial_pose.pose.orientation.w = 0.0
|
||||
navigator.setInitialPose(initial_pose)
|
||||
|
||||
# Wait for navigation to fully activate
|
||||
navigator.waitUntilNav2Active()
|
||||
|
||||
# Do security route until dead
|
||||
while rclpy.ok():
|
||||
# Send our route
|
||||
route_poses = []
|
||||
pose = PoseStamped()
|
||||
pose.header.frame_id = 'map'
|
||||
pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
pose.pose.orientation.w = 1.0
|
||||
for pt in security_route:
|
||||
pose.pose.position.x = pt[0]
|
||||
pose.pose.position.y = pt[1]
|
||||
route_poses.append(deepcopy(pose))
|
||||
navigator.goThroughPoses(route_poses)
|
||||
|
||||
# Do something during our route (e.x. AI detection on camera images for anomalies)
|
||||
# Simply print ETA for the demonstation
|
||||
i = 0
|
||||
while not navigator.isTaskComplete():
|
||||
i += 1
|
||||
feedback = navigator.getFeedback()
|
||||
if feedback and i % 5 == 0:
|
||||
print('Estimated time to complete current route: ' + '{0:.0f}'.format(
|
||||
Duration.from_msg(feedback.estimated_time_remaining).nanoseconds / 1e9)
|
||||
+ ' seconds.')
|
||||
|
||||
# Some failure mode, must stop since the robot is clearly stuck
|
||||
if Duration.from_msg(feedback.navigation_time) > Duration(seconds=180.0):
|
||||
print('Navigation has exceeded timeout of 180s, canceling request.')
|
||||
navigator.cancelTask()
|
||||
|
||||
# If at end of route, reverse the route to restart
|
||||
security_route.reverse()
|
||||
|
||||
result = navigator.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
print('Route complete! Restarting...')
|
||||
elif result == TaskResult.CANCELED:
|
||||
print('Security route was canceled, exiting.')
|
||||
exit(1)
|
||||
elif result == TaskResult.FAILED:
|
||||
print('Security route failed! Restarting from other side...')
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,56 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2022 Joshua Wallace
|
||||
#
|
||||
# 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.
|
||||
|
||||
from time import sleep
|
||||
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator
|
||||
import rclpy
|
||||
|
||||
"""
|
||||
Basic navigation demo to go to pose.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
|
||||
navigator = BasicNavigator()
|
||||
|
||||
# Set our demo's initial pose
|
||||
initial_pose = PoseStamped()
|
||||
initial_pose.header.frame_id = 'map'
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
initial_pose.pose.position.x = 3.45
|
||||
initial_pose.pose.position.y = 2.15
|
||||
initial_pose.pose.orientation.z = 1.0
|
||||
initial_pose.pose.orientation.w = 0.0
|
||||
navigator.setInitialPose(initial_pose)
|
||||
|
||||
# Wait for navigation to fully activate, since autostarting nav2
|
||||
navigator.waitUntilNav2Active()
|
||||
|
||||
navigator.assistedTeleop(time_allowance=20)
|
||||
while not navigator.isTaskComplete():
|
||||
# Publish twist commands to be filtered by the assisted teleop action
|
||||
sleep(0.2)
|
||||
pass
|
||||
|
||||
navigator.lifecycleShutdown()
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,93 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
#
|
||||
# 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.
|
||||
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
import rclpy
|
||||
|
||||
|
||||
"""
|
||||
Basic navigation demo to follow a given path after smoothing
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
|
||||
navigator = BasicNavigator()
|
||||
|
||||
# Set our demo's initial pose
|
||||
initial_pose = PoseStamped()
|
||||
initial_pose.header.frame_id = 'map'
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
initial_pose.pose.position.x = 3.45
|
||||
initial_pose.pose.position.y = 2.15
|
||||
initial_pose.pose.orientation.z = 1.0
|
||||
initial_pose.pose.orientation.w = 0.0
|
||||
navigator.setInitialPose(initial_pose)
|
||||
|
||||
# Wait for navigation to fully activate, since autostarting nav2
|
||||
navigator.waitUntilNav2Active()
|
||||
|
||||
# Go to our demos first goal pose
|
||||
goal_pose = PoseStamped()
|
||||
goal_pose.header.frame_id = 'map'
|
||||
goal_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose.pose.position.x = -3.0
|
||||
goal_pose.pose.position.y = -2.0
|
||||
goal_pose.pose.orientation.w = 1.0
|
||||
|
||||
# Get the path, smooth it
|
||||
path = navigator.getPath(initial_pose, goal_pose)
|
||||
smoothed_path = navigator.smoothPath(path)
|
||||
|
||||
# Follow path
|
||||
navigator.followPath(smoothed_path)
|
||||
|
||||
i = 0
|
||||
while not navigator.isTaskComplete():
|
||||
################################################
|
||||
#
|
||||
# Implement some code here for your application!
|
||||
#
|
||||
################################################
|
||||
|
||||
# Do something with the feedback
|
||||
i += 1
|
||||
feedback = navigator.getFeedback()
|
||||
if feedback and i % 5 == 0:
|
||||
print('Estimated distance remaining to goal position: ' +
|
||||
'{0:.3f}'.format(feedback.distance_to_goal) +
|
||||
'\nCurrent speed of the robot: ' +
|
||||
'{0:.3f}'.format(feedback.speed))
|
||||
|
||||
# Do something depending on the return code
|
||||
result = navigator.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
print('Goal succeeded!')
|
||||
elif result == TaskResult.CANCELED:
|
||||
print('Goal was canceled!')
|
||||
elif result == TaskResult.FAILED:
|
||||
print('Goal failed!')
|
||||
else:
|
||||
print('Goal has an invalid return status!')
|
||||
|
||||
navigator.lifecycleShutdown()
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,139 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
#
|
||||
# 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.
|
||||
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
import rclpy
|
||||
from rclpy.duration import Duration
|
||||
|
||||
"""
|
||||
Basic navigation demo to go to poses.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
|
||||
navigator = BasicNavigator()
|
||||
|
||||
# Set our demo's initial pose
|
||||
initial_pose = PoseStamped()
|
||||
initial_pose.header.frame_id = 'map'
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
initial_pose.pose.position.x = 3.45
|
||||
initial_pose.pose.position.y = 2.15
|
||||
initial_pose.pose.orientation.z = 1.0
|
||||
initial_pose.pose.orientation.w = 0.0
|
||||
navigator.setInitialPose(initial_pose)
|
||||
|
||||
# Activate navigation, if not autostarted. This should be called after setInitialPose()
|
||||
# or this will initialize at the origin of the map and update the costmap with bogus readings.
|
||||
# If autostart, you should `waitUntilNav2Active()` instead.
|
||||
# navigator.lifecycleStartup()
|
||||
|
||||
# Wait for navigation to fully activate, since autostarting nav2
|
||||
navigator.waitUntilNav2Active()
|
||||
|
||||
# If desired, you can change or load the map as well
|
||||
# navigator.changeMap('/path/to/map.yaml')
|
||||
|
||||
# You may use the navigator to clear or obtain costmaps
|
||||
# navigator.clearAllCostmaps() # also have clearLocalCostmap() and clearGlobalCostmap()
|
||||
# global_costmap = navigator.getGlobalCostmap()
|
||||
# local_costmap = navigator.getLocalCostmap()
|
||||
|
||||
# set our demo's goal poses
|
||||
goal_poses = []
|
||||
goal_pose1 = PoseStamped()
|
||||
goal_pose1.header.frame_id = 'map'
|
||||
goal_pose1.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose1.pose.position.x = 1.5
|
||||
goal_pose1.pose.position.y = 0.55
|
||||
goal_pose1.pose.orientation.w = 0.707
|
||||
goal_pose1.pose.orientation.z = 0.707
|
||||
goal_poses.append(goal_pose1)
|
||||
|
||||
# additional goals can be appended
|
||||
goal_pose2 = PoseStamped()
|
||||
goal_pose2.header.frame_id = 'map'
|
||||
goal_pose2.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose2.pose.position.x = 1.5
|
||||
goal_pose2.pose.position.y = -3.75
|
||||
goal_pose2.pose.orientation.w = 0.707
|
||||
goal_pose2.pose.orientation.z = 0.707
|
||||
goal_poses.append(goal_pose2)
|
||||
goal_pose3 = PoseStamped()
|
||||
goal_pose3.header.frame_id = 'map'
|
||||
goal_pose3.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose3.pose.position.x = -3.6
|
||||
goal_pose3.pose.position.y = -4.75
|
||||
goal_pose3.pose.orientation.w = 0.707
|
||||
goal_pose3.pose.orientation.z = 0.707
|
||||
goal_poses.append(goal_pose3)
|
||||
|
||||
# sanity check a valid path exists
|
||||
# path = navigator.getPathThroughPoses(initial_pose, goal_poses)
|
||||
|
||||
navigator.goThroughPoses(goal_poses)
|
||||
|
||||
i = 0
|
||||
while not navigator.isTaskComplete():
|
||||
################################################
|
||||
#
|
||||
# Implement some code here for your application!
|
||||
#
|
||||
################################################
|
||||
|
||||
# Do something with the feedback
|
||||
i = i + 1
|
||||
feedback = navigator.getFeedback()
|
||||
if feedback and i % 5 == 0:
|
||||
print('Estimated time of arrival: ' + '{0:.0f}'.format(
|
||||
Duration.from_msg(feedback.estimated_time_remaining).nanoseconds / 1e9)
|
||||
+ ' seconds.')
|
||||
|
||||
# Some navigation timeout to demo cancellation
|
||||
if Duration.from_msg(feedback.navigation_time) > Duration(seconds=600.0):
|
||||
navigator.cancelTask()
|
||||
|
||||
# Some navigation request change to demo preemption
|
||||
if Duration.from_msg(feedback.navigation_time) > Duration(seconds=35.0):
|
||||
goal_pose4 = PoseStamped()
|
||||
goal_pose4.header.frame_id = 'map'
|
||||
goal_pose4.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose4.pose.position.x = -5.0
|
||||
goal_pose4.pose.position.y = -4.75
|
||||
goal_pose4.pose.orientation.w = 0.707
|
||||
goal_pose4.pose.orientation.z = 0.707
|
||||
navigator.goThroughPoses([goal_pose4])
|
||||
|
||||
# Do something depending on the return code
|
||||
result = navigator.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
print('Goal succeeded!')
|
||||
elif result == TaskResult.CANCELED:
|
||||
print('Goal was canceled!')
|
||||
elif result == TaskResult.FAILED:
|
||||
print('Goal failed!')
|
||||
else:
|
||||
print('Goal has an invalid return status!')
|
||||
|
||||
navigator.lifecycleShutdown()
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,112 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
#
|
||||
# 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.
|
||||
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
import rclpy
|
||||
from rclpy.duration import Duration
|
||||
|
||||
"""
|
||||
Basic navigation demo to go to pose.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
|
||||
navigator = BasicNavigator()
|
||||
|
||||
# Set our demo's initial pose
|
||||
initial_pose = PoseStamped()
|
||||
initial_pose.header.frame_id = 'map'
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
initial_pose.pose.position.x = 3.45
|
||||
initial_pose.pose.position.y = 2.15
|
||||
initial_pose.pose.orientation.z = 1.0
|
||||
initial_pose.pose.orientation.w = 0.0
|
||||
navigator.setInitialPose(initial_pose)
|
||||
|
||||
# Activate navigation, if not autostarted. This should be called after setInitialPose()
|
||||
# or this will initialize at the origin of the map and update the costmap with bogus readings.
|
||||
# If autostart, you should `waitUntilNav2Active()` instead.
|
||||
# navigator.lifecycleStartup()
|
||||
|
||||
# Wait for navigation to fully activate, since autostarting nav2
|
||||
navigator.waitUntilNav2Active()
|
||||
|
||||
# If desired, you can change or load the map as well
|
||||
# navigator.changeMap('/path/to/map.yaml')
|
||||
|
||||
# You may use the navigator to clear or obtain costmaps
|
||||
# navigator.clearAllCostmaps() # also have clearLocalCostmap() and clearGlobalCostmap()
|
||||
# global_costmap = navigator.getGlobalCostmap()
|
||||
# local_costmap = navigator.getLocalCostmap()
|
||||
|
||||
# Go to our demos first goal pose
|
||||
goal_pose = PoseStamped()
|
||||
goal_pose.header.frame_id = 'map'
|
||||
goal_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose.pose.position.x = -2.0
|
||||
goal_pose.pose.position.y = -0.5
|
||||
goal_pose.pose.orientation.w = 1.0
|
||||
|
||||
# sanity check a valid path exists
|
||||
# path = navigator.getPath(initial_pose, goal_pose)
|
||||
|
||||
navigator.goToPose(goal_pose)
|
||||
|
||||
i = 0
|
||||
while not navigator.isTaskComplete():
|
||||
################################################
|
||||
#
|
||||
# Implement some code here for your application!
|
||||
#
|
||||
################################################
|
||||
|
||||
# Do something with the feedback
|
||||
i = i + 1
|
||||
feedback = navigator.getFeedback()
|
||||
if feedback and i % 5 == 0:
|
||||
print('Estimated time of arrival: ' + '{0:.0f}'.format(
|
||||
Duration.from_msg(feedback.estimated_time_remaining).nanoseconds / 1e9)
|
||||
+ ' seconds.')
|
||||
|
||||
# Some navigation timeout to demo cancellation
|
||||
if Duration.from_msg(feedback.navigation_time) > Duration(seconds=600.0):
|
||||
navigator.cancelTask()
|
||||
|
||||
# Some navigation request change to demo preemption
|
||||
if Duration.from_msg(feedback.navigation_time) > Duration(seconds=18.0):
|
||||
goal_pose.pose.position.x = -3.0
|
||||
navigator.goToPose(goal_pose)
|
||||
|
||||
# Do something depending on the return code
|
||||
result = navigator.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
print('Goal succeeded!')
|
||||
elif result == TaskResult.CANCELED:
|
||||
print('Goal was canceled!')
|
||||
elif result == TaskResult.FAILED:
|
||||
print('Goal failed!')
|
||||
else:
|
||||
print('Goal has an invalid return status!')
|
||||
|
||||
navigator.lifecycleShutdown()
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,142 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
#
|
||||
# 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.
|
||||
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from nav2_simple_commander.robot_navigator import BasicNavigator, TaskResult
|
||||
import rclpy
|
||||
from rclpy.duration import Duration
|
||||
|
||||
"""
|
||||
Basic navigation demo to go to poses.
|
||||
"""
|
||||
|
||||
|
||||
def main():
|
||||
rclpy.init()
|
||||
|
||||
navigator = BasicNavigator()
|
||||
|
||||
# Set our demo's initial pose
|
||||
initial_pose = PoseStamped()
|
||||
initial_pose.header.frame_id = 'map'
|
||||
initial_pose.header.stamp = navigator.get_clock().now().to_msg()
|
||||
initial_pose.pose.position.x = 3.45
|
||||
initial_pose.pose.position.y = 2.15
|
||||
initial_pose.pose.orientation.z = 1.0
|
||||
initial_pose.pose.orientation.w = 0.0
|
||||
navigator.setInitialPose(initial_pose)
|
||||
|
||||
# Activate navigation, if not autostarted. This should be called after setInitialPose()
|
||||
# or this will initialize at the origin of the map and update the costmap with bogus readings.
|
||||
# If autostart, you should `waitUntilNav2Active()` instead.
|
||||
# navigator.lifecycleStartup()
|
||||
|
||||
# Wait for navigation to fully activate, since autostarting nav2
|
||||
navigator.waitUntilNav2Active()
|
||||
|
||||
# If desired, you can change or load the map as well
|
||||
# navigator.changeMap('/path/to/map.yaml')
|
||||
|
||||
# You may use the navigator to clear or obtain costmaps
|
||||
# navigator.clearAllCostmaps() # also have clearLocalCostmap() and clearGlobalCostmap()
|
||||
# global_costmap = navigator.getGlobalCostmap()
|
||||
# local_costmap = navigator.getLocalCostmap()
|
||||
|
||||
# set our demo's goal poses to follow
|
||||
goal_poses = []
|
||||
goal_pose1 = PoseStamped()
|
||||
goal_pose1.header.frame_id = 'map'
|
||||
goal_pose1.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose1.pose.position.x = 1.5
|
||||
goal_pose1.pose.position.y = 0.55
|
||||
goal_pose1.pose.orientation.w = 0.707
|
||||
goal_pose1.pose.orientation.z = 0.707
|
||||
goal_poses.append(goal_pose1)
|
||||
|
||||
# additional goals can be appended
|
||||
goal_pose2 = PoseStamped()
|
||||
goal_pose2.header.frame_id = 'map'
|
||||
goal_pose2.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose2.pose.position.x = 1.5
|
||||
goal_pose2.pose.position.y = -3.75
|
||||
goal_pose2.pose.orientation.w = 0.707
|
||||
goal_pose2.pose.orientation.z = 0.707
|
||||
goal_poses.append(goal_pose2)
|
||||
goal_pose3 = PoseStamped()
|
||||
goal_pose3.header.frame_id = 'map'
|
||||
goal_pose3.header.stamp = navigator.get_clock().now().to_msg()
|
||||
goal_pose3.pose.position.x = -3.6
|
||||
goal_pose3.pose.position.y = -4.75
|
||||
goal_pose3.pose.orientation.w = 0.707
|
||||
goal_pose3.pose.orientation.z = 0.707
|
||||
goal_poses.append(goal_pose3)
|
||||
|
||||
# sanity check a valid path exists
|
||||
# path = navigator.getPath(initial_pose, goal_pose1)
|
||||
|
||||
nav_start = navigator.get_clock().now()
|
||||
navigator.followWaypoints(goal_poses)
|
||||
|
||||
i = 0
|
||||
while not navigator.isTaskComplete():
|
||||
################################################
|
||||
#
|
||||
# Implement some code here for your application!
|
||||
#
|
||||
################################################
|
||||
|
||||
# Do something with the feedback
|
||||
i = i + 1
|
||||
feedback = navigator.getFeedback()
|
||||
if feedback and i % 5 == 0:
|
||||
print('Executing current waypoint: ' +
|
||||
str(feedback.current_waypoint + 1) + '/' + str(len(goal_poses)))
|
||||
now = navigator.get_clock().now()
|
||||
|
||||
# Some navigation timeout to demo cancellation
|
||||
if now - nav_start > Duration(seconds=600.0):
|
||||
navigator.cancelTask()
|
||||
|
||||
# Some follow waypoints request change to demo preemption
|
||||
if now - nav_start > Duration(seconds=35.0):
|
||||
goal_pose4 = PoseStamped()
|
||||
goal_pose4.header.frame_id = 'map'
|
||||
goal_pose4.header.stamp = now.to_msg()
|
||||
goal_pose4.pose.position.x = -5.0
|
||||
goal_pose4.pose.position.y = -4.75
|
||||
goal_pose4.pose.orientation.w = 0.707
|
||||
goal_pose4.pose.orientation.z = 0.707
|
||||
goal_poses = [goal_pose4]
|
||||
nav_start = now
|
||||
navigator.followWaypoints(goal_poses)
|
||||
|
||||
# Do something depending on the return code
|
||||
result = navigator.getResult()
|
||||
if result == TaskResult.SUCCEEDED:
|
||||
print('Goal succeeded!')
|
||||
elif result == TaskResult.CANCELED:
|
||||
print('Goal was canceled!')
|
||||
elif result == TaskResult.FAILED:
|
||||
print('Goal failed!')
|
||||
else:
|
||||
print('Goal has an invalid return status!')
|
||||
|
||||
navigator.lifecycleShutdown()
|
||||
|
||||
exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
# Copyright 2022 Afif Swaidan
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
This is a Python3 API for a Footprint Collision Checker.
|
||||
|
||||
It provides the needed methods to manipulate the coordinates
|
||||
and calculate the cost of a Footprint
|
||||
"""
|
||||
|
||||
from math import cos, sin
|
||||
|
||||
from geometry_msgs.msg import Point32, Polygon
|
||||
from nav2_simple_commander.costmap_2d import PyCostmap2D
|
||||
from nav2_simple_commander.line_iterator import LineIterator
|
||||
|
||||
NO_INFORMATION = 255
|
||||
LETHAL_OBSTACLE = 254
|
||||
INSCRIBED_INFLATED_OBSTACLE = 253
|
||||
MAX_NON_OBSTACLE = 252
|
||||
FREE_SPACE = 0
|
||||
|
||||
|
||||
class FootprintCollisionChecker:
|
||||
"""
|
||||
FootprintCollisionChecker.
|
||||
|
||||
FootprintCollisionChecker Class for getting the cost
|
||||
and checking the collisions of a Footprint
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""Initialize the FootprintCollisionChecker Object."""
|
||||
self.costmap_ = None
|
||||
pass
|
||||
|
||||
def footprintCost(self, footprint: Polygon):
|
||||
"""
|
||||
Iterate over all the points in a footprint and check for collision.
|
||||
|
||||
Args
|
||||
----
|
||||
footprint (Polygon): The footprint to calculate the collision cost for
|
||||
|
||||
Returns
|
||||
-------
|
||||
LETHAL_OBSTACLE (int): If collision was found, 254 will be returned
|
||||
footprint_cost (float): The maximum cost found in the footprint points
|
||||
|
||||
"""
|
||||
footprint_cost = 0.0
|
||||
x1 = 0.0
|
||||
y1 = 0.0
|
||||
|
||||
x0, y0 = self.worldToMapValidated(footprint.points[0].x, footprint.points[0].y)
|
||||
|
||||
if x0 is None or y0 is None:
|
||||
return LETHAL_OBSTACLE
|
||||
|
||||
xstart = x0
|
||||
ystart = y0
|
||||
|
||||
for i in range(len(footprint.points) - 1):
|
||||
x1, y1 = self.worldToMapValidated(
|
||||
footprint.points[i + 1].x, footprint.points[i + 1].y
|
||||
)
|
||||
|
||||
if x1 is None or y1 is None:
|
||||
return LETHAL_OBSTACLE
|
||||
|
||||
footprint_cost = max(float(self.lineCost(x0, x1, y0, y1)), footprint_cost)
|
||||
x0 = x1
|
||||
y0 = y1
|
||||
|
||||
if footprint_cost == LETHAL_OBSTACLE:
|
||||
return footprint_cost
|
||||
|
||||
return max(float(self.lineCost(xstart, x1, ystart, y1)), footprint_cost)
|
||||
|
||||
def lineCost(self, x0, x1, y0, y1, step_size=0.5):
|
||||
"""
|
||||
Iterate over all the points along a line and check for collision.
|
||||
|
||||
Args
|
||||
----
|
||||
x0 (float): Abscissa of the initial point in map coordinates
|
||||
y0 (float): Ordinate of the initial point in map coordinates
|
||||
x1 (float): Abscissa of the final point in map coordinates
|
||||
y1 (float): Ordinate of the final point in map coordinates
|
||||
step_size (float): Optional, Increments' resolution, defaults to 0.5
|
||||
|
||||
Returns
|
||||
-------
|
||||
LETHAL_OBSTACLE (int): If collision was found, 254 will be returned
|
||||
line_cost (float): The maximum cost found in the line points
|
||||
|
||||
"""
|
||||
line_cost = 0.0
|
||||
point_cost = -1.0
|
||||
line_iterator = LineIterator(x0, y0, x1, y1, step_size)
|
||||
|
||||
while line_iterator.isValid():
|
||||
point_cost = self.pointCost(
|
||||
int(line_iterator.getX()), int(line_iterator.getY())
|
||||
)
|
||||
|
||||
if point_cost == LETHAL_OBSTACLE:
|
||||
return point_cost
|
||||
|
||||
if line_cost < point_cost:
|
||||
line_cost = point_cost
|
||||
|
||||
line_iterator.advance()
|
||||
|
||||
return line_cost
|
||||
|
||||
def worldToMapValidated(self, wx: float, wy: float):
|
||||
"""
|
||||
Get the map coordinate XY using world coordinate XY.
|
||||
|
||||
Args
|
||||
----
|
||||
wx (float): world coordinate X
|
||||
wy (float): world coordinate Y
|
||||
|
||||
Returns
|
||||
-------
|
||||
None: if coordinates are invalid
|
||||
tuple of int: mx, my (if coordinates are valid)
|
||||
mx (int): map coordinate X
|
||||
my (int): map coordinate Y
|
||||
|
||||
"""
|
||||
if self.costmap_ is None:
|
||||
raise ValueError(
|
||||
'Costmap not specified, use setCostmap to specify the costmap first'
|
||||
)
|
||||
return self.costmap_.worldToMap(wx, wy)
|
||||
|
||||
def pointCost(self, x: int, y: int):
|
||||
"""
|
||||
Get the cost of a point in the costmap using map coordinates XY.
|
||||
|
||||
Args
|
||||
----
|
||||
mx (int): map coordinate X
|
||||
my (int): map coordinate Y
|
||||
|
||||
Returns
|
||||
-------
|
||||
np.uint8: cost of a point
|
||||
|
||||
"""
|
||||
if self.costmap_ is None:
|
||||
raise ValueError(
|
||||
'Costmap not specified, use setCostmap to specify the costmap first'
|
||||
)
|
||||
return self.costmap_.getCostXY(x, y)
|
||||
|
||||
def setCostmap(self, costmap: PyCostmap2D):
|
||||
"""
|
||||
Specify which costmap to use.
|
||||
|
||||
Args
|
||||
----
|
||||
costmap (PyCostmap2D): costmap to use in the object's methods
|
||||
|
||||
Returns
|
||||
-------
|
||||
None
|
||||
|
||||
"""
|
||||
self.costmap_ = costmap
|
||||
return None
|
||||
|
||||
def footprintCostAtPose(self, x: float, y: float, theta: float, footprint: Polygon):
|
||||
"""
|
||||
Get the cost of a footprint at a specific Pose in map coordinates.
|
||||
|
||||
Args
|
||||
----
|
||||
x (float): map coordinate X
|
||||
y (float): map coordinate Y
|
||||
theta (float): absolute rotation angle of the footprint
|
||||
footprint (Polygon): the footprint to calculate its cost at the given Pose
|
||||
|
||||
Returns
|
||||
-------
|
||||
LETHAL_OBSTACLE (int): If collision was found, 254 will be returned
|
||||
footprint_cost (float): The maximum cost found in the footprint points
|
||||
|
||||
"""
|
||||
cos_th = cos(theta)
|
||||
sin_th = sin(theta)
|
||||
oriented_footprint = Polygon()
|
||||
|
||||
for i in range(len(footprint.points)):
|
||||
new_pt = Point32()
|
||||
new_pt.x = x + (
|
||||
footprint.points[i].x * cos_th - footprint.points[i].y * sin_th
|
||||
)
|
||||
new_pt.y = y + (
|
||||
footprint.points[i].x * sin_th + footprint.points[i].y * cos_th
|
||||
)
|
||||
oriented_footprint.points.append(new_pt)
|
||||
|
||||
return self.footprintCost(oriented_footprint)
|
||||
@@ -0,0 +1,177 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
# Copyright 2022 Afif Swaidan
|
||||
#
|
||||
# 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.
|
||||
|
||||
"""
|
||||
This is a Python3 API for a line iterator.
|
||||
|
||||
It provides the ability to iterate
|
||||
through the points of a line.
|
||||
"""
|
||||
|
||||
from cmath import sqrt
|
||||
|
||||
|
||||
class LineIterator():
|
||||
"""
|
||||
LineIterator.
|
||||
|
||||
LineIterator Python3 API for iterating along the points of a given line
|
||||
"""
|
||||
|
||||
def __init__(self, x0, y0, x1, y1, step_size=1.0):
|
||||
"""
|
||||
Initialize the LineIterator.
|
||||
|
||||
Args
|
||||
----
|
||||
x0 (float): Abscissa of the initial point
|
||||
y0 (float): Ordinate of the initial point
|
||||
x1 (float): Abscissa of the final point
|
||||
y1 (float): Ordinate of the final point
|
||||
step_size (float): Optional, Increments' resolution, defaults to 1
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError: When one (or more) of the inputs is not a number
|
||||
ValueError: When step_size is not a positive number
|
||||
|
||||
"""
|
||||
if type(x0) not in [int, float]:
|
||||
raise TypeError("x0 must be a number (int or float)")
|
||||
|
||||
if type(y0) not in [int, float]:
|
||||
raise TypeError("y0 must be a number (int or float)")
|
||||
|
||||
if type(x1) not in [int, float]:
|
||||
raise TypeError("x1 must be a number (int or float)")
|
||||
|
||||
if type(y1) not in [int, float]:
|
||||
raise TypeError("y1 must be a number (int or float)")
|
||||
|
||||
if type(step_size) not in [int, float]:
|
||||
raise TypeError("step_size must be a number (int or float)")
|
||||
|
||||
if step_size <= 0:
|
||||
raise ValueError("step_size must be a positive number")
|
||||
|
||||
self.x0_ = x0
|
||||
self.y0_ = y0
|
||||
self.x1_ = x1
|
||||
self.y1_ = y1
|
||||
self.x_ = x0
|
||||
self.y_ = y0
|
||||
self.step_size_ = step_size
|
||||
|
||||
if x1 != x0 and y1 != y0:
|
||||
self.valid_ = True
|
||||
self.m_ = (y1-y0)/(x1-x0)
|
||||
self.b_ = y1 - (self.m_*x1)
|
||||
elif x1 == x0 and y1 != y0:
|
||||
self.valid_ = True
|
||||
elif y1 == y1 and x1 != x0:
|
||||
self.valid_ = True
|
||||
self.m_ = (y1-y0)/(x1-x0)
|
||||
self.b_ = y1 - (self.m_*x1)
|
||||
else:
|
||||
self.valid_ = False
|
||||
raise ValueError(
|
||||
"Line has zero length (All 4 points have same coordinates)")
|
||||
|
||||
def isValid(self):
|
||||
"""Check if line is valid."""
|
||||
return self.valid_
|
||||
|
||||
def advance(self):
|
||||
"""Advance to the next point in the line."""
|
||||
if self.x1_ > self.x0_:
|
||||
if self.x_ < self.x1_:
|
||||
self.x_ = round(self.clamp(
|
||||
self.x_ + self.step_size_, self.x0_, self.x1_), 5)
|
||||
self.y_ = round(self.m_ * self.x_ + self.b_, 5)
|
||||
else:
|
||||
self.valid_ = False
|
||||
elif self.x1_ < self.x0_:
|
||||
if self.x_ > self.x1_:
|
||||
self.x_ = round(self.clamp(
|
||||
self.x_ - self.step_size_, self.x1_, self.x0_), 5)
|
||||
self.y_ = round(self.m_ * self.x_ + self.b_, 5)
|
||||
else:
|
||||
self.valid_ = False
|
||||
else:
|
||||
if self.y1_ > self.y0_:
|
||||
if self.y_ < self.y1_:
|
||||
self.y_ = round(self.clamp(
|
||||
self.y_ + self.step_size_, self.y0_, self.y1_), 5)
|
||||
else:
|
||||
self.valid_ = False
|
||||
elif self.y1_ < self.y0_:
|
||||
if self.y_ > self.y1_:
|
||||
self.y_ = round(self.clamp(
|
||||
self.y_ - self.step_size_, self.y1_, self.y0_), 5)
|
||||
else:
|
||||
self.valid_ = False
|
||||
else:
|
||||
self.valid_ = False
|
||||
|
||||
def getX(self):
|
||||
"""Get the abscissa of the current point."""
|
||||
return self.x_
|
||||
|
||||
def getY(self):
|
||||
"""Get the ordinate of the current point."""
|
||||
return self.y_
|
||||
|
||||
def getX0(self):
|
||||
"""Get the abscissa of the initial point."""
|
||||
return self.x0_
|
||||
|
||||
def getY0(self):
|
||||
"""Get the ordinate of the intial point."""
|
||||
return self.y0_
|
||||
|
||||
def getX1(self):
|
||||
"""Get the abscissa of the final point."""
|
||||
return self.x1_
|
||||
|
||||
def getY1(self):
|
||||
"""Get the ordinate of the final point."""
|
||||
return self.y1_
|
||||
|
||||
def get_line_length(self):
|
||||
"""Get the length of the line."""
|
||||
return sqrt(pow(self.x1_ - self.x0_, 2) + pow(self.y1_ - self.y0_, 2))
|
||||
|
||||
def clamp(self, n, min_n, max_n):
|
||||
"""
|
||||
Clamp n to be between min_n and max_n.
|
||||
|
||||
Args
|
||||
----
|
||||
n (float): input value
|
||||
min_n (float): minimum value
|
||||
max_n (float): maximum value
|
||||
|
||||
Returns
|
||||
-------
|
||||
n (float): input value clamped between given min and max
|
||||
|
||||
"""
|
||||
if n < min_n:
|
||||
return min_n
|
||||
elif n > max_n:
|
||||
return max_n
|
||||
else:
|
||||
return n
|
||||
@@ -0,0 +1,593 @@
|
||||
#! /usr/bin/env python3
|
||||
# Copyright 2021 Samsung Research America
|
||||
#
|
||||
# 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.
|
||||
|
||||
|
||||
from enum import Enum
|
||||
import time
|
||||
|
||||
from action_msgs.msg import GoalStatus
|
||||
from builtin_interfaces.msg import Duration
|
||||
from geometry_msgs.msg import Point
|
||||
from geometry_msgs.msg import PoseStamped
|
||||
from geometry_msgs.msg import PoseWithCovarianceStamped
|
||||
from lifecycle_msgs.srv import GetState
|
||||
from nav2_msgs.action import AssistedTeleop, BackUp, Spin
|
||||
from nav2_msgs.action import ComputePathThroughPoses, ComputePathToPose
|
||||
from nav2_msgs.action import FollowPath, FollowWaypoints, NavigateThroughPoses, NavigateToPose
|
||||
from nav2_msgs.action import SmoothPath
|
||||
from nav2_msgs.srv import ClearEntireCostmap, GetCostmap, LoadMap, ManageLifecycleNodes
|
||||
|
||||
import rclpy
|
||||
from rclpy.action import ActionClient
|
||||
from rclpy.duration import Duration as rclpyDuration
|
||||
from rclpy.node import Node
|
||||
from rclpy.qos import QoSDurabilityPolicy, QoSHistoryPolicy
|
||||
from rclpy.qos import QoSProfile, QoSReliabilityPolicy
|
||||
|
||||
|
||||
class TaskResult(Enum):
|
||||
UNKNOWN = 0
|
||||
SUCCEEDED = 1
|
||||
CANCELED = 2
|
||||
FAILED = 3
|
||||
|
||||
|
||||
class BasicNavigator(Node):
|
||||
|
||||
def __init__(self, node_name='basic_navigator', namespace=''):
|
||||
super().__init__(node_name=node_name, namespace=namespace)
|
||||
self.initial_pose = PoseStamped()
|
||||
self.initial_pose.header.frame_id = 'map'
|
||||
self.goal_handle = None
|
||||
self.result_future = None
|
||||
self.feedback = None
|
||||
self.status = None
|
||||
|
||||
amcl_pose_qos = QoSProfile(
|
||||
durability=QoSDurabilityPolicy.TRANSIENT_LOCAL,
|
||||
reliability=QoSReliabilityPolicy.RELIABLE,
|
||||
history=QoSHistoryPolicy.KEEP_LAST,
|
||||
depth=1)
|
||||
|
||||
self.initial_pose_received = False
|
||||
self.nav_through_poses_client = ActionClient(self,
|
||||
NavigateThroughPoses,
|
||||
'navigate_through_poses')
|
||||
self.nav_to_pose_client = ActionClient(self, NavigateToPose, 'navigate_to_pose')
|
||||
self.follow_waypoints_client = ActionClient(self, FollowWaypoints, 'follow_waypoints')
|
||||
self.follow_path_client = ActionClient(self, FollowPath, 'follow_path')
|
||||
self.compute_path_to_pose_client = ActionClient(self, ComputePathToPose,
|
||||
'compute_path_to_pose')
|
||||
self.compute_path_through_poses_client = ActionClient(self, ComputePathThroughPoses,
|
||||
'compute_path_through_poses')
|
||||
self.smoother_client = ActionClient(self, SmoothPath, 'smooth_path')
|
||||
self.spin_client = ActionClient(self, Spin, 'spin')
|
||||
self.backup_client = ActionClient(self, BackUp, 'backup')
|
||||
self.assisted_teleop_client = ActionClient(self, AssistedTeleop, 'assisted_teleop')
|
||||
self.localization_pose_sub = self.create_subscription(PoseWithCovarianceStamped,
|
||||
'amcl_pose',
|
||||
self._amclPoseCallback,
|
||||
amcl_pose_qos)
|
||||
self.initial_pose_pub = self.create_publisher(PoseWithCovarianceStamped,
|
||||
'initialpose',
|
||||
10)
|
||||
self.change_maps_srv = self.create_client(LoadMap, 'map_server/load_map')
|
||||
self.clear_costmap_global_srv = self.create_client(
|
||||
ClearEntireCostmap, 'global_costmap/clear_entirely_global_costmap')
|
||||
self.clear_costmap_local_srv = self.create_client(
|
||||
ClearEntireCostmap, 'local_costmap/clear_entirely_local_costmap')
|
||||
self.get_costmap_global_srv = self.create_client(GetCostmap, 'global_costmap/get_costmap')
|
||||
self.get_costmap_local_srv = self.create_client(GetCostmap, 'local_costmap/get_costmap')
|
||||
|
||||
def destroyNode(self):
|
||||
self.destroy_node()
|
||||
|
||||
def destroy_node(self):
|
||||
self.nav_through_poses_client.destroy()
|
||||
self.nav_to_pose_client.destroy()
|
||||
self.follow_waypoints_client.destroy()
|
||||
self.follow_path_client.destroy()
|
||||
self.compute_path_to_pose_client.destroy()
|
||||
self.compute_path_through_poses_client.destroy()
|
||||
self.smoother_client.destroy()
|
||||
self.spin_client.destroy()
|
||||
self.backup_client.destroy()
|
||||
super().destroy_node()
|
||||
|
||||
def setInitialPose(self, initial_pose):
|
||||
"""Set the initial pose to the localization system."""
|
||||
self.initial_pose_received = False
|
||||
self.initial_pose = initial_pose
|
||||
self._setInitialPose()
|
||||
|
||||
def goThroughPoses(self, poses, behavior_tree=''):
|
||||
"""Send a `NavThroughPoses` action request."""
|
||||
self.debug("Waiting for 'NavigateThroughPoses' action server")
|
||||
while not self.nav_through_poses_client.wait_for_server(timeout_sec=1.0):
|
||||
self.info("'NavigateThroughPoses' action server not available, waiting...")
|
||||
|
||||
goal_msg = NavigateThroughPoses.Goal()
|
||||
goal_msg.poses = poses
|
||||
goal_msg.behavior_tree = behavior_tree
|
||||
|
||||
self.info(f'Navigating with {len(goal_msg.poses)} goals....')
|
||||
send_goal_future = self.nav_through_poses_client.send_goal_async(goal_msg,
|
||||
self._feedbackCallback)
|
||||
rclpy.spin_until_future_complete(self, send_goal_future)
|
||||
self.goal_handle = send_goal_future.result()
|
||||
|
||||
if not self.goal_handle.accepted:
|
||||
self.error(f'Goal with {len(poses)} poses was rejected!')
|
||||
return False
|
||||
|
||||
self.result_future = self.goal_handle.get_result_async()
|
||||
return True
|
||||
|
||||
def goToPose(self, pose, behavior_tree=''):
|
||||
"""Send a `NavToPose` action request."""
|
||||
self.debug("Waiting for 'NavigateToPose' action server")
|
||||
while not self.nav_to_pose_client.wait_for_server(timeout_sec=1.0):
|
||||
self.info("'NavigateToPose' action server not available, waiting...")
|
||||
|
||||
goal_msg = NavigateToPose.Goal()
|
||||
goal_msg.pose = pose
|
||||
goal_msg.behavior_tree = behavior_tree
|
||||
|
||||
self.info('Navigating to goal: ' + str(pose.pose.position.x) + ' ' +
|
||||
str(pose.pose.position.y) + '...')
|
||||
send_goal_future = self.nav_to_pose_client.send_goal_async(goal_msg,
|
||||
self._feedbackCallback)
|
||||
rclpy.spin_until_future_complete(self, send_goal_future)
|
||||
self.goal_handle = send_goal_future.result()
|
||||
|
||||
if not self.goal_handle.accepted:
|
||||
self.error('Goal to ' + str(pose.pose.position.x) + ' ' +
|
||||
str(pose.pose.position.y) + ' was rejected!')
|
||||
return False
|
||||
|
||||
self.result_future = self.goal_handle.get_result_async()
|
||||
return True
|
||||
|
||||
def followWaypoints(self, poses):
|
||||
"""Send a `FollowWaypoints` action request."""
|
||||
self.debug("Waiting for 'FollowWaypoints' action server")
|
||||
while not self.follow_waypoints_client.wait_for_server(timeout_sec=1.0):
|
||||
self.info("'FollowWaypoints' action server not available, waiting...")
|
||||
|
||||
goal_msg = FollowWaypoints.Goal()
|
||||
goal_msg.poses = poses
|
||||
|
||||
self.info(f'Following {len(goal_msg.poses)} goals....')
|
||||
send_goal_future = self.follow_waypoints_client.send_goal_async(goal_msg,
|
||||
self._feedbackCallback)
|
||||
rclpy.spin_until_future_complete(self, send_goal_future)
|
||||
self.goal_handle = send_goal_future.result()
|
||||
|
||||
if not self.goal_handle.accepted:
|
||||
self.error(f'Following {len(poses)} waypoints request was rejected!')
|
||||
return False
|
||||
|
||||
self.result_future = self.goal_handle.get_result_async()
|
||||
return True
|
||||
|
||||
def spin(self, spin_dist=1.57, time_allowance=10):
|
||||
self.debug("Waiting for 'Spin' action server")
|
||||
while not self.spin_client.wait_for_server(timeout_sec=1.0):
|
||||
self.info("'Spin' action server not available, waiting...")
|
||||
goal_msg = Spin.Goal()
|
||||
goal_msg.target_yaw = spin_dist
|
||||
goal_msg.time_allowance = Duration(sec=time_allowance)
|
||||
|
||||
self.info(f'Spinning to angle {goal_msg.target_yaw}....')
|
||||
send_goal_future = self.spin_client.send_goal_async(goal_msg, self._feedbackCallback)
|
||||
rclpy.spin_until_future_complete(self, send_goal_future)
|
||||
self.goal_handle = send_goal_future.result()
|
||||
|
||||
if not self.goal_handle.accepted:
|
||||
self.error('Spin request was rejected!')
|
||||
return False
|
||||
|
||||
self.result_future = self.goal_handle.get_result_async()
|
||||
return True
|
||||
|
||||
def backup(self, backup_dist=0.15, backup_speed=0.025, time_allowance=10):
|
||||
self.debug("Waiting for 'Backup' action server")
|
||||
while not self.backup_client.wait_for_server(timeout_sec=1.0):
|
||||
self.info("'Backup' action server not available, waiting...")
|
||||
goal_msg = BackUp.Goal()
|
||||
goal_msg.target = Point(x=float(backup_dist))
|
||||
goal_msg.speed = backup_speed
|
||||
goal_msg.time_allowance = Duration(sec=time_allowance)
|
||||
|
||||
self.info(f'Backing up {goal_msg.target.x} m at {goal_msg.speed} m/s....')
|
||||
send_goal_future = self.backup_client.send_goal_async(goal_msg, self._feedbackCallback)
|
||||
rclpy.spin_until_future_complete(self, send_goal_future)
|
||||
self.goal_handle = send_goal_future.result()
|
||||
|
||||
if not self.goal_handle.accepted:
|
||||
self.error('Backup request was rejected!')
|
||||
return False
|
||||
|
||||
self.result_future = self.goal_handle.get_result_async()
|
||||
return True
|
||||
|
||||
def assistedTeleop(self, time_allowance=30):
|
||||
self.debug("Wainting for 'assisted_teleop' action server")
|
||||
while not self.assisted_teleop_client.wait_for_server(timeout_sec=1.0):
|
||||
self.info("'assisted_teleop' action server not available, waiting...")
|
||||
goal_msg = AssistedTeleop.Goal()
|
||||
goal_msg.time_allowance = Duration(sec=time_allowance)
|
||||
|
||||
self.info("Running 'assisted_teleop'....")
|
||||
send_goal_future = \
|
||||
self.assisted_teleop_client.send_goal_async(goal_msg, self._feedbackCallback)
|
||||
rclpy.spin_until_future_complete(self, send_goal_future)
|
||||
self.goal_handle = send_goal_future.result()
|
||||
|
||||
if not self.goal_handle.accepted:
|
||||
self.error('Assisted Teleop request was rejected!')
|
||||
return False
|
||||
|
||||
self.result_future = self.goal_handle.get_result_async()
|
||||
return True
|
||||
|
||||
def followPath(self, path, controller_id='', goal_checker_id=''):
|
||||
"""Send a `FollowPath` action request."""
|
||||
self.debug("Waiting for 'FollowPath' action server")
|
||||
while not self.follow_path_client.wait_for_server(timeout_sec=1.0):
|
||||
self.info("'FollowPath' action server not available, waiting...")
|
||||
|
||||
goal_msg = FollowPath.Goal()
|
||||
goal_msg.path = path
|
||||
goal_msg.controller_id = controller_id
|
||||
goal_msg.goal_checker_id = goal_checker_id
|
||||
|
||||
self.info('Executing path...')
|
||||
send_goal_future = self.follow_path_client.send_goal_async(goal_msg,
|
||||
self._feedbackCallback)
|
||||
rclpy.spin_until_future_complete(self, send_goal_future)
|
||||
self.goal_handle = send_goal_future.result()
|
||||
|
||||
if not self.goal_handle.accepted:
|
||||
self.error('Follow path was rejected!')
|
||||
return False
|
||||
|
||||
self.result_future = self.goal_handle.get_result_async()
|
||||
return True
|
||||
|
||||
def cancelTask(self):
|
||||
"""Cancel pending task request of any type."""
|
||||
self.info('Canceling current task.')
|
||||
if self.result_future:
|
||||
future = self.goal_handle.cancel_goal_async()
|
||||
rclpy.spin_until_future_complete(self, future)
|
||||
return
|
||||
|
||||
def isTaskComplete(self):
|
||||
"""Check if the task request of any type is complete yet."""
|
||||
if not self.result_future:
|
||||
# task was cancelled or completed
|
||||
return True
|
||||
rclpy.spin_until_future_complete(self, self.result_future, timeout_sec=0.10)
|
||||
if self.result_future.result():
|
||||
self.status = self.result_future.result().status
|
||||
if self.status != GoalStatus.STATUS_SUCCEEDED:
|
||||
self.debug(f'Task with failed with status code: {self.status}')
|
||||
return True
|
||||
else:
|
||||
# Timed out, still processing, not complete yet
|
||||
return False
|
||||
|
||||
self.debug('Task succeeded!')
|
||||
return True
|
||||
|
||||
def getFeedback(self):
|
||||
"""Get the pending action feedback message."""
|
||||
return self.feedback
|
||||
|
||||
def getResult(self):
|
||||
"""Get the pending action result message."""
|
||||
if self.status == GoalStatus.STATUS_SUCCEEDED:
|
||||
return TaskResult.SUCCEEDED
|
||||
elif self.status == GoalStatus.STATUS_ABORTED:
|
||||
return TaskResult.FAILED
|
||||
elif self.status == GoalStatus.STATUS_CANCELED:
|
||||
return TaskResult.CANCELED
|
||||
else:
|
||||
return TaskResult.UNKNOWN
|
||||
|
||||
def waitUntilNav2Active(self, navigator='bt_navigator', localizer='amcl'):
|
||||
"""Block until the full navigation system is up and running."""
|
||||
self._waitForNodeToActivate(localizer)
|
||||
if localizer == 'amcl':
|
||||
self._waitForInitialPose()
|
||||
self._waitForNodeToActivate(navigator)
|
||||
self.info('Nav2 is ready for use!')
|
||||
return
|
||||
|
||||
def _getPathImpl(self, start, goal, planner_id='', use_start=False):
|
||||
"""
|
||||
Send a `ComputePathToPose` action request.
|
||||
|
||||
Internal implementation to get the full result, not just the path.
|
||||
"""
|
||||
self.debug("Waiting for 'ComputePathToPose' action server")
|
||||
while not self.compute_path_to_pose_client.wait_for_server(timeout_sec=1.0):
|
||||
self.info("'ComputePathToPose' action server not available, waiting...")
|
||||
|
||||
goal_msg = ComputePathToPose.Goal()
|
||||
goal_msg.start = start
|
||||
goal_msg.goal = goal
|
||||
goal_msg.planner_id = planner_id
|
||||
goal_msg.use_start = use_start
|
||||
|
||||
self.info('Getting path...')
|
||||
send_goal_future = self.compute_path_to_pose_client.send_goal_async(goal_msg)
|
||||
rclpy.spin_until_future_complete(self, send_goal_future)
|
||||
self.goal_handle = send_goal_future.result()
|
||||
|
||||
if not self.goal_handle.accepted:
|
||||
self.error('Get path was rejected!')
|
||||
return None
|
||||
|
||||
self.result_future = self.goal_handle.get_result_async()
|
||||
rclpy.spin_until_future_complete(self, self.result_future)
|
||||
self.status = self.result_future.result().status
|
||||
if self.status != GoalStatus.STATUS_SUCCEEDED:
|
||||
self.warn(f'Getting path failed with status code: {self.status}')
|
||||
return None
|
||||
|
||||
return self.result_future.result().result
|
||||
|
||||
def getPath(self, start, goal, planner_id='', use_start=False):
|
||||
"""Send a `ComputePathToPose` action request."""
|
||||
rtn = self._getPathImpl(start, goal, planner_id, use_start)
|
||||
if not rtn:
|
||||
return None
|
||||
else:
|
||||
return rtn.path
|
||||
|
||||
def getPathThroughPoses(self, start, goals, planner_id='', use_start=False):
|
||||
"""Send a `ComputePathThroughPoses` action request."""
|
||||
self.debug("Waiting for 'ComputePathThroughPoses' action server")
|
||||
while not self.compute_path_through_poses_client.wait_for_server(timeout_sec=1.0):
|
||||
self.info("'ComputePathThroughPoses' action server not available, waiting...")
|
||||
|
||||
goal_msg = ComputePathThroughPoses.Goal()
|
||||
goal_msg.start = start
|
||||
goal_msg.goals = goals
|
||||
goal_msg.planner_id = planner_id
|
||||
goal_msg.use_start = use_start
|
||||
|
||||
self.info('Getting path...')
|
||||
send_goal_future = self.compute_path_through_poses_client.send_goal_async(goal_msg)
|
||||
rclpy.spin_until_future_complete(self, send_goal_future)
|
||||
self.goal_handle = send_goal_future.result()
|
||||
|
||||
if not self.goal_handle.accepted:
|
||||
self.error('Get path was rejected!')
|
||||
return None
|
||||
|
||||
self.result_future = self.goal_handle.get_result_async()
|
||||
rclpy.spin_until_future_complete(self, self.result_future)
|
||||
self.status = self.result_future.result().status
|
||||
if self.status != GoalStatus.STATUS_SUCCEEDED:
|
||||
self.warn(f'Getting path failed with status code: {self.status}')
|
||||
return None
|
||||
|
||||
return self.result_future.result().result.path
|
||||
|
||||
def _smoothPathImpl(self, path, smoother_id='', max_duration=2.0, check_for_collision=False):
|
||||
"""
|
||||
Send a `SmoothPath` action request.
|
||||
|
||||
Internal implementation to get the full result, not just the path.
|
||||
"""
|
||||
self.debug("Waiting for 'SmoothPath' action server")
|
||||
while not self.smoother_client.wait_for_server(timeout_sec=1.0):
|
||||
self.info("'SmoothPath' action server not available, waiting...")
|
||||
|
||||
goal_msg = SmoothPath.Goal()
|
||||
goal_msg.path = path
|
||||
goal_msg.max_smoothing_duration = rclpyDuration(seconds=max_duration).to_msg()
|
||||
goal_msg.smoother_id = smoother_id
|
||||
goal_msg.check_for_collisions = check_for_collision
|
||||
|
||||
self.info('Smoothing path...')
|
||||
send_goal_future = self.smoother_client.send_goal_async(goal_msg)
|
||||
rclpy.spin_until_future_complete(self, send_goal_future)
|
||||
self.goal_handle = send_goal_future.result()
|
||||
|
||||
if not self.goal_handle.accepted:
|
||||
self.error('Smooth path was rejected!')
|
||||
return None
|
||||
|
||||
self.result_future = self.goal_handle.get_result_async()
|
||||
rclpy.spin_until_future_complete(self, self.result_future)
|
||||
self.status = self.result_future.result().status
|
||||
if self.status != GoalStatus.STATUS_SUCCEEDED:
|
||||
self.warn(f'Getting path failed with status code: {self.status}')
|
||||
return None
|
||||
|
||||
return self.result_future.result().result
|
||||
|
||||
def smoothPath(self, path, smoother_id='', max_duration=2.0, check_for_collision=False):
|
||||
"""Send a `SmoothPath` action request."""
|
||||
rtn = self._smoothPathImpl(
|
||||
path, smoother_id, max_duration, check_for_collision)
|
||||
if not rtn:
|
||||
return None
|
||||
else:
|
||||
return rtn.path
|
||||
|
||||
def changeMap(self, map_filepath):
|
||||
"""Change the current static map in the map server."""
|
||||
while not self.change_maps_srv.wait_for_service(timeout_sec=1.0):
|
||||
self.info('change map service not available, waiting...')
|
||||
req = LoadMap.Request()
|
||||
req.map_url = map_filepath
|
||||
future = self.change_maps_srv.call_async(req)
|
||||
rclpy.spin_until_future_complete(self, future)
|
||||
status = future.result().result
|
||||
if status != LoadMap.Response().RESULT_SUCCESS:
|
||||
self.error('Change map request failed!')
|
||||
else:
|
||||
self.info('Change map request was successful!')
|
||||
return
|
||||
|
||||
def clearAllCostmaps(self):
|
||||
"""Clear all costmaps."""
|
||||
self.clearLocalCostmap()
|
||||
self.clearGlobalCostmap()
|
||||
return
|
||||
|
||||
def clearLocalCostmap(self):
|
||||
"""Clear local costmap."""
|
||||
while not self.clear_costmap_local_srv.wait_for_service(timeout_sec=1.0):
|
||||
self.info('Clear local costmaps service not available, waiting...')
|
||||
req = ClearEntireCostmap.Request()
|
||||
future = self.clear_costmap_local_srv.call_async(req)
|
||||
rclpy.spin_until_future_complete(self, future)
|
||||
return
|
||||
|
||||
def clearGlobalCostmap(self):
|
||||
"""Clear global costmap."""
|
||||
while not self.clear_costmap_global_srv.wait_for_service(timeout_sec=1.0):
|
||||
self.info('Clear global costmaps service not available, waiting...')
|
||||
req = ClearEntireCostmap.Request()
|
||||
future = self.clear_costmap_global_srv.call_async(req)
|
||||
rclpy.spin_until_future_complete(self, future)
|
||||
return
|
||||
|
||||
def getGlobalCostmap(self):
|
||||
"""Get the global costmap."""
|
||||
while not self.get_costmap_global_srv.wait_for_service(timeout_sec=1.0):
|
||||
self.info('Get global costmaps service not available, waiting...')
|
||||
req = GetCostmap.Request()
|
||||
future = self.get_costmap_global_srv.call_async(req)
|
||||
rclpy.spin_until_future_complete(self, future)
|
||||
return future.result().map
|
||||
|
||||
def getLocalCostmap(self):
|
||||
"""Get the local costmap."""
|
||||
while not self.get_costmap_local_srv.wait_for_service(timeout_sec=1.0):
|
||||
self.info('Get local costmaps service not available, waiting...')
|
||||
req = GetCostmap.Request()
|
||||
future = self.get_costmap_local_srv.call_async(req)
|
||||
rclpy.spin_until_future_complete(self, future)
|
||||
return future.result().map
|
||||
|
||||
def lifecycleStartup(self):
|
||||
"""Startup nav2 lifecycle system."""
|
||||
self.info('Starting up lifecycle nodes based on lifecycle_manager.')
|
||||
for srv_name, srv_type in self.get_service_names_and_types():
|
||||
if srv_type[0] == 'nav2_msgs/srv/ManageLifecycleNodes':
|
||||
self.info(f'Starting up {srv_name}')
|
||||
mgr_client = self.create_client(ManageLifecycleNodes, srv_name)
|
||||
while not mgr_client.wait_for_service(timeout_sec=1.0):
|
||||
self.info(f'{srv_name} service not available, waiting...')
|
||||
req = ManageLifecycleNodes.Request()
|
||||
req.command = ManageLifecycleNodes.Request().STARTUP
|
||||
future = mgr_client.call_async(req)
|
||||
|
||||
# starting up requires a full map->odom->base_link TF tree
|
||||
# so if we're not successful, try forwarding the initial pose
|
||||
while True:
|
||||
rclpy.spin_until_future_complete(self, future, timeout_sec=0.10)
|
||||
if not future:
|
||||
self._waitForInitialPose()
|
||||
else:
|
||||
break
|
||||
self.info('Nav2 is ready for use!')
|
||||
return
|
||||
|
||||
def lifecycleShutdown(self):
|
||||
"""Shutdown nav2 lifecycle system."""
|
||||
self.info('Shutting down lifecycle nodes based on lifecycle_manager.')
|
||||
for srv_name, srv_type in self.get_service_names_and_types():
|
||||
if srv_type[0] == 'nav2_msgs/srv/ManageLifecycleNodes':
|
||||
self.info(f'Shutting down {srv_name}')
|
||||
mgr_client = self.create_client(ManageLifecycleNodes, srv_name)
|
||||
while not mgr_client.wait_for_service(timeout_sec=1.0):
|
||||
self.info(f'{srv_name} service not available, waiting...')
|
||||
req = ManageLifecycleNodes.Request()
|
||||
req.command = ManageLifecycleNodes.Request().SHUTDOWN
|
||||
future = mgr_client.call_async(req)
|
||||
rclpy.spin_until_future_complete(self, future)
|
||||
future.result()
|
||||
return
|
||||
|
||||
def _waitForNodeToActivate(self, node_name):
|
||||
# Waits for the node within the tester namespace to become active
|
||||
self.debug(f'Waiting for {node_name} to become active..')
|
||||
node_service = f'{node_name}/get_state'
|
||||
state_client = self.create_client(GetState, node_service)
|
||||
while not state_client.wait_for_service(timeout_sec=1.0):
|
||||
self.info(f'{node_service} service not available, waiting...')
|
||||
|
||||
req = GetState.Request()
|
||||
state = 'unknown'
|
||||
while state != 'active':
|
||||
self.debug(f'Getting {node_name} state...')
|
||||
future = state_client.call_async(req)
|
||||
rclpy.spin_until_future_complete(self, future)
|
||||
if future.result() is not None:
|
||||
state = future.result().current_state.label
|
||||
self.debug(f'Result of get_state: {state}')
|
||||
time.sleep(2)
|
||||
return
|
||||
|
||||
def _waitForInitialPose(self):
|
||||
while not self.initial_pose_received:
|
||||
self.info('Setting initial pose')
|
||||
self._setInitialPose()
|
||||
self.info('Waiting for amcl_pose to be received')
|
||||
rclpy.spin_once(self, timeout_sec=1.0)
|
||||
return
|
||||
|
||||
def _amclPoseCallback(self, msg):
|
||||
self.debug('Received amcl pose')
|
||||
self.initial_pose_received = True
|
||||
return
|
||||
|
||||
def _feedbackCallback(self, msg):
|
||||
self.debug('Received action feedback message')
|
||||
self.feedback = msg.feedback
|
||||
return
|
||||
|
||||
def _setInitialPose(self):
|
||||
msg = PoseWithCovarianceStamped()
|
||||
msg.pose.pose = self.initial_pose.pose
|
||||
msg.header.frame_id = self.initial_pose.header.frame_id
|
||||
msg.header.stamp = self.initial_pose.header.stamp
|
||||
self.info('Publishing Initial Pose')
|
||||
self.initial_pose_pub.publish(msg)
|
||||
return
|
||||
|
||||
def info(self, msg):
|
||||
self.get_logger().info(msg)
|
||||
return
|
||||
|
||||
def warn(self, msg):
|
||||
self.get_logger().warn(msg)
|
||||
return
|
||||
|
||||
def error(self, msg):
|
||||
self.get_logger().error(msg)
|
||||
return
|
||||
|
||||
def debug(self, msg):
|
||||
self.get_logger().debug(msg)
|
||||
return
|
||||
@@ -0,0 +1,24 @@
|
||||
<?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>nav2_simple_commander</name>
|
||||
<version>1.1.18</version>
|
||||
<description>An importable library for writing mobile robot applications in python3</description>
|
||||
<maintainer email="stevenmacenski@gmail.com">steve</maintainer>
|
||||
<license>Apache-2.0</license>
|
||||
|
||||
<exec_depend>rclpy</exec_depend>
|
||||
<exec_depend>geometry_msgs</exec_depend>
|
||||
<exec_depend>nav2_msgs</exec_depend>
|
||||
<exec_depend>action_msgs</exec_depend>
|
||||
<exec_depend>lifecycle_msgs</exec_depend>
|
||||
|
||||
<test_depend>ament_copyright</test_depend>
|
||||
<test_depend>ament_flake8</test_depend>
|
||||
<test_depend>ament_pep257</test_depend>
|
||||
<test_depend>python3-pytest</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_python</build_type>
|
||||
</export>
|
||||
</package>
|
||||
@@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
junit_family=xunit2
|
||||
@@ -0,0 +1,4 @@
|
||||
[develop]
|
||||
script_dir=$base/lib/nav2_simple_commander
|
||||
[install]
|
||||
install_scripts=$base/lib/nav2_simple_commander
|
||||
@@ -0,0 +1,39 @@
|
||||
from glob import glob
|
||||
import os
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
|
||||
package_name = 'nav2_simple_commander'
|
||||
|
||||
setup(
|
||||
name=package_name,
|
||||
version='1.0.0',
|
||||
packages=[package_name],
|
||||
data_files=[
|
||||
('share/ament_index/resource_index/packages',
|
||||
['resource/' + package_name]),
|
||||
('share/' + package_name, ['package.xml']),
|
||||
(os.path.join('share', package_name), glob('launch/*')),
|
||||
],
|
||||
install_requires=['setuptools'],
|
||||
zip_safe=True,
|
||||
maintainer='steve',
|
||||
maintainer_email='stevenmacenski@gmail.com',
|
||||
description='An importable library for writing mobile robot applications in python3',
|
||||
license='Apache-2.0',
|
||||
tests_require=['pytest'],
|
||||
entry_points={
|
||||
'console_scripts': [
|
||||
'example_nav_to_pose = nav2_simple_commander.example_nav_to_pose:main',
|
||||
'example_nav_through_poses = nav2_simple_commander.example_nav_through_poses:main',
|
||||
'example_waypoint_follower = nav2_simple_commander.example_waypoint_follower:main',
|
||||
'example_follow_path = nav2_simple_commander.example_follow_path:main',
|
||||
'demo_picking = nav2_simple_commander.demo_picking:main',
|
||||
'demo_inspection = nav2_simple_commander.demo_inspection:main',
|
||||
'demo_security = nav2_simple_commander.demo_security:main',
|
||||
'demo_recoveries = nav2_simple_commander.demo_recoveries:main',
|
||||
'example_assisted_teleop = nav2_simple_commander.example_assisted_teleop:main',
|
||||
],
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
from ament_copyright.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.copyright
|
||||
@pytest.mark.linter
|
||||
def test_copyright():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found errors'
|
||||
@@ -0,0 +1,25 @@
|
||||
# Copyright 2017 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.
|
||||
|
||||
from ament_flake8.main import main_with_errors
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.flake8
|
||||
@pytest.mark.linter
|
||||
def test_flake8():
|
||||
rc, errors = main_with_errors(argv=[])
|
||||
assert rc == 0, \
|
||||
'Found %d code style errors / warnings:\n' % len(errors) + \
|
||||
'\n'.join(errors)
|
||||
@@ -0,0 +1,107 @@
|
||||
# Copyright 2022 Afif Swaidan
|
||||
#
|
||||
# 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 unittest
|
||||
from cmath import sqrt
|
||||
from nav2_simple_commander.line_iterator import LineIterator
|
||||
|
||||
|
||||
class TestLineIterator(unittest.TestCase):
|
||||
|
||||
def test_type_error(self):
|
||||
# Test if a type error raised when passing invalid arguements types
|
||||
self.assertRaises(TypeError, LineIterator, 0, 0, '10', 10, '1')
|
||||
|
||||
def test_value_error(self):
|
||||
# Test if a value error raised when passing negative or zero step_size
|
||||
self.assertRaises(ValueError, LineIterator, 0, 0, 10, 10, -2)
|
||||
# Test if a value error raised when passing zero length line
|
||||
self.assertRaises(ValueError, LineIterator, 2, 2, 2, 2, 1)
|
||||
|
||||
def test_get_xy(self):
|
||||
# Test if the initial and final coordinates are returned correctly
|
||||
lt = LineIterator(0, 0, 5, 5, 1)
|
||||
self.assertEqual(lt.getX0(), 0)
|
||||
self.assertEqual(lt.getY0(), 0)
|
||||
self.assertEqual(lt.getX1(), 5)
|
||||
self.assertEqual(lt.getY1(), 5)
|
||||
|
||||
def test_line_length(self):
|
||||
# Test if the line length is calculated correctly
|
||||
lt = LineIterator(0, 0, 5, 5, 1)
|
||||
self.assertEqual(lt.get_line_length(), sqrt(pow(5, 2) + pow(5, 2)))
|
||||
|
||||
def test_straight_line(self):
|
||||
# Test if the calculations are correct for y = x
|
||||
lt = LineIterator(0, 0, 5, 5, 1)
|
||||
i = 0
|
||||
while lt.isValid():
|
||||
self.assertEqual(lt.getX(), lt.getX0() + i)
|
||||
self.assertEqual(lt.getY(), lt.getY0() + i)
|
||||
lt.advance()
|
||||
i += 1
|
||||
|
||||
# Test if the calculations are correct for y = 2x (positive slope)
|
||||
lt = LineIterator(0, 0, 5, 10, 1)
|
||||
i = 0
|
||||
while lt.isValid():
|
||||
self.assertEqual(lt.getX(), lt.getX0() + i)
|
||||
self.assertEqual(lt.getY(), lt.getY0() + (i*2))
|
||||
lt.advance()
|
||||
i += 1
|
||||
|
||||
# Test if the calculations are correct for y = -2x (negative slope)
|
||||
lt = LineIterator(0, 0, 5, -10, 1)
|
||||
i = 0
|
||||
while lt.isValid():
|
||||
self.assertEqual(lt.getX(), lt.getX0() + i)
|
||||
self.assertEqual(lt.getY(), lt.getY0() + (-i*2))
|
||||
lt.advance()
|
||||
i += 1
|
||||
|
||||
def test_hor_line(self):
|
||||
# Test if the calculations are correct for y = 0x+b (horizontal line)
|
||||
lt = LineIterator(0, 10, 5, 10, 1)
|
||||
i = 0
|
||||
while lt.isValid():
|
||||
self.assertEqual(lt.getX(), lt.getX0() + i)
|
||||
self.assertEqual(lt.getY(), lt.getY0())
|
||||
lt.advance()
|
||||
i += 1
|
||||
|
||||
def test_ver_line(self):
|
||||
# Test if the calculations are correct for x = n (vertical line)
|
||||
lt = LineIterator(5, 0, 5, 10, 1)
|
||||
i = 0
|
||||
while lt.isValid():
|
||||
self.assertEqual(lt.getX(), lt.getX0())
|
||||
self.assertEqual(lt.getY(), lt.getY0() + i)
|
||||
lt.advance()
|
||||
i += 1
|
||||
|
||||
def test_clamp(self):
|
||||
# Test if the increments are clamped to avoid crossing the final points
|
||||
# when step_size is large with respect to line length
|
||||
lt = LineIterator(0, 0, 5, 5, 10)
|
||||
self.assertEqual(lt.getX(), 0)
|
||||
self.assertEqual(lt.getY(), 0)
|
||||
lt.advance()
|
||||
while lt.isValid():
|
||||
self.assertEqual(lt.getX(), 5)
|
||||
self.assertEqual(lt.getY(), 5)
|
||||
lt.advance()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -0,0 +1,23 @@
|
||||
# Copyright 2015 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.
|
||||
|
||||
from ament_pep257.main import main
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.mark.linter
|
||||
@pytest.mark.pep257
|
||||
def test_pep257():
|
||||
rc = main(argv=['.', 'test'])
|
||||
assert rc == 0, 'Found code style errors / warnings'
|
||||
Reference in New Issue
Block a user