ROS2 Webots For WSL
==================
> [name=Wei-Wen Wu] [time=2024 01 31]
[toc]
Install
-------
### ROS2
> **Reference**:
> [Ubuntu (Debian packages)](https://docs.ros.org/en/humble/Installation/Ubuntu-Install-Debians.html "Ubuntu (Debian packages)")
> [name=Open Robotics][time=2024]
### webots_ros2
```sh
sudo apt-get install ros-humble-webots-ros2
```
> **Reference**
> [Install webots_ros2](https://docs.ros.org/en/humble/Tutorials/Advanced/Simulators/Webots/Installation-Windows.html#install-webots-ros2 "安裝 webots_ros2")
> [name=ROS 2 Documentation]
:::warning
:no_entry_sign: **No need to install Webots.**
```bash
# You can install the Cyberbotics.asc signature file using this command.
sudo mkdir -p /etc/apt/keyrings
cd /etc/apt/keyrings
sudo wget -q https://cyberbotics.com/Cyberbotics.asc
# Then, you can configure your APT package manager by adding the Cyberbotics repository. Simply execute the following lines.
echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/Cyberbotics.asc] https://cyberbotics.com/debian binary-amd64/" | sudo tee /etc/apt/sources.list.d/Cyberbotics.list
sudo apt update
# Then proceed to the installation of Webots using.
sudo apt install webots
```
> **Reference**
> [Install Webots](https://cyberbotics.com/doc/guide/installation-procedure "安裝 Webots")
> [name=Cyberbotics(https://cyberbotics.com/#cyberbotics)]
:::
Use ROS2
--------
```bash
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws
git clone https://github.com/ros2/examples src/examples -b humble
colcon build --symlink-install
colcon test
source install/setup.bash
```
Best practice is to check for dependencies every time you clone.
```bash
# cd if you're still in the ``src`` directory with the ``ros_tutorials`` clone
cd ..
rosdep install -i --from-path src --rosdistro humble -y
```
If you already have all your dependencies, the console will return:
```!
#All required rosdeps installed successfully
```
> **Reference**
> [Using `colcon` to build packages](https://docs.ros.org/en/humble/Tutorials/Beginner-Client-Libraries/Colcon-Tutorial.html "用於 colcon 構建套件")
> [name=ROS 2 Documentation]
Use Webots
----------
Create the package structure.
```bash
ros2 pkg create --build-type ament_python --license Apache-2.0 --node-name my_robot_driver my_package --dependencies rclpy geometry_msgs webots_ros2_driver
```
> **Reference**: [Setting up a robot simulation (Basic)](https://docs.ros.org/en/humble/Tutorials/Advanced/Simulators/Webots/Setting-Up-Simulation-Webots-Basic.html "設定機器人模擬(基本)")
>
The list of all existing interfaces and the corresponding parameters can be found [**`on the devices reference page`**][References Devices]. For available devices that are not configured in the URDF file, the interface will be automatically created and default values will be used for ROS parameters (e.g. update rate, topic name, and frame name).
[References Devices]: https://github.com/cyberbotics/webots_ros2/wiki/References-Devices "參考資料設備"
:::spoiler Python files to start and install
* launch file
```python=
import os
import pathlib
import launch
from launch_ros.actions import Node
from launch import LaunchDescription
from ament_index_python.packages import get_package_share_directory
from webots_ros2_driver.webots_launcher import WebotsLauncher, Ros2SupervisorLauncher
def generate_launch_description():
package_dir = get_package_share_directory('my_package')
robot_description = pathlib.Path(os.path.join(package_dir, 'resource', 'my_robot.urdf')).read_text()
webots = WebotsLauncher(
world=os.path.join(package_dir, 'worlds', 'my_world.wbt')
)
ros2_supervisor = Ros2SupervisorLauncher()
my_robot_driver = Node(
package='webots_ros2_driver',
executable='driver',
output='screen',
additional_env={'WEBOTS_CONTROLLER_URL': 'my_robot'},
parameters=[
{'robot_description': robot_description},
]
)
return LaunchDescription([
webots,
my_robot_driver,
ros2_supervisor,
launch.actions.RegisterEventHandler(
event_handler=launch.event_handlers.OnProcessExit(
target_action=webots,
on_exit=[launch.actions.EmitEvent(event=launch.events.Shutdown())],
)
)
])
```
* setup.py
```python=
from setuptools import setup
package_name = 'my_package'
data_files = []
data_files.append(('share/ament_index/resource_index/packages', ['resource/' + package_name]))
data_files.append(('share/' + package_name + '/launch', ['launch/robot_launch.py']))
data_files.append(('share/' + package_name + '/worlds', ['worlds/my_world.wbt']))
data_files.append(('share/' + package_name + '/resource', ['resource/my_robot.urdf']))
data_files.append(('share/' + package_name, ['package.xml']))
setup(
name=package_name,
version='0.0.0',
packages=[package_name],
data_files=data_files,
install_requires=['setuptools'],
zip_safe=True,
maintainer='user',
maintainer_email='user.name@mail.com',
description='TODO: Package description',
license='TODO: License declaration',
tests_require=['pytest'],
entry_points={
'console_scripts': [
'my_robot_driver = my_package.my_robot_driver:main',
],
},
)
```
:::
Folder structure.
-----------------
```graphviz
digraph hierarchy {
nodesep=0.2 // increases the separation between nodes
node [color=Red,fontname=Courier,shape=box] //All nodes will this shape and colour
edge [color=Blue, style=dashed] //All the lines look like this
ros2_ws->{build install log src}
install->"local_setup.bash"
src->{examples my_package}
my_package->{launch my_package resource test worlds "LICENSE" "package.xml" "setup.cfg" "setup.py"}
launch->"robot_launch.py"
resource->"my_robot.urdf"
worlds->"my_world.wbt"
}
```
Example
-------
```bash
source /opt/ros/humble/setup.bash # <humble> is the version of ROS, please modify it according to your own situation.
source install/local_setup.bash # Execute in the '~/ros2_ws' directory, <ros2_ws> is the workspace of ros2.
ros2 launch my_package robot_launch.py
```
:::warning
:no_entry_sign: No need to execute.
```bash
export WEBOTS_HOME=/usr/local/webots
export WEBOTS_HOME=/mnt/c/Program\ Files/Webots # C:\Program Files\Webots
sudo snap install webots # [1]
export WEBOTS_HOME=/snap/webots/current/usr/share/webots # [2]
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/snap/webots/25/usr/share/webots/lib/controller
```
> [1] => [Webots linux snap package](https://snapcraft.io/webots "Webots linux snap 包")
> [2] => [Installation Procedure (Installing the Snap Package)](https://cyberbotics.com/doc/guide/installation-procedure#installing-the-snap-package "Webots安裝流程(安裝Snap包)")
:::
After execution, it will be displayed:
```!
Do you want Webots R2023b to be automatically installed in C:\Program Files\Webots ([Y]es/[N]o)?:
```
Then press ==y== to install Webots.
:::info
:information_source: Note: It is recommended to uninstall Windows webots first.
:::
Commands
--------
```bash
### Common ###
echo $PYTHONPATH # print <environment variables>.[1]
sudo rm -rf <dir> # Forcefully delete a folder.
### ROS2 build [2] ###
colcon build # Compile Package.
colcon build --packages-select <package1> <package2> ... # Install a specific package.
colcon build --symlink-install # No need to recompile.
### RQT [3] ###
# It can be used to display ROS information and control the operation of ROS.
sudo apt install ros-humble-rqt-* # Install RQT.
ros2 run rqt_graph rqt_graph # Display the current topic-node relationship diagram.
ros2 run rqt_gui rqt_gui # Enable more plugins.
```
> **Reference**:
> `[1]`[ROS2 front-end basic tutorial | Python dependency search process](https://blog.csdn.net/qq_27865227/article/details/125349984 "ROS2前端基礎教學| Python依賴搜尋過程")
> [name=小魚]
> `[2]` [ROS2 Workspace](https://ithelp.ithome.com.tw/articles/10318186 "Day3 ROS2 Workspace")
> [name=leochien1110(https://ithelp.ithome.com.tw/users/20135014/ironman)]
> `[3]` [ROS2 RQT tools](https://ithelp.ithome.com.tw/articles/10337960 "Day26 ROS2 RQT工具")
> [name=leochien1110(https://ithelp.ithome.com.tw/users/20135014/ironman)]
ROS2 Tutorial
============
| 通訊方式 | 描述 | 方向 | 同步 | 應用範例 |
|:--------:|:---------------------------------------------:|:----:|:------:|:----------------------------:|
| topic | 不間斷傳送資料對接收端 | 單向 | 非同步 | 機器人現在狀態、感測器資料 |
| service | 發送 request 並等待處理結果 | 雙向 | 同步 | 觸發狀態變化、事件 |
| action | 發送 request 但不等待,可以得知或中斷處理過程 | 雙向 | 非同步 | 命令機器人導航、機器手臂移動 |
> **Reference**: [ROS2 communication method](https://hackmd.io/@evshary/ROS2Note/https%3A%2F%2Fhackmd.io%2F%40evshary%2FROS2comm#%E7%B8%BD%E7%B5%90 "ROS2 通訊方式總結")
Node
----
[![Nodes][Nodes-TopicandService.gif]][Nodes-TopicandService.gif]
**Example**
```python=
import rclpy
def main(args=None):
rclpy.init(args=args) # 初始化ROS
# 創建一個叫做hello_world_py的Node
node = rclpy.create_node('hello_world_py') # def create_node(node_name: str,...):...
# 用Node的get_logger() function來print出Hello World!
node.get_logger().info('Hello World!')
# 指定rate為1Hz
rate = node.create_rate(1)
# 讓Node持續運行直到ROS關閉(Ctrl+C)
while rclpy.ok():
node.get_logger().info('Hello World in Loop!')
rclpy.spin_once(node) # 讓Node執行一次,會等待1/rate秒
# 關閉ROS
rclpy.shutdown()
```
> **Reference**: [ROS2 Node Python](https://ithelp.ithome.com.tw/articles/10318225 "Day6 - ROS2 Node Python")
Topic
-----
[![Topic][Topic-MultiplePublisherandMultipleSubscriber.gif]][Topic-MultiplePublisherandMultipleSubscriber.gif]
**Publisher**
```python=
# publisher_member_function.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalPublisher(Node):
def __init__(self):
super().__init__('minimal_publisher')
self.publisher_ = self.create_publisher(String, 'topic', 10) # def create_publisher(msg_type: Any, topic: str, qos_profile: QoSProfile | int,...) -> Publisher
# 這邊的Timer是用來定時發布訊息的,第一個參數是時間間隔,這邊是0.5秒發布一次訊息(2Hz)。第二個參數是Timer的callback function,每次Timer觸發時,就會執行。
timer_period = 0.5 # seconds
self.timer = self.create_timer(timer_period, self.timer_callback)
self.i = 0
def timer_callback(self):
msg = String()
msg.data = 'Hello World: %d' % self.i
self.publisher_.publish(msg)
self.get_logger().info('Publishing: "%s"' % msg.data)
self.i += 1
def main(args=None):
rclpy.init(args=args)
minimal_publisher = MinimalPublisher()
rclpy.spin(minimal_publisher)
# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_publisher.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
```
**Subscriber**
```python=
# subscriber_member_function.py
import rclpy
from rclpy.node import Node
from std_msgs.msg import String
class MinimalSubscriber(Node):
def __init__(self):
super().__init__('minimal_subscriber')
self.subscription = self.create_subscription(
String,
'topic',
self.listener_callback,
10)
self.subscription # prevent unused variable warning
def listener_callback(self, msg):
self.get_logger().info('I heard: "%s"' % msg.data)
def main(args=None):
rclpy.init(args=args)
minimal_subscriber = MinimalSubscriber()
rclpy.spin(minimal_subscriber)
# Destroy the node explicitly
# (optional - otherwise it will be done automatically
# when the garbage collector destroys the node object)
minimal_subscriber.destroy_node()
rclpy.shutdown()
if __name__ == '__main__':
main()
```
**Command**
```bash
# https://hackmd.io/@evshary/ROS2Note/https%3A%2F%2Fhackmd.io%2F%40evshary%2FROS2command#ROS-2-topic
ros2 topic list -t
ros2 topic echo /cmd_vel
ros2 topic pub -1 /interface std_msgs/msg/String "data: 123"
```
> **Reference**: [ROS2 Topic](https://ithelp.ithome.com.tw/articles/10323283 "Day8 ROS2 Topic")、[ROS2 Publisher & Subscriber - Python](https://ithelp.ithome.com.tw/articles/10318221 "Day9 ROS2 Publisher & Subscriber - Python")、[Writing a simple publisher and subscriber (Python)](https://docs.ros.org/en/foxy/Tutorials/Beginner-Client-Libraries/Writing-A-Simple-Py-Publisher-And-Subscriber.html "編寫一個簡單的Publisher和Subscriber (Python)")
Service
-------
[![Service][Service-SingleServiceClient.gif]][Service-SingleServiceClient.gif]
Action
------
[![Action][Action-SingleActionClient.gif]][Action-SingleActionClient.gif]
<!--Reference-->
[Nodes-TopicandService.gif]: https://docs.ros.org/en/foxy/_images/Nodes-TopicandService.gif "Nodes-TopicandService.gif"
[Topic-MultiplePublisherandMultipleSubscriber.gif]: https://docs.ros.org/en/foxy/_images/Topic-MultiplePublisherandMultipleSubscriber.gif "Topic-MultiplePublisherandMultipleSubscriber.gif"
[Service-SingleServiceClient.gif]: https://docs.ros.org/en/foxy/_images/Service-SingleServiceClient.gif "Service-SingleServiceClient.gif"
[Action-SingleActionClient.gif]: https://docs.ros.org/en/foxy/_images/Action-SingleActionClient.gif "Action-SingleActionClient.gif"
---
> **Reference**
> [HackMD Function Introduction](https://hackmd.io/features-tw?both "Hackmd功能介紹")
> [name=hackmd(https://hackmd.io/c/tutorials-tw/%2Fs%2Ffeatures-tw)]
###### tags: `Webots` `ROS2` `WSL`
*Copyright © 2024 Wei-Wen Wu*
*[ROS2]: Robot Operating System