├── LICENSE ├── README.md ├── build ├── .built_by ├── COLCON_IGNORE └── my_robot_navigation │ ├── build │ └── lib │ │ └── my_robot_navigation │ │ ├── __init__.py │ │ └── obstacle_avoidance.py │ ├── colcon_build.rc │ ├── colcon_command_prefix_setup_py.sh │ ├── colcon_command_prefix_setup_py.sh.env │ ├── install.log │ ├── my_robot_navigation.egg-info │ ├── PKG-INFO │ ├── SOURCES.txt │ ├── dependency_links.txt │ ├── entry_points.txt │ ├── requires.txt │ ├── top_level.txt │ └── zip-safe │ └── prefix_override │ ├── __pycache__ │ └── sitecustomize.cpython-310.pyc │ └── sitecustomize.py ├── images ├── animation.png ├── cmd_vel.png └── logs.png ├── install ├── .colcon_install_layout ├── COLCON_IGNORE ├── _local_setup_util_ps1.py ├── _local_setup_util_sh.py ├── local_setup.bash ├── local_setup.ps1 ├── local_setup.sh ├── local_setup.zsh ├── my_robot_navigation │ ├── lib │ │ ├── my_robot_navigation │ │ │ └── obstacle_avoidance │ │ └── python3.10 │ │ │ └── site-packages │ │ │ ├── my_robot_navigation-0.0.0-py3.10.egg-info │ │ │ ├── PKG-INFO │ │ │ ├── SOURCES.txt │ │ │ ├── dependency_links.txt │ │ │ ├── entry_points.txt │ │ │ ├── requires.txt │ │ │ ├── top_level.txt │ │ │ └── zip-safe │ │ │ └── my_robot_navigation │ │ │ ├── __init__.py │ │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-310.pyc │ │ │ └── obstacle_avoidance.cpython-310.pyc │ │ │ └── obstacle_avoidance.py │ └── share │ │ ├── ament_index │ │ └── resource_index │ │ │ └── packages │ │ │ └── my_robot_navigation │ │ ├── colcon-core │ │ └── packages │ │ │ └── my_robot_navigation │ │ └── my_robot_navigation │ │ ├── __pycache__ │ │ └── robot_navigation.launch.cpython-310.pyc │ │ ├── hook │ │ ├── ament_prefix_path.dsv │ │ ├── ament_prefix_path.ps1 │ │ ├── ament_prefix_path.sh │ │ ├── pythonpath.dsv │ │ ├── pythonpath.ps1 │ │ └── pythonpath.sh │ │ ├── package.bash │ │ ├── package.dsv │ │ ├── package.ps1 │ │ ├── package.sh │ │ ├── package.xml │ │ ├── package.zsh │ │ └── robot_navigation.launch.py ├── setup.bash ├── setup.ps1 ├── setup.sh └── setup.zsh └── src └── my_robot_navigation ├── launch └── robot_navigation.launch.py ├── my_robot_navigation ├── __init__.py └── obstacle_avoidance.py ├── package.xml ├── resource └── my_robot_navigation ├── setup.cfg ├── setup.py └── test ├── test_copyright.py ├── test_flake8.py └── test_pep257.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Anson 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo 2 | This repository contains the implementation of an autonomous navigation and obstacle avoidance robot simulated using ROS2 (Humble) and Gazebo. The project features a TurtleBot3 Burger robot navigating a custom hexagonal obstacle course, utilizing LIDAR data for real-time obstacle detection and avoidance. 3 | 4 | --- 5 | 6 | ## Project Overview 7 | 8 | The robot autonomously navigates a Gazebo-simulated environment, avoiding obstacles with a custom ROS2 Python node that processes LIDAR data and publishes velocity commands. The system integrates mechanical simulation, sensor processing, and control algorithms, making it a practical example for mechatronics and robotics applications. 9 | 10 | ## Screenshots 11 | 12 | ### Gazebo Simulation 13 | ![Gazebo Simulation](images/animation.png) 14 | *The TurtleBot3 navigating a hexagonal obstacle course with LIDAR data visualized.* 15 | 16 | ### Velocity Commands (/cmd_vel) 17 | ![/cmd_vel Output](images/cmd_vel.png) 18 | *Sample output from `ros2 topic echo /cmd_vel`, showing velocity commands (e.g., `angular.z: 0.5` for turning).* 19 | 20 | ### Obstacle Avoidance Logs 21 | ![Obstacle Avoidance Logs](images/logs.png) 22 | *Terminal logs demonstrating real-time obstacle detection and movement adjustments.* 23 | 24 | --- 25 | ### Key Features 26 | - **Obstacle Avoidance**: Detects obstacles within 0.5 meters using LIDAR and adjusts movement (turns right or moves forward). 27 | - **Real-Time Simulation**: Runs in Gazebo with ROS2 Humble, visualized via RViz. 28 | - **Custom Node**: A Python-based ROS2 node (`obstacle_avoidance.py`) handles sensor data and control logic. 29 | - **Documentation**: Includes setup instructions, usage, and troubleshooting for reproducibility. 30 | 31 | --- 32 | 33 | ## Prerequisites 34 | 35 | ### Software 36 | - **Ubuntu 22.04** (recommended for ROS2 Humble). 37 | - **ROS2 Humble** (install via [official guide](https://docs.ros.org/en/humble/Installation.html)). 38 | - **Gazebo** (included with ROS2 Humble). 39 | - **TurtleBot3 Packages**: 40 | ```bash 41 | sudo apt install ros-humble-turtlebot3 ros-humble-turtlebot3-gazebo 42 | ``` 43 | - **Python 3.10** (system Python, avoid Conda to prevent version conflicts). 44 | - **pip** (for installing `lxml`): 45 | ```bash 46 | pip3 install lxml 47 | ``` 48 | 49 | ### Hardware 50 | - A computer with at least 8GB RAM to run Gazebo and ROS2 smoothly. 51 | 52 | --- 53 | 54 | ## Installation 55 | 56 | 1. **Clone the Repository** 57 | ```bash 58 | git clone https://github.com/anson10/Obstacle-Avoidance-Robot-in-Gazebo-with-ROS2 59 | 60 | cd Obstacle-Avoidance-Robot-in-Gazebo-with-ROS2 61 | ``` 62 | 63 | 2. **Set Up the Workspace** 64 | - Create a ROS2 workspace: 65 | ```bash 66 | mkdir -p ~/ros2_ws/src 67 | cd ~/ros2_ws/src 68 | ``` 69 | - Copy the repository contents into `~/ros2_ws/src/my_robot_navigation`. 70 | 71 | 3. **Install Dependencies** 72 | - Ensure `lxml` is installed for Gazebo model spawning: 73 | ```bash 74 | pip3 install lxml 75 | ``` 76 | - Deactivate any Conda environment to use system Python 3.10: 77 | ```bash 78 | conda deactivate 79 | ``` 80 | 81 | 4. **Build the Workspace** 82 | ```bash 83 | cd ~/ros2_ws 84 | colcon build 85 | source install/setup.bash 86 | ``` 87 | 88 | 5. **Set Environment Variable** 89 | - Export the TurtleBot3 model: 90 | ```bash 91 | export TURTLEBOT3_MODEL=burger 92 | ``` 93 | - (Optional) Add to `~/.bashrc` for persistence: 94 | ```bash 95 | echo "export TURTLEBOT3_MODEL=burger" >> ~/.bashrc 96 | source ~/.bashrc 97 | ``` 98 | 99 | --- 100 | 101 | ## Usage 102 | 103 | 1. **Launch the Simulation** 104 | - Start the project with the launch file: 105 | ```bash 106 | ros2 launch my_robot_navigation robot_navigation.launch.py 107 | ``` 108 | - This opens Gazebo with the TurtleBot3 and runs the obstacle avoidance node. 109 | 110 | 2. **Visualize in RViz (Optional)** 111 | - Open a new terminal and launch RViz: 112 | ```bash 113 | ros2 run rviz2 rviz2 114 | ``` 115 | - Configure RViz: 116 | - Set "Fixed Frame" to `odom`. 117 | - Add "LaserScan" display for `/scan`. 118 | - Add "RobotModel" display for the TurtleBot3. 119 | 120 | 3. **Monitor Topics** 121 | - List available topics: 122 | ```bash 123 | ros2 topic list 124 | ``` 125 | - View LIDAR data or velocity commands: 126 | ```bash 127 | ros2 topic echo /scan 128 | ros2 topic echo /cmd_vel 129 | ``` 130 | 131 | 4. **Expected Behavior** 132 | - Logs indicate "Path clear, moving forward" when no obstacles are near. 133 | - Logs show "Obstacle detected X.XXm ahead, turning!" when avoiding obstacles (see screenshots). 134 | 135 | --- 136 | 137 | 138 | 139 | ## Code Structure 140 | 141 | - **`my_robot_navigation/`**: 142 | - **`launch/robot_navigation.launch.py`**: Launches Gazebo and the obstacle avoidance node. 143 | - **`obstacle_avoidance.py`**: Python script for LIDAR processing and velocity control. 144 | - **`setup.py`**: Configures the ROS2 package and executable. 145 | 146 | ## Customization 147 | 148 | - **Adjust Parameters**: Modify `min_distance` (e.g., `0.5` to `1.0`) or velocities (`linear.x`, `angular.z`) in `obstacle_avoidance.py`. 149 | - **Enhance Algorithm**: Integrate Navigation2 for path planning or add sensor fusion with IMU data. 150 | - **Custom World**: Edit the Gazebo world file to design new obstacle layouts. 151 | 152 | ## Contributing 153 | 154 | Contributions are welcome! Please fork the repository and submit pull requests for: 155 | - Bug fixes or performance improvements. 156 | - Advanced features (e.g., SLAM, machine learning for navigation). 157 | - Documentation enhancements. 158 | 159 | ## License 160 | 161 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 162 | -------------------------------------------------------------------------------- /build/.built_by: -------------------------------------------------------------------------------- 1 | colcon 2 | -------------------------------------------------------------------------------- /build/COLCON_IGNORE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/build/COLCON_IGNORE -------------------------------------------------------------------------------- /build/my_robot_navigation/build/lib/my_robot_navigation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/build/my_robot_navigation/build/lib/my_robot_navigation/__init__.py -------------------------------------------------------------------------------- /build/my_robot_navigation/build/lib/my_robot_navigation/obstacle_avoidance.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import rclpy 4 | from rclpy.node import Node 5 | from sensor_msgs.msg import LaserScan 6 | from geometry_msgs.msg import Twist 7 | 8 | class ObstacleAvoidanceNode(Node): 9 | def __init__(self): 10 | super().__init__('obstacle_avoidance_node') 11 | # Subscriber to LIDAR data 12 | self.subscription = self.create_subscription( 13 | LaserScan, 14 | '/scan', # TurtleBot3 LIDAR topic 15 | self.lidar_callback, 16 | 10 17 | ) 18 | # Publisher for velocity commands 19 | self.publisher = self.create_publisher( 20 | Twist, 21 | '/cmd_vel', # TurtleBot3 velocity topic 22 | 10 23 | ) 24 | self.get_logger().info("Obstacle Avoidance Node Started") 25 | 26 | def lidar_callback(self, msg): 27 | # Process LIDAR data (360 degrees, 0 = front, 180 = rear) 28 | ranges = msg.ranges 29 | min_distance = min(ranges[0:60] + ranges[300:360]) # Check front 120 degrees 30 | twist = Twist() 31 | 32 | if min_distance < 0.5: # If obstacle closer than 0.5m 33 | twist.linear.x = 0.0 34 | twist.angular.z = 0.5 # Turn right 35 | self.get_logger().info(f"Obstacle detected {min_distance:.2f}m ahead, turning!") 36 | else: 37 | twist.linear.x = 0.2 # Move forward 38 | twist.angular.z = 0.0 39 | self.get_logger().info("Path clear, moving forward") 40 | 41 | self.publisher.publish(twist) 42 | 43 | def main(args=None): 44 | rclpy.init(args=args) 45 | node = ObstacleAvoidanceNode() 46 | try: 47 | rclpy.spin(node) 48 | except KeyboardInterrupt: 49 | node.get_logger().info("Shutting down") 50 | finally: 51 | node.destroy_node() 52 | rclpy.shutdown() 53 | 54 | if __name__ == '__main__': 55 | main() 56 | -------------------------------------------------------------------------------- /build/my_robot_navigation/colcon_build.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /build/my_robot_navigation/colcon_command_prefix_setup_py.sh: -------------------------------------------------------------------------------- 1 | # generated from colcon_core/shell/template/command_prefix.sh.em 2 | -------------------------------------------------------------------------------- /build/my_robot_navigation/colcon_command_prefix_setup_py.sh.env: -------------------------------------------------------------------------------- 1 | AMENT_PREFIX_PATH=/home/anson/ros2_ws/install/my_robot_navigation:/opt/ros/humble 2 | COLCON=1 3 | COLCON_PREFIX_PATH=/home/anson/ros2_ws/install 4 | CONDA_EXE=/home/anson/miniconda3/bin/conda 5 | CONDA_PYTHON_EXE=/home/anson/miniconda3/bin/python 6 | CONDA_SHLVL=0 7 | DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus 8 | DISPLAY=:0 9 | ENVMAN_LOAD=loaded 10 | GAZEBO_MODEL_PATH=/usr/share/gazebo-11/models 11 | GAZEBO_PLUGIN_PATH=/usr/lib/x86_64-linux-gnu/gazebo-11/plugins 12 | GAZEBO_RESOURCE_PATH=/usr/share/gazebo-11 13 | HOME=/home/anson 14 | HOSTTYPE=x86_64 15 | LANG=C.UTF-8 16 | LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/gazebo-11/plugins:/opt/ros/humble/opt/rviz_ogre_vendor/lib:/opt/ros/humble/lib/x86_64-linux-gnu:/opt/ros/humble/lib 17 | LESSCLOSE=/usr/bin/lesspipe %s %s 18 | LESSOPEN=| /usr/bin/lesspipe %s 19 | LOGNAME=anson 20 | LS_COLORS=rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.zst=01;31:*.tzst=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.wim=01;31:*.swm=01;31:*.dwm=01;31:*.esd=01;31:*.jpg=01;35:*.jpeg=01;35:*.mjpg=01;35:*.mjpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.webp=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36: 21 | NAME=Anson 22 | NVM_BIN=/home/anson/.nvm/versions/node/v20.18.0/bin 23 | NVM_CD_FLAGS= 24 | NVM_DIR=/home/anson/.nvm 25 | NVM_INC=/home/anson/.nvm/versions/node/v20.18.0/include/node 26 | OLDPWD=/home/anson/ros2_ws 27 | PATH=/home/anson/.local/bin:/opt/ros/humble/bin:/home/anson/.local/bin:/home/anson/go/bin:/home/anson/.local/opt/go/bin:/home/anson/.nvm/versions/node/v20.18.0/bin:/home/anson/miniconda3/condabin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/usr/lib/wsl/lib:/mnt/c/Program Files/WindowsApps/MicrosoftCorporationII.WindowsSubsystemForLinux_2.3.26.0_x64__8wekyb3d8bbwe:/mnt/c/WINDOWS/system32:/mnt/c/WINDOWS:/mnt/c/WINDOWS/System32/Wbem:/mnt/c/WINDOWS/System32/WindowsPowerShell/v1.0:/mnt/c/WINDOWS/System32/OpenSSH:/mnt/c/Program Files (x86)/NVIDIA Corporation/PhysX/Common:/mnt/c/Program Files/NVIDIA Corporation/NVIDIA NvDLISR:/mnt/c/Program Files/dotnet:/mnt/c/Program Files/Microsoft SQL Server/150/Tools/Binn:/mnt/c/Program Files/Microsoft SQL Server/Client SDK/ODBC/170/Tools/Binn:/mnt/c/Program Files (x86)/Windows Kits/10/Windows Performance Toolkit:/mnt/c/Users/sanso/AppData/Local/Microsoft/WindowsApps:/mnt/c/Users/sanso/AppData/Local/Programs/Microsoft VS Code/bin:/mnt/c/Users/sanso/.dotnet/tools:/mnt/c/Program Files/gnuplot/bin:/snap/bin:/usr/local/cuda/bin:/usr/bin 28 | PULSE_SERVER=unix:/mnt/wslg/PulseServer 29 | PWD=/home/anson/ros2_ws/build/my_robot_navigation 30 | PYTHONPATH=/home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages:/opt/ros/humble/lib/python3.10/site-packages:/opt/ros/humble/local/lib/python3.10/dist-packages 31 | ROS_DISTRO=humble 32 | ROS_LOCALHOST_ONLY=0 33 | ROS_PYTHON_VERSION=3 34 | ROS_VERSION=2 35 | SHELL=/bin/bash 36 | SHLVL=1 37 | TERM=xterm-256color 38 | TURTLEBOT3_MODEL=burger 39 | USER=anson 40 | WAYLAND_DISPLAY=wayland-0 41 | WSL2_GUI_APPS_ENABLED=1 42 | WSLENV= 43 | WSL_DISTRO_NAME=Ubuntu-22.04 44 | WSL_INTEROP=/run/WSL/304_interop 45 | XDG_DATA_DIRS=/usr/local/share:/usr/share:/var/lib/snapd/desktop 46 | XDG_RUNTIME_DIR=/run/user/1000/ 47 | _=/usr/bin/colcon 48 | _CE_CONDA= 49 | _CE_M= 50 | -------------------------------------------------------------------------------- /build/my_robot_navigation/install.log: -------------------------------------------------------------------------------- 1 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/obstacle_avoidance.py 2 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/__init__.py 3 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/__pycache__/obstacle_avoidance.cpython-310.pyc 4 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/__pycache__/__init__.cpython-310.pyc 5 | /home/anson/ros2_ws/install/my_robot_navigation/share/ament_index/resource_index/packages/my_robot_navigation 6 | /home/anson/ros2_ws/install/my_robot_navigation/share/my_robot_navigation/package.xml 7 | /home/anson/ros2_ws/install/my_robot_navigation/share/my_robot_navigation/robot_navigation.launch.py 8 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/top_level.txt 9 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/entry_points.txt 10 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/PKG-INFO 11 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/requires.txt 12 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/SOURCES.txt 13 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/zip-safe 14 | /home/anson/ros2_ws/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/dependency_links.txt 15 | /home/anson/ros2_ws/install/my_robot_navigation/lib/my_robot_navigation/obstacle_avoidance 16 | -------------------------------------------------------------------------------- /build/my_robot_navigation/my_robot_navigation.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: my-robot-navigation 3 | Version: 0.0.0 4 | Summary: Obstacle avoidance robot with ROS2 and Gazebo 5 | Home-page: UNKNOWN 6 | Maintainer: anson 7 | Maintainer-email: sansonmsa@gmail.com 8 | License: Apache License 2.0 9 | Platform: UNKNOWN 10 | 11 | UNKNOWN 12 | 13 | -------------------------------------------------------------------------------- /build/my_robot_navigation/my_robot_navigation.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | package.xml 2 | setup.cfg 3 | setup.py 4 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/PKG-INFO 5 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/SOURCES.txt 6 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/dependency_links.txt 7 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/entry_points.txt 8 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/requires.txt 9 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/top_level.txt 10 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/zip-safe 11 | launch/robot_navigation.launch.py 12 | my_robot_navigation/__init__.py 13 | my_robot_navigation/obstacle_avoidance.py 14 | resource/my_robot_navigation 15 | test/test_copyright.py 16 | test/test_flake8.py 17 | test/test_pep257.py -------------------------------------------------------------------------------- /build/my_robot_navigation/my_robot_navigation.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /build/my_robot_navigation/my_robot_navigation.egg-info/entry_points.txt: -------------------------------------------------------------------------------- 1 | [console_scripts] 2 | obstacle_avoidance = my_robot_navigation.obstacle_avoidance:main 3 | 4 | -------------------------------------------------------------------------------- /build/my_robot_navigation/my_robot_navigation.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | -------------------------------------------------------------------------------- /build/my_robot_navigation/my_robot_navigation.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | my_robot_navigation 2 | -------------------------------------------------------------------------------- /build/my_robot_navigation/my_robot_navigation.egg-info/zip-safe: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /build/my_robot_navigation/prefix_override/__pycache__/sitecustomize.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/build/my_robot_navigation/prefix_override/__pycache__/sitecustomize.cpython-310.pyc -------------------------------------------------------------------------------- /build/my_robot_navigation/prefix_override/sitecustomize.py: -------------------------------------------------------------------------------- 1 | import sys 2 | if sys.prefix == '/usr': 3 | sys.real_prefix = sys.prefix 4 | sys.prefix = sys.exec_prefix = '/home/anson/ros2_ws/install/my_robot_navigation' 5 | -------------------------------------------------------------------------------- /images/animation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/images/animation.png -------------------------------------------------------------------------------- /images/cmd_vel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/images/cmd_vel.png -------------------------------------------------------------------------------- /images/logs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/images/logs.png -------------------------------------------------------------------------------- /install/.colcon_install_layout: -------------------------------------------------------------------------------- 1 | isolated 2 | -------------------------------------------------------------------------------- /install/COLCON_IGNORE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/install/COLCON_IGNORE -------------------------------------------------------------------------------- /install/_local_setup_util_ps1.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2019 Dirk Thomas 2 | # Licensed under the Apache License, Version 2.0 3 | 4 | import argparse 5 | from collections import OrderedDict 6 | import os 7 | from pathlib import Path 8 | import sys 9 | 10 | 11 | FORMAT_STR_COMMENT_LINE = '# {comment}' 12 | FORMAT_STR_SET_ENV_VAR = 'Set-Item -Path "Env:{name}" -Value "{value}"' 13 | FORMAT_STR_USE_ENV_VAR = '$env:{name}' 14 | FORMAT_STR_INVOKE_SCRIPT = '_colcon_prefix_powershell_source_script "{script_path}"' # noqa: E501 15 | FORMAT_STR_REMOVE_LEADING_SEPARATOR = '' # noqa: E501 16 | FORMAT_STR_REMOVE_TRAILING_SEPARATOR = '' # noqa: E501 17 | 18 | DSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate' 19 | DSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate' 20 | DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists' 21 | DSV_TYPE_SET = 'set' 22 | DSV_TYPE_SET_IF_UNSET = 'set-if-unset' 23 | DSV_TYPE_SOURCE = 'source' 24 | 25 | 26 | def main(argv=sys.argv[1:]): # noqa: D103 27 | parser = argparse.ArgumentParser( 28 | description='Output shell commands for the packages in topological ' 29 | 'order') 30 | parser.add_argument( 31 | 'primary_extension', 32 | help='The file extension of the primary shell') 33 | parser.add_argument( 34 | 'additional_extension', nargs='?', 35 | help='The additional file extension to be considered') 36 | parser.add_argument( 37 | '--merged-install', action='store_true', 38 | help='All install prefixes are merged into a single location') 39 | args = parser.parse_args(argv) 40 | 41 | packages = get_packages(Path(__file__).parent, args.merged_install) 42 | 43 | ordered_packages = order_packages(packages) 44 | for pkg_name in ordered_packages: 45 | if _include_comments(): 46 | print( 47 | FORMAT_STR_COMMENT_LINE.format_map( 48 | {'comment': 'Package: ' + pkg_name})) 49 | prefix = os.path.abspath(os.path.dirname(__file__)) 50 | if not args.merged_install: 51 | prefix = os.path.join(prefix, pkg_name) 52 | for line in get_commands( 53 | pkg_name, prefix, args.primary_extension, 54 | args.additional_extension 55 | ): 56 | print(line) 57 | 58 | for line in _remove_ending_separators(): 59 | print(line) 60 | 61 | 62 | def get_packages(prefix_path, merged_install): 63 | """ 64 | Find packages based on colcon-specific files created during installation. 65 | 66 | :param Path prefix_path: The install prefix path of all packages 67 | :param bool merged_install: The flag if the packages are all installed 68 | directly in the prefix or if each package is installed in a subdirectory 69 | named after the package 70 | :returns: A mapping from the package name to the set of runtime 71 | dependencies 72 | :rtype: dict 73 | """ 74 | packages = {} 75 | # since importing colcon_core isn't feasible here the following constant 76 | # must match colcon_core.location.get_relative_package_index_path() 77 | subdirectory = 'share/colcon-core/packages' 78 | if merged_install: 79 | # return if workspace is empty 80 | if not (prefix_path / subdirectory).is_dir(): 81 | return packages 82 | # find all files in the subdirectory 83 | for p in (prefix_path / subdirectory).iterdir(): 84 | if not p.is_file(): 85 | continue 86 | if p.name.startswith('.'): 87 | continue 88 | add_package_runtime_dependencies(p, packages) 89 | else: 90 | # for each subdirectory look for the package specific file 91 | for p in prefix_path.iterdir(): 92 | if not p.is_dir(): 93 | continue 94 | if p.name.startswith('.'): 95 | continue 96 | p = p / subdirectory / p.name 97 | if p.is_file(): 98 | add_package_runtime_dependencies(p, packages) 99 | 100 | # remove unknown dependencies 101 | pkg_names = set(packages.keys()) 102 | for k in packages.keys(): 103 | packages[k] = {d for d in packages[k] if d in pkg_names} 104 | 105 | return packages 106 | 107 | 108 | def add_package_runtime_dependencies(path, packages): 109 | """ 110 | Check the path and if it exists extract the packages runtime dependencies. 111 | 112 | :param Path path: The resource file containing the runtime dependencies 113 | :param dict packages: A mapping from package names to the sets of runtime 114 | dependencies to add to 115 | """ 116 | content = path.read_text() 117 | dependencies = set(content.split(os.pathsep) if content else []) 118 | packages[path.name] = dependencies 119 | 120 | 121 | def order_packages(packages): 122 | """ 123 | Order packages topologically. 124 | 125 | :param dict packages: A mapping from package name to the set of runtime 126 | dependencies 127 | :returns: The package names 128 | :rtype: list 129 | """ 130 | # select packages with no dependencies in alphabetical order 131 | to_be_ordered = list(packages.keys()) 132 | ordered = [] 133 | while to_be_ordered: 134 | pkg_names_without_deps = [ 135 | name for name in to_be_ordered if not packages[name]] 136 | if not pkg_names_without_deps: 137 | reduce_cycle_set(packages) 138 | raise RuntimeError( 139 | 'Circular dependency between: ' + ', '.join(sorted(packages))) 140 | pkg_names_without_deps.sort() 141 | pkg_name = pkg_names_without_deps[0] 142 | to_be_ordered.remove(pkg_name) 143 | ordered.append(pkg_name) 144 | # remove item from dependency lists 145 | for k in list(packages.keys()): 146 | if pkg_name in packages[k]: 147 | packages[k].remove(pkg_name) 148 | return ordered 149 | 150 | 151 | def reduce_cycle_set(packages): 152 | """ 153 | Reduce the set of packages to the ones part of the circular dependency. 154 | 155 | :param dict packages: A mapping from package name to the set of runtime 156 | dependencies which is modified in place 157 | """ 158 | last_depended = None 159 | while len(packages) > 0: 160 | # get all remaining dependencies 161 | depended = set() 162 | for pkg_name, dependencies in packages.items(): 163 | depended = depended.union(dependencies) 164 | # remove all packages which are not dependent on 165 | for name in list(packages.keys()): 166 | if name not in depended: 167 | del packages[name] 168 | if last_depended: 169 | # if remaining packages haven't changed return them 170 | if last_depended == depended: 171 | return packages.keys() 172 | # otherwise reduce again 173 | last_depended = depended 174 | 175 | 176 | def _include_comments(): 177 | # skipping comment lines when COLCON_TRACE is not set speeds up the 178 | # processing especially on Windows 179 | return bool(os.environ.get('COLCON_TRACE')) 180 | 181 | 182 | def get_commands(pkg_name, prefix, primary_extension, additional_extension): 183 | commands = [] 184 | package_dsv_path = os.path.join(prefix, 'share', pkg_name, 'package.dsv') 185 | if os.path.exists(package_dsv_path): 186 | commands += process_dsv_file( 187 | package_dsv_path, prefix, primary_extension, additional_extension) 188 | return commands 189 | 190 | 191 | def process_dsv_file( 192 | dsv_path, prefix, primary_extension=None, additional_extension=None 193 | ): 194 | commands = [] 195 | if _include_comments(): 196 | commands.append(FORMAT_STR_COMMENT_LINE.format_map({'comment': dsv_path})) 197 | with open(dsv_path, 'r') as h: 198 | content = h.read() 199 | lines = content.splitlines() 200 | 201 | basenames = OrderedDict() 202 | for i, line in enumerate(lines): 203 | # skip over empty or whitespace-only lines 204 | if not line.strip(): 205 | continue 206 | # skip over comments 207 | if line.startswith('#'): 208 | continue 209 | try: 210 | type_, remainder = line.split(';', 1) 211 | except ValueError: 212 | raise RuntimeError( 213 | "Line %d in '%s' doesn't contain a semicolon separating the " 214 | 'type from the arguments' % (i + 1, dsv_path)) 215 | if type_ != DSV_TYPE_SOURCE: 216 | # handle non-source lines 217 | try: 218 | commands += handle_dsv_types_except_source( 219 | type_, remainder, prefix) 220 | except RuntimeError as e: 221 | raise RuntimeError( 222 | "Line %d in '%s' %s" % (i + 1, dsv_path, e)) from e 223 | else: 224 | # group remaining source lines by basename 225 | path_without_ext, ext = os.path.splitext(remainder) 226 | if path_without_ext not in basenames: 227 | basenames[path_without_ext] = set() 228 | assert ext.startswith('.') 229 | ext = ext[1:] 230 | if ext in (primary_extension, additional_extension): 231 | basenames[path_without_ext].add(ext) 232 | 233 | # add the dsv extension to each basename if the file exists 234 | for basename, extensions in basenames.items(): 235 | if not os.path.isabs(basename): 236 | basename = os.path.join(prefix, basename) 237 | if os.path.exists(basename + '.dsv'): 238 | extensions.add('dsv') 239 | 240 | for basename, extensions in basenames.items(): 241 | if not os.path.isabs(basename): 242 | basename = os.path.join(prefix, basename) 243 | if 'dsv' in extensions: 244 | # process dsv files recursively 245 | commands += process_dsv_file( 246 | basename + '.dsv', prefix, primary_extension=primary_extension, 247 | additional_extension=additional_extension) 248 | elif primary_extension in extensions and len(extensions) == 1: 249 | # source primary-only files 250 | commands += [ 251 | FORMAT_STR_INVOKE_SCRIPT.format_map({ 252 | 'prefix': prefix, 253 | 'script_path': basename + '.' + primary_extension})] 254 | elif additional_extension in extensions: 255 | # source non-primary files 256 | commands += [ 257 | FORMAT_STR_INVOKE_SCRIPT.format_map({ 258 | 'prefix': prefix, 259 | 'script_path': basename + '.' + additional_extension})] 260 | 261 | return commands 262 | 263 | 264 | def handle_dsv_types_except_source(type_, remainder, prefix): 265 | commands = [] 266 | if type_ in (DSV_TYPE_SET, DSV_TYPE_SET_IF_UNSET): 267 | try: 268 | env_name, value = remainder.split(';', 1) 269 | except ValueError: 270 | raise RuntimeError( 271 | "doesn't contain a semicolon separating the environment name " 272 | 'from the value') 273 | try_prefixed_value = os.path.join(prefix, value) if value else prefix 274 | if os.path.exists(try_prefixed_value): 275 | value = try_prefixed_value 276 | if type_ == DSV_TYPE_SET: 277 | commands += _set(env_name, value) 278 | elif type_ == DSV_TYPE_SET_IF_UNSET: 279 | commands += _set_if_unset(env_name, value) 280 | else: 281 | assert False 282 | elif type_ in ( 283 | DSV_TYPE_APPEND_NON_DUPLICATE, 284 | DSV_TYPE_PREPEND_NON_DUPLICATE, 285 | DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS 286 | ): 287 | try: 288 | env_name_and_values = remainder.split(';') 289 | except ValueError: 290 | raise RuntimeError( 291 | "doesn't contain a semicolon separating the environment name " 292 | 'from the values') 293 | env_name = env_name_and_values[0] 294 | values = env_name_and_values[1:] 295 | for value in values: 296 | if not value: 297 | value = prefix 298 | elif not os.path.isabs(value): 299 | value = os.path.join(prefix, value) 300 | if ( 301 | type_ == DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS and 302 | not os.path.exists(value) 303 | ): 304 | comment = f'skip extending {env_name} with not existing ' \ 305 | f'path: {value}' 306 | if _include_comments(): 307 | commands.append( 308 | FORMAT_STR_COMMENT_LINE.format_map({'comment': comment})) 309 | elif type_ == DSV_TYPE_APPEND_NON_DUPLICATE: 310 | commands += _append_unique_value(env_name, value) 311 | else: 312 | commands += _prepend_unique_value(env_name, value) 313 | else: 314 | raise RuntimeError( 315 | 'contains an unknown environment hook type: ' + type_) 316 | return commands 317 | 318 | 319 | env_state = {} 320 | 321 | 322 | def _append_unique_value(name, value): 323 | global env_state 324 | if name not in env_state: 325 | if os.environ.get(name): 326 | env_state[name] = set(os.environ[name].split(os.pathsep)) 327 | else: 328 | env_state[name] = set() 329 | # append even if the variable has not been set yet, in case a shell script sets the 330 | # same variable without the knowledge of this Python script. 331 | # later _remove_ending_separators() will cleanup any unintentional leading separator 332 | extend = FORMAT_STR_USE_ENV_VAR.format_map({'name': name}) + os.pathsep 333 | line = FORMAT_STR_SET_ENV_VAR.format_map( 334 | {'name': name, 'value': extend + value}) 335 | if value not in env_state[name]: 336 | env_state[name].add(value) 337 | else: 338 | if not _include_comments(): 339 | return [] 340 | line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) 341 | return [line] 342 | 343 | 344 | def _prepend_unique_value(name, value): 345 | global env_state 346 | if name not in env_state: 347 | if os.environ.get(name): 348 | env_state[name] = set(os.environ[name].split(os.pathsep)) 349 | else: 350 | env_state[name] = set() 351 | # prepend even if the variable has not been set yet, in case a shell script sets the 352 | # same variable without the knowledge of this Python script. 353 | # later _remove_ending_separators() will cleanup any unintentional trailing separator 354 | extend = os.pathsep + FORMAT_STR_USE_ENV_VAR.format_map({'name': name}) 355 | line = FORMAT_STR_SET_ENV_VAR.format_map( 356 | {'name': name, 'value': value + extend}) 357 | if value not in env_state[name]: 358 | env_state[name].add(value) 359 | else: 360 | if not _include_comments(): 361 | return [] 362 | line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) 363 | return [line] 364 | 365 | 366 | # generate commands for removing prepended underscores 367 | def _remove_ending_separators(): 368 | # do nothing if the shell extension does not implement the logic 369 | if FORMAT_STR_REMOVE_TRAILING_SEPARATOR is None: 370 | return [] 371 | 372 | global env_state 373 | commands = [] 374 | for name in env_state: 375 | # skip variables that already had values before this script started prepending 376 | if name in os.environ: 377 | continue 378 | commands += [ 379 | FORMAT_STR_REMOVE_LEADING_SEPARATOR.format_map({'name': name}), 380 | FORMAT_STR_REMOVE_TRAILING_SEPARATOR.format_map({'name': name})] 381 | return commands 382 | 383 | 384 | def _set(name, value): 385 | global env_state 386 | env_state[name] = value 387 | line = FORMAT_STR_SET_ENV_VAR.format_map( 388 | {'name': name, 'value': value}) 389 | return [line] 390 | 391 | 392 | def _set_if_unset(name, value): 393 | global env_state 394 | line = FORMAT_STR_SET_ENV_VAR.format_map( 395 | {'name': name, 'value': value}) 396 | if env_state.get(name, os.environ.get(name)): 397 | line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) 398 | return [line] 399 | 400 | 401 | if __name__ == '__main__': # pragma: no cover 402 | try: 403 | rc = main() 404 | except RuntimeError as e: 405 | print(str(e), file=sys.stderr) 406 | rc = 1 407 | sys.exit(rc) 408 | -------------------------------------------------------------------------------- /install/_local_setup_util_sh.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016-2019 Dirk Thomas 2 | # Licensed under the Apache License, Version 2.0 3 | 4 | import argparse 5 | from collections import OrderedDict 6 | import os 7 | from pathlib import Path 8 | import sys 9 | 10 | 11 | FORMAT_STR_COMMENT_LINE = '# {comment}' 12 | FORMAT_STR_SET_ENV_VAR = 'export {name}="{value}"' 13 | FORMAT_STR_USE_ENV_VAR = '${name}' 14 | FORMAT_STR_INVOKE_SCRIPT = 'COLCON_CURRENT_PREFIX="{prefix}" _colcon_prefix_sh_source_script "{script_path}"' # noqa: E501 15 | FORMAT_STR_REMOVE_LEADING_SEPARATOR = 'if [ "$(echo -n ${name} | head -c 1)" = ":" ]; then export {name}=${{{name}#?}} ; fi' # noqa: E501 16 | FORMAT_STR_REMOVE_TRAILING_SEPARATOR = 'if [ "$(echo -n ${name} | tail -c 1)" = ":" ]; then export {name}=${{{name}%?}} ; fi' # noqa: E501 17 | 18 | DSV_TYPE_APPEND_NON_DUPLICATE = 'append-non-duplicate' 19 | DSV_TYPE_PREPEND_NON_DUPLICATE = 'prepend-non-duplicate' 20 | DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS = 'prepend-non-duplicate-if-exists' 21 | DSV_TYPE_SET = 'set' 22 | DSV_TYPE_SET_IF_UNSET = 'set-if-unset' 23 | DSV_TYPE_SOURCE = 'source' 24 | 25 | 26 | def main(argv=sys.argv[1:]): # noqa: D103 27 | parser = argparse.ArgumentParser( 28 | description='Output shell commands for the packages in topological ' 29 | 'order') 30 | parser.add_argument( 31 | 'primary_extension', 32 | help='The file extension of the primary shell') 33 | parser.add_argument( 34 | 'additional_extension', nargs='?', 35 | help='The additional file extension to be considered') 36 | parser.add_argument( 37 | '--merged-install', action='store_true', 38 | help='All install prefixes are merged into a single location') 39 | args = parser.parse_args(argv) 40 | 41 | packages = get_packages(Path(__file__).parent, args.merged_install) 42 | 43 | ordered_packages = order_packages(packages) 44 | for pkg_name in ordered_packages: 45 | if _include_comments(): 46 | print( 47 | FORMAT_STR_COMMENT_LINE.format_map( 48 | {'comment': 'Package: ' + pkg_name})) 49 | prefix = os.path.abspath(os.path.dirname(__file__)) 50 | if not args.merged_install: 51 | prefix = os.path.join(prefix, pkg_name) 52 | for line in get_commands( 53 | pkg_name, prefix, args.primary_extension, 54 | args.additional_extension 55 | ): 56 | print(line) 57 | 58 | for line in _remove_ending_separators(): 59 | print(line) 60 | 61 | 62 | def get_packages(prefix_path, merged_install): 63 | """ 64 | Find packages based on colcon-specific files created during installation. 65 | 66 | :param Path prefix_path: The install prefix path of all packages 67 | :param bool merged_install: The flag if the packages are all installed 68 | directly in the prefix or if each package is installed in a subdirectory 69 | named after the package 70 | :returns: A mapping from the package name to the set of runtime 71 | dependencies 72 | :rtype: dict 73 | """ 74 | packages = {} 75 | # since importing colcon_core isn't feasible here the following constant 76 | # must match colcon_core.location.get_relative_package_index_path() 77 | subdirectory = 'share/colcon-core/packages' 78 | if merged_install: 79 | # return if workspace is empty 80 | if not (prefix_path / subdirectory).is_dir(): 81 | return packages 82 | # find all files in the subdirectory 83 | for p in (prefix_path / subdirectory).iterdir(): 84 | if not p.is_file(): 85 | continue 86 | if p.name.startswith('.'): 87 | continue 88 | add_package_runtime_dependencies(p, packages) 89 | else: 90 | # for each subdirectory look for the package specific file 91 | for p in prefix_path.iterdir(): 92 | if not p.is_dir(): 93 | continue 94 | if p.name.startswith('.'): 95 | continue 96 | p = p / subdirectory / p.name 97 | if p.is_file(): 98 | add_package_runtime_dependencies(p, packages) 99 | 100 | # remove unknown dependencies 101 | pkg_names = set(packages.keys()) 102 | for k in packages.keys(): 103 | packages[k] = {d for d in packages[k] if d in pkg_names} 104 | 105 | return packages 106 | 107 | 108 | def add_package_runtime_dependencies(path, packages): 109 | """ 110 | Check the path and if it exists extract the packages runtime dependencies. 111 | 112 | :param Path path: The resource file containing the runtime dependencies 113 | :param dict packages: A mapping from package names to the sets of runtime 114 | dependencies to add to 115 | """ 116 | content = path.read_text() 117 | dependencies = set(content.split(os.pathsep) if content else []) 118 | packages[path.name] = dependencies 119 | 120 | 121 | def order_packages(packages): 122 | """ 123 | Order packages topologically. 124 | 125 | :param dict packages: A mapping from package name to the set of runtime 126 | dependencies 127 | :returns: The package names 128 | :rtype: list 129 | """ 130 | # select packages with no dependencies in alphabetical order 131 | to_be_ordered = list(packages.keys()) 132 | ordered = [] 133 | while to_be_ordered: 134 | pkg_names_without_deps = [ 135 | name for name in to_be_ordered if not packages[name]] 136 | if not pkg_names_without_deps: 137 | reduce_cycle_set(packages) 138 | raise RuntimeError( 139 | 'Circular dependency between: ' + ', '.join(sorted(packages))) 140 | pkg_names_without_deps.sort() 141 | pkg_name = pkg_names_without_deps[0] 142 | to_be_ordered.remove(pkg_name) 143 | ordered.append(pkg_name) 144 | # remove item from dependency lists 145 | for k in list(packages.keys()): 146 | if pkg_name in packages[k]: 147 | packages[k].remove(pkg_name) 148 | return ordered 149 | 150 | 151 | def reduce_cycle_set(packages): 152 | """ 153 | Reduce the set of packages to the ones part of the circular dependency. 154 | 155 | :param dict packages: A mapping from package name to the set of runtime 156 | dependencies which is modified in place 157 | """ 158 | last_depended = None 159 | while len(packages) > 0: 160 | # get all remaining dependencies 161 | depended = set() 162 | for pkg_name, dependencies in packages.items(): 163 | depended = depended.union(dependencies) 164 | # remove all packages which are not dependent on 165 | for name in list(packages.keys()): 166 | if name not in depended: 167 | del packages[name] 168 | if last_depended: 169 | # if remaining packages haven't changed return them 170 | if last_depended == depended: 171 | return packages.keys() 172 | # otherwise reduce again 173 | last_depended = depended 174 | 175 | 176 | def _include_comments(): 177 | # skipping comment lines when COLCON_TRACE is not set speeds up the 178 | # processing especially on Windows 179 | return bool(os.environ.get('COLCON_TRACE')) 180 | 181 | 182 | def get_commands(pkg_name, prefix, primary_extension, additional_extension): 183 | commands = [] 184 | package_dsv_path = os.path.join(prefix, 'share', pkg_name, 'package.dsv') 185 | if os.path.exists(package_dsv_path): 186 | commands += process_dsv_file( 187 | package_dsv_path, prefix, primary_extension, additional_extension) 188 | return commands 189 | 190 | 191 | def process_dsv_file( 192 | dsv_path, prefix, primary_extension=None, additional_extension=None 193 | ): 194 | commands = [] 195 | if _include_comments(): 196 | commands.append(FORMAT_STR_COMMENT_LINE.format_map({'comment': dsv_path})) 197 | with open(dsv_path, 'r') as h: 198 | content = h.read() 199 | lines = content.splitlines() 200 | 201 | basenames = OrderedDict() 202 | for i, line in enumerate(lines): 203 | # skip over empty or whitespace-only lines 204 | if not line.strip(): 205 | continue 206 | # skip over comments 207 | if line.startswith('#'): 208 | continue 209 | try: 210 | type_, remainder = line.split(';', 1) 211 | except ValueError: 212 | raise RuntimeError( 213 | "Line %d in '%s' doesn't contain a semicolon separating the " 214 | 'type from the arguments' % (i + 1, dsv_path)) 215 | if type_ != DSV_TYPE_SOURCE: 216 | # handle non-source lines 217 | try: 218 | commands += handle_dsv_types_except_source( 219 | type_, remainder, prefix) 220 | except RuntimeError as e: 221 | raise RuntimeError( 222 | "Line %d in '%s' %s" % (i + 1, dsv_path, e)) from e 223 | else: 224 | # group remaining source lines by basename 225 | path_without_ext, ext = os.path.splitext(remainder) 226 | if path_without_ext not in basenames: 227 | basenames[path_without_ext] = set() 228 | assert ext.startswith('.') 229 | ext = ext[1:] 230 | if ext in (primary_extension, additional_extension): 231 | basenames[path_without_ext].add(ext) 232 | 233 | # add the dsv extension to each basename if the file exists 234 | for basename, extensions in basenames.items(): 235 | if not os.path.isabs(basename): 236 | basename = os.path.join(prefix, basename) 237 | if os.path.exists(basename + '.dsv'): 238 | extensions.add('dsv') 239 | 240 | for basename, extensions in basenames.items(): 241 | if not os.path.isabs(basename): 242 | basename = os.path.join(prefix, basename) 243 | if 'dsv' in extensions: 244 | # process dsv files recursively 245 | commands += process_dsv_file( 246 | basename + '.dsv', prefix, primary_extension=primary_extension, 247 | additional_extension=additional_extension) 248 | elif primary_extension in extensions and len(extensions) == 1: 249 | # source primary-only files 250 | commands += [ 251 | FORMAT_STR_INVOKE_SCRIPT.format_map({ 252 | 'prefix': prefix, 253 | 'script_path': basename + '.' + primary_extension})] 254 | elif additional_extension in extensions: 255 | # source non-primary files 256 | commands += [ 257 | FORMAT_STR_INVOKE_SCRIPT.format_map({ 258 | 'prefix': prefix, 259 | 'script_path': basename + '.' + additional_extension})] 260 | 261 | return commands 262 | 263 | 264 | def handle_dsv_types_except_source(type_, remainder, prefix): 265 | commands = [] 266 | if type_ in (DSV_TYPE_SET, DSV_TYPE_SET_IF_UNSET): 267 | try: 268 | env_name, value = remainder.split(';', 1) 269 | except ValueError: 270 | raise RuntimeError( 271 | "doesn't contain a semicolon separating the environment name " 272 | 'from the value') 273 | try_prefixed_value = os.path.join(prefix, value) if value else prefix 274 | if os.path.exists(try_prefixed_value): 275 | value = try_prefixed_value 276 | if type_ == DSV_TYPE_SET: 277 | commands += _set(env_name, value) 278 | elif type_ == DSV_TYPE_SET_IF_UNSET: 279 | commands += _set_if_unset(env_name, value) 280 | else: 281 | assert False 282 | elif type_ in ( 283 | DSV_TYPE_APPEND_NON_DUPLICATE, 284 | DSV_TYPE_PREPEND_NON_DUPLICATE, 285 | DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS 286 | ): 287 | try: 288 | env_name_and_values = remainder.split(';') 289 | except ValueError: 290 | raise RuntimeError( 291 | "doesn't contain a semicolon separating the environment name " 292 | 'from the values') 293 | env_name = env_name_and_values[0] 294 | values = env_name_and_values[1:] 295 | for value in values: 296 | if not value: 297 | value = prefix 298 | elif not os.path.isabs(value): 299 | value = os.path.join(prefix, value) 300 | if ( 301 | type_ == DSV_TYPE_PREPEND_NON_DUPLICATE_IF_EXISTS and 302 | not os.path.exists(value) 303 | ): 304 | comment = f'skip extending {env_name} with not existing ' \ 305 | f'path: {value}' 306 | if _include_comments(): 307 | commands.append( 308 | FORMAT_STR_COMMENT_LINE.format_map({'comment': comment})) 309 | elif type_ == DSV_TYPE_APPEND_NON_DUPLICATE: 310 | commands += _append_unique_value(env_name, value) 311 | else: 312 | commands += _prepend_unique_value(env_name, value) 313 | else: 314 | raise RuntimeError( 315 | 'contains an unknown environment hook type: ' + type_) 316 | return commands 317 | 318 | 319 | env_state = {} 320 | 321 | 322 | def _append_unique_value(name, value): 323 | global env_state 324 | if name not in env_state: 325 | if os.environ.get(name): 326 | env_state[name] = set(os.environ[name].split(os.pathsep)) 327 | else: 328 | env_state[name] = set() 329 | # append even if the variable has not been set yet, in case a shell script sets the 330 | # same variable without the knowledge of this Python script. 331 | # later _remove_ending_separators() will cleanup any unintentional leading separator 332 | extend = FORMAT_STR_USE_ENV_VAR.format_map({'name': name}) + os.pathsep 333 | line = FORMAT_STR_SET_ENV_VAR.format_map( 334 | {'name': name, 'value': extend + value}) 335 | if value not in env_state[name]: 336 | env_state[name].add(value) 337 | else: 338 | if not _include_comments(): 339 | return [] 340 | line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) 341 | return [line] 342 | 343 | 344 | def _prepend_unique_value(name, value): 345 | global env_state 346 | if name not in env_state: 347 | if os.environ.get(name): 348 | env_state[name] = set(os.environ[name].split(os.pathsep)) 349 | else: 350 | env_state[name] = set() 351 | # prepend even if the variable has not been set yet, in case a shell script sets the 352 | # same variable without the knowledge of this Python script. 353 | # later _remove_ending_separators() will cleanup any unintentional trailing separator 354 | extend = os.pathsep + FORMAT_STR_USE_ENV_VAR.format_map({'name': name}) 355 | line = FORMAT_STR_SET_ENV_VAR.format_map( 356 | {'name': name, 'value': value + extend}) 357 | if value not in env_state[name]: 358 | env_state[name].add(value) 359 | else: 360 | if not _include_comments(): 361 | return [] 362 | line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) 363 | return [line] 364 | 365 | 366 | # generate commands for removing prepended underscores 367 | def _remove_ending_separators(): 368 | # do nothing if the shell extension does not implement the logic 369 | if FORMAT_STR_REMOVE_TRAILING_SEPARATOR is None: 370 | return [] 371 | 372 | global env_state 373 | commands = [] 374 | for name in env_state: 375 | # skip variables that already had values before this script started prepending 376 | if name in os.environ: 377 | continue 378 | commands += [ 379 | FORMAT_STR_REMOVE_LEADING_SEPARATOR.format_map({'name': name}), 380 | FORMAT_STR_REMOVE_TRAILING_SEPARATOR.format_map({'name': name})] 381 | return commands 382 | 383 | 384 | def _set(name, value): 385 | global env_state 386 | env_state[name] = value 387 | line = FORMAT_STR_SET_ENV_VAR.format_map( 388 | {'name': name, 'value': value}) 389 | return [line] 390 | 391 | 392 | def _set_if_unset(name, value): 393 | global env_state 394 | line = FORMAT_STR_SET_ENV_VAR.format_map( 395 | {'name': name, 'value': value}) 396 | if env_state.get(name, os.environ.get(name)): 397 | line = FORMAT_STR_COMMENT_LINE.format_map({'comment': line}) 398 | return [line] 399 | 400 | 401 | if __name__ == '__main__': # pragma: no cover 402 | try: 403 | rc = main() 404 | except RuntimeError as e: 405 | print(str(e), file=sys.stderr) 406 | rc = 1 407 | sys.exit(rc) 408 | -------------------------------------------------------------------------------- /install/local_setup.bash: -------------------------------------------------------------------------------- 1 | # generated from colcon_bash/shell/template/prefix.bash.em 2 | 3 | # This script extends the environment with all packages contained in this 4 | # prefix path. 5 | 6 | # a bash script is able to determine its own path if necessary 7 | if [ -z "$COLCON_CURRENT_PREFIX" ]; then 8 | _colcon_prefix_bash_COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)" 9 | else 10 | _colcon_prefix_bash_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" 11 | fi 12 | 13 | # function to prepend a value to a variable 14 | # which uses colons as separators 15 | # duplicates as well as trailing separators are avoided 16 | # first argument: the name of the result variable 17 | # second argument: the value to be prepended 18 | _colcon_prefix_bash_prepend_unique_value() { 19 | # arguments 20 | _listname="$1" 21 | _value="$2" 22 | 23 | # get values from variable 24 | eval _values=\"\$$_listname\" 25 | # backup the field separator 26 | _colcon_prefix_bash_prepend_unique_value_IFS="$IFS" 27 | IFS=":" 28 | # start with the new value 29 | _all_values="$_value" 30 | _contained_value="" 31 | # iterate over existing values in the variable 32 | for _item in $_values; do 33 | # ignore empty strings 34 | if [ -z "$_item" ]; then 35 | continue 36 | fi 37 | # ignore duplicates of _value 38 | if [ "$_item" = "$_value" ]; then 39 | _contained_value=1 40 | continue 41 | fi 42 | # keep non-duplicate values 43 | _all_values="$_all_values:$_item" 44 | done 45 | unset _item 46 | if [ -z "$_contained_value" ]; then 47 | if [ -n "$COLCON_TRACE" ]; then 48 | if [ "$_all_values" = "$_value" ]; then 49 | echo "export $_listname=$_value" 50 | else 51 | echo "export $_listname=$_value:\$$_listname" 52 | fi 53 | fi 54 | fi 55 | unset _contained_value 56 | # restore the field separator 57 | IFS="$_colcon_prefix_bash_prepend_unique_value_IFS" 58 | unset _colcon_prefix_bash_prepend_unique_value_IFS 59 | # export the updated variable 60 | eval export $_listname=\"$_all_values\" 61 | unset _all_values 62 | unset _values 63 | 64 | unset _value 65 | unset _listname 66 | } 67 | 68 | # add this prefix to the COLCON_PREFIX_PATH 69 | _colcon_prefix_bash_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX" 70 | unset _colcon_prefix_bash_prepend_unique_value 71 | 72 | # check environment variable for custom Python executable 73 | if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then 74 | if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then 75 | echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist" 76 | return 1 77 | fi 78 | _colcon_python_executable="$COLCON_PYTHON_EXECUTABLE" 79 | else 80 | # try the Python executable known at configure time 81 | _colcon_python_executable="/usr/bin/python3" 82 | # if it doesn't exist try a fall back 83 | if [ ! -f "$_colcon_python_executable" ]; then 84 | if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then 85 | echo "error: unable to find python3 executable" 86 | return 1 87 | fi 88 | _colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"` 89 | fi 90 | fi 91 | 92 | # function to source another script with conditional trace output 93 | # first argument: the path of the script 94 | _colcon_prefix_sh_source_script() { 95 | if [ -f "$1" ]; then 96 | if [ -n "$COLCON_TRACE" ]; then 97 | echo "# . \"$1\"" 98 | fi 99 | . "$1" 100 | else 101 | echo "not found: \"$1\"" 1>&2 102 | fi 103 | } 104 | 105 | # get all commands in topological order 106 | _colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_bash_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh bash)" 107 | unset _colcon_python_executable 108 | if [ -n "$COLCON_TRACE" ]; then 109 | echo "$(declare -f _colcon_prefix_sh_source_script)" 110 | echo "# Execute generated script:" 111 | echo "# <<<" 112 | echo "${_colcon_ordered_commands}" 113 | echo "# >>>" 114 | echo "unset _colcon_prefix_sh_source_script" 115 | fi 116 | eval "${_colcon_ordered_commands}" 117 | unset _colcon_ordered_commands 118 | 119 | unset _colcon_prefix_sh_source_script 120 | 121 | unset _colcon_prefix_bash_COLCON_CURRENT_PREFIX 122 | -------------------------------------------------------------------------------- /install/local_setup.ps1: -------------------------------------------------------------------------------- 1 | # generated from colcon_powershell/shell/template/prefix.ps1.em 2 | 3 | # This script extends the environment with all packages contained in this 4 | # prefix path. 5 | 6 | # check environment variable for custom Python executable 7 | if ($env:COLCON_PYTHON_EXECUTABLE) { 8 | if (!(Test-Path "$env:COLCON_PYTHON_EXECUTABLE" -PathType Leaf)) { 9 | echo "error: COLCON_PYTHON_EXECUTABLE '$env:COLCON_PYTHON_EXECUTABLE' doesn't exist" 10 | exit 1 11 | } 12 | $_colcon_python_executable="$env:COLCON_PYTHON_EXECUTABLE" 13 | } else { 14 | # use the Python executable known at configure time 15 | $_colcon_python_executable="/usr/bin/python3" 16 | # if it doesn't exist try a fall back 17 | if (!(Test-Path "$_colcon_python_executable" -PathType Leaf)) { 18 | if (!(Get-Command "python3" -ErrorAction SilentlyContinue)) { 19 | echo "error: unable to find python3 executable" 20 | exit 1 21 | } 22 | $_colcon_python_executable="python3" 23 | } 24 | } 25 | 26 | # function to source another script with conditional trace output 27 | # first argument: the path of the script 28 | function _colcon_prefix_powershell_source_script { 29 | param ( 30 | $_colcon_prefix_powershell_source_script_param 31 | ) 32 | # source script with conditional trace output 33 | if (Test-Path $_colcon_prefix_powershell_source_script_param) { 34 | if ($env:COLCON_TRACE) { 35 | echo ". '$_colcon_prefix_powershell_source_script_param'" 36 | } 37 | . "$_colcon_prefix_powershell_source_script_param" 38 | } else { 39 | Write-Error "not found: '$_colcon_prefix_powershell_source_script_param'" 40 | } 41 | } 42 | 43 | # get all commands in topological order 44 | $_colcon_ordered_commands = & "$_colcon_python_executable" "$(Split-Path $PSCommandPath -Parent)/_local_setup_util_ps1.py" ps1 45 | 46 | # execute all commands in topological order 47 | if ($env:COLCON_TRACE) { 48 | echo "Execute generated script:" 49 | echo "<<<" 50 | $_colcon_ordered_commands.Split([Environment]::NewLine, [StringSplitOptions]::RemoveEmptyEntries) | Write-Output 51 | echo ">>>" 52 | } 53 | if ($_colcon_ordered_commands) { 54 | $_colcon_ordered_commands.Split([Environment]::NewLine, [StringSplitOptions]::RemoveEmptyEntries) | Invoke-Expression 55 | } 56 | -------------------------------------------------------------------------------- /install/local_setup.sh: -------------------------------------------------------------------------------- 1 | # generated from colcon_core/shell/template/prefix.sh.em 2 | 3 | # This script extends the environment with all packages contained in this 4 | # prefix path. 5 | 6 | # since a plain shell script can't determine its own path when being sourced 7 | # either use the provided COLCON_CURRENT_PREFIX 8 | # or fall back to the build time prefix (if it exists) 9 | _colcon_prefix_sh_COLCON_CURRENT_PREFIX="/home/anson/ros2_ws/install" 10 | if [ -z "$COLCON_CURRENT_PREFIX" ]; then 11 | if [ ! -d "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX" ]; then 12 | echo "The build time path \"$_colcon_prefix_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2 13 | unset _colcon_prefix_sh_COLCON_CURRENT_PREFIX 14 | return 1 15 | fi 16 | else 17 | _colcon_prefix_sh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" 18 | fi 19 | 20 | # function to prepend a value to a variable 21 | # which uses colons as separators 22 | # duplicates as well as trailing separators are avoided 23 | # first argument: the name of the result variable 24 | # second argument: the value to be prepended 25 | _colcon_prefix_sh_prepend_unique_value() { 26 | # arguments 27 | _listname="$1" 28 | _value="$2" 29 | 30 | # get values from variable 31 | eval _values=\"\$$_listname\" 32 | # backup the field separator 33 | _colcon_prefix_sh_prepend_unique_value_IFS="$IFS" 34 | IFS=":" 35 | # start with the new value 36 | _all_values="$_value" 37 | _contained_value="" 38 | # iterate over existing values in the variable 39 | for _item in $_values; do 40 | # ignore empty strings 41 | if [ -z "$_item" ]; then 42 | continue 43 | fi 44 | # ignore duplicates of _value 45 | if [ "$_item" = "$_value" ]; then 46 | _contained_value=1 47 | continue 48 | fi 49 | # keep non-duplicate values 50 | _all_values="$_all_values:$_item" 51 | done 52 | unset _item 53 | if [ -z "$_contained_value" ]; then 54 | if [ -n "$COLCON_TRACE" ]; then 55 | if [ "$_all_values" = "$_value" ]; then 56 | echo "export $_listname=$_value" 57 | else 58 | echo "export $_listname=$_value:\$$_listname" 59 | fi 60 | fi 61 | fi 62 | unset _contained_value 63 | # restore the field separator 64 | IFS="$_colcon_prefix_sh_prepend_unique_value_IFS" 65 | unset _colcon_prefix_sh_prepend_unique_value_IFS 66 | # export the updated variable 67 | eval export $_listname=\"$_all_values\" 68 | unset _all_values 69 | unset _values 70 | 71 | unset _value 72 | unset _listname 73 | } 74 | 75 | # add this prefix to the COLCON_PREFIX_PATH 76 | _colcon_prefix_sh_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX" 77 | unset _colcon_prefix_sh_prepend_unique_value 78 | 79 | # check environment variable for custom Python executable 80 | if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then 81 | if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then 82 | echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist" 83 | return 1 84 | fi 85 | _colcon_python_executable="$COLCON_PYTHON_EXECUTABLE" 86 | else 87 | # try the Python executable known at configure time 88 | _colcon_python_executable="/usr/bin/python3" 89 | # if it doesn't exist try a fall back 90 | if [ ! -f "$_colcon_python_executable" ]; then 91 | if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then 92 | echo "error: unable to find python3 executable" 93 | return 1 94 | fi 95 | _colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"` 96 | fi 97 | fi 98 | 99 | # function to source another script with conditional trace output 100 | # first argument: the path of the script 101 | _colcon_prefix_sh_source_script() { 102 | if [ -f "$1" ]; then 103 | if [ -n "$COLCON_TRACE" ]; then 104 | echo "# . \"$1\"" 105 | fi 106 | . "$1" 107 | else 108 | echo "not found: \"$1\"" 1>&2 109 | fi 110 | } 111 | 112 | # get all commands in topological order 113 | _colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_sh_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh)" 114 | unset _colcon_python_executable 115 | if [ -n "$COLCON_TRACE" ]; then 116 | echo "_colcon_prefix_sh_source_script() { 117 | if [ -f \"\$1\" ]; then 118 | if [ -n \"\$COLCON_TRACE\" ]; then 119 | echo \"# . \\\"\$1\\\"\" 120 | fi 121 | . \"\$1\" 122 | else 123 | echo \"not found: \\\"\$1\\\"\" 1>&2 124 | fi 125 | }" 126 | echo "# Execute generated script:" 127 | echo "# <<<" 128 | echo "${_colcon_ordered_commands}" 129 | echo "# >>>" 130 | echo "unset _colcon_prefix_sh_source_script" 131 | fi 132 | eval "${_colcon_ordered_commands}" 133 | unset _colcon_ordered_commands 134 | 135 | unset _colcon_prefix_sh_source_script 136 | 137 | unset _colcon_prefix_sh_COLCON_CURRENT_PREFIX 138 | -------------------------------------------------------------------------------- /install/local_setup.zsh: -------------------------------------------------------------------------------- 1 | # generated from colcon_zsh/shell/template/prefix.zsh.em 2 | 3 | # This script extends the environment with all packages contained in this 4 | # prefix path. 5 | 6 | # a zsh script is able to determine its own path if necessary 7 | if [ -z "$COLCON_CURRENT_PREFIX" ]; then 8 | _colcon_prefix_zsh_COLCON_CURRENT_PREFIX="$(builtin cd -q "`dirname "${(%):-%N}"`" > /dev/null && pwd)" 9 | else 10 | _colcon_prefix_zsh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" 11 | fi 12 | 13 | # function to convert array-like strings into arrays 14 | # to workaround SH_WORD_SPLIT not being set 15 | _colcon_prefix_zsh_convert_to_array() { 16 | local _listname=$1 17 | local _dollar="$" 18 | local _split="{=" 19 | local _to_array="(\"$_dollar$_split$_listname}\")" 20 | eval $_listname=$_to_array 21 | } 22 | 23 | # function to prepend a value to a variable 24 | # which uses colons as separators 25 | # duplicates as well as trailing separators are avoided 26 | # first argument: the name of the result variable 27 | # second argument: the value to be prepended 28 | _colcon_prefix_zsh_prepend_unique_value() { 29 | # arguments 30 | _listname="$1" 31 | _value="$2" 32 | 33 | # get values from variable 34 | eval _values=\"\$$_listname\" 35 | # backup the field separator 36 | _colcon_prefix_zsh_prepend_unique_value_IFS="$IFS" 37 | IFS=":" 38 | # start with the new value 39 | _all_values="$_value" 40 | _contained_value="" 41 | # workaround SH_WORD_SPLIT not being set 42 | _colcon_prefix_zsh_convert_to_array _values 43 | # iterate over existing values in the variable 44 | for _item in $_values; do 45 | # ignore empty strings 46 | if [ -z "$_item" ]; then 47 | continue 48 | fi 49 | # ignore duplicates of _value 50 | if [ "$_item" = "$_value" ]; then 51 | _contained_value=1 52 | continue 53 | fi 54 | # keep non-duplicate values 55 | _all_values="$_all_values:$_item" 56 | done 57 | unset _item 58 | if [ -z "$_contained_value" ]; then 59 | if [ -n "$COLCON_TRACE" ]; then 60 | if [ "$_all_values" = "$_value" ]; then 61 | echo "export $_listname=$_value" 62 | else 63 | echo "export $_listname=$_value:\$$_listname" 64 | fi 65 | fi 66 | fi 67 | unset _contained_value 68 | # restore the field separator 69 | IFS="$_colcon_prefix_zsh_prepend_unique_value_IFS" 70 | unset _colcon_prefix_zsh_prepend_unique_value_IFS 71 | # export the updated variable 72 | eval export $_listname=\"$_all_values\" 73 | unset _all_values 74 | unset _values 75 | 76 | unset _value 77 | unset _listname 78 | } 79 | 80 | # add this prefix to the COLCON_PREFIX_PATH 81 | _colcon_prefix_zsh_prepend_unique_value COLCON_PREFIX_PATH "$_colcon_prefix_zsh_COLCON_CURRENT_PREFIX" 82 | unset _colcon_prefix_zsh_prepend_unique_value 83 | unset _colcon_prefix_zsh_convert_to_array 84 | 85 | # check environment variable for custom Python executable 86 | if [ -n "$COLCON_PYTHON_EXECUTABLE" ]; then 87 | if [ ! -f "$COLCON_PYTHON_EXECUTABLE" ]; then 88 | echo "error: COLCON_PYTHON_EXECUTABLE '$COLCON_PYTHON_EXECUTABLE' doesn't exist" 89 | return 1 90 | fi 91 | _colcon_python_executable="$COLCON_PYTHON_EXECUTABLE" 92 | else 93 | # try the Python executable known at configure time 94 | _colcon_python_executable="/usr/bin/python3" 95 | # if it doesn't exist try a fall back 96 | if [ ! -f "$_colcon_python_executable" ]; then 97 | if ! /usr/bin/env python3 --version > /dev/null 2> /dev/null; then 98 | echo "error: unable to find python3 executable" 99 | return 1 100 | fi 101 | _colcon_python_executable=`/usr/bin/env python3 -c "import sys; print(sys.executable)"` 102 | fi 103 | fi 104 | 105 | # function to source another script with conditional trace output 106 | # first argument: the path of the script 107 | _colcon_prefix_sh_source_script() { 108 | if [ -f "$1" ]; then 109 | if [ -n "$COLCON_TRACE" ]; then 110 | echo "# . \"$1\"" 111 | fi 112 | . "$1" 113 | else 114 | echo "not found: \"$1\"" 1>&2 115 | fi 116 | } 117 | 118 | # get all commands in topological order 119 | _colcon_ordered_commands="$($_colcon_python_executable "$_colcon_prefix_zsh_COLCON_CURRENT_PREFIX/_local_setup_util_sh.py" sh zsh)" 120 | unset _colcon_python_executable 121 | if [ -n "$COLCON_TRACE" ]; then 122 | echo "$(declare -f _colcon_prefix_sh_source_script)" 123 | echo "# Execute generated script:" 124 | echo "# <<<" 125 | echo "${_colcon_ordered_commands}" 126 | echo "# >>>" 127 | echo "unset _colcon_prefix_sh_source_script" 128 | fi 129 | eval "${_colcon_ordered_commands}" 130 | unset _colcon_ordered_commands 131 | 132 | unset _colcon_prefix_sh_source_script 133 | 134 | unset _colcon_prefix_zsh_COLCON_CURRENT_PREFIX 135 | -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/my_robot_navigation/obstacle_avoidance: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | # EASY-INSTALL-ENTRY-SCRIPT: 'my-robot-navigation==0.0.0','console_scripts','obstacle_avoidance' 3 | import re 4 | import sys 5 | 6 | # for compatibility with easy_install; see #2198 7 | __requires__ = 'my-robot-navigation==0.0.0' 8 | 9 | try: 10 | from importlib.metadata import distribution 11 | except ImportError: 12 | try: 13 | from importlib_metadata import distribution 14 | except ImportError: 15 | from pkg_resources import load_entry_point 16 | 17 | 18 | def importlib_load_entry_point(spec, group, name): 19 | dist_name, _, _ = spec.partition('==') 20 | matches = ( 21 | entry_point 22 | for entry_point in distribution(dist_name).entry_points 23 | if entry_point.group == group and entry_point.name == name 24 | ) 25 | return next(matches).load() 26 | 27 | 28 | globals().setdefault('load_entry_point', importlib_load_entry_point) 29 | 30 | 31 | if __name__ == '__main__': 32 | sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) 33 | sys.exit(load_entry_point('my-robot-navigation==0.0.0', 'console_scripts', 'obstacle_avoidance')()) 34 | -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: my-robot-navigation 3 | Version: 0.0.0 4 | Summary: Obstacle avoidance robot with ROS2 and Gazebo 5 | Home-page: UNKNOWN 6 | Maintainer: anson 7 | Maintainer-email: sansonmsa@gmail.com 8 | License: Apache License 2.0 9 | Platform: UNKNOWN 10 | 11 | UNKNOWN 12 | 13 | -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | package.xml 2 | setup.cfg 3 | setup.py 4 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/PKG-INFO 5 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/SOURCES.txt 6 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/dependency_links.txt 7 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/entry_points.txt 8 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/requires.txt 9 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/top_level.txt 10 | ../../build/my_robot_navigation/my_robot_navigation.egg-info/zip-safe 11 | launch/robot_navigation.launch.py 12 | my_robot_navigation/__init__.py 13 | my_robot_navigation/obstacle_avoidance.py 14 | resource/my_robot_navigation 15 | test/test_copyright.py 16 | test/test_flake8.py 17 | test/test_pep257.py -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/entry_points.txt: -------------------------------------------------------------------------------- 1 | [console_scripts] 2 | obstacle_avoidance = my_robot_navigation.obstacle_avoidance:main 3 | 4 | -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/requires.txt: -------------------------------------------------------------------------------- 1 | setuptools 2 | -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | my_robot_navigation 2 | -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation-0.0.0-py3.10.egg-info/zip-safe: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/__init__.py -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/__pycache__/obstacle_avoidance.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/__pycache__/obstacle_avoidance.cpython-310.pyc -------------------------------------------------------------------------------- /install/my_robot_navigation/lib/python3.10/site-packages/my_robot_navigation/obstacle_avoidance.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import rclpy 4 | from rclpy.node import Node 5 | from sensor_msgs.msg import LaserScan 6 | from geometry_msgs.msg import Twist 7 | 8 | class ObstacleAvoidanceNode(Node): 9 | def __init__(self): 10 | super().__init__('obstacle_avoidance_node') 11 | # Subscriber to LIDAR data 12 | self.subscription = self.create_subscription( 13 | LaserScan, 14 | '/scan', # TurtleBot3 LIDAR topic 15 | self.lidar_callback, 16 | 10 17 | ) 18 | # Publisher for velocity commands 19 | self.publisher = self.create_publisher( 20 | Twist, 21 | '/cmd_vel', # TurtleBot3 velocity topic 22 | 10 23 | ) 24 | self.get_logger().info("Obstacle Avoidance Node Started") 25 | 26 | def lidar_callback(self, msg): 27 | # Process LIDAR data (360 degrees, 0 = front, 180 = rear) 28 | ranges = msg.ranges 29 | min_distance = min(ranges[0:60] + ranges[300:360]) # Check front 120 degrees 30 | twist = Twist() 31 | 32 | if min_distance < 0.5: # If obstacle closer than 0.5m 33 | twist.linear.x = 0.0 34 | twist.angular.z = 0.5 # Turn right 35 | self.get_logger().info(f"Obstacle detected {min_distance:.2f}m ahead, turning!") 36 | else: 37 | twist.linear.x = 0.2 # Move forward 38 | twist.angular.z = 0.0 39 | self.get_logger().info("Path clear, moving forward") 40 | 41 | self.publisher.publish(twist) 42 | 43 | def main(args=None): 44 | rclpy.init(args=args) 45 | node = ObstacleAvoidanceNode() 46 | try: 47 | rclpy.spin(node) 48 | except KeyboardInterrupt: 49 | node.get_logger().info("Shutting down") 50 | finally: 51 | node.destroy_node() 52 | rclpy.shutdown() 53 | 54 | if __name__ == '__main__': 55 | main() 56 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/ament_index/resource_index/packages/my_robot_navigation: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/install/my_robot_navigation/share/ament_index/resource_index/packages/my_robot_navigation -------------------------------------------------------------------------------- /install/my_robot_navigation/share/colcon-core/packages/my_robot_navigation: -------------------------------------------------------------------------------- 1 | geometry_msgs:rclcpp:sensor_msgs -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/__pycache__/robot_navigation.launch.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/install/my_robot_navigation/share/my_robot_navigation/__pycache__/robot_navigation.launch.cpython-310.pyc -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/hook/ament_prefix_path.dsv: -------------------------------------------------------------------------------- 1 | prepend-non-duplicate;AMENT_PREFIX_PATH; 2 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/hook/ament_prefix_path.ps1: -------------------------------------------------------------------------------- 1 | # generated from colcon_powershell/shell/template/hook_prepend_value.ps1.em 2 | 3 | colcon_prepend_unique_value AMENT_PREFIX_PATH "$env:COLCON_CURRENT_PREFIX" 4 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/hook/ament_prefix_path.sh: -------------------------------------------------------------------------------- 1 | # generated from colcon_core/shell/template/hook_prepend_value.sh.em 2 | 3 | _colcon_prepend_unique_value AMENT_PREFIX_PATH "$COLCON_CURRENT_PREFIX" 4 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/hook/pythonpath.dsv: -------------------------------------------------------------------------------- 1 | prepend-non-duplicate;PYTHONPATH;lib/python3.10/site-packages 2 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/hook/pythonpath.ps1: -------------------------------------------------------------------------------- 1 | # generated from colcon_powershell/shell/template/hook_prepend_value.ps1.em 2 | 3 | colcon_prepend_unique_value PYTHONPATH "$env:COLCON_CURRENT_PREFIX\lib/python3.10/site-packages" 4 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/hook/pythonpath.sh: -------------------------------------------------------------------------------- 1 | # generated from colcon_core/shell/template/hook_prepend_value.sh.em 2 | 3 | _colcon_prepend_unique_value PYTHONPATH "$COLCON_CURRENT_PREFIX/lib/python3.10/site-packages" 4 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/package.bash: -------------------------------------------------------------------------------- 1 | # generated from colcon_bash/shell/template/package.bash.em 2 | 3 | # This script extends the environment for this package. 4 | 5 | # a bash script is able to determine its own path if necessary 6 | if [ -z "$COLCON_CURRENT_PREFIX" ]; then 7 | # the prefix is two levels up from the package specific share directory 8 | _colcon_package_bash_COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`/../.." > /dev/null && pwd)" 9 | else 10 | _colcon_package_bash_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" 11 | fi 12 | 13 | # function to source another script with conditional trace output 14 | # first argument: the path of the script 15 | # additional arguments: arguments to the script 16 | _colcon_package_bash_source_script() { 17 | if [ -f "$1" ]; then 18 | if [ -n "$COLCON_TRACE" ]; then 19 | echo "# . \"$1\"" 20 | fi 21 | . "$@" 22 | else 23 | echo "not found: \"$1\"" 1>&2 24 | fi 25 | } 26 | 27 | # source sh script of this package 28 | _colcon_package_bash_source_script "$_colcon_package_bash_COLCON_CURRENT_PREFIX/share/my_robot_navigation/package.sh" 29 | 30 | unset _colcon_package_bash_source_script 31 | unset _colcon_package_bash_COLCON_CURRENT_PREFIX 32 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/package.dsv: -------------------------------------------------------------------------------- 1 | source;share/my_robot_navigation/hook/pythonpath.ps1 2 | source;share/my_robot_navigation/hook/pythonpath.dsv 3 | source;share/my_robot_navigation/hook/pythonpath.sh 4 | source;share/my_robot_navigation/hook/ament_prefix_path.ps1 5 | source;share/my_robot_navigation/hook/ament_prefix_path.dsv 6 | source;share/my_robot_navigation/hook/ament_prefix_path.sh 7 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/package.ps1: -------------------------------------------------------------------------------- 1 | # generated from colcon_powershell/shell/template/package.ps1.em 2 | 3 | # function to append a value to a variable 4 | # which uses colons as separators 5 | # duplicates as well as leading separators are avoided 6 | # first argument: the name of the result variable 7 | # second argument: the value to be prepended 8 | function colcon_append_unique_value { 9 | param ( 10 | $_listname, 11 | $_value 12 | ) 13 | 14 | # get values from variable 15 | if (Test-Path Env:$_listname) { 16 | $_values=(Get-Item env:$_listname).Value 17 | } else { 18 | $_values="" 19 | } 20 | $_duplicate="" 21 | # start with no values 22 | $_all_values="" 23 | # iterate over existing values in the variable 24 | if ($_values) { 25 | $_values.Split(";") | ForEach { 26 | # not an empty string 27 | if ($_) { 28 | # not a duplicate of _value 29 | if ($_ -eq $_value) { 30 | $_duplicate="1" 31 | } 32 | if ($_all_values) { 33 | $_all_values="${_all_values};$_" 34 | } else { 35 | $_all_values="$_" 36 | } 37 | } 38 | } 39 | } 40 | # append only non-duplicates 41 | if (!$_duplicate) { 42 | # avoid leading separator 43 | if ($_all_values) { 44 | $_all_values="${_all_values};${_value}" 45 | } else { 46 | $_all_values="${_value}" 47 | } 48 | } 49 | 50 | # export the updated variable 51 | Set-Item env:\$_listname -Value "$_all_values" 52 | } 53 | 54 | # function to prepend a value to a variable 55 | # which uses colons as separators 56 | # duplicates as well as trailing separators are avoided 57 | # first argument: the name of the result variable 58 | # second argument: the value to be prepended 59 | function colcon_prepend_unique_value { 60 | param ( 61 | $_listname, 62 | $_value 63 | ) 64 | 65 | # get values from variable 66 | if (Test-Path Env:$_listname) { 67 | $_values=(Get-Item env:$_listname).Value 68 | } else { 69 | $_values="" 70 | } 71 | # start with the new value 72 | $_all_values="$_value" 73 | # iterate over existing values in the variable 74 | if ($_values) { 75 | $_values.Split(";") | ForEach { 76 | # not an empty string 77 | if ($_) { 78 | # not a duplicate of _value 79 | if ($_ -ne $_value) { 80 | # keep non-duplicate values 81 | $_all_values="${_all_values};$_" 82 | } 83 | } 84 | } 85 | } 86 | # export the updated variable 87 | Set-Item env:\$_listname -Value "$_all_values" 88 | } 89 | 90 | # function to source another script with conditional trace output 91 | # first argument: the path of the script 92 | # additional arguments: arguments to the script 93 | function colcon_package_source_powershell_script { 94 | param ( 95 | $_colcon_package_source_powershell_script 96 | ) 97 | # source script with conditional trace output 98 | if (Test-Path $_colcon_package_source_powershell_script) { 99 | if ($env:COLCON_TRACE) { 100 | echo ". '$_colcon_package_source_powershell_script'" 101 | } 102 | . "$_colcon_package_source_powershell_script" 103 | } else { 104 | Write-Error "not found: '$_colcon_package_source_powershell_script'" 105 | } 106 | } 107 | 108 | 109 | # a powershell script is able to determine its own path 110 | # the prefix is two levels up from the package specific share directory 111 | $env:COLCON_CURRENT_PREFIX=(Get-Item $PSCommandPath).Directory.Parent.Parent.FullName 112 | 113 | colcon_package_source_powershell_script "$env:COLCON_CURRENT_PREFIX\share/my_robot_navigation/hook/pythonpath.ps1" 114 | colcon_package_source_powershell_script "$env:COLCON_CURRENT_PREFIX\share/my_robot_navigation/hook/ament_prefix_path.ps1" 115 | 116 | Remove-Item Env:\COLCON_CURRENT_PREFIX 117 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/package.sh: -------------------------------------------------------------------------------- 1 | # generated from colcon_core/shell/template/package.sh.em 2 | 3 | # This script extends the environment for this package. 4 | 5 | # function to prepend a value to a variable 6 | # which uses colons as separators 7 | # duplicates as well as trailing separators are avoided 8 | # first argument: the name of the result variable 9 | # second argument: the value to be prepended 10 | _colcon_prepend_unique_value() { 11 | # arguments 12 | _listname="$1" 13 | _value="$2" 14 | 15 | # get values from variable 16 | eval _values=\"\$$_listname\" 17 | # backup the field separator 18 | _colcon_prepend_unique_value_IFS=$IFS 19 | IFS=":" 20 | # start with the new value 21 | _all_values="$_value" 22 | # workaround SH_WORD_SPLIT not being set in zsh 23 | if [ "$(command -v colcon_zsh_convert_to_array)" ]; then 24 | colcon_zsh_convert_to_array _values 25 | fi 26 | # iterate over existing values in the variable 27 | for _item in $_values; do 28 | # ignore empty strings 29 | if [ -z "$_item" ]; then 30 | continue 31 | fi 32 | # ignore duplicates of _value 33 | if [ "$_item" = "$_value" ]; then 34 | continue 35 | fi 36 | # keep non-duplicate values 37 | _all_values="$_all_values:$_item" 38 | done 39 | unset _item 40 | # restore the field separator 41 | IFS=$_colcon_prepend_unique_value_IFS 42 | unset _colcon_prepend_unique_value_IFS 43 | # export the updated variable 44 | eval export $_listname=\"$_all_values\" 45 | unset _all_values 46 | unset _values 47 | 48 | unset _value 49 | unset _listname 50 | } 51 | 52 | # since a plain shell script can't determine its own path when being sourced 53 | # either use the provided COLCON_CURRENT_PREFIX 54 | # or fall back to the build time prefix (if it exists) 55 | _colcon_package_sh_COLCON_CURRENT_PREFIX="/home/anson/ros2_ws/install/my_robot_navigation" 56 | if [ -z "$COLCON_CURRENT_PREFIX" ]; then 57 | if [ ! -d "$_colcon_package_sh_COLCON_CURRENT_PREFIX" ]; then 58 | echo "The build time path \"$_colcon_package_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2 59 | unset _colcon_package_sh_COLCON_CURRENT_PREFIX 60 | return 1 61 | fi 62 | COLCON_CURRENT_PREFIX="$_colcon_package_sh_COLCON_CURRENT_PREFIX" 63 | fi 64 | unset _colcon_package_sh_COLCON_CURRENT_PREFIX 65 | 66 | # function to source another script with conditional trace output 67 | # first argument: the path of the script 68 | # additional arguments: arguments to the script 69 | _colcon_package_sh_source_script() { 70 | if [ -f "$1" ]; then 71 | if [ -n "$COLCON_TRACE" ]; then 72 | echo "# . \"$1\"" 73 | fi 74 | . "$@" 75 | else 76 | echo "not found: \"$1\"" 1>&2 77 | fi 78 | } 79 | 80 | # source sh hooks 81 | _colcon_package_sh_source_script "$COLCON_CURRENT_PREFIX/share/my_robot_navigation/hook/pythonpath.sh" 82 | _colcon_package_sh_source_script "$COLCON_CURRENT_PREFIX/share/my_robot_navigation/hook/ament_prefix_path.sh" 83 | 84 | unset _colcon_package_sh_source_script 85 | unset COLCON_CURRENT_PREFIX 86 | 87 | # do not unset _colcon_prepend_unique_value since it might be used by non-primary shell hooks 88 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | my_robot_navigation 5 | 0.0.0 6 | TODO: Package description 7 | anson 8 | TODO: License declaration 9 | 10 | rclcpp 11 | sensor_msgs 12 | geometry_msgs 13 | 14 | ament_copyright 15 | ament_flake8 16 | ament_pep257 17 | python3-pytest 18 | 19 | 20 | ament_python 21 | 22 | 23 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/package.zsh: -------------------------------------------------------------------------------- 1 | # generated from colcon_zsh/shell/template/package.zsh.em 2 | 3 | # This script extends the environment for this package. 4 | 5 | # a zsh script is able to determine its own path if necessary 6 | if [ -z "$COLCON_CURRENT_PREFIX" ]; then 7 | # the prefix is two levels up from the package specific share directory 8 | _colcon_package_zsh_COLCON_CURRENT_PREFIX="$(builtin cd -q "`dirname "${(%):-%N}"`/../.." > /dev/null && pwd)" 9 | else 10 | _colcon_package_zsh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" 11 | fi 12 | 13 | # function to source another script with conditional trace output 14 | # first argument: the path of the script 15 | # additional arguments: arguments to the script 16 | _colcon_package_zsh_source_script() { 17 | if [ -f "$1" ]; then 18 | if [ -n "$COLCON_TRACE" ]; then 19 | echo "# . \"$1\"" 20 | fi 21 | . "$@" 22 | else 23 | echo "not found: \"$1\"" 1>&2 24 | fi 25 | } 26 | 27 | # function to convert array-like strings into arrays 28 | # to workaround SH_WORD_SPLIT not being set 29 | colcon_zsh_convert_to_array() { 30 | local _listname=$1 31 | local _dollar="$" 32 | local _split="{=" 33 | local _to_array="(\"$_dollar$_split$_listname}\")" 34 | eval $_listname=$_to_array 35 | } 36 | 37 | # source sh script of this package 38 | _colcon_package_zsh_source_script "$_colcon_package_zsh_COLCON_CURRENT_PREFIX/share/my_robot_navigation/package.sh" 39 | unset convert_zsh_to_array 40 | 41 | unset _colcon_package_zsh_source_script 42 | unset _colcon_package_zsh_COLCON_CURRENT_PREFIX 43 | -------------------------------------------------------------------------------- /install/my_robot_navigation/share/my_robot_navigation/robot_navigation.launch.py: -------------------------------------------------------------------------------- 1 | from launch import LaunchDescription 2 | from launch_ros.actions import Node 3 | from launch.actions import ExecuteProcess 4 | from launch.substitutions import LaunchConfiguration 5 | from launch.actions import SetEnvironmentVariable 6 | 7 | def generate_launch_description(): 8 | return LaunchDescription([ 9 | # Set the TURTLEBOT3_MODEL environment variable 10 | SetEnvironmentVariable('TURTLEBOT3_MODEL', 'burger'), 11 | # Launch Gazebo with TurtleBot3 12 | ExecuteProcess( 13 | cmd=['ros2', 'launch', 'turtlebot3_gazebo', 'turtlebot3_world.launch.py'], 14 | output='screen' 15 | ), 16 | # Launch the obstacle avoidance node 17 | Node( 18 | package='my_robot_navigation', 19 | executable='obstacle_avoidance', 20 | name='obstacle_avoidance_node', 21 | output='screen' 22 | ) 23 | ]) 24 | -------------------------------------------------------------------------------- /install/setup.bash: -------------------------------------------------------------------------------- 1 | # generated from colcon_bash/shell/template/prefix_chain.bash.em 2 | 3 | # This script extends the environment with the environment of other prefix 4 | # paths which were sourced when this file was generated as well as all packages 5 | # contained in this prefix path. 6 | 7 | # function to source another script with conditional trace output 8 | # first argument: the path of the script 9 | _colcon_prefix_chain_bash_source_script() { 10 | if [ -f "$1" ]; then 11 | if [ -n "$COLCON_TRACE" ]; then 12 | echo "# . \"$1\"" 13 | fi 14 | . "$1" 15 | else 16 | echo "not found: \"$1\"" 1>&2 17 | fi 18 | } 19 | 20 | # source chained prefixes 21 | # setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script 22 | COLCON_CURRENT_PREFIX="/opt/ros/humble" 23 | _colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash" 24 | 25 | # source this prefix 26 | # setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script 27 | COLCON_CURRENT_PREFIX="$(builtin cd "`dirname "${BASH_SOURCE[0]}"`" > /dev/null && pwd)" 28 | _colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash" 29 | 30 | unset COLCON_CURRENT_PREFIX 31 | unset _colcon_prefix_chain_bash_source_script 32 | -------------------------------------------------------------------------------- /install/setup.ps1: -------------------------------------------------------------------------------- 1 | # generated from colcon_powershell/shell/template/prefix_chain.ps1.em 2 | 3 | # This script extends the environment with the environment of other prefix 4 | # paths which were sourced when this file was generated as well as all packages 5 | # contained in this prefix path. 6 | 7 | # function to source another script with conditional trace output 8 | # first argument: the path of the script 9 | function _colcon_prefix_chain_powershell_source_script { 10 | param ( 11 | $_colcon_prefix_chain_powershell_source_script_param 12 | ) 13 | # source script with conditional trace output 14 | if (Test-Path $_colcon_prefix_chain_powershell_source_script_param) { 15 | if ($env:COLCON_TRACE) { 16 | echo ". '$_colcon_prefix_chain_powershell_source_script_param'" 17 | } 18 | . "$_colcon_prefix_chain_powershell_source_script_param" 19 | } else { 20 | Write-Error "not found: '$_colcon_prefix_chain_powershell_source_script_param'" 21 | } 22 | } 23 | 24 | # source chained prefixes 25 | _colcon_prefix_chain_powershell_source_script "/opt/ros/humble\local_setup.ps1" 26 | 27 | # source this prefix 28 | $env:COLCON_CURRENT_PREFIX=(Split-Path $PSCommandPath -Parent) 29 | _colcon_prefix_chain_powershell_source_script "$env:COLCON_CURRENT_PREFIX\local_setup.ps1" 30 | -------------------------------------------------------------------------------- /install/setup.sh: -------------------------------------------------------------------------------- 1 | # generated from colcon_core/shell/template/prefix_chain.sh.em 2 | 3 | # This script extends the environment with the environment of other prefix 4 | # paths which were sourced when this file was generated as well as all packages 5 | # contained in this prefix path. 6 | 7 | # since a plain shell script can't determine its own path when being sourced 8 | # either use the provided COLCON_CURRENT_PREFIX 9 | # or fall back to the build time prefix (if it exists) 10 | _colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX=/home/anson/ros2_ws/install 11 | if [ ! -z "$COLCON_CURRENT_PREFIX" ]; then 12 | _colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX="$COLCON_CURRENT_PREFIX" 13 | elif [ ! -d "$_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX" ]; then 14 | echo "The build time path \"$_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX\" doesn't exist. Either source a script for a different shell or set the environment variable \"COLCON_CURRENT_PREFIX\" explicitly." 1>&2 15 | unset _colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX 16 | return 1 17 | fi 18 | 19 | # function to source another script with conditional trace output 20 | # first argument: the path of the script 21 | _colcon_prefix_chain_sh_source_script() { 22 | if [ -f "$1" ]; then 23 | if [ -n "$COLCON_TRACE" ]; then 24 | echo "# . \"$1\"" 25 | fi 26 | . "$1" 27 | else 28 | echo "not found: \"$1\"" 1>&2 29 | fi 30 | } 31 | 32 | # source chained prefixes 33 | # setting COLCON_CURRENT_PREFIX avoids relying on the build time prefix of the sourced script 34 | COLCON_CURRENT_PREFIX="/opt/ros/humble" 35 | _colcon_prefix_chain_sh_source_script "$COLCON_CURRENT_PREFIX/local_setup.sh" 36 | 37 | 38 | # source this prefix 39 | # setting COLCON_CURRENT_PREFIX avoids relying on the build time prefix of the sourced script 40 | COLCON_CURRENT_PREFIX="$_colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX" 41 | _colcon_prefix_chain_sh_source_script "$COLCON_CURRENT_PREFIX/local_setup.sh" 42 | 43 | unset _colcon_prefix_chain_sh_COLCON_CURRENT_PREFIX 44 | unset _colcon_prefix_chain_sh_source_script 45 | unset COLCON_CURRENT_PREFIX 46 | -------------------------------------------------------------------------------- /install/setup.zsh: -------------------------------------------------------------------------------- 1 | # generated from colcon_zsh/shell/template/prefix_chain.zsh.em 2 | 3 | # This script extends the environment with the environment of other prefix 4 | # paths which were sourced when this file was generated as well as all packages 5 | # contained in this prefix path. 6 | 7 | # function to source another script with conditional trace output 8 | # first argument: the path of the script 9 | _colcon_prefix_chain_zsh_source_script() { 10 | if [ -f "$1" ]; then 11 | if [ -n "$COLCON_TRACE" ]; then 12 | echo "# . \"$1\"" 13 | fi 14 | . "$1" 15 | else 16 | echo "not found: \"$1\"" 1>&2 17 | fi 18 | } 19 | 20 | # source chained prefixes 21 | # setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script 22 | COLCON_CURRENT_PREFIX="/opt/ros/humble" 23 | _colcon_prefix_chain_zsh_source_script "$COLCON_CURRENT_PREFIX/local_setup.zsh" 24 | 25 | # source this prefix 26 | # setting COLCON_CURRENT_PREFIX avoids determining the prefix in the sourced script 27 | COLCON_CURRENT_PREFIX="$(builtin cd -q "`dirname "${(%):-%N}"`" > /dev/null && pwd)" 28 | _colcon_prefix_chain_zsh_source_script "$COLCON_CURRENT_PREFIX/local_setup.zsh" 29 | 30 | unset COLCON_CURRENT_PREFIX 31 | unset _colcon_prefix_chain_zsh_source_script 32 | -------------------------------------------------------------------------------- /src/my_robot_navigation/launch/robot_navigation.launch.py: -------------------------------------------------------------------------------- 1 | from launch import LaunchDescription 2 | from launch_ros.actions import Node 3 | from launch.actions import ExecuteProcess 4 | from launch.substitutions import LaunchConfiguration 5 | from launch.actions import SetEnvironmentVariable 6 | 7 | def generate_launch_description(): 8 | return LaunchDescription([ 9 | # Set the TURTLEBOT3_MODEL environment variable 10 | SetEnvironmentVariable('TURTLEBOT3_MODEL', 'burger'), 11 | # Launch Gazebo with TurtleBot3 12 | ExecuteProcess( 13 | cmd=['ros2', 'launch', 'turtlebot3_gazebo', 'turtlebot3_world.launch.py'], 14 | output='screen' 15 | ), 16 | # Launch the obstacle avoidance node 17 | Node( 18 | package='my_robot_navigation', 19 | executable='obstacle_avoidance', 20 | name='obstacle_avoidance_node', 21 | output='screen' 22 | ) 23 | ]) 24 | -------------------------------------------------------------------------------- /src/my_robot_navigation/my_robot_navigation/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/src/my_robot_navigation/my_robot_navigation/__init__.py -------------------------------------------------------------------------------- /src/my_robot_navigation/my_robot_navigation/obstacle_avoidance.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import rclpy 4 | from rclpy.node import Node 5 | from sensor_msgs.msg import LaserScan 6 | from geometry_msgs.msg import Twist 7 | 8 | class ObstacleAvoidanceNode(Node): 9 | def __init__(self): 10 | super().__init__('obstacle_avoidance_node') 11 | # Subscriber to LIDAR data 12 | self.subscription = self.create_subscription( 13 | LaserScan, 14 | '/scan', # TurtleBot3 LIDAR topic 15 | self.lidar_callback, 16 | 10 17 | ) 18 | # Publisher for velocity commands 19 | self.publisher = self.create_publisher( 20 | Twist, 21 | '/cmd_vel', # TurtleBot3 velocity topic 22 | 10 23 | ) 24 | self.get_logger().info("Obstacle Avoidance Node Started") 25 | 26 | def lidar_callback(self, msg): 27 | # Process LIDAR data (360 degrees, 0 = front, 180 = rear) 28 | ranges = msg.ranges 29 | min_distance = min(ranges[0:60] + ranges[300:360]) # Check front 120 degrees 30 | twist = Twist() 31 | 32 | if min_distance < 0.5: # If obstacle closer than 0.5m 33 | twist.linear.x = 0.0 34 | twist.angular.z = 0.5 # Turn right 35 | self.get_logger().info(f"Obstacle detected {min_distance:.2f}m ahead, turning!") 36 | else: 37 | twist.linear.x = 0.2 # Move forward 38 | twist.angular.z = 0.0 39 | self.get_logger().info("Path clear, moving forward") 40 | 41 | self.publisher.publish(twist) 42 | 43 | def main(args=None): 44 | rclpy.init(args=args) 45 | node = ObstacleAvoidanceNode() 46 | try: 47 | rclpy.spin(node) 48 | except KeyboardInterrupt: 49 | node.get_logger().info("Shutting down") 50 | finally: 51 | node.destroy_node() 52 | rclpy.shutdown() 53 | 54 | if __name__ == '__main__': 55 | main() 56 | -------------------------------------------------------------------------------- /src/my_robot_navigation/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | my_robot_navigation 5 | 0.0.0 6 | TODO: Package description 7 | anson 8 | TODO: License declaration 9 | 10 | rclcpp 11 | sensor_msgs 12 | geometry_msgs 13 | 14 | ament_copyright 15 | ament_flake8 16 | ament_pep257 17 | python3-pytest 18 | 19 | 20 | ament_python 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/my_robot_navigation/resource/my_robot_navigation: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anson10/Autonomous-Robot-Navigation-and-Obstacle-Avoidance-Using-ROS2-and-Gazebo/1734feacfc11f11ac6af202adf24a75c49924c93/src/my_robot_navigation/resource/my_robot_navigation -------------------------------------------------------------------------------- /src/my_robot_navigation/setup.cfg: -------------------------------------------------------------------------------- 1 | [develop] 2 | script_dir=$base/lib/my_robot_navigation 3 | [install] 4 | install_scripts=$base/lib/my_robot_navigation 5 | -------------------------------------------------------------------------------- /src/my_robot_navigation/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | import os 3 | from glob import glob 4 | 5 | package_name = 'my_robot_navigation' 6 | 7 | setup( 8 | name=package_name, 9 | version='0.0.0', 10 | packages=[package_name], 11 | data_files=[ 12 | ('share/ament_index/resource_index/packages', ['resource/' + package_name]), 13 | ('share/' + package_name, ['package.xml']), 14 | (os.path.join('share', package_name), glob('launch/*.launch.py')), 15 | ], 16 | install_requires=['setuptools'], 17 | zip_safe=True, 18 | maintainer='anson', 19 | maintainer_email='sansonmsa@gmail.com', 20 | description='Obstacle avoidance robot with ROS2 and Gazebo', 21 | license='Apache License 2.0', 22 | tests_require=['pytest'], 23 | entry_points={ 24 | 'console_scripts': [ 25 | 'obstacle_avoidance = my_robot_navigation.obstacle_avoidance:main', 26 | ], 27 | }, 28 | ) 29 | -------------------------------------------------------------------------------- /src/my_robot_navigation/test/test_copyright.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ament_copyright.main import main 16 | import pytest 17 | 18 | 19 | # Remove the `skip` decorator once the source file(s) have a copyright header 20 | @pytest.mark.skip(reason='No copyright header has been placed in the generated source file.') 21 | @pytest.mark.copyright 22 | @pytest.mark.linter 23 | def test_copyright(): 24 | rc = main(argv=['.', 'test']) 25 | assert rc == 0, 'Found errors' 26 | -------------------------------------------------------------------------------- /src/my_robot_navigation/test/test_flake8.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ament_flake8.main import main_with_errors 16 | import pytest 17 | 18 | 19 | @pytest.mark.flake8 20 | @pytest.mark.linter 21 | def test_flake8(): 22 | rc, errors = main_with_errors(argv=[]) 23 | assert rc == 0, \ 24 | 'Found %d code style errors / warnings:\n' % len(errors) + \ 25 | '\n'.join(errors) 26 | -------------------------------------------------------------------------------- /src/my_robot_navigation/test/test_pep257.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Open Source Robotics Foundation, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from ament_pep257.main import main 16 | import pytest 17 | 18 | 19 | @pytest.mark.linter 20 | @pytest.mark.pep257 21 | def test_pep257(): 22 | rc = main(argv=['.', 'test']) 23 | assert rc == 0, 'Found code style errors / warnings' 24 | --------------------------------------------------------------------------------