├── simulation_ws ├── src │ ├── cloudwatch_simulation │ │ ├── maps │ │ │ └── .gitignore │ │ ├── launch │ │ │ ├── view_empty_world.launch │ │ │ ├── test_world.launch │ │ │ ├── start_map_service.launch │ │ │ ├── small_house.launch │ │ │ ├── bookstore.launch │ │ │ ├── empty_world.launch │ │ │ ├── turtlebot3_navigation.launch │ │ │ ├── worldforge_turtlebot_navigation.launch │ │ │ ├── small_house_turtlebot_navigation.launch │ │ │ └── bookstore_turtlebot_navigation.launch │ │ ├── CMakeLists.txt │ │ ├── package.xml │ │ └── worlds │ │ │ └── empty.world │ ├── test_nodes │ │ ├── src │ │ │ └── test_nodes │ │ │ │ └── __init__.py │ │ ├── setup.py │ │ ├── package.xml │ │ ├── launch │ │ │ └── test.launch │ │ ├── CMakeLists.txt │ │ └── nodes │ │ │ └── navigation_test.py │ ├── aws_robomaker_simulation_common │ │ ├── src │ │ │ └── aws_robomaker_simulation_common │ │ │ │ └── __init__.py │ │ ├── setup.py │ │ ├── README.md │ │ ├── CMakeLists.txt │ │ ├── package.xml │ │ ├── worlds │ │ │ └── empty.world │ │ └── nodes │ │ │ └── route_manager.py │ └── LICENSE.txt └── .rosinstall ├── robot_ws ├── src │ ├── cloudwatch_robot │ │ ├── src │ │ │ └── cloudwatch_robot │ │ │ │ └── __init__.py │ │ ├── config │ │ │ ├── health_metrics_config.yaml │ │ │ ├── cloudwatch_logs_config.yaml │ │ │ └── cloudwatch_metrics_config.yaml │ │ ├── deploymentScripts │ │ │ ├── post_launch_file.sh │ │ │ ├── pre_launch_file.sh │ │ │ └── post_check_monitor_nodes.sh │ │ ├── setup.py │ │ ├── launch │ │ │ ├── deploy_rotate.launch │ │ │ ├── deploy_await_commands.launch │ │ │ ├── rotate.launch │ │ │ ├── await_commands.launch │ │ │ └── monitoring.launch │ │ ├── package.xml │ │ ├── nodes │ │ │ ├── rotate.py │ │ │ ├── monitor_speed.py │ │ │ ├── monitor_distance_to_goal.py │ │ │ └── monitor_obstacle_distance.py │ │ └── CMakeLists.txt │ └── LICENSE.txt └── .rosinstall ├── map_config ├── bookstore.rb ├── default.rb ├── worldforge.rb ├── small_house.rb ├── small_warehouse.rb ├── no_roof_small_warehouse.rb └── default_args.json ├── scripts ├── build.sh ├── bundle.sh ├── genmap.sh ├── genmap_script.sh ├── add_map_plugin.py └── setup.sh ├── docs └── images │ ├── BookstoreRVizPlan01.png │ └── CloudWatchMetrics01.png ├── NOTICE ├── .github ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── lint.yml │ ├── ros1.yml │ └── ros2.yml ├── CODE_OF_CONDUCT.md ├── .gitignore ├── LICENSE ├── Makefile ├── CONTRIBUTING.md ├── roboMakerSettings.json └── README.md /simulation_ws/src/cloudwatch_simulation/maps/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simulation_ws/src/test_nodes/src/test_nodes/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/src/cloudwatch_robot/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /simulation_ws/src/aws_robomaker_simulation_common/src/aws_robomaker_simulation_common/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /map_config/bookstore.rb: -------------------------------------------------------------------------------- 1 | @map_res = 0.05 2 | @robot_init_x = 0.0 3 | @robot_init_y = 0.0 4 | @map_size_x = 25 5 | @map_size_y = 25 -------------------------------------------------------------------------------- /map_config/default.rb: -------------------------------------------------------------------------------- 1 | @map_res = 0.05 2 | @robot_init_x = 0.0 3 | @robot_init_y = 0.0 4 | @map_size_x = 25 5 | @map_size_y = 25 -------------------------------------------------------------------------------- /map_config/worldforge.rb: -------------------------------------------------------------------------------- 1 | @map_res = 0.05 2 | @robot_init_x = 0.0 3 | @robot_init_y = 0.0 4 | @map_size_x = 30 5 | @map_size_y = 30 -------------------------------------------------------------------------------- /map_config/small_house.rb: -------------------------------------------------------------------------------- 1 | @map_res = 0.05 2 | @robot_init_x = 3.0 3 | @robot_init_y = 3.0 4 | @map_size_x = 25 5 | @map_size_y = 25 6 | -------------------------------------------------------------------------------- /map_config/small_warehouse.rb: -------------------------------------------------------------------------------- 1 | @map_res = 0.05 2 | @robot_init_x = 0.0 3 | @robot_init_y = 0.0 4 | @map_size_x = 25 5 | @map_size_y = 25 -------------------------------------------------------------------------------- /map_config/no_roof_small_warehouse.rb: -------------------------------------------------------------------------------- 1 | @map_res = 0.05 2 | @robot_init_x = 0.0 3 | @robot_init_y = 0.0 4 | @map_size_x = 25 5 | @map_size_y = 25 -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | COLCON_LOG_PATH="$@/logs" colcon build --base-paths "$@" --build-base "$@/build" --install-base "$@/install" 4 | -------------------------------------------------------------------------------- /docs/images/BookstoreRVizPlan01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-robotics/aws-robomaker-sample-application-cloudwatch/ros1/docs/images/BookstoreRVizPlan01.png -------------------------------------------------------------------------------- /docs/images/CloudWatchMetrics01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-robotics/aws-robomaker-sample-application-cloudwatch/ros1/docs/images/CloudWatchMetrics01.png -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/config/health_metrics_config.yaml: -------------------------------------------------------------------------------- 1 | # metrics sampling interval in seconds 2 | interval: 5 3 | 4 | #Robot Id 5 | robot_id: Turtlebot3 -------------------------------------------------------------------------------- /scripts/bundle.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | COLCON_LOG_PATH="$@/logs" colcon bundle --base-paths "$@" --build-base "$@/build" --install-base "$@/install" --bundle-base "$@/bundle" 4 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | This product includes software developed by 4 | Amazon Technologies, Inc (http://www.amazon.com/). 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *Issue #, if available:* 2 | 3 | *Description of changes:* 4 | 5 | 6 | By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice. 7 | -------------------------------------------------------------------------------- /robot_ws/.rosinstall: -------------------------------------------------------------------------------- 1 | # Amazon ROS CloudServiceIntegration package dependencies 2 | - git: {local-name: src/deps/turtlebot3-description-reduced-mesh, uri: "https://github.com/aws-robotics/turtlebot3-description-reduced-mesh.git", version: "ros1"} 3 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/launch/view_empty_world.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | simulation_ws/src/aws-robomaker-bookstore-world 2 | simulation_ws/src/aws-robomaker-small-house-world 3 | robot_ws/build 4 | robot_ws/bundle 5 | robot_ws/install 6 | robot_ws/log 7 | robot_ws/src/deps 8 | simulation_ws/build 9 | simulation_ws/bundle 10 | simulation_ws/install 11 | simulation_ws/log 12 | simulation_ws/src/deps 13 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/deploymentScripts/post_launch_file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a script which can be run after launching the ROS processes. 3 | # You can include a post-check of ROS processes in this post-launch file. 4 | # Non-zero exit status from script would cause robot deployment failure. 5 | # Use "deploymentScripts/post_launch_file.sh" as the postLaunchFile path. 6 | 7 | echo Hello World, post-launch 8 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/deploymentScripts/pre_launch_file.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a script which can be run before running the ROS launch file. 3 | # You can include a pre-check of robot environment in this pre-launch file. 4 | # Non-zero exit status from script would cause robot deployment failure. 5 | # Use "deploymentScripts/pre_launch_file.sh" as the preLaunchFile path. 6 | 7 | echo Hello World, pre-launch 8 | -------------------------------------------------------------------------------- /simulation_ws/src/test_nodes/setup.py: -------------------------------------------------------------------------------- 1 | ## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD 2 | ## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html 3 | 4 | from distutils.core import setup 5 | from catkin_pkg.python_setup import generate_distutils_setup 6 | 7 | # fetch values from package.xml 8 | setup_args = generate_distutils_setup( 9 | packages=['test_nodes'], 10 | package_dir={'': 'src'} 11 | ) 12 | 13 | setup(**setup_args) 14 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/setup.py: -------------------------------------------------------------------------------- 1 | # ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD 2 | # See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html 3 | 4 | from distutils.core import setup 5 | 6 | from catkin_pkg.python_setup import generate_distutils_setup 7 | 8 | # fetch values from package.xml 9 | setup_args = generate_distutils_setup( 10 | packages=['cloudwatch_robot'], 11 | package_dir={'': 'src'} 12 | ) 13 | 14 | setup(**setup_args) 15 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/launch/deploy_rotate.launch: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /simulation_ws/src/aws_robomaker_simulation_common/setup.py: -------------------------------------------------------------------------------- 1 | # ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD 2 | # See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html 3 | 4 | from distutils.core import setup 5 | 6 | from catkin_pkg.python_setup import generate_distutils_setup 7 | 8 | # fetch values from package.xml 9 | setup_args = generate_distutils_setup( 10 | packages=['aws_robomaker_simulation_common'], 11 | package_dir={'': 'src'} 12 | ) 13 | 14 | setup(**setup_args) 15 | -------------------------------------------------------------------------------- /simulation_ws/src/aws_robomaker_simulation_common/README.md: -------------------------------------------------------------------------------- 1 | # AWS RoboMaker Simulations Common utilities and helpers 2 | 3 | ### Route Manager 4 | Node that sends a route of target goals to the robot. Sends to the move_base server. 5 | 6 | Includes in your .launch: 7 | ``` 8 | 9 | ... 10 | 11 | 12 | 13 | ... 14 | 15 | ``` 16 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(cloudwatch_simulation) 3 | 4 | find_package(catkin REQUIRED COMPONENTS 5 | gazebo_ros 6 | turtlebot3_description # required to install .rviz model 7 | ) 8 | 9 | catkin_package() 10 | 11 | install(DIRECTORY launch worlds maps 12 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} 13 | ) 14 | 15 | # Copy the rviz model for easier access in RoboMaker RViz 16 | install(FILES ${turtlebot3_description_DIR}/../rviz/model.rviz 17 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/rviz 18 | RENAME turtlebot3_model.rviz 19 | ) 20 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/launch/test_world.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /simulation_ws/src/test_nodes/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | test_nodes 4 | 1.2.1 5 | 6 | AWS RoboMaker simulation test nodes. 7 | 8 | MIT 9 | AWS RoboMaker 10 | AWS RoboMaker 11 | catkin 12 | rospy 13 | std_msgs 14 | rosgraph_msgs 15 | aws_robomaker_simulation_ros_pkgs 16 | ros_monitoring_msgs 17 | 18 | -------------------------------------------------------------------------------- /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | name: "Lint cloudwatch-sample-application-ros1" 2 | on: 3 | pull_request: 4 | 5 | jobs: 6 | ament_lint: 7 | runs-on: ubuntu-latest 8 | container: 9 | image: rostooling/setup-ros-docker:ubuntu-focal-ros-rolling-ros-base-latest 10 | strategy: 11 | fail-fast: false 12 | matrix: 13 | linter: [flake8, pep257, xmllint, copyright] 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: ros-tooling/action-ros-lint@v0.1 17 | with: 18 | distribution: rolling 19 | linter: ${{ matrix.linter }} 20 | package-name: | 21 | aws_robomaker_simulation_common 22 | cloudwatch_simulation 23 | cloudwatch_robot 24 | -------------------------------------------------------------------------------- /map_config/default_args.json: -------------------------------------------------------------------------------- 1 | { 2 | "no_roof_small_warehouse": [ 3 | "./map_config/no_roof_small_warehouse.rb", 4 | "aws-robomaker-small-warehouse-world" 5 | ], 6 | "bookstore": [ 7 | "./map_config/bookstore.rb", 8 | "aws-robomaker-bookstore-world" 9 | ], 10 | "small_warehouse": [ 11 | "./map_config/small_warehouse.rb", 12 | "aws-robomaker-small-warehouse-world" 13 | ], 14 | "small_house": [ 15 | "./map_config/small_house.rb", 16 | "aws-robomaker-small-house-world" 17 | ], 18 | "worldforge": [ 19 | "./map_config/worldforge.rb", 20 | "./simulation_ws/src/aws_robomaker_worldforge_pkgs/aws_robomaker_worldforge_worlds/worlds" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/launch/start_map_service.launch: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/launch/small_house.launch: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/deploymentScripts/post_check_monitor_nodes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # This is a script which can be run after launching the ROS processes. 3 | # You can include a post-check of ROS processes in this post-launch file. 4 | # Non-zero exit status from script would cause robot deployment failure. 5 | # Use "deploymentScripts/post_launch_file.sh" as the postLaunchFile path. 6 | 7 | # Wait all monitoring nodes starts. 8 | sleep 30 9 | # ping and verify nodes started 10 | set -e 11 | rosnode ping -c 3 monitor_speed 12 | rosnode ping -c 3 monitor_obstacle_distance 13 | rosnode ping -c 3 monitor_distance_to_goal 14 | rosnode ping -c 3 cloudwatch_logger 15 | rosnode ping -c 3 cloudwatch_metrics_collector 16 | rosnode ping -c 3 health_metric_collector 17 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/launch/deploy_await_commands.launch: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /simulation_ws/src/aws_robomaker_simulation_common/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.3) 2 | project(aws_robomaker_simulation_common) 3 | 4 | find_package(catkin REQUIRED COMPONENTS 5 | gazebo_ros 6 | turtlebot3_navigation # required for copy of .rviz file 7 | ) 8 | 9 | catkin_package() 10 | catkin_python_setup() 11 | 12 | install(DIRECTORY worlds 13 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} 14 | ) 15 | 16 | catkin_install_python(PROGRAMS 17 | nodes/route_manager.py 18 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 19 | ) 20 | 21 | # Copy the rviz models for easier access in AWS RoboMaker RViz 22 | install(FILES ${turtlebot3_navigation_DIR}/../rviz/turtlebot3_navigation.rviz 23 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/rviz 24 | RENAME turtlebot3_navigation.rviz 25 | ) 26 | -------------------------------------------------------------------------------- /simulation_ws/src/test_nodes/launch/test.launch: -------------------------------------------------------------------------------- 1 | 2 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/launch/bookstore.launch: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/config/cloudwatch_logs_config.yaml: -------------------------------------------------------------------------------- 1 | # name of the log group to use. If it doesn't exist, will try to create it first 2 | # default value is: ros_log_group 3 | log_group_name: robomaker_cloudwatch_monitoring_example 4 | 5 | # name of the log stream to send logs to. If it doesn't exist, will try to create it first 6 | # default value is: ros_log_stream 7 | log_stream_name: turtlebot3 8 | 9 | # how often to send a batch of logs to cloudwatch in the log group and log stream specified 10 | # default value is: 5.0 11 | publish_frequency: 5.0 12 | 13 | # whether to subscribe to the rosout_agg topic to get logs 14 | # default value is: true 15 | sub_to_rosout: true 16 | 17 | # other topics to subscribe to get logs 18 | # default value is: empty list 19 | topics: [] 20 | 21 | #aws region configuration 22 | aws_client_configuration: 23 | region: "us-west-2" 24 | connect_timeout_ms: 9000 25 | request_timeout_ms: 9000 26 | -------------------------------------------------------------------------------- /simulation_ws/.rosinstall: -------------------------------------------------------------------------------- 1 | - git: {local-name: src/deps/aws-robomaker-bookstore-world, uri: "https://github.com/aws-robotics/aws-robomaker-bookstore-world", version: ros1} 2 | - git: {local-name: src/deps/aws-robomaker-small-house-world, uri: "https://github.com/aws-robotics/aws-robomaker-small-house-world", version: ros1} 3 | - git: {local-name: src/deps/aws-robomaker-small-warehouse-world, uri: "https://github.com/aws-robotics/aws-robomaker-small-warehouse-world", version: ros1} 4 | - git: {local-name: src/deps/aws-robomaker-simulation-ros-pkgs, uri: 'https://github.com/aws-robotics/aws-robomaker-simulation-ros-pkgs.git', version: master} 5 | - git: {local-name: src/deps/map_generation_plugin, uri: 'https://github.com/marinaKollmitz/gazebo_ros_2Dmap_plugin', version: 0820610f46235cd7ce1458ea030ef83b1616da37} 6 | - git: {local-name: src/deps/turtlebot3-description-reduced-mesh, uri: "https://github.com/aws-robotics/turtlebot3-description-reduced-mesh.git", version: "ros1"} 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this 4 | software and associated documentation files (the "Software"), to deal in the Software 5 | without restriction, including without limitation the rights to use, copy, modify, 6 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 7 | permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 10 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 11 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 12 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 13 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 14 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/launch/empty_world.launch: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /simulation_ws/src/aws_robomaker_simulation_common/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | aws_robomaker_simulation_common 4 | 1.2.1 5 | 6 | AWS RoboMaker simulation package for commonly used nodes and helpers. 7 | 8 | Apache 2.0 9 | AWS RoboMaker 10 | AWS RoboMaker 11 | catkin 12 | turtlebot3_navigation 13 | gazebo_ros 14 | gazebo 15 | gazebo_plugins 16 | 17 | 18 | moveit_ros 19 | dwa_local_planner 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /robot_ws/src/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /simulation_ws/src/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2018 Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all setup build build_robot build_simulation bundle bundle_robot bundle_simulation clean clean_robot_build clean_robot_bundle clean_simulation_build clean_simulation_bundle 2 | .DEFAULT_GOAL := all 3 | 4 | all: bundle 5 | 6 | # This forces each step in 'ci' to run in serial, but each step will run all of its commands in parallel 7 | ci: 8 | $(MAKE) setup 9 | $(MAKE) build 10 | $(MAKE) bundle 11 | 12 | setup: 13 | scripts/setup.sh 14 | 15 | build: build_robot build_simulation 16 | 17 | build_robot: 18 | scripts/build.sh ./robot_ws 19 | 20 | build_simulation: 21 | scripts/build.sh ./simulation_ws 22 | 23 | bundle: bundle_robot bundle_simulation 24 | 25 | bundle_robot: build_robot 26 | scripts/bundle.sh ./robot_ws 27 | 28 | bundle_simulation: build_simulation 29 | scripts/bundle.sh ./simulation_ws 30 | 31 | clean: clean_robot_build clean_robot_bundle clean_simulation_build clean_simulation_bundle 32 | 33 | clean_robot_build: 34 | rm -rf ./robot_ws/build ./robot_ws/install 35 | 36 | clean_robot_bundle: 37 | rm -rf ./robot_ws/bundle 38 | 39 | clean_simulation_build: 40 | rm -rf ./simulation_ws/build ./simulation_ws/install 41 | 42 | clean_simulation_bundle: 43 | rm -rf ./simulation_ws/bundle 44 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | cloudwatch_simulation 4 | 1.2.1 5 | 6 | AWS RoboMaker simulation package for launching a TurtleBot3 in a empty world or a small house. 7 | 8 | MIT 9 | AWS RoboMaker 10 | AWS RoboMaker 11 | catkin 12 | roscpp 13 | std_msgs 14 | sensor_msgs 15 | geometry_msgs 16 | nav_msgs 17 | tf 18 | gazebo_ros 19 | gazebo_plugins 20 | 21 | turtlebot3_description 22 | 23 | turtlebot3_navigation 24 | gazebo 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/launch/rotate.launch: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/launch/turtlebot3_navigation.launch: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /simulation_ws/src/aws_robomaker_simulation_common/worlds/empty.world: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | model://sun 11 | 12 | 13 | 14 | 15 | model://ground_plane 16 | 17 | 18 | 19 | 1000.0 20 | 0.001 21 | 1 22 | 23 | 24 | quick 25 | 150 26 | 0 27 | 1.400000 28 | 1 29 | 30 | 31 | 0.00001 32 | 0.2 33 | 2000.000000 34 | 0.01000 35 | 36 | 37 | 38 | 39 | 0.4 0.4 0.4 1 40 | 0.7 0.7 0.7 1 41 | true 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/launch/await_commands.launch: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | cloudwatch_robot 4 | 1.2.1 5 | 6 | AWS RoboMaker robot package that provides a sample application for integrating AWS CloudWatch with ROS. 7 | 8 | MIT 9 | AWS RoboMaker 10 | AWS RoboMaker 11 | catkin 12 | rospy 13 | roscpp 14 | 15 | std_msgs 16 | sensor_msgs 17 | geometry_msgs 18 | nav_msgs 19 | 20 | message_generation 21 | message_runtime 22 | message_runtime 23 | 24 | 25 | cloudwatch_logger 26 | cloudwatch_metrics_collector 27 | cloudwatch_logs_common 28 | cloudwatch_metrics_common 29 | aws_common 30 | aws_ros1_common 31 | ros_monitoring_msgs 32 | 33 | 34 | health_metric_collector 35 | 36 | 37 | turtlebot3_msgs 38 | robot_state_publisher 39 | 40 | turtlebot3_bringup 41 | 42 | turtlebot3_description 43 | 44 | turtlebot3_navigation 45 | 46 | turtlebot3_description_reduced_mesh 47 | 48 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/nodes/rotate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | # software and associated documentation files (the "Software"), to deal in the Software 7 | # without restriction, including without limitation the rights to use, copy, modify, 8 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | # permit persons to whom the Software is furnished to do so. 10 | # 11 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | from geometry_msgs.msg import Twist 19 | 20 | import rospy 21 | 22 | 23 | class Rotator: 24 | 25 | def __init__(self): 26 | self._cmd_pub = rospy.Publisher('/cmd_vel', Twist, queue_size=1) 27 | 28 | def rotate_forever(self): 29 | self.twist = Twist() 30 | 31 | direction = 1 32 | angular_speed = 0.2 33 | r = rospy.Rate(0.1) 34 | while not rospy.is_shutdown(): 35 | self.twist.angular.z = direction * angular_speed 36 | self._cmd_pub.publish(self.twist) 37 | rospy.loginfo('Rotating Robot: %s', self.twist) 38 | r.sleep() 39 | 40 | 41 | def main(): 42 | rospy.init_node('rotate') 43 | try: 44 | rotator = Rotator() 45 | rotator.rotate_forever() 46 | except rospy.ROSInterruptException: 47 | pass 48 | 49 | 50 | if __name__ == '__main__': 51 | main() 52 | -------------------------------------------------------------------------------- /scripts/genmap.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # ------------------------------------------------------------------------- 4 | 5 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | # ------------------------------------------------------------------------- 20 | 21 | # This script adds map generation plugin to the input world and plugin config parameters 22 | 23 | # Input args via command line: 24 | # [required] 25 | # [required] 26 | # [required] 27 | 28 | set -e 29 | 30 | if [ $# -ne 3 ]; then 31 | echo "expects 3 arguments" 32 | exit 2 33 | fi 34 | 35 | worldfile=$2 36 | config=$1 37 | worldbody=`xpath -q -e '/sdf/world/*' $worldfile` 38 | template=""" 39 | <% 40 | %> 41 | 42 | 43 | $worldbody 44 | 45 | <%= @map_res %> 46 | 0.3 47 | <%= @map_size_x %> 48 | <%= @map_size_y %> 49 | <%= @robot_init_x %> 50 | <%= @robot_init_y %> 51 | 52 | 53 | 54 | """ 55 | echo $template | erb -r "$config" | xmllint --format - > $3 56 | echo $3 57 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/launch/worldforge_turtlebot_navigation.launch: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /scripts/genmap_script.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # ------------------------------------------------------------------------- 4 | 5 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | 19 | # ------------------------------------------------------------------------- 20 | 21 | # This script generates map files for the input world 22 | 23 | # Input args via command line: 24 | # [required] 25 | 26 | set -e 27 | 28 | if [ $# -ne 1 ]; then 29 | echo "Expect 1 argument"; 30 | exit 2; 31 | fi 32 | 33 | if [ $1 = "worldforge" ] && [ -z "$WORLD_ID" ]; then 34 | echo "For WorldForge world, please set WORLD_ID to your worldforge world as per the README instructions" 35 | exit 2; 36 | fi 37 | 38 | echo "Sudo password may be needed to install system dependencies" 39 | sudo apt-get install ruby-dev libxml-xpath-perl libxml2-utils 40 | 41 | cd simulation_ws 42 | rosws update 43 | rosdep install --from-paths src --ignore-src -r -y 44 | cd .. 45 | 46 | set +e 47 | 48 | OUTPUT=`python scripts/add_map_plugin.py default --world_name $1` 49 | 50 | if [ $? -eq 2 ] 51 | then 52 | echo $OUTPUT 53 | exit $? 54 | else 55 | world_source_path=$OUTPUT 56 | fi 57 | 58 | set -e 59 | 60 | cd simulation_ws 61 | colcon build 62 | source install/local_setup.sh 63 | cd .. 64 | 65 | map_output_path=$(dirname $(dirname $world_source_path))/maps/map 66 | 67 | roslaunch cloudwatch_simulation start_map_service.launch & 68 | 69 | python << END 70 | import rospy 71 | 72 | rospy.wait_for_service('/gazebo_2Dmap_plugin/generate_map') 73 | END 74 | 75 | rosservice call /gazebo_2Dmap_plugin/generate_map 76 | rosrun map_server map_saver -f $map_output_path /map:=/map2d 77 | 78 | cd simulation_ws 79 | colcon build 80 | 81 | kill $! 82 | 83 | echo "--- Map file generated at $map_output_path" 84 | -------------------------------------------------------------------------------- /simulation_ws/src/test_nodes/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Set minimum required version of cmake, project name and compile options 3 | ################################################################################ 4 | cmake_minimum_required(VERSION 2.8.3) 5 | project(test_nodes) 6 | 7 | ################################################################################ 8 | # Find catkin packages and libraries for catkin and system dependencies 9 | ################################################################################ 10 | find_package(catkin REQUIRED COMPONENTS 11 | rospy 12 | std_msgs 13 | rosgraph_msgs 14 | ) 15 | 16 | ################################################################################ 17 | # Setup for python modules and scripts 18 | ################################################################################ 19 | catkin_python_setup() 20 | 21 | ################################################################################ 22 | # Declare ROS messages, services and actions 23 | ################################################################################ 24 | 25 | ################################################################################ 26 | # Declare ROS dynamic reconfigure parameters 27 | ################################################################################ 28 | 29 | ################################################################################ 30 | # Declare catkin specific configuration to be passed to dependent projects 31 | ################################################################################ 32 | catkin_package( 33 | CATKIN_DEPENDS 34 | rospy 35 | std_msgs 36 | rosgraph_msgs 37 | ) 38 | 39 | ################################################################################ 40 | # Build 41 | ################################################################################ 42 | 43 | ################################################################################ 44 | # Install 45 | ################################################################################ 46 | catkin_install_python(PROGRAMS 47 | nodes/navigation_test.py 48 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 49 | ) 50 | 51 | install(DIRECTORY launch 52 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} 53 | ) 54 | 55 | ################################################################################ 56 | # Test 57 | ################################################################################ 58 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/nodes/monitor_speed.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | # software and associated documentation files (the "Software"), to deal in the Software 7 | # without restriction, including without limitation the rights to use, copy, modify, 8 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | # permit persons to whom the Software is furnished to do so. 10 | # 11 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | import time 19 | 20 | from nav_msgs.msg import Odometry 21 | from ros_monitoring_msgs.msg import MetricData, MetricDimension, MetricList 22 | import rospy 23 | from std_msgs.msg import Header 24 | 25 | 26 | class Monitor: 27 | 28 | def __init__(self, data_topic, data_msg, metric_topic, transform): 29 | self.metrics_pub = rospy.Publisher(metric_topic, MetricList, queue_size=1) 30 | self.topic_sub = rospy.Subscriber(data_topic, data_msg, self.callback) 31 | self.transform = transform 32 | 33 | def callback(self, message): 34 | self.metrics_pub.publish(self.transform(message)) 35 | 36 | 37 | def odom_to_speed(odom): 38 | header = Header() 39 | header.stamp = rospy.Time.from_sec(time.time()) 40 | 41 | dimensions = [ 42 | MetricDimension(name='robot_id', value='Turtlebot3'), 43 | MetricDimension(name='category', value='RobotOperations'), 44 | ] 45 | 46 | linear_speed = MetricData( 47 | header=header, 48 | metric_name='linear_speed', 49 | unit=MetricData.UNIT_NONE, 50 | value=odom.twist.twist.linear.x, 51 | time_stamp=rospy.Time.from_sec(time.time()), 52 | dimensions=dimensions, 53 | ) 54 | 55 | angular_speed = MetricData( 56 | header=header, 57 | metric_name='angular_speed', 58 | unit=MetricData.UNIT_NONE, 59 | value=odom.twist.twist.angular.z, 60 | time_stamp=rospy.Time.from_sec(time.time()), 61 | dimensions=dimensions, 62 | ) 63 | 64 | return MetricList([linear_speed, angular_speed]) 65 | 66 | 67 | def main(): 68 | rospy.init_node('speed_monitor') 69 | monitor = Monitor(data_topic='/odom', 70 | data_msg=Odometry, 71 | metric_topic='/metrics', 72 | transform=odom_to_speed) 73 | if (monitor): 74 | rospy.spin() 75 | 76 | 77 | if __name__ == '__main__': 78 | main() 79 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/launch/small_house_turtlebot_navigation.launch: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/nodes/monitor_distance_to_goal.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | # software and associated documentation files (the "Software"), to deal in the Software 7 | # without restriction, including without limitation the rights to use, copy, modify, 8 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | # permit persons to whom the Software is furnished to do so. 10 | # 11 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | from itertools import izip 19 | import time 20 | 21 | from nav_msgs.msg import Path 22 | import numpy as np 23 | from ros_monitoring_msgs.msg import MetricData, MetricDimension, MetricList 24 | import rospy 25 | from std_msgs.msg import Header 26 | 27 | 28 | class MonitorDistanceToGoal: 29 | 30 | def __init__(self): 31 | self.scan_sub = rospy.Subscriber( 32 | '/move_base/NavfnROS/plan', Path, callback=self.report_metric 33 | ) 34 | self.metrics_pub = rospy.Publisher('/metrics', MetricList, queue_size=1) 35 | 36 | def calc_path_distance(self, msg): 37 | points = [(p.pose.position.x, p.pose.position.y) for p in msg.poses] 38 | array = np.array(points, dtype=np.dtype('f8', 'f8')) 39 | return sum((np.linalg.norm(p0 - p1) for p0, p1 in izip(array[:-2], array[1:]))) 40 | 41 | def report_metric(self, msg): 42 | if not msg.poses: 43 | rospy.logdebug('Path empty, not calculating distance') 44 | return 45 | 46 | distance = self.calc_path_distance(msg) 47 | rospy.logdebug('Distance to goal: %s', distance) 48 | 49 | header = Header() 50 | header.stamp = rospy.Time.from_sec(time.time()) 51 | 52 | dimensions = [ 53 | MetricDimension(name='robot_id', value='Turtlebot3'), 54 | MetricDimension(name='category', value='RobotOperations'), 55 | ] 56 | metric = MetricData( 57 | header=header, 58 | metric_name='distance_to_goal', 59 | unit=MetricData.UNIT_NONE, 60 | value=distance, 61 | time_stamp=rospy.Time.from_sec(time.time()), 62 | dimensions=dimensions, 63 | ) 64 | 65 | self.metrics_pub.publish(MetricList([metric])) 66 | 67 | 68 | def main(): 69 | rospy.init_node('monitor_goal_to_distance') 70 | try: 71 | monitor = MonitorDistanceToGoal() 72 | if (monitor): 73 | rospy.spin() 74 | except rospy.ROSInterruptException: 75 | pass 76 | 77 | 78 | if __name__ == '__main__': 79 | main() 80 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/launch/monitoring.launch: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | $(arg aws_region) 38 | $(arg aws_metrics_namespace)-$(arg launch_id) 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | $(arg aws_region) 49 | $(arg log_group_name)-$(arg launch_id) 50 | 51 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/nodes/monitor_obstacle_distance.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # 5 | # Permission is hereby granted, free of charge, to any person obtaining a copy of this 6 | # software and associated documentation files (the "Software"), to deal in the Software 7 | # without restriction, including without limitation the rights to use, copy, modify, 8 | # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 9 | # permit persons to whom the Software is furnished to do so. 10 | # 11 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, 12 | # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 13 | # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 14 | # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 15 | # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 16 | # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 17 | 18 | import time 19 | 20 | from ros_monitoring_msgs.msg import MetricData, MetricDimension, MetricList 21 | import rospy 22 | from sensor_msgs.msg import LaserScan 23 | from std_msgs.msg import Header 24 | 25 | 26 | class MonitorNearestObstacle: 27 | 28 | def __init__(self): 29 | self.scan_sub = rospy.Subscriber('scan', LaserScan, callback=self.report_metric) 30 | self.metrics_pub = rospy.Publisher('/metrics', MetricList, queue_size=1) 31 | 32 | def filter_scan(self, msg): 33 | rospy.loginfo( 34 | 'Filtering scan values in value range (%s,%s)', msg.range_min, msg.range_max 35 | ) 36 | return [ 37 | msg.ranges[i] 38 | for i in range(360) 39 | if msg.ranges[i] >= msg.range_min and msg.ranges[i] <= msg.range_max 40 | ] 41 | 42 | def report_metric(self, msg): 43 | filtered_scan = self.filter_scan(msg) 44 | if not filtered_scan: 45 | rospy.loginfo( 46 | 'No obstacles with scan range (%s,%s)', msg.range_min, msg.range_max 47 | ) 48 | return 49 | 50 | min_distance = min(filtered_scan) 51 | rospy.loginfo('Nearest obstacle: %s', min_distance) 52 | 53 | header = Header() 54 | header.stamp = rospy.Time.from_sec(time.time()) 55 | 56 | dimensions = [ 57 | MetricDimension(name='robot_id', value='Turtlebot3'), 58 | MetricDimension(name='category', value='RobotOperations'), 59 | ] 60 | metric = MetricData( 61 | header=header, 62 | metric_name='nearest_obstacle_distance', 63 | unit=MetricData.UNIT_NONE, 64 | value=min_distance, 65 | time_stamp=rospy.Time.from_sec(time.time()), 66 | dimensions=dimensions, 67 | ) 68 | 69 | self.metrics_pub.publish(MetricList([metric])) 70 | 71 | 72 | def main(): 73 | rospy.init_node('monitor_obstacle_distance') 74 | try: 75 | monitor = MonitorNearestObstacle() 76 | if (monitor): 77 | rospy.spin() 78 | except rospy.ROSInterruptException: 79 | pass 80 | 81 | 82 | if __name__ == '__main__': 83 | main() 84 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/launch/bookstore_turtlebot_navigation.launch: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 26 | 27 | 28 | 29 | 30 | 31 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /scripts/add_map_plugin.py: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------------------------------- 2 | 3 | # Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | # ------------------------------------------------------------------------- 18 | 19 | import sys 20 | import os 21 | import subprocess 22 | import argparse 23 | import json 24 | 25 | 26 | def process_args(args, default_args): 27 | ''' 28 | Processes arguments for map plugin tool. 29 | ''' 30 | 31 | # default arguments 32 | if hasattr(args, 'world_name'): 33 | world_name = args.world_name 34 | config_file = default_args[world_name][0] 35 | try: 36 | world_file = "simulation_ws/src/deps/{0}/worlds/{1}.world".format(default_args[world_name][1], world_name) if world_name!="worldforge" \ 37 | else os.path.join(default_args[world_name][1], os.environ['WORLD_ID'], os.environ['WORLD_ID']+".world") 38 | except KeyError: 39 | raise KeyError("Please set WORLD_ID to your worldforge world as per the README instructions") 40 | output_file = "simulation_ws/src/cloudwatch_simulation/worlds/map_plugin.world" 41 | 42 | #custom config file, world file, output file 43 | else: 44 | config_file, world_file, output_file = args.config_file, args.world_file, args.output_file 45 | 46 | p_args = [os.path.abspath(x) for x in [config_file, world_file, output_file]] 47 | 48 | return p_args 49 | 50 | 51 | def main(): 52 | 53 | default_arg_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), os.pardir, 'map_config/default_args.json') 54 | with open(default_arg_path, 'r') as f: 55 | default_args = json.load(f) 56 | 57 | parser = argparse.ArgumentParser(description="Arguments for default/custom usage of map_plugin tool.") 58 | 59 | subparsers = parser.add_subparsers(help='types of usage') 60 | 61 | # default tool usage 62 | default_parser = subparsers.add_parser("default") 63 | default_parser.add_argument("--world_name", required=True, help="takes a default world_name, each referring to an existing aws-robotics worlds", choices=list(default_args.keys()), type=str) 64 | 65 | # custom tool usage 66 | custom_parser = subparsers.add_parser("custom") 67 | custom_parser.add_argument("-c", "--config_file", required=True, help="config file (.rb) for the map plugin parameters", type=str) 68 | custom_parser.add_argument("-w", "--world_file", required=True, help="path to the original world file", type=str) 69 | custom_parser.add_argument("-o", "--output_file", required=True, help="output path of the new world file", type=str) 70 | 71 | args = process_args(parser.parse_args(), default_args) 72 | 73 | plugin_tool_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), "genmap.sh") 74 | cmd = ' '.join([plugin_tool_path] + args) 75 | 76 | try: 77 | out = subprocess.check_output(cmd, shell=True, stderr=subprocess.STDOUT) 78 | except subprocess.CalledProcessError as e: 79 | print("Execution failed with exitcode {0}\n\n{1}".format(e.returncode, e.output)) 80 | sys.exit(e.returncode) 81 | else: 82 | print("{}\n".format(out)) 83 | 84 | if __name__=="__main__": 85 | main() 86 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check [existing open](https://github.com/aws/aws-robomaker-sample-application-cloudwatch/issues), or [recently closed](https://github.com/aws/aws-robomaker-sample-application-cloudwatch/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *master* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any ['help wanted'](https://github.com/aws/aws-robomaker-sample-application-cloudwatch/labels/help%20wanted) issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](https://github.com/aws/aws-robomaker-sample-application-cloudwatch/blob/master/LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | 61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes. 62 | -------------------------------------------------------------------------------- /simulation_ws/src/cloudwatch_simulation/worlds/empty.world: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | 0.8 -0.75 0.35 0 0.25 2.35 13 | 14 | 15 | 16 | 17 | 18 | model://sun 19 | 20 | 21 | 22 | 23 | model://ground_plane 24 | 25 | 26 | 27 | 28 | 29 | 30 | false 31 | model://robocup_3Dsim_ball 32 | 33 | 34 | 0 0 0.10 0 0 0 35 | 36 | 2 2 0.01 37 | 38 | 10 39 | 40 | random 41 | 42 | 43 | 44 | 45 | 46 | 47 | false 48 | 0 0 0.10 0.0 0.0 0.0 49 | 50 | 51 | 52 | 53 | 0.1 0.1 0.1 54 | model://robocup_3Dsim_ball/meshes/ball.dae 55 | 56 | 57 | 58 | 59 | 60 | 61 | 0.1 62 | 63 | 64 | 65 | 66 | 0.50 67 | 68 | 0.01 69 | 0.0 70 | 0.0 71 | 0.01 72 | 0.0 73 | 0.01 74 | 75 | 76 | 77 | 78 | 0 0 0.10 0 0 0 79 | 80 | 3 3 0.01 81 | 82 | 3 83 | 84 | random 85 | 86 | 87 | 88 | 89 | 1000.0 90 | 0.001 91 | 1 92 | 93 | 94 | quick 95 | 150 96 | 0 97 | 1.400000 98 | 1 99 | 100 | 101 | 0.00001 102 | 0.2 103 | 2000.000000 104 | 0.01000 105 | 106 | 107 | 108 | 109 | 0.4 0.4 0.4 1 110 | 0.7 0.7 0.7 1 111 | true 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/config/cloudwatch_metrics_config.yaml: -------------------------------------------------------------------------------- 1 | # An optional metric namespace parameter. If provided it will set the namespace for all metrics provided by this node to 2 | # the provided value. If the node is running on a AWS RoboMaker system then the provided launch file will ignore this parameter in 3 | # favor of the namespace specified by the RoboMaker ecosystem 4 | aws_metrics_namespace: "robomaker_cloudwatch_monitoring_example" 5 | 6 | # An optional list of topics to listen to. If not provided or is empty the node will just listen on the global "metrics" 7 | # topic. If this list is not empty then the node will not subscribe to the "metrics" topic and will only subscribe to the 8 | # topics in the list. 9 | #aws_monitored_metric_topics: ["metrics"] 10 | 11 | # This is the AWS Client Configuration used by the AWS service client in the Node. If given the node will load the 12 | # provided configuration when initializing the client. 13 | aws_client_configuration: 14 | # Specifies where you want the client to communicate. Examples include us-east-1 or us-west-1. You must ensure that 15 | # the service you want to use has an endpoint in the region you configure. 16 | region: "us-west-2" 17 | 18 | # Built in the constructor and pulls information from your operating system. Do not alter the user agent. 19 | #userAgent: "" 20 | 21 | # Use this to override the http endpoint used to talk to a service. If you set this, you must also set authenticationRegion. 22 | #endpointOverride: "" 23 | 24 | # These settings allow you to configure a proxy for all communication with AWS. Examples of when this functionality 25 | # might be useful include debugging in conjunction with the Burp suite, or using a proxy to connect to the Internet. 26 | #proxyHost: "" 27 | #proxyPort: 0 28 | #proxyUserName: "" 29 | #proxyPassword: "" 30 | 31 | # Enables you to tell the HTTP client where to find your SSL certificate trust store (for example, a directory 32 | # prepared with OpenSSL's c_rehash utility). You shouldn't need to do this unless you are using symlinks in your 33 | # environment. This has no effect on Windows or OS X. 34 | #caPath: "" 35 | #caFile: "" 36 | 37 | # Values that determine the length of time, in milliseconds, to wait before timing out a request. You can increase 38 | # this value if you need to transfer large files, such as in Amazon S3 or Amazon CloudFront. 39 | requestTimeoutMs: 2000 40 | connectTimeoutMs: 2000 41 | 42 | # The maximum number of allowed connections to a single server for your HTTP communications. The default value is 25. 43 | # You can set this value as high as you can support the bandwidth. We recommend a value around 25. 44 | #maxConnections: 25 45 | 46 | 47 | # Use dual stack endpoint in the endpoint calculation. You must ensure that the service you want to use supports ipv6 in the region you select. 48 | #useDualStack: false 49 | 50 | # Enable adjustment for clock skew 51 | #enableClockSkewAdjustment: true 52 | 53 | # If set to true the http stack will follow redirect codes 54 | #followRedirects: false 55 | 56 | # Specifies whether to enable SSL certificate verification. If necessary, you can disable SSL certificate verification by setting verifySSL to false. 57 | #verifySSL: true 58 | 59 | # This is the storage resolution level for presenting metrics in CloudWatch. 60 | # Valid values are 1 and 60. Setting this to 1 specifies this metric as a 61 | # high-resolution metric, so that CloudWatch stores the metric with sub-minute 62 | # resolution down to one second. Setting this to 60 specifies this metric as a 63 | # regular-resolution metric, which CloudWatch stores at 1-minute resolution. 64 | # Currently, high resolution is available only for custom metrics. For more 65 | # information about high-resolution metrics, see http://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/publishingMetrics.html 66 | storage_resolution: 1 67 | -------------------------------------------------------------------------------- /robot_ws/src/cloudwatch_robot/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # Set minimum required version of cmake, project name and compile options 3 | ################################################################################ 4 | cmake_minimum_required(VERSION 2.8.3) 5 | project(cloudwatch_robot) 6 | 7 | ################################################################################ 8 | # Find catkin packages and libraries for catkin and system dependencies 9 | ################################################################################ 10 | find_package(catkin REQUIRED COMPONENTS 11 | rospy 12 | std_msgs 13 | sensor_msgs 14 | geometry_msgs 15 | nav_msgs 16 | message_generation 17 | cloudwatch_metrics_collector 18 | cloudwatch_logger 19 | health_metric_collector 20 | turtlebot3_description # required to install turtlebot3 .rviz model 21 | turtlebot3_navigation # required to install navigation .rviz model 22 | ) 23 | 24 | ################################################################################ 25 | # Setup for python modules and scripts 26 | ################################################################################ 27 | catkin_python_setup() 28 | 29 | ################################################################################ 30 | # Declare ROS messages, services and actions 31 | ################################################################################ 32 | #add_action_files( 33 | # FILES 34 | # Turtlebot3.action 35 | #) 36 | 37 | #generate_messages( 38 | # DEPENDENCIES 39 | # std_msgs 40 | # geometry_msgs 41 | # nav_msgs 42 | #) 43 | 44 | ################################################################################ 45 | # Declare ROS dynamic reconfigure parameters 46 | ################################################################################ 47 | 48 | ################################################################################ 49 | # Declare catkin specific configuration to be passed to dependent projects 50 | ################################################################################ 51 | catkin_package( 52 | DEPENDS 53 | rospy 54 | std_msgs 55 | geometry_msgs 56 | nav_msgs 57 | message_runtime 58 | cloudwatch_metrics_collector 59 | cloudwatch_logger 60 | health_metric_collector 61 | ) 62 | 63 | ################################################################################ 64 | # Build 65 | ################################################################################ 66 | include_directories( 67 | include 68 | ${catkin_INCLUDE_DIRS} 69 | ) 70 | 71 | ################################################################################ 72 | # Install 73 | ################################################################################ 74 | catkin_install_python(PROGRAMS 75 | nodes/rotate.py 76 | nodes/monitor_speed.py 77 | nodes/monitor_obstacle_distance.py 78 | nodes/monitor_distance_to_goal.py 79 | DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} 80 | ) 81 | 82 | install(DIRECTORY launch config 83 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION} 84 | ) 85 | 86 | install(DIRECTORY deploymentScripts 87 | DESTINATION ${CATKIN_GLOBAL_SHARE_DESTINATION} 88 | ) 89 | 90 | # Copy the rviz model for easier access in AWS RoboMaker RViz 91 | install(FILES ${turtlebot3_description_DIR}/../rviz/model.rviz 92 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/rviz 93 | RENAME turtlebot3_model.rviz 94 | ) 95 | 96 | # Copy the rviz model for easier access in AWS RoboMaker RViz 97 | install(FILES ${turtlebot3_navigation_DIR}/../rviz/turtlebot3_navigation.rviz 98 | DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}/rviz 99 | RENAME turtlebot3_navigation.rviz 100 | ) 101 | 102 | ################################################################################ 103 | # Test 104 | ################################################################################ -------------------------------------------------------------------------------- /roboMakerSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "runConfigurations":[ 3 | { 4 | "id":"CloudWatch_Cfg01", 5 | "name":"CloudWatch Robot", 6 | "type":"colcon build", 7 | "cfg":{ 8 | "workingDir":"./CloudWatch/robot_ws", 9 | "cmdArgs":"" 10 | } 11 | }, 12 | { 13 | "id":"CloudWatch_Cfg02", 14 | "name":"CloudWatch Robot", 15 | "type":"colcon bundle", 16 | "cfg":{ 17 | "workingDir":"./CloudWatch/robot_ws", 18 | "cmdArgs":"" 19 | } 20 | }, 21 | { 22 | "id":"CloudWatch_Cfg03", 23 | "name":"CloudWatch Simulation", 24 | "type":"colcon build", 25 | "cfg":{ 26 | "workingDir":"./CloudWatch/simulation_ws", 27 | "cmdArgs":"" 28 | } 29 | }, 30 | { 31 | "id":"CloudWatch_Cfg04", 32 | "name":"CloudWatch Simulation", 33 | "type":"colcon bundle", 34 | "cfg":{ 35 | "workingDir":"./CloudWatch/simulation_ws", 36 | "cmdArgs":"" 37 | } 38 | }, 39 | { 40 | "id":"CloudWatch_SimulationJob", 41 | "name":"CloudWatch Robot Monitoring", 42 | "type":"simulation", 43 | "cfg":{ 44 | "robotApp":{ 45 | "name":"RoboMakerCloudWatchRobot", 46 | "sourceBundleFile":"./CloudWatch/robot_ws/bundle/output.tar", 47 | "s3Bucket":"", 48 | "architecture":"X86_64", 49 | "launchConfig":{ 50 | "packageName":"cloudwatch_robot", 51 | "launchFile":"await_commands.launch", 52 | "environmentVariables":{ 53 | "TURTLEBOT3_MODEL":"waffle_pi" 54 | } 55 | }, 56 | "robotSoftwareSuite":{ 57 | "name":"ROS", 58 | "version":"" 59 | } 60 | }, 61 | "simulationApp":{ 62 | "name":"RoboMakerCloudWatchSimulation", 63 | "sourceBundleFile":"./CloudWatch/simulation_ws/bundle/output.tar", 64 | "s3Bucket":"", 65 | "architecture":"X86_64", 66 | "launchConfig":{ 67 | "packageName":"cloudwatch_simulation", 68 | "launchFile":"bookstore_turtlebot_navigation.launch", 69 | "environmentVariables":{ 70 | "TURTLEBOT3_MODEL":"waffle_pi" 71 | } 72 | }, 73 | "robotSoftwareSuite":{ 74 | "name":"ROS", 75 | "version":"" 76 | }, 77 | "simulationSoftwareSuite":{ 78 | "name":"Gazebo", 79 | "version":"" 80 | }, 81 | "renderingEngine":{ 82 | "name":"OGRE", 83 | "version":"1.x" 84 | } 85 | }, 86 | "simulation":{ 87 | "outputLocation":"", 88 | "failureBehavior":"Fail", 89 | "maxJobDurationInSeconds":28800, 90 | "iamRole":"", 91 | "vpcConfig": { 92 | "assignPublicIp": true, 93 | "securityGroups": [ "" ], 94 | "subnets": [ "" ] 95 | } 96 | } 97 | } 98 | }, 99 | { 100 | "id":"CloudWatch_wf1", 101 | "type":"workflow", 102 | "name":"CloudWatch - Build and Bundle All", 103 | "runCfgIds":[ 104 | "CloudWatch_Cfg01", 105 | "CloudWatch_Cfg02", 106 | "CloudWatch_Cfg03", 107 | "CloudWatch_Cfg04" 108 | ] 109 | } 110 | ] 111 | } 112 | 113 | -------------------------------------------------------------------------------- /scripts/setup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -ex 3 | 4 | while [[ "$#" -gt 0 ]]; do 5 | case $1 in 6 | --install-ros) ros_distro=$2; shift ;; 7 | esac 8 | shift 9 | done 10 | 11 | supported_ros_distros=("melodic") 12 | 13 | install_ros(){ 14 | echo "Installing ROS $ros_distro" 15 | #Install ROS Prerequisites 16 | apt update 17 | apt-get install -y lsb-release gnupg2 curl && apt-get clean all 18 | rm -f "/etc/apt/sources.list.d/ros-latest.list" 19 | sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list' 20 | apt-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654 21 | curl -sSL 'http://keyserver.ubuntu.com/pks/lookup?op=get&search=0xC1CF6E31E6BADE8868B172B4F42ED6FBAB17C654' | apt-key add - 22 | apt update 23 | 24 | #Install ROS $ros_distro 25 | apt install -y ros-$ros_distro-desktop-full 26 | source /opt/ros/$ros_distro/setup.bash 27 | } 28 | 29 | 30 | setup_sample_app(){ 31 | echo "Setting up sample application. Installing tools and dependencies." 32 | #setup key for colcon bundle 33 | apt-key adv --fetch-keys 'http://packages.osrfoundation.org/gazebo.key' 34 | apt update 35 | apt install -y python-rosdep git 36 | if [ ! -f "/etc/ros/rosdep/sources.list.d/20-default.list" ]; 37 | then 38 | rosdep init 39 | fi 40 | rosdep update 41 | 42 | apt-get install -y python3-apt python3-pip python3-vcstool 43 | pip3 install -U setuptools colcon-common-extensions colcon-ros-bundle 44 | 45 | cd robot_ws 46 | vcs import < .rosinstall 47 | rosdep install --from-paths src --ignore-src -r -y 48 | cd .. 49 | 50 | cd simulation_ws 51 | vcs import < .rosinstall 52 | rosdep install --from-paths src --ignore-src -r -y 53 | # need to install pyparsing==2.0.2 at the end to make sure that it overwrites 54 | # pyparsing-3.0 to make sure colcon build works 55 | pip3 install -U pyparsing==2.0.2 56 | cd .. 57 | } 58 | 59 | if [ -d "/opt/ros" ]; 60 | then 61 | echo "A ROS installation already exists in your environment." 62 | if [ ! -z "$ros_distro" ]; 63 | then 64 | echo "Ignoring request to install $ros_distro because a ROS installation already exists in your environment." 65 | fi 66 | 67 | found_supported_ros=false 68 | #check if their ros installation(s) are supported 69 | for distro in $supported_ros_distros 70 | do 71 | if [ -d "/opt/ros/$distro" ] 72 | then 73 | source /opt/ros/$distro/setup.bash 74 | #save to verify installation below 75 | ros_distro=$distro 76 | found_supported_ros=true 77 | break 78 | fi 79 | done 80 | 81 | #if supported versions are not found 82 | if [ ! found_supported_ros ] 83 | then 84 | echo "ERROR: your installed ROS Distro(s) are not supported. Exiting." 85 | exit 1 86 | fi 87 | 88 | elif [ -z "$ros_distro" ]; 89 | then 90 | echo "No ROS Installation was found and no ROS Distro was specified. Defaulting to installing ROS Melodic" 91 | ros_distro="melodic" 92 | install_ros 93 | source /opt/ros/$ros_distro/setup.bash 94 | elif [[ " ${supported_ros_distros[@]} " =~ " ${ros_distro} " ]]; #check if item in list 95 | then 96 | echo "No ROS Installation found. Installing $ros_distro" 97 | install_ros 98 | source /opt/ros/$ros_distro/setup.bash 99 | elif [[ ! " ${supported_ros_distros[@]} " =~ " ${ros_distro} " ]]; #check if item in list 100 | then 101 | echo "The selected ROS Distro $ros_distro is not supported. Exiting." 102 | exit 0 103 | fi 104 | 105 | #Verify Installation 106 | if [ $ROS_DISTRO != $ros_distro ]; 107 | then 108 | echo "The ROS installation was unsuccessful, Sample Application setup cannot continue. Exiting." 109 | exit 1 110 | else 111 | echo "ROS Installation was successful, continuing with Sample Application setup." 112 | fi 113 | 114 | 115 | setup_sample_app 116 | -------------------------------------------------------------------------------- /.github/workflows/ros1.yml: -------------------------------------------------------------------------------- 1 | name: build-and-bundle 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - ros1 7 | schedule: 8 | - cron: '0 */2 * * *' # runs every 2 hrs on a daily basis 9 | 10 | jobs: 11 | build_and_bundle_ros1: 12 | strategy: 13 | matrix: 14 | distro: ['melodic'] 15 | gazebo: [9] 16 | include: 17 | - distro: melodic 18 | gazebo: 9 19 | ubuntu_distro: bionic 20 | runs-on: ubuntu-latest 21 | if: ${{ github.event_name != 'push' || github.ref != 'refs/heads/ros2' }} 22 | name: 'Build and Bundle (ROS1)' 23 | container: 24 | image: ubuntu:${{ matrix.ubuntu_distro }} 25 | outputs: 26 | robot_ws_build_result: ${{ steps.robot_ws_build.outcome }} 27 | simulation_ws_build_result: ${{ steps.simulation_ws_build.outcome }} 28 | steps: 29 | - name: Checkout Branch 30 | uses: actions/checkout@v1 31 | - name: Scan using git-secrets 32 | uses: aws-robotics/aws-robomaker-github-actions/git-secrets-scan-action@3.0.6 33 | - id: robot_ws_build 34 | name: Build and Bundle Robot Workspace 35 | uses: aws-robotics/aws-robomaker-github-actions/robomaker-sample-app-ci@3.0.8 36 | with: 37 | ros-distro: ${{ matrix.distro }} 38 | workspace-dir: robot_ws 39 | generate-sources: true 40 | retries: 3 41 | - id: simulation_ws_build 42 | name: Build and Bundle Simulation Workspace 43 | uses: aws-robotics/aws-robomaker-github-actions/robomaker-sample-app-ci@3.0.8 44 | with: 45 | ros-distro: ${{ matrix.distro }} 46 | workspace-dir: simulation_ws 47 | retries: 3 48 | - name: Configure AWS Credentials 49 | uses: aws-actions/configure-aws-credentials@v1.5.4 50 | with: 51 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_ROS1 }} 52 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ROS1 }} 53 | aws-region: ${{ secrets.AWS_REGION }} 54 | if: ${{ github.event_name == 'schedule' && contains(steps.robot_ws_build.outcome, 'success') && contains(steps.simulation_ws_build.outcome, 'success') }} 55 | - id: upload_bundle 56 | name: Upload bundle to S3 57 | uses: aws-robotics/aws-robomaker-github-actions/s3-cp-action@3.0.6 58 | env: 59 | AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET_ROS1 }} 60 | AWS_REGION: ${{ secrets.AWS_REGION }} 61 | FILES: 'sources.zip sources.tar.gz robot_ws.tar simulation_ws.tar' 62 | DEST: 'travis/cloudwatch/${{ matrix.distro }}/gazebo${{ matrix.gazebo }}/${{ steps.robot_ws_build.outputs.sample-app-version }}.${{ github.run_number }}/' 63 | if: ${{ github.event_name == 'schedule' && contains(steps.robot_ws_build.outcome, 'success') && contains(steps.simulation_ws_build.outcome, 'success') }} # upload to S3 on "schedule" build if the build was successful 64 | - name: Get time stamp 65 | id: time 66 | run: echo "::set-output name=timestamp::$(date +%s)" 67 | if: ${{ github.event_name == 'schedule' && contains(steps.robot_ws_build.outcome, 'success') && contains(steps.simulation_ws_build.outcome, 'success') }} 68 | - name: Update App-manifest version number 69 | id: update-app-manifest 70 | uses: aws-robotics/aws-robomaker-github-actions/codecommit-put-file-action@3.0.6 71 | env: 72 | AWS_CODECOMMIT_REPO_NAME: '${{ secrets.AWS_CODECOMMIT_REPO_NAME_PREFIX_CLOUDWATCH }}-${{ matrix.distro }}-gazebo${{ matrix.gazebo }}' 73 | AWS_CODECOMMIT_BRANCH_NAME: ${{ secrets.AWS_CODECOMMIT_BRANCH_NAME_CLOUDWATCH }} 74 | DEST_FILE_CONTENT: '{"application_version": "${{ steps.robot_ws_build.outputs.sample-app-version }}.${{ github.run_number }}", "timestamp":"${{ steps.time.outputs.timestamp }}"}' 75 | DEST_FILE_PATH: '/version.json' 76 | COMMIT_MSG: 'Updating to version ${{ steps.robot_ws_build.outputs.sample-app-version }}.${{ github.run_number }}. Commit for this version bump: ${{ github.sha }}.' 77 | USER_EMAIL: 'ros-contributions@amazon.com' 78 | USER_NAME: 'ros-contributions' 79 | if: ${{ github.event_name == 'schedule' && contains(steps.robot_ws_build.outcome, 'success') && contains(steps.simulation_ws_build.outcome, 'success') }} # Update app-manifest version number if the build was successful 80 | 81 | log_workflow_status_to_cloudwatch: 82 | runs-on: ubuntu-latest 83 | container: 84 | image: ubuntu:bionic 85 | needs: 86 | - build_and_bundle_ros1 87 | if: ${{ always() && github.event_name != 'pull_request' }} 88 | steps: 89 | - name: Configure AWS Credentials 90 | uses: aws-actions/configure-aws-credentials@v1.5.4 91 | with: 92 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_ROS1 }} 93 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ROS1 }} 94 | aws-region: ${{ secrets.AWS_REGION }} 95 | - name: Log Robot Workspace Build Status 96 | uses: ros-tooling/action-cloudwatch-metrics@0.0.4 97 | with: 98 | namespace: RobotWorkspaceBuild 99 | metric-value: ${{ contains(needs.build_and_bundle_ros1.outputs.robot_ws_build_result, 'success') }} 100 | - name: Log Simulation Workspace Build Status 101 | uses: ros-tooling/action-cloudwatch-metrics@0.0.4 102 | with: 103 | namespace: SimulationWorkspaceBuild 104 | metric-value: ${{ contains(needs.build_and_bundle_ros1.outputs.simulation_ws_build_result, 'success') }} 105 | - name: Log Bundle Upload Status 106 | uses: ros-tooling/action-cloudwatch-metrics@0.0.4 107 | with: 108 | namespace: BundleUpload 109 | metric-value: ${{ contains(needs.build_and_bundle_ros1.result, 'success') }} 110 | if: ${{ github.event_name == 'schedule' }} 111 | -------------------------------------------------------------------------------- /simulation_ws/src/test_nodes/nodes/navigation_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import rospy 4 | import rostest 5 | import time 6 | import os 7 | import unittest 8 | 9 | from rosgraph_msgs.msg import Clock 10 | from ros_monitoring_msgs.msg import MetricList 11 | from robomaker_simulation_msgs.msg import Tag 12 | from robomaker_simulation_msgs.srv import Cancel, AddTags 13 | 14 | METRIC_NAME = "distance_to_goal" 15 | 16 | 17 | class NavigationTest(unittest.TestCase): 18 | """ 19 | This test case will send a number of expected goals and monitor their status. 20 | If the robot reaches all of the destinations, it will mark the test as passed. 21 | """ 22 | 23 | def cancel_job(self): 24 | rospy.wait_for_service("/robomaker/job/cancel") 25 | requestCancel = rospy.ServiceProxy("/robomaker/job/cancel", Cancel) 26 | response = requestCancel() 27 | if response.success: 28 | self.is_cancelled = True 29 | rospy.loginfo("Successfully requested cancel job") 30 | self.set_tag( 31 | name=self.test_name + 32 | "_Time_Elapsed_End", 33 | value=str( 34 | time.time()).split( 35 | ".", 36 | 1)[0]) 37 | else: 38 | rospy.logerr("Cancel request failed: %s", response.message) 39 | 40 | def set_tag(self, name, value): 41 | rospy.wait_for_service("/robomaker/job/add_tags") 42 | requestAddTags = rospy.ServiceProxy("/robomaker/job/add_tags", AddTags) 43 | tags = ([Tag(key=name, value=value)]) 44 | response = requestAddTags(tags) 45 | if response.success: 46 | rospy.loginfo("Successfully added tags: %s", tags) 47 | else: 48 | rospy.logerr( 49 | "Add tags request failed for tags (%s): %s", 50 | tags, 51 | response.message) 52 | 53 | def setUp(self): 54 | self.latch = False 55 | self.successful_navigations = 0 56 | self.test_name = "Robot_Monitoring_Tests_" + \ 57 | str(time.time()).split(".", 1)[0] 58 | self.is_completed = False 59 | rospy.loginfo("Test Name: %s", self.test_name) 60 | self.navigation_success_count = rospy.get_param( 61 | 'NAVIGATION_SUCCESS_COUNT') 62 | self.timeout = rospy.get_param("SIM_TIMEOUT_SECONDS") 63 | 64 | def set_latched(self): 65 | self.latch = True 66 | 67 | def set_unlatched(self): 68 | self.latch = False 69 | 70 | def increment_navigations(self): 71 | self.successful_navigations = self.successful_navigations + 1 72 | 73 | def is_complete(self): 74 | return self.successful_navigations >= self.navigation_success_count 75 | 76 | def check_timeout(self, msg): 77 | """ 78 | Cancel the test if it times out. The timeout is based on the 79 | /clock topic (simulation time). 80 | """ 81 | if msg.clock.secs > self.timeout and not self.is_cancelled: 82 | rospy.loginfo("Test timed out, cancelling job") 83 | self.set_tag(name=self.test_name + "_Status", value="Failed") 84 | self.set_tag( 85 | name=self.test_name + 86 | "_Timed_Out", 87 | value=str( 88 | self.timeout)) 89 | self.cancel_job() 90 | 91 | def check_complete(self, msgs): 92 | for msg in msgs.metrics: 93 | if msg.metric_name == METRIC_NAME: 94 | rospy.loginfo("Metric Name: %s", msg.metric_name) 95 | rospy.loginfo("Metric Value: %s", msg.value) 96 | """ 97 | If our distance to goal metric drops below .5 and we've 98 | achieved our goal count then we are complete and we tag the 99 | job success and cancel. Else, we continue checking progress 100 | towards a new goal once the distance to goal climbs back 101 | above 1. Note that we're using what the nav stack thinks 102 | is the distance to goal, in the real world we'd want to use 103 | a ground truth value to ensure accuracy. 104 | """ 105 | if msg.value <= .5 and self.is_completed == False and self.latch == False: 106 | self.set_latched() 107 | self.increment_navigations() 108 | self.set_tag( 109 | name=self.test_name + "_Successful_Nav_" + str( 110 | self.successful_navigations), value=str( 111 | self.successful_navigations)) 112 | if self.is_complete(): 113 | self.is_completed = True 114 | self.set_tag( 115 | name=self.test_name + "_Status", value="Passed") 116 | self.cancel_job() 117 | elif msg.value > 1 and self.is_completed == False: 118 | self.set_unlatched() 119 | 120 | def test_navigation(self): 121 | try: 122 | self.is_cancelled = False 123 | self.set_tag( 124 | name=self.test_name + 125 | "_Time_Elapsed_Start", 126 | value=str( 127 | time.time()).split( 128 | ".", 129 | 1)[0]) 130 | rospy.Subscriber("/metrics", MetricList, self.check_complete) 131 | rospy.Subscriber("/clock", Clock, self.check_timeout) 132 | rospy.spin() 133 | except Exception as e: 134 | rospy.logerror("Error", e) 135 | self.set_tag(name=self.test_name, value="Failed") 136 | # We cancel the job here and let the service bring down the 137 | # simulation. We don't exit. 138 | self.cancel_job() 139 | 140 | def runTest(self): 141 | # Start the navigation test 142 | self.test_navigation() 143 | 144 | 145 | if __name__ == "__main__": 146 | rospy.init_node("navigation_test", log_level=rospy.INFO) 147 | rostest.rosrun("test_nodes", "navigation_test", NavigationTest) 148 | -------------------------------------------------------------------------------- /.github/workflows/ros2.yml: -------------------------------------------------------------------------------- 1 | name: build-and-bundle 2 | on: 3 | pull_request: 4 | push: 5 | branches: 6 | - ros2 7 | schedule: 8 | - cron: '0 */2 * * *' # runs every 2 hrs on a daily basis 9 | 10 | jobs: 11 | build_and_bundle_ros2: 12 | strategy: 13 | matrix: 14 | distro: ['dashing'] 15 | gazebo: [9] 16 | include: 17 | - distro: dashing 18 | gazebo: 9 19 | ubuntu_distro: bionic 20 | runs-on: ubuntu-latest 21 | if: ${{ github.event_name == 'schedule' || github.ref == 'refs/heads/ros2' }} 22 | name: 'Build and Bundle (ROS2)' 23 | container: 24 | image: rostooling/setup-ros-docker:ubuntu-${{ matrix.ubuntu_distro }}-ros-${{ matrix.distro }}-ros-base-latest 25 | outputs: 26 | robot_ws_build_result: ${{ steps.robot_ws_build.outcome }} 27 | simulation_ws_build_result: ${{ steps.simulation_ws_build.outcome }} 28 | steps: 29 | - name: Checkout Branch 30 | uses: actions/checkout@v1 31 | with: 32 | ref: 'ros2' 33 | - name: Setup Permissions 34 | run: | 35 | # TODO(ros-tooling/setup-ros-docker#7): calling chown is necessary for now 36 | sudo chown -R rosbuild:rosbuild "$HOME" . 37 | - name: Scan using git-secrets 38 | uses: aws-robotics/aws-robomaker-github-actions/git-secrets-scan-action@2.4.2 39 | - id: robot_ws_build 40 | name: Build and Bundle Robot Workspace 41 | uses: aws-robotics/aws-robomaker-github-actions/robomaker-sample-app-ci@2.4.2 42 | with: 43 | ros-distro: ${{ matrix.distro }} 44 | gazebo-version: ${{ matrix.gazebo }} 45 | workspace-dir: robot_ws 46 | generate-sources: true 47 | colcon-bundle-retries: 3 48 | - id: simulation_ws_build 49 | name: Build and Bundle Simulation Workspace 50 | uses: aws-robotics/aws-robomaker-github-actions/robomaker-sample-app-ci@2.4.2 51 | with: 52 | ros-distro: ${{ matrix.distro }} 53 | gazebo-version: ${{ matrix.gazebo }} 54 | workspace-dir: simulation_ws 55 | colcon-bundle-retries: 3 56 | - name: Configure AWS Credentials 57 | uses: aws-actions/configure-aws-credentials@v1.5.4 58 | with: 59 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_ROS2 }} 60 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ROS2 }} 61 | aws-region: ${{ secrets.AWS_REGION }} 62 | if: ${{ github.event_name == 'schedule' && contains(steps.robot_ws_build.outcome, 'success') && contains(steps.simulation_ws_build.outcome, 'success') }} 63 | - id: upload_bundle 64 | name: Upload bundle to S3 65 | uses: aws-robotics/aws-robomaker-github-actions/s3-cp-action@2.4.2 66 | env: 67 | AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET_ROS2 }} 68 | AWS_REGION: ${{ secrets.AWS_REGION }} 69 | FILES: 'sources.zip sources.tar.gz robot_ws.tar simulation_ws.tar' 70 | DEST: 'travis/cloudwatch/${{ steps.robot_ws_build.outputs.ros-distro }}/${{ steps.robot_ws_build.outputs.gazebo-version }}/${{ steps.robot_ws_build.outputs.sample-app-version }}.${{ github.run_number }}/' 71 | if: ${{ github.event_name == 'schedule' && contains(steps.robot_ws_build.outcome, 'success') && contains(steps.simulation_ws_build.outcome, 'success') }} # upload to S3 on "schedule" build if the build was successful 72 | - name: Get time stamp 73 | id: time 74 | run: echo "::set-output name=timestamp::$(date +%s)" 75 | if: ${{ github.event_name == 'schedule' && contains(steps.robot_ws_build.outcome, 'success') && contains(steps.simulation_ws_build.outcome, 'success') }} 76 | - name: Update App-manifest version number 77 | id: update-app-manifest 78 | uses: aws-robotics/aws-robomaker-github-actions/codecommit-put-file-action@2.4.2 79 | env: 80 | AWS_CODECOMMIT_REPO_NAME: '${{ secrets.AWS_CODECOMMIT_REPO_NAME_PREFIX_CLOUDWATCH }}-${{ matrix.distro }}-gazebo${{ matrix.gazebo }}' 81 | AWS_CODECOMMIT_BRANCH_NAME: ${{ secrets.AWS_CODECOMMIT_BRANCH_NAME_CLOUDWATCH }} 82 | DEST_FILE_CONTENT: '{"application_version": "${{ steps.robot_ws_build.outputs.sample-app-version }}.${{ github.run_number }}", "timestamp":"${{ steps.time.outputs.timestamp }}"}' 83 | DEST_FILE_PATH: '/version.json' 84 | COMMIT_MSG: 'Updating to version ${{ steps.robot_ws_build.outputs.sample-app-version }}.${{ github.run_number }}. Commit for this version bump: ${{ github.sha }}.' 85 | USER_EMAIL: 'ros-contributions@amazon.com' 86 | USER_NAME: 'ros-contributions' 87 | if: ${{ github.event_name == 'schedule' && contains(steps.robot_ws_build.outcome, 'success') && contains(steps.simulation_ws_build.outcome, 'success') }} # Update app-manifest version number if the build was successful 88 | 89 | log_workflow_status_to_cloudwatch: 90 | runs-on: ubuntu-latest 91 | container: 92 | image: ubuntu:bionic 93 | needs: 94 | - build_and_bundle_ros2 95 | if: ${{ always() && github.event_name != 'pull_request' }} 96 | steps: 97 | - name: Configure AWS Credentials 98 | uses: aws-actions/configure-aws-credentials@v1.5.4 99 | with: 100 | aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID_ROS2 }} 101 | aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ROS2 }} 102 | aws-region: ${{ secrets.AWS_REGION }} 103 | - name: Log Robot Workspace Build Status 104 | uses: ros-tooling/action-cloudwatch-metrics@0.0.4 105 | with: 106 | metric-dimensions: >- 107 | [ 108 | { "Name": "github.event_name", "Value": "${{ github.event_name }}" }, 109 | { "Name": "github.ref", "Value": "refs/heads/ros2" }, 110 | { "Name": "github.repository", "Value": "${{ github.repository }}" } 111 | ] 112 | namespace: RobotWorkspaceBuild 113 | metric-value: ${{ contains(needs.build_and_bundle_ros2.outputs.robot_ws_build_result, 'success') }} 114 | - name: Log Simulation Workspace Build Status 115 | uses: ros-tooling/action-cloudwatch-metrics@0.0.4 116 | with: 117 | metric-dimensions: >- 118 | [ 119 | { "Name": "github.event_name", "Value": "${{ github.event_name }}" }, 120 | { "Name": "github.ref", "Value": "refs/heads/ros2" }, 121 | { "Name": "github.repository", "Value": "${{ github.repository }}" } 122 | ] 123 | namespace: SimulationWorkspaceBuild 124 | metric-value: ${{ contains(needs.build_and_bundle_ros2.outputs.simulation_ws_build_result, 'success') }} 125 | - name: Log Bundle Upload Status 126 | uses: ros-tooling/action-cloudwatch-metrics@0.0.4 127 | with: 128 | metric-dimensions: >- 129 | [ 130 | { "Name": "github.event_name", "Value": "${{ github.event_name }}" }, 131 | { "Name": "github.ref", "Value": "refs/heads/ros2" }, 132 | { "Name": "github.repository", "Value": "${{ github.repository }}" } 133 | ] 134 | namespace: BundleUpload 135 | metric-value: ${{ contains(needs.build_and_bundle_ros2.result, 'success') }} 136 | if: ${{ github.event_name == 'schedule' }} 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS RoboMaker Sample Application - CloudWatch Monitoring 2 | 3 | Monitor health and operational metrics for a fleet of robots in a simulated home using AWS CloudWatch Metrics and AWS CloudWatch Logs. Streamed metrics include speed, distance to nearest obstacle, distance to current goal, robot CPU utilization, and RAM usage. 4 | 5 | It demonstrates how to emit metrics and logs to AWS CloudWatch to monitor your robots. 6 | 7 | _RoboMaker sample applications include third-party software licensed under open-source licenses and is provided for demonstration purposes only. Incorporation or use of RoboMaker sample applications in connection with your production workloads or a commercial products or devices may affect your legal rights or obligations under the applicable open-source licenses. Source code information can be found [here](https://github.com/aws-robotics/aws-robomaker-sample-application-cloudwatch)._ 8 | 9 | 10 | ## Installation 11 | 12 | The following will be installed 13 | 14 | - [Colcon](https://colcon.readthedocs.io/en/released/user/installation.html) - Used for building and bundling the application. 15 | - [vcstool](https://github.com/dirk-thomas/vcstool#how-to-install-vcstool) - Used to pull in sample app dependencies that are only available from source, not from apt or pip. 16 | - [rosdep](http://wiki.ros.org/rosdep#Installing_rosdep) - rosdep is a command-line tool for installing system dependencies of ROS packages. 17 | 18 | If ROS is detected, then ROS Installation will be skipped. 19 | - [ROS Melodic](http://wiki.ros.org/melodic/Installation/Ubuntu) - Other versions may work, however they have not been tested 20 | 21 | ## AWS Setup 22 | 23 | ### AWS Credentials 24 | You will need to create an AWS Account and configure the credentials to be able to communicate with AWS services. You may find [AWS Configuration and Credential Files](https://docs.aws.amazon.com/cli/latest/userguide/cli-config-files.html) helpful. 25 | 26 | ### AWS Permissions 27 | To run this application you will need an IAM user with the following permissions: 28 | ``` 29 | logs:PutLogEvents 30 | logs:DescribeLogGroups 31 | logs:DescribeLogStreams 32 | logs:CreateLogStream 33 | logs:CreateLogGroup 34 | ``` 35 | 36 | You can find instructions for creating a new IAM Policy [here](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_create.html#access_policies_create-start). In the JSON tab paste the following policy document: 37 | 38 | ``` 39 | { 40 | "Version": "2012-10-17", 41 | "Statement": [ 42 | { 43 | "Sid": "CloudWatchRobotRole", 44 | "Effect": "Allow", 45 | "Action": [ 46 | "cloudwatch:PutMetricData", 47 | "logs:PutLogEvents", 48 | "logs:DescribeLogGroups", 49 | "logs:DescribeLogStreams", 50 | "logs:CreateLogStream", 51 | "logs:CreateLogGroup" 52 | ], 53 | "Resource": "*" 54 | } 55 | ] 56 | } 57 | ``` 58 | 59 | ## Build 60 | 61 | ### Install requirements 62 | Follow links above for instructions on installing required software. 63 | 64 | - *Full Setup Including ROS Install* 65 | currently only melodic is supported, ROS Installation will be skipped if its already present. 66 | 67 | ```bash 68 | source scripts/setup.sh 69 | ``` 70 | 71 | ### Robot 72 | 73 | ```bash 74 | cd robot_ws 75 | colcon build 76 | ``` 77 | 78 | ### Simulation 79 | 80 | ```bash 81 | cd simulation_ws 82 | colcon build 83 | ``` 84 | 85 | ## Run 86 | 87 | The `TURTLEBOT3_MODEL` environment variable must be set when running both robot and simulation application. Currently only `waffle_pi` is supported. Set it by 88 | 89 | ```bash 90 | export TURTLEBOT3_MODEL=waffle_pi 91 | ``` 92 | 93 | Launch the application with the following commands: 94 | 95 | - *Running Robot Application on a Robot* 96 | ```bash 97 | source robot_ws/install/local_setup.sh 98 | roslaunch cloudwatch_robot deploy_rotate.launch 99 | ``` 100 | 101 | - *Running Robot Application in a Simulation* 102 | ```bash 103 | source robot_ws/install/local_setup.sh 104 | roslaunch cloudwatch_robot [command] 105 | ``` 106 | There are two robot launch commands: 107 | - `rotate.launch` - The robot starts rotating 108 | - `await_commands.launch` - The robot is idle waiting movement commands, use this for teleop and navigation 109 | 110 | 111 | - *Running Simulation Application* 112 | ```bash 113 | source simulation_ws/install/local_setup.sh 114 | roslaunch cloudwatch_simulation [command] 115 | ``` 116 | There are three simulation launch commands for three different worlds: 117 | - `empty_world.launch` - Empty world with some balls surrounding the turtlebot at (0,0) 118 | - `bookstore_turtlebot_navigation.launch` - A retail space where the robot navigates to random goals 119 | - `small_house_turtlebot_navigation.launch` - A retail space where the robot navigates to random goals 120 | 121 | Alternatively, to run turtlebot navigation to follow dynamic goals, 122 | ```bash 123 | roslaunch cloudwatch_simulation [command] follow_route:=false dynamic_route:=true 124 | ``` 125 | 126 | Note that when running robot applications on a robot, `use_sim_time` should be set to `false` (which is the default value in `deploy_rotate.launch` and `deploy_await_commands.launch`). When running robot applications along with simulation applications, `use_sim_time` should be set to `true` for both applications (which is the default value in `rotate.launch`, `await_commands.launch` and all the launch files in simulation workspace). 127 | 128 | When running simulation applications, run command with `gui:=true` to run gazebo client for visualization. 129 | 130 | For navigation, you can generate a map with map generation plugin. See [this](#generate-occupancy-map-via-map-generation-plugin) for instructions. 131 | 132 | ![CloudWatchMetrics01.png](docs/images/BookstoreRVizPlan01.png) 133 | 134 | ### Run with a AWS Robomaker WorldForge world 135 | 136 | Pre-requisite: Generate a map for your worldforge exported world following these [instructions](#generate-map-for-a-worldforge-world-with-default-config). 137 | 138 | Build your workspace to reference the newly generated maps, 139 | ```bash 140 | cd simulation_ws 141 | colcon build 142 | ``` 143 | 144 | Launch the navigation application with the following commands: 145 | ```bash 146 | export TURTLEBOT3_MODEL=waffle_pi 147 | source simulation_ws/install/local_setup.sh 148 | roslaunch cloudwatch_simulation worldforge_turtlebot_navigation.launch 149 | ``` 150 | 151 | ### Monitoring with CloudWatch Logs 152 | Robot logs from ROS nodes are streamed into CloudWatch Logs to Log Group `robomaker_cloudwatch_monitoring_example`. See `cloudwatch_robot/config/cloudwatch_logs_config.yaml`. 153 | 154 | ### Monitoring with CloudWatch Metrics 155 | Robot metrics from ROS nodes are reported into CloudWatch Metrics `robomaker_cloudwatch_monitoring_example`. Metric resolution is configured at 10 seconds. See `cloudwatch_robot/config/cloudwatch_metrics_config.yaml`. 156 | 157 | Operational metrics include: 158 | - linear speed 159 | - angular speed 160 | - distance to nearest obstacle (closest lidar scan return) 161 | - distance to planned goal (bookstore only, requires its navigation system) 162 | 163 | Health metrics include CPU and RAM usage. 164 | 165 | ![CloudWatchMetrics01.png](docs/images/CloudWatchMetrics01.png) 166 | 167 | ## Using this sample with RoboMaker 168 | 169 | You first need to install colcon-ros-bundle. Python 3.5 or above is required. 170 | 171 | ```bash 172 | pip3 install colcon-ros-bundle 173 | ``` 174 | 175 | After colcon-ros-bundle is installed you need to build your robot or simulation, then you can bundle with: 176 | 177 | ```bash 178 | # Bundling Robot Application 179 | cd robot_ws 180 | source install/local_setup.sh 181 | colcon bundle 182 | 183 | # Bundling Simulation Application 184 | cd simulation_ws 185 | source install/local_setup.sh 186 | colcon bundle 187 | ``` 188 | 189 | This produces the artifacts `robot_ws/bundle/output.tar` and `simulation_ws/bundle/output.tar` respectively. 190 | 191 | You'll need to upload these to an s3 bucket, then you can use these files to 192 | [create a robot application](https://docs.aws.amazon.com/robomaker/latest/dg/create-robot-application.html), 193 | [create a simulation application](https://docs.aws.amazon.com/robomaker/latest/dg/create-simulation-application.html), 194 | and [create a simulation job](https://docs.aws.amazon.com/robomaker/latest/dg/create-simulation-job.html) in RoboMaker. 195 | 196 | 197 | ## Generate Occupancy Map via map generation plugin 198 | 199 | Procedurally generate an occupancy map for any gazebo world. This map can then be plugged in to navigate a robot in Worldforge worlds. For other aws-robotics worlds, this procedure is optional for the use-cases mentioned in this README. 200 | 201 | 202 | ### Generate map for a aws-robotics world with default config 203 | 204 | Currently, the following aws-robotics worlds are supported, 205 | - [`bookstore`](https://github.com/aws-robotics/aws-robomaker-bookstore-world) 206 | - [`small_house`](https://github.com/aws-robotics/aws-robomaker-small-house-world) 207 | - [`small_warehouse`](https://github.com/aws-robotics/aws-robomaker-small-warehouse-world) 208 | - [`no_roof_small_warehouse`](https://github.com/aws-robotics/aws-robomaker-small-warehouse-world) 209 | 210 | 211 | To generate a map, simply run 212 | ```bash 213 | ./scripts/genmap_script.sh 214 | ``` 215 | 216 | where `` can be any value in the list above. 217 | 218 | ### Generate map for a WorldForge world with default config 219 | 220 | After exporting a world from WorldForge, unzip and move the contents under simulation_ws workspace 221 | 222 | ```bash 223 | unzip exported_world.zip 224 | mv aws_robomaker_worldforge_pkgs simulation_ws/src/ 225 | 226 | #For worldforge worlds, set WORLD_ID to the name of your WF exported world (eg: generation_40r67s111n9x_world_3), 227 | export WORLD_ID= 228 | 229 | # Run map generation script 230 | ./scripts/genmap_script.sh worldforge 231 | ``` 232 | 233 | ### Generate map for a custom world with custom config 234 | 235 | ```bash 236 | # Install dependencies (Ubuntu >18.04) 237 | sudo apt-get install ruby-dev libxml-xpath-perl libxml2-utils 238 | ``` 239 | 240 | ```bash 241 | # Fetch and install ROS dependencies 242 | cd simulation_ws 243 | vcs import < .rosinstall 244 | rosdep install --from-paths src -r -y 245 | cd .. 246 | ``` 247 | 248 | ```bash 249 | # Run plugin with custom world/config, 250 | python scripts/add_map_plugin.py custom -c -w -o 251 | ``` 252 | 253 | ```bash 254 | # Build with plugin added 255 | cd simulation_ws 256 | colcon build 257 | source install/local_setup.sh 258 | 259 | # Start map service (for custom worlds, relocate your world file with the added plugin to src/cloudwatch_simulation/worlds/map_plugin.world before running this) 260 | roslaunch cloudwatch_simulation start_map_service.launch 261 | 262 | # Generate map (start in a different terminal AFTER you see "[INFO] [*] occupancy map plugin started" message in previous terminal) 263 | rosservice call /gazebo_2Dmap_plugin/generate_map 264 | 265 | # Save map 266 | rosrun map_server map_saver -f /map:=/map2d 267 | ``` 268 | 269 | ```bash 270 | # Move the generated map file to cloudwatch_simulation simulation workspace map directory 271 | mv simulation_ws/src/cloudwatch_simulation/maps/map.yaml 272 | ``` 273 | 274 | ## AWS ROS Packages used by this Sample 275 | 276 | - RoboMakerUtils-Common 277 | - RoboMakerUtils-ROS1 278 | - CloudWatch-Common 279 | - CloudWatchLogs-ROS1 280 | - CloudWatchMetrics-ROS1 281 | - HealthMetricsCollector-ROS1 282 | - MonitoringMessages-ROS1 283 | 284 | ## License 285 | 286 | MIT-0 - See LICENSE for further information 287 | 288 | ## How to Contribute 289 | 290 | Create issues and pull requests against this Repository on Github 291 | -------------------------------------------------------------------------------- /simulation_ws/src/aws_robomaker_simulation_common/nodes/route_manager.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import itertools 18 | from math import cos, sin 19 | import random 20 | 21 | import actionlib 22 | from geometry_msgs.msg import Point, Quaternion 23 | from move_base_msgs.msg import MoveBaseAction, MoveBaseGoal 24 | from nav_msgs.msg import MapMetaData, OccupancyGrid, Path 25 | import rospy 26 | import tf.transformations as transform 27 | 28 | 29 | class GoalGenerator(): 30 | """ 31 | Generate new goals for robot. 32 | 33 | - Description 34 | ------------- 35 | Reads map data published on /map and /map_metadata topics 36 | and provides valid random goal poses in the map. 37 | 38 | Assumes that the map is held static after node 39 | initialisation and is not updated while the node is running. 40 | """ 41 | 42 | def __init__(self): 43 | # Assuming map is static after node init and not updated while the node 44 | # is running. If not, this must be refreshed at regular intervals or on 45 | # some callbacks. 46 | self.meta_data = rospy.wait_for_message('map_metadata', MapMetaData) 47 | self.occupancy_data = rospy.wait_for_message('map', OccupancyGrid) 48 | 49 | # map.yaml only specifies x,y and yaw transforms of the origin wrt 50 | # world frame 51 | self.map_orientation = transform.euler_from_quaternion( 52 | [ 53 | self.meta_data.origin.orientation.x, 54 | self.meta_data.origin.orientation.y, 55 | self.meta_data.origin.orientation.z, 56 | self.meta_data.origin.orientation.w]) 57 | self.map_yaw = self.map_orientation[2] # (in radians) 58 | self.map_origin_x0 = self.meta_data.origin.position.x 59 | self.map_origin_y0 = self.meta_data.origin.position.y 60 | self.resolution = self.meta_data.resolution 61 | 62 | def ravel_index(self, x, y): 63 | """ 64 | Ravel 2d grid coordinates in row-major order. 65 | 66 | Args 67 | ---- 68 | x(int): in grid coordinates 69 | y(int): in grid coordinates 70 | 71 | Returns 72 | ------- 73 | int 74 | 75 | """ 76 | return y * (self.meta_data.width) + x 77 | 78 | def grid_to_world_2d(self, x, y): 79 | """ 80 | Transform x-y planar grid coordinates to world coordinates. 81 | 82 | The function adheres to the assumption that 83 | grid-world transform is only x-y translation and yaw rotation. 84 | 85 | Args 86 | ---- 87 | x(int): in grid coordinates 88 | x(int): in grid coordinates 89 | 90 | Returns 91 | ------- 92 | List(int): in world coordinates 93 | 94 | """ 95 | x_world = self.map_origin_x0 + \ 96 | (cos(self.map_yaw) * (self.resolution * x) 97 | - sin(self.map_yaw) * (self.resolution * y)) 98 | y_world = self.map_origin_y0 + \ 99 | (sin(self.map_yaw) * (self.resolution * x) + 100 | cos(self.map_yaw) * (self.resolution * y)) 101 | 102 | return [x_world, y_world] 103 | 104 | def _create_pos( 105 | self, 106 | x_world, 107 | y_world, 108 | z_world, 109 | euler_orientation_x, 110 | euler_orientation_y, 111 | euler_orientation_z): 112 | """ 113 | Wrap 3D euler location and orientation input to a Pose. 114 | 115 | Args 116 | ---- 117 | x(float): in world coordinates 118 | y(float): in world coordinates 119 | z(float): in world coordinates 120 | orientation_x(float): in world coordinates, 121 | orientation_y(float): in world coordinates 122 | orientation_z(float): in world coordinates 123 | 124 | Returns 125 | ------- 126 | Pose: 127 | { 128 | 'position': { 129 | 'x': double 130 | 'y': double 131 | 'z': double 132 | } 133 | 'orientation': { 134 | 'x': double 135 | 'y': double 136 | 'z': double 137 | 'w': double 138 | } 139 | } 140 | 141 | """ 142 | position = { 143 | 'x': x_world, 144 | 'y': y_world, 145 | 'z': z_world 146 | } 147 | 148 | # Make sure the quaternion is valid and normalized 149 | quaternion_orientation = transform.quaternion_from_euler( 150 | euler_orientation_x, euler_orientation_y, euler_orientation_z) 151 | orientation = { 152 | 'x': quaternion_orientation[0], 153 | 'y': quaternion_orientation[1], 154 | 'z': quaternion_orientation[2], 155 | 'w': quaternion_orientation[3] 156 | } 157 | 158 | p = { 159 | 'pose': 160 | { 161 | 'position': position, 162 | 'orientation': orientation 163 | } 164 | } 165 | 166 | return p 167 | 168 | def check_noise(self, x, y, row_id=None): 169 | """ 170 | Check if the point in the world is not a map consistency. 171 | 172 | - Low resolution/ noisy sensor data might lead to 173 | noisy patches in the map. 174 | - This function checks if the random valid point is not a noisy 175 | bleap on the map by looking for its neighbor consistency. 176 | 177 | Args 178 | ---- 179 | x (int): in grid coordinates 180 | y (int): in grid coordinates 181 | 182 | Returns 183 | ------- 184 | bool. False if noise, else True 185 | 186 | """ 187 | # to make it depend on resolution 188 | delta_x = max(2, self.meta_data.width // 50) 189 | delta_y = max(2, self.meta_data.height // 50) 190 | 191 | l_bound, r_bound = max( 192 | 0, x - delta_x), min(self.meta_data.width - 1, x + delta_x) 193 | t_bound, b_bound = max( 194 | 0, y - delta_y), min(self.meta_data.height - 1, y + delta_y) 195 | 196 | for _x in range(l_bound, r_bound): 197 | for _y in range(t_bound, b_bound): 198 | _row_id = self.ravel_index(_x, _y) 199 | if self.occupancy_data.data[_row_id] != 0: 200 | return False 201 | 202 | return True 203 | 204 | def get_next(self): 205 | """ 206 | Get next goal. 207 | 208 | Scan the map for a valid goal. 209 | Convert to world coordinates and wraps as a Pose 210 | to be consumed by route manager. 211 | 212 | Args 213 | ---- 214 | 215 | Returns 216 | ------- 217 | Pose: 218 | { 219 | 'position': { 220 | 'x': double 221 | 'y': double 222 | 'z': double 223 | } 224 | 'orientation': { 225 | 'x': double 226 | 'y': double 227 | 'z': double 228 | 'w': double 229 | } 230 | } 231 | 232 | """ 233 | z_world_floor = 0. 234 | euler_orientation = [0., 0., 0.] 235 | 236 | rospy.loginfo('Searching for a valid goal') 237 | timeout_iter = 100 238 | iteration = 0 239 | while iteration < timeout_iter: 240 | _x = random.randint(0, self.meta_data.width - 1) 241 | _y = random.randint(0, self.meta_data.height - 1) 242 | _row_id = self.ravel_index(_x, _y) 243 | if self.occupancy_data.data[_row_id] == 0 and self.check_noise( 244 | _x, _y, row_id=_row_id): 245 | x_world, y_world = self.grid_to_world_2d(_x, _y) 246 | rospy.loginfo('Valid goal found!') 247 | return self._create_pos( 248 | x_world, y_world, z_world_floor, *euler_orientation) 249 | 250 | rospy.logerr('Could not find a valid goal in the world. Check that \ 251 | your occupancy map has Trinary value representation and is not \ 252 | visually noisy/incorrect') 253 | return None 254 | 255 | 256 | class RouteManager(): 257 | """ 258 | Send goals to move_base server for the specified route. Routes forever. 259 | 260 | Loads the route from yaml. 261 | Use RViz to record 2D nav goals. 262 | Echo the input goal on topic /move_base_simple/goal 263 | """ 264 | 265 | route_modes = { 266 | 'inorder': lambda goals: itertools.cycle(goals), 267 | 'random': lambda goals: ( 268 | random.choice(goals) for i in itertools.count()), 269 | 'dynamic': lambda goals: GoalGenerator()} 270 | 271 | def __init__(self): 272 | self.route = [] 273 | 274 | self.client = actionlib.SimpleActionClient('move_base', MoveBaseAction) 275 | self.client.wait_for_server() 276 | 277 | self.route_mode = rospy.get_param('~mode') 278 | if self.route_mode not in RouteManager.route_modes: 279 | rospy.logerr( 280 | 'Route mode %s unknown, exiting route manager', 281 | self.route_mode) 282 | return 283 | 284 | poses = rospy.get_param('~poses', []) 285 | if not poses and self.route_mode != 'dynamic': 286 | rospy.loginfo( 287 | 'Route manager initialized no goals, unable to route') 288 | 289 | self.goals = RouteManager.route_modes[self.route_mode](poses) 290 | rospy.loginfo('Route manager initialized in %s mode', self.route_mode) 291 | 292 | self.bad_goal_counter = 0 293 | 294 | def to_move_goal(self, pose): 295 | if pose is None: 296 | raise ValueError('Goal position cannot be NULL') 297 | 298 | goal = MoveBaseGoal() 299 | goal.target_pose.header.stamp = rospy.Time.now() 300 | goal.target_pose.header.frame_id = 'map' 301 | goal.target_pose.pose.position = Point(**pose['pose']['position']) 302 | goal.target_pose.pose.orientation = Quaternion( 303 | **pose['pose']['orientation']) 304 | return goal 305 | 306 | def route_forever(self): 307 | rate = rospy.Rate(1) 308 | while not rospy.is_shutdown(): 309 | if self.bad_goal_counter > 10: 310 | rospy.loginfo( 311 | 'Stopping route manager due to too many bad goals.\ 312 | Check that your occupancy map has Trinary value\ 313 | representation and is not\ 314 | visually noisy/incorrect' 315 | ) 316 | return 317 | else: 318 | rospy.loginfo( 319 | 'Route mode is %s, getting next goal', 320 | self.route_mode) 321 | try: 322 | if self.route_mode == 'dynamic': 323 | # TODO: flake8 linting standard does not allow over-riding 324 | # the built-in python method next() which is required to 325 | # make 'self.goals' a iterator. 326 | current_goal = self.to_move_goal(self.goals.get_next()) 327 | else: 328 | current_goal = self.to_move_goal(next(self.goals)) 329 | except ValueError as e: 330 | rospy.loginfo( 331 | 'No valid goal was found in the map, stopping route manager\ 332 | due to following exception,\n{0}'.format(str(e))) 333 | return 334 | 335 | rospy.loginfo('Sending target goal: %s', current_goal) 336 | self.client.send_goal(current_goal) 337 | 338 | try: 339 | # wait 5sec for global plan to be published. If not, scan 340 | # for a new goal.. 341 | rospy.wait_for_message( 342 | '/move_base/DWAPlannerROS/global_plan', 343 | Path, 344 | timeout=5 345 | ) 346 | 347 | if not self.client.wait_for_result(): 348 | rospy.logerr( 349 | 'Move server not ready, will try again...') 350 | elif self.client.get_result(): 351 | rospy.loginfo('Goal done: %s', current_goal) 352 | 353 | rate.sleep() 354 | 355 | except rospy.exceptions.ROSException: 356 | self.bad_goal_counter += 1 357 | rospy.logwarn( 358 | 'No plan found for goal. Scanning for a new goal...') 359 | 360 | 361 | def main(): 362 | rospy.init_node('route_manager') 363 | try: 364 | route_manger = RouteManager() 365 | route_manger.route_forever() 366 | except rospy.ROSInterruptException: 367 | pass 368 | 369 | 370 | if __name__ == '__main__': 371 | main() 372 | --------------------------------------------------------------------------------