Làm sao để mô phỏng nhiều robot sử dụng Nav2 trong không gian Gazebo với ROS2 Humble (Phần 2) – Vietnamese ROS2 Tutorial

Làm sao để mô phỏng nhiều robot sử dụng Nav2 trong không gian Gazebo với ROS2 Humble (Phần 2) – Vietnamese ROS2 Tutorial

This tutorial is created by Robotics Ambassador Eric



Robotics Ambassador: theconstruct.ai/robotics-ambassador/
 
Những điều bạn sẽ được biết trong blog này:
  1. Cách khai báo tọa độ cho robot để di chuyển trong không gian Gazebo và RViz
  2. Cách tạo câu lệnh để điều khiển robot di chuyển với gói Nav2
  3. Cách sử dụng Nav2 trên RViz để tạo đường đi cho robot

Phần 1: https://www.theconstruct.ai/lam-sao-de-mo-phong-nhieu-robot-su-dung-nav2-trong-khong-gian-gazebo-voi-ros2-humble-phan-1-ros2-vietnamese-tutorial/

Danh sách nguồn tài liệu bổ trợ trong post này

  1. Sử dụng rosject: **https://app.theconstructsim.com/rosjects/750453**
  2. The Construct**: https://app.theconstructsim.com/**
  3. Khóa học về ROS2 →
    1. Introduction to Gazebo Sim with ROS2: https://app.theconstructsim.com/courses/introduction-to-gazebo-ignition-with-ros2-170/
    2. ROS2 Navigation: https://app.theconstruct.ai/courses/148

Tổng quan

Ở phần 1 của blog “Làm sao để mô phỏng nhiều robots trên Gazebo với Nav2” chúng ta đã tạo thành công gọi được robots trên môi trường Gazebo và Rviz. Tiếp theo, ở phần 2 chúng ta sẽ được làm quen với gói Navigation ROS2. Có thể mọi người đã quen với việc sử dụng Nav2 với một robot nhưng với nhiều robots thì sẽ sử dụng như thế nào. Vì vậy, blog này sẽ giới thiệu với các bạn 2 cách để điều khiển robot với câu lệnh và click chuột.

Khởi động rosject

Như mọi lần rosject sẽ là công cụ hoàn hảo cho việc thực hiện project này, nơi các bạn dễ dàng truy cập và khởi tạo các project cùng với ROS ở các phiên bản khác nhau:

**https://app.theconstructsim.com/rosjects/750453**

Như hình bên dưới là một rosject hoàn chỉnh, bạn chỉ việc nhấn vào nút RUN để khởi động rosject.

Sau khi nhấn vào nút RUN bạn sẽ thấy rosject được chạy trên màn hình. Ở đây, rosject cung cấp cho bạn cả terminal để có thể nhập các câu lệnh:


https://flic.kr/p/2pGt7Ge

Bây giờ cùng nhau đến bước tiếp theo trong ví dụ này nhé.

Khởi động và chạy mô phỏng Gazebo

Sau khi thực hiện theo hướng như phần 1, sau đây các bạn chỉ cần chạy launch file vào môi trường Gazebo và Rviz bằng câu lệnh sau. Bên cạnh đó, cửa sổ RViz có thể bật tắt bằng cách sử dụng chế độ enable_rviz:

ros2 launch turtlebot3_multi_robot gazebo_multi_nav2_world.launch.py enable_drive:=True

Ở đây các bạn cần chú ý cho mình chức năng enable_drive:=True . Khi các bạn chọn True đồng nghĩa với việc các bạn sẽ điều khiển tất cả các robots cùng một lúc ví dụ như hình ở dưới. Các robot sẽ tự động di chuyển đến đích theo vị trí các bạn chọn sẵn. Cho nên khi các bạn muốn điều khiển một robot nào trong số các robot có trong môi trường. Các bạn cần phải lựa chọn enable_drive:=False

https://flic.kr/p/2pMW58B
https://flic.kr/p/2pMQtHy

Cách di chuyển robot với Nav2 Goal trên Rviz

Nếu các bạn đã quen thuộc với Rviz thì các bạn sẽ thấy rằng Rviz cũng cấp cho chúng ta rất nhiều công cụ bổ ích trong đó bao gồm cả Navigation. Lệnh khởi chạy (launch) cài đặt pose ban đầu bằng việc sử dụng topic /initialpose. Bên dưới thanh công cụ của Rviz các bạn sẽ thấy kí hiệu mũi tên màu xanh với tên gọi là Nav2 Goal.

https://flic.kr/p/2pMWNNc

Khi các bạn click vào nút này, các bạn có thể lựa chọn bất kỳ điểm nào trên môi trường RViz bao gồm cả hướng của điểm đến. Với lựa chọn này Nav2 package sẽ giúp bạn tạo ra đường đi phù hợp cho robot. Bạn cũng có thể thay thế Nav2 package bằng một thuật toán xây dựng đường đi cho robot như RRT*, A*,… phù hợp với project của bạn. Sau khi thành công chọn điểm, đường đi của robot sẽ xuất hiện như đường chỉ màu hồng ở hình bên dưới đây.

https://flic.kr/p/2pMQtHo
https://flic.kr/p/2pMW58G
 

Cách tạo command line để di chuyển robot với Nav2

Bên cạnh đó, mình sẽ giới thiệu cho bạn một lựa chọn khác đó là thiết lập điểm xuất phát và điểm đến bằng câu lệnh thay vì chọn rviz. Với điểm khởi đầu bạn có thể khởi tạo bằng câu lệnh:

ros2 topic pub -1 --qos-reliability reliable /tb2/initialpose geometry_msgs/PoseWithCovarianceStamped "{header: {frame_id: map}, pose: {pose: {position: {x: -1.5, y: 0.5, z: 0.0}, orientation: {x: 0.0, y: 0.0, z: 1.01, w: 1.0}}, }}"

Những điều bạn cần lưu ý khi khởi tạo câu lệnh cho vị trí bắt đầu. Đầu tiên các bạn cần chọn robot nào để di chuyển, ở đây mình chọn robot 2 tương ứng với topic /tb2/initialpose sau đó là khai báo tọa độ của điểm với đầy đủ các thông tin như câu lệnh mình để trên.

Sau khi nhập câu lệnh thành công bạn sẽ thấy thông báo như hình bên dưới.

https://flic.kr/p/2pMQtHU

Sau đó điểm đến sử dụng câu lệnh:

ros2 action send_goal /tb2/navigate_to_pose nav2_msgs/action/NavigateToPose "pose: {header: {frame_id: map}, pose: {position: {x: -3.0, y: 1.5, z: 0.0}, orientation:{x: 0.0, y: 0.0, z: 0, w: 1.0000000}}}"

Tương tự như câu lệnh khởi tạo điểm xuất phát, điểm đến các bạn cũng cần khai báo đầy đủ thông tin và với cú pháp action send_goal . Một điều nữa bạn cần lưu ý là khi bạn chọn điểm kết thúc cho robot bạn cần phải cẩn thận tránh những điểm trùng với chướng ngại vật hoặc tọa độ mà robot không thể di chuyển tới được. Điều này là một trong những bất tiện khi các bạn sử dụng câu lệnh đòi hỏi bạn phải nắm rõ vị trí của robot. Nếu nhập sai bạn sẽ thấy thông báo như hình bên dưới.

https://flic.kr/p/2pMUUYV

Còn đây là khi bạn nhập đúng câu lệnh, lúc này bạn sẽ thấy robot di chuyển không chỉ trong Gazebo mà còn ở Rviz.

https://flic.kr/p/2pMW58X
 
 
https://flic.kr/p/2pMWNPK
 

Đến đây là kết thúc phần 2 của blog này, với 2 phần tuy không dài nhưng các bạn cũng có thể biết được một số thông tin bổ ích với Navigation2. Hy vọng qua blog này các bạn đã có thêm một số kiến thức cũng như công cụ hỗ trợ cho các dự án liên quan tới robotics sử dụng ROS. Bên cạnh đó, các bạn có thể theo dõi các blog khác cũng như các khóa học liên quan trên The Construct nhé.


Video Tutorial

Crea una Interfaz de Servicio Personalizada – Spanish ROS Tutorial

Crea una Interfaz de Servicio Personalizada – Spanish ROS Tutorial

This tutorial is created by Robotics Ambassador 017 Jose

Rosbotics Ambassador Program (https://www.theconstruct.ai/robotics-ambassador/

Lo que vamos a aprender

  1. Crear una interfaz de servicio personalizada.
  2. Emplear la interfaz creada para comandar el Turtlebot3.

Lista de recursos usados en esta publicación

  1. Usa este rosject: https://app.theconstruct.ai/l/603ea5b0/
  2. The Construct: https://app.theconstructsim.com/
  3. Cursos ROS: ROS2 Basics in 5 Days (Python): https://app.theconstructsim.com/courses/132

Resumen

ROS (Robot Operating System) se está convirtiendo en el “framework” estándar para programar robots. En este tutorial emplearemos los conceptos de servicios para comandar el robot “Turtlebot3” haciendo uso de una interfaz de servicio personalizada que crearemos.Todo esto es simulado en un mundo de Gazebo. Ten en cuenta que se usará ROS2 Humble para este tutorial.

PASO 1: Abriendo el rosject

Para seguir este tutorial, necesitamos tener instalado ROS2 HUMBLE en nuestro sistema, y lo ideal sería tener un ros2_ws (Espacio de Trabajo ROS2). Para facilitarte la vida, ya hemos preparado un rosject para eso: https://app.theconstruct.ai/l/603ea5b0/.

Simplemente copiando el rosject (haciendo clic en el enlace de arriba), tendrás una configuración ya preparada para ti.

Después de haber copiado el rosject a tu propia área de trabajo, deberías ver el botón RUN. Haz clic en ese botón para lanzar el rosject (abajo tienes un ejemplo de rosject).

Rosject

Tras pulsar el botón RUN, deberías tener cargado el rosject. Ahora, pasemos a la siguiente sección para ponernos manos a la obra.

PASO 2: Creación del paquete

El robot a usar es el Turtlebot3.

Rosject

Primero crearemos el paquete con las dependencias necesarias. Tener en cuenta que, para crear una interfaz de servicio personalizada solo puede hacer con un paquete CMake. Para ello ejecutamos en un terminal lo siguiente:

cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake tutorial_interface_srv --dependencies rclcpp std_msgs

PASO 3: Creación de la interfaz

Luego crearemos dentro del paquete la carpeta srv que contendrá la interfaz de servicio. Dentro de este creamos el archivo Mover.srv

cd ~/ros2_ws/src/tutorial_interface_srv
mkdir srv
cd srv
touch Mover.srv

Dentro de este archivo colocamos el contenido de la interfaz que necesitamos, el cual se compone de la parte de petición (request) y respuesta (response), que se separan mediante 3 guiones (—). Toda esta estructura permite la comunicación entre cliente y servidor.

Dentro de el archivo recién creado colocar lo siguiente: Mover.srv

float64 angulo                  # Angulo a girar
float64 velocidad_lineal        # Velocidad Lineal (en m/s)
int32 tiempo                    # Duración del movimiento (en segundos)
---
bool completado                 # Se logró?

PASO 4: Modificando CMakeLists.txt y package.xml

Una vez creado el archivo del servicio personalizado, necesitamos modificar los siguientes archivos para que nuestro paquete compile correctamente:

CMakeLists.txt: Para convertir las interfaces definidas en código específico de un lenguaje (como C++ y Python) para que puedan utilizarse en esos lenguajes.
package.xml: Añadimos las dependencias adicionales necesarias.

CMakeLists.txt

En CMakeLists.txt debemos tener lo siguiente:

find_package()

find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)

rosidl_generate_interfaces()

Aquí se colocará la interfaz de servicio que creamos dentro de la carpeta srv.

rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/Mover.srv"
)

Con lo cual CMakeList.txt deberia quedarnos mínimante con lo siguiente:

cmake_minimum_required(VERSION 3.8)
project(tutorial_interface_srv)
​
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
  add_compile_options(-Wall -Wextra -Wpedantic)
endif()
​
# find dependencies
find_package(ament_cmake REQUIRED)
find_package(rclcpp REQUIRED)
find_package(std_msgs REQUIRED)
find_package(rosidl_default_generators REQUIRED)
​
if(BUILD_TESTING)
  find_package(ament_lint_auto REQUIRED)
  # the following line skips the linter which checks for copyrights
  # comment the line when a copyright and license is added to all source files
  set(ament_cmake_copyright_FOUND TRUE)
  # the following line skips cpplint (only works in a git repo)
  # comment the line when this package is in a git repo and when
  # a copyright and license is added to all source files
  set(ament_cmake_cpplint_FOUND TRUE)
  ament_lint_auto_find_test_dependencies()
endif()
​
rosidl_generate_interfaces(${PROJECT_NAME}
  "srv/Mover.srv"
)
​
ament_package()

package.xml

En package.xml debemos añadir:

<build_depend>rosidl_default_generators</build_depend>
<exec_depend>rosidl_default_runtime</exec_depend>
<member_of_group>rosidl_interface_packages</member_of_group>

Y deberia quearnos mínimamente con lo siguiente:

<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="3">
  <name>tutorial_interface_srv</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email="user@todo.todo">user</maintainer>
  <license>TODO: License declaration</license>
​
  <buildtool_depend>ament_cmake</buildtool_depend>
​
  <depend>rclcpp</depend>
  <depend>std_msgs</depend>
​
  <build_depend>rosidl_default_generators</build_depend>
  <exec_depend>rosidl_default_runtime</exec_depend>
  <member_of_group>rosidl_interface_packages</member_of_group>
​
  <test_depend>ament_lint_auto</test_depend>
  <test_depend>ament_lint_common</test_depend>
​
  <export>
    <build_type>ament_cmake</build_type>
  </export>
</package>

PASO 5: Compilación del paquete

Por último, compilamos el paquete para poder usar la interfaz de servicio creada.

cd ~/ros2_ws
colcon build --packages-select tutorial_interface_srv
source install/setup.bash

Es importante realizar el último paso, de lo contrario no se encontrará la interfaz de servicio que se generó. Para asegurarnos de que interfaz se generó correctamente usamos los siguiente:

ros2 interface show tutorial_interface_srv/srv/Mover

Esto nos debería mostrar el contenido de Mover.srv en la terminal:

Simulación en Gazebo del Turtlebot3 moviendose

PASO 6: Usando la interfaz

Para hacer uso de la interfaz, se creo un paquete (tutorial_servicios) que contiene un servidor y un cliente que se comunican mediante el mensaje Mover.srv que acabamos de generar.

Simulación en Gazebo del Turtlebot3 moviendose

El cliente hace una petición al servidor enviándole el ángulo que necesita girar, la velocidad lineal y el tiempo que debe moverse con esa velocidad. Una vez realizado el movimiento el servidor enviará un mensaje de confirmación al cliente como respuesta. Para ello, ejecutamos la simulación en Gazebo (la cual se puede obtener desde este link).

export TURTLEBOT3_MODEL=waffle
ros2 launch turtlebot3_gazebo empty_world.launch.py

Simulación en Gazebo del Turtlebot3 moviendose

Luego en otra terminal ejecutamos el servidor:

ros2 launch tutorial_servicios mover_server_launch_file.launch.py

Y en otra terminal ejecutamos el cliente:

ros2 launch tutorial_servicios mover_client_launch_file.launch.py

Y listo!! ya estamos haciendo uso de la interfaz de servicio recién generada.

Simulación en Gazebo del Turtlebot3 moviendose


Video Tutorial

Mô phỏng nhiều robot với Nav2 trong ROS2 Humble & Gazebo (Phần 1) – ROS2 Vietnamese Tutorial

Mô phỏng nhiều robot với Nav2 trong ROS2 Humble & Gazebo (Phần 1) – ROS2 Vietnamese Tutorial

This tutorial is created by Robotics Ambassador Eric

Những điều bạn sẽ được biết trong blog này:

  1. Cách cài đặt các packages cần thiết để mô phỏng nhiều robots ở ROS2 Humble
  2. Cách tạo launch file để mô phỏng nhiều robot trên gazebo
  3. Mô phỏng robots trên Rviz và gazebo

Phần 2: https://www.theconstruct.ai/lam-sao-de-mo-phong-nhieu-robot-su-dung-nav2-trong-khong-gian-gazebo-voi-ros2-humble-phan-2-vietnamese-ros2-tutorial/

Danh sách nguồn tài liệu bổ trợ trong post này

  1. Sử dụng rosject: **https://app.theconstructsim.com/rosjects/750453**
  2. The Construct**: https://app.theconstructsim.com/**
  3. Khóa học về ROS2 →
    1. Introduction to Gazebo Sim with ROS2: https://app.theconstructsim.com/courses/introduction-to-gazebo-ignition-with-ros2-170/

Tổng quan

Khi làm việc với dự án yêu cầu bạn phải kết hợp nhiều robots với nhau để thực hiện một nhiệm vụ nào đó, vấn đề đầu tiên đặt ra là làm sao để có thể mô phỏng được nhiều robot trên một môi trường cụ thể. Do đó, blog này sẽ giúp bạn biết cách thực hiện điều đó. Đây sẽ là phần 1 của blog này với mong muốn sẽ giúp cho các bạn biết cách tạo một launch file để tích hợp nhiều robots và mô phỏng chúng trên Gazebo cùng với Rviz. Tiếp theo đó sẽ là phần 2 sẽ hướng dẫn các bạn sử dụng Nav2 package cùng với nhiều robot trong môi trường Gazebo.

Khởi động rosject

Như mọi lần rosject sẽ là công cụ hoàn hảo cho việc thực hiện project này, nơi các bạn dễ dàng truy cập và khởi tạo các project cùng với ROS ở các phiên bản khác nhau:

**https://app.theconstructsim.com/rosjects/750453**

Như hình bên dưới là một rosject hoàn chỉnh, bạn chỉ việc nhấn vào nút RUN để khởi động rosject.

Sau khi nhấn vào nút RUN bạn sẽ thấy rosject được chạy trên màn hình. Ở đây, rosject cung cấp cho bạn cả terminal để có thể nhập các câu lệnh:

https://flic.kr/p/2pGt7Ge

Bây giờ cùng nhau đến bước tiếp theo trong ví dụ này nhé.

Cài đặt các package cần thiết

Đầu tiên các bạn cần cài đặt các package cần thiết để có thể thực hiện project này, sau khi vào môi trường làm việc catkin là ros2_ws các bạn làm theo bằng cách thực hiện các câu lệnh sau đây:

cd ros2_ws/src

git clone https://github.com/Eric-nguyen1402/turtlebot3_multi_robot

source /opt/ros/humble/setup.bash

cd ..

sudo rosdep install --from-paths src --ignore-src -r -y

Sau bước này thì các bạn sẽ nhìn thấy thông báo cài đặt thành công như hình bên dưới.

https://flic.kr/p/2pGmTxi

Tiếp theo sử dụng câu lệnh sau để build môi trường catkin

colcon build --symlink-install

source ./install/setup.bash

https://flic.kr/p/2pGrq9K

Sau khi tải thành công các bạn sẽ nhìn thấy folder turtlebot3_multi_robot trong src. ở đây sẽ bao gồm các file cần thiết cho project này. Tiếp theo chúng ta sẽ đến mô phỏng và đi sâu vào làm sao để tạo được launch file.

https://flic.kr/p/2pGtH5J

Mô phỏng trên Gazebo cùng nhiều robots

Thông thường khi làm việc với turtlebot3 các bạn luôn phải chọn mô hình robot và truy xuất môi trường cũng như đường dẫn đến package. Tuy nhiên, trong blog này thay vì phải command mỗi lần chạy chương trình. Mình sẽ hướng dẫn các bạn cách khai báo trong launch file để chương trình tự động thực hiện cài đặt.

https://flic.kr/p/2pGtH6f

Sau đó để sử dụng môi trường gazebo và robot, bạn cần truy xuất môi trường gazebo và chạy ros launch package:

ros2 launch turtlebot3_multi_robot gazebo_multi_nav2_world.launch.py enable_drive:=True

Vì chúng ta sẽ mô phỏng không chỉ một mà nhiều robot cùng một lúc nên khi thực hiện câu lệnh chương trình cần một lúc để xử lý được yêu cầu nên các bạn kiên nhẫn nhé.

https://flic.kr/p/2pGmTxy

Mô phỏng trên RViz cùng nhiều robots

Khi thực hiện ros launch ở trên, 4 cửa sổ Rviz tương ứng với mỗi robot cũng sẽ xuất hiện đồng thời như hifnh dưới. Mỗi cửa sổ đại diện cho một robot.

https://flic.kr/p/2pGst9h

Bên cạnh đó, nếu các bạn kiểm tra ros topic thì các bạn sẽ thấy đầy đủ đồng thời tất cả topic của robot được khai báo (tb1, tb2, tb3, tb4). Từ đây chúng ta có thể áp dụng vào nhiều project sử dụng nhiều robots cùng lúc.

source ~/ros2_ws/install/setup.bash

ros2 topic list

https://flic.kr/p/2pGmTyf
https://flic.kr/p/2pGtH7h
https://flic.kr/p/2pGt7Hw
https://flic.kr/p/2pGtH7C

Cách tạo launch file để mô phỏng nhiều robots cùng lúc

Sau đây, mình sẽ giải thích các phần trong launch file để các bạn tùy ý có thể thay đổi và thực hiện project cho riêng mình.

1. Khởi tạo Gazebo server và client ¶

Để có thể chạy mô phỏng trên Gazebo chúng ta cần khai báo và khởi tạo Gazebo server và client như sau

gzserver_cmd = IncludeLaunchDescription(

    `PythonLaunchDescriptionSource(`

        `os.path.join(get_package_share_directory("gazebo_ros"), "launch", "gzserver.launch.py")`

    `),`

    `launch_arguments={"world": world}.items(),`

`)`
 
`gzclient_cmd = IncludeLaunchDescription(`

    `PythonLaunchDescriptionSource(`

        `os.path.join(get_package_share_directory("gazebo_ros"), "launch", "gzclient.launch.py")`

    `),`

`)`

Triển khai robots dưới dạng lưới

Khi mô phỏng trên gazebo, chúng ta cần biết vị trí cần bố trí robot hay nói cách khác chúng ta cần khởi tạo tọa độ cho robot trên môi trường gazebo. Để làm việc đó, cách tiếp cận đơn giản là sử dụng hàng và cột để xác định vị trí theo dạng lưới. Với mỗi vị trí trên lưới, nó sẽ tạo ra một tên và không gian duy nhất cho từng robot. Sau đó, tạo và cấu hình nút robot_state_publisher và spawn_entity.py để xuất hiện robot trong gazebo.

for i in range(COLS):

`for j in range(ROWS):`

    `name = "turtlebot" + str(i) + "_" + str(j)`

    `...`

    `turtlebot_state_publisher = Node(`

        `...`

    `)`

    `spawn_turtlebot3_burger = Node(`

        `...`

    `)`

    `...`

Cách sắp xếp tuần tự với trình xử lý sự kiện (Event Handlers)

Chúng ta sửu dụng phương pháp tuần tự để robot xuất hiện, đảm bảo rằng mỗi robot được hoàn toàn trang bị trước khi đến với robot tiếp theo. Điều này được thực hiện thông qua hai chức năng RegisterEventHandler và OnProcessExit để chờ quá trình trước đó được hoàn thành.

if last_action is None:

`ld.add_action(turtlebot_state_publisher)`

`ld.add_action(spawn_turtlebot3_burger)`

else:

`spawn_turtlebot3_event = RegisterEventHandler(`

    `...`

`)`

`ld.add_action(spawn_turtlebot3_event)`

Bật chế độ chuyển động

Sau khi các robots được hoàn toàn khởi tạo, chúng ta có thể bắt đầu đùng các nút để điều khiển robot dựa trên enable_drive, cho phép điều khiển chuyển động của robots trong môi trường mô phỏng

drive_turtlebot3_burger = Node(

            `package="turtlebot3_gazebo",`

            `executable="turtlebot3_drive",`

            `namespace=namespace,`

            `output="screen",`

            `condition=IfCondition(enable_drive),`

        `)`

Đến đây là kết thúc phần 1 của blog này, các bạn có thể theo doxi phần 2 để kết hợp nhiều robots cùng với Nav2 package. Hy vọng qua blog này các bạn đã có thêm một số kiến thức cũng như công cụ hỗ trợ cho các dự án liên quan tới robotics sử dụng ROS. Bên cạnh đó, các bạn có thể theo dõi các blog khác cũng như các khóa học liên quan trên The Construct nhé.

 

Video Tutorial

Moviendo el Turtlebot3 a posiciones deseadas en Gazebo – Spanish ROS Tutorial

Moviendo el Turtlebot3 a posiciones deseadas en Gazebo – Spanish ROS Tutorial

This tutorial is created by Rosbotics Ambassador 017 Jose

Rosbotics Ambassador Program ((https://www.theconstruct.ai/rosbotics-ambassador/)

Lo que vamos a aprender

  1. Subscribirse al tópico /odom y asi conocer la posición estimada del robot.
  2. Publicar en el tópico /cmd_vel, para así mover al robot de acuerdo con la posición estimada.

Lista de recursos usados en esta publicación

  1. Usa este rosject: https://app.theconstructsim.com/l/5fc977f5/
  2. The Construct: https://app.theconstructsim.com/
  3. Cursos ROS: ROS2 Basics in 5 Days (Python): https://app.theconstructsim.com/courses/132

Resumen

ROS (Robot Operating System) se está convirtiendo en el “framework” estándar para programar robots. En este tutorial emplearemos los conceptos de tópicos y nodos para mover el robot “Turtlebot3” hacia múltiples posiciones deseadas. Hacemos uso de los mensajes del tópico /odom para conocer la posición estimada del robot y publicar en el tópico /cmd_vel para mover el robot a las posiciones deseadas. Todo esto es simulado en un mundo de Gazebo. Ten encuesta que se usará ROS2 Humble para este tutorial.

PASO 1: Abriendo el rosject

Para seguir este tutorial, necesitamos tener instalado ROS2 HUMBLE en nuestro sistema, y lo ideal sería tener un ros2_ws (Espacio de Trabajo ROS2). Para facilitarte la vida, ya hemos preparado un rosject para eso: https://app.theconstructsim.com/l/5fc977f5/.

Simplemente copiando el rosject (haciendo clic en el enlace de arriba), tendrás una configuración ya preparada para ti.

Después de haber copiado el rosject a tu propia área de trabajo, deberías ver el botón RUN. Haz clic en ese botón para lanzar el rosject (abajo tienes un ejemplo de rosject).

Rosject

Tras pulsar el botón RUN, deberías tener cargado el rosject. Ahora, pasemos a la siguiente sección para ponernos manos a la obra.

PASO 2: Desarrollo del paquete

El robot a usar es el Turtlebot3.

Rosject

Estrategia de control

El Turtlebot3 se moverá por una serie de posiciones en un orden específico, es decir, se moverá de su posición inicial a la 1ra posición deseada, una vez alcanzada, se moverá a la 2da posición y así sucesivamente. Para lograr esto, necesitamos de una estrategia.

Tengamos en cuenta que el robot conocerá las posiciones que debe alcanzar y su posición actual estimada gracias al tópico /odom, del cual aprovecharemos la posición en X, posición Y, y la rotación en Z.

Posicion robot

Con ello, para alcanzar cada posición el Turtlebot3 realizará 2 movimientos: Primero, girar hasta que quede mirando a la posición deseada; Segundo, avanzar de manera recta hasta llegar a la posición.

Movimiento robot

Adicionalmente, debemos tener en cuenta el robot no se moverá en una línea recta de manera perfecta, por lo que se requiere compensar esa desviación conforme avanza. Esto se puede realizar añadiendo una velocidad angular cuando la orientación cambie demasiado.

Creación del paquete

Primero creamos un paquete en python para realizar un nodo-publicador-subscriptor que mueva al Turtlebot3 de la forma deseada. Necesitamos añadir las dependencias necesarias. Para ello ejecutamos en un terminal lo siguiente:

cd ~/ros2_ws/src
ros2 pkg create --build-type ament_python turtlebot3_tutorial --dependencies std_msgs geometry_msgs

Creación del nodo

Creamos el archivo que contendrá el nodo.

cd ~/ros2_ws/src/turtlebot3_tutorial/turtlebot3_tutorial
touch turtlebot3_node.py

Copiamos el siguiente código dentro del archivo.

import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
from nav_msgs.msg import Odometry
from rclpy.qos import ReliabilityPolicy, QoSProfile
import numpy as np
import math
​
class Robot(Node):
​
    def __init__(self):
        super().__init__('turtlebot3_node')
        # Posiciones estimadas
        self.pos_x = 0
        self.pos_y = 0
        self.roll = 0
        self.pitch = 0
        self.yaw = 0
​
        # Velocidad de desplazamiento
        self.vel_linear_x = 0.2
        self.vel_angular_z = 0.4
​
        # Máximo error
        self.linear_error = 0.08
        self.angular_error = 0.05   
​
        # Posiciones deseadas
        self.posiciones = [[1,1], [0,2], [0, 0]]
        self.index = 0
        self.pos_x_d, self.pos_y_d = self.posiciones[self.index]
        self.angulo_d = 0
​
        # Fase
        self.fase = 1 
​
        # Subscriptor
        self.subscriber = self.create_subscription(
            Odometry,
            '/odom',
            self.listener_callback,
            QoSProfile(depth=10, reliability=ReliabilityPolicy.RELIABLE))
​
        # Publicador
        self.publisher_ = self.create_publisher(Twist, 'cmd_vel', 10)
        self.vel = Twist()
        timer_period = 0.1
        self.timer = self.create_timer(timer_period, self.timer_callback)
​
    def timer_callback(self):
​
        if (self.index < len(self.posiciones)): 
            self.pos_x_d, self.pos_y_d = self.posiciones[self.index]
 
            if (self.fase == 1):
                # Calcular ángulo de rotación
                self.angulo_d = math.atan2(self.pos_y_d - self.pos_y, self.pos_x_d - self.pos_x) 
                if ( abs(self.yaw - self.angulo_d) > self.angular_error ):
                    self.vel.linear.x = 0.0
                    self.vel.angular.z = self.vel_angular_z if self.yaw < self.angulo_d else -self.vel_angular_z 
                else: 
                    self.vel.linear.x = 0.0 
                    self.vel.angular.z = 0.0 
                    self.fase += 1 
            if (self.fase == 2): 
                if ( math.sqrt((self.pos_x - self.pos_x_d)**2 + (self.pos_y - self.pos_y_d)**2) > self.linear_error ):
                    self.vel.linear.x = self.vel_linear_x
                    if ( abs(self.yaw - self.angulo_d) > self.angular_error):
                        self.vel.angular.z = self.vel_angular_z if self.yaw < self.angulo_d else -self.vel_angular_z
                    else:
                        self.vel.angular.z = 0.0
                else:
                    self.vel.linear.x = 0.0
                    self.vel.angular.z = 0.0
                    self.fase = 1
                    # Siguiente posición deseada
                    self.index += 1

        self.publisher_.publish(self.vel)
​
    def listener_callback(self, msg):
        self.pos_x = msg.pose.pose.position.x
        self.pos_y = msg.pose.pose.position.y
        quaternion = [msg.pose.pose.orientation.x,
                      msg.pose.pose.orientation.y,
                      msg.pose.pose.orientation.z,
                      msg.pose.pose.orientation.w]
        self.roll, self.pitch, self.yaw = self.euler_from_quaternion(quaternion)
​
        self.get_logger().info('Pos_x: "%f"' % self.pos_x)
        self.get_logger().info('Pos_y: "%f"' % self.pos_y)
        self.get_logger().info('Ori_z: "%f"' % self.yaw)
        self.get_logger().info('-------------------------')
​
    def euler_from_quaternion(self, quaternion):
        """
        Converts quaternion (w in last place) to euler roll, pitch, yaw
        quaternion = [x, y, z, w]
        Below should be replaced when porting for ROS2 Python tf_conversions is done.
        """
        x = quaternion[0]
        y = quaternion[1]
        z = quaternion[2]
        w = quaternion[3]
​
        sinr_cosp = 2 * (w * x + y * z)
        cosr_cosp = 1 - 2 * (x * x + y * y)
        roll = np.arctan2(sinr_cosp, cosr_cosp)
​
        sinp = 2 * (w * y - z * x)
        pitch = np.arcsin(sinp)
​
        siny_cosp = 2 * (w * z + x * y)
        cosy_cosp = 1 - 2 * (y * y + z * z)
        yaw = np.arctan2(siny_cosp, cosy_cosp)
​
        return roll, pitch, yaw
            
def main(args=None):
    # initialize the ROS communication
    rclpy.init(args=args)
    robot = Robot()
    
    rclpy.spin(robot)
​
    robot.destroy_node()
    rclpy.shutdown()
​
if __name__ == '__main__':
    main()

Repasemos las partes más importantes del código:

  • Aquí iniciamos el subscriptor del tópico /odom y el publicador al tópico /cmd_vel
        # Subscriptor
        self.subscriber = self.create_subscription(
            Odometry,
            '/odom',
            self.listener_callback,
            QoSProfile(depth=10, reliability=ReliabilityPolicy.RELIABLE))
​
        # Publicador
        self.publisher_ = self.create_publisher(Twist, 'cmd_vel', 10)
        self.vel = Twist()
        timer_period = 0.1
        self.timer = self.create_timer(timer_period, self.timer_callback)
  • En el método listener_callback recibimos las posiciones estimadas del robot y las acondicionamos para emplearlas en la lógica de control.
    def listener_callback(self, msg):
        self.pos_x = msg.pose.pose.position.x
        self.pos_y = msg.pose.pose.position.y
        quaternion = [msg.pose.pose.orientation.x,
                      msg.pose.pose.orientation.y,
                      msg.pose.pose.orientation.z,
                      msg.pose.pose.orientation.w]
        self.roll, self.pitch, self.yaw = self.euler_from_quaternion(quaternion)

        self.get_logger().info('Pos_x: "%f"' % self.pos_x)
        self.get_logger().info('Pos_y: "%f"' % self.pos_y)
        self.get_logger().info('Ori_z: "%f"' % self.yaw)
        self.get_logger().info('-------------------------')
  • Debido a que en el tópico /odom la orientación es expresada en cuaterniones, el método euler_from_quaternion es necesario para convertir la orientación del robot de cuaterniones a la notación roll-pitch-yaw. Esto nos permite obtener la rotación del robot alrededor del eje z (yaw).
    def euler_from_quaternion(self, quaternion):
        """
        Converts quaternion (w in last place) to euler roll, pitch, yaw
        quaternion = [x, y, z, w]
        Below should be replaced when porting for ROS2 Python tf_conversions is done.
        """
        x = quaternion[0]
        y = quaternion[1]
        z = quaternion[2]
        w = quaternion[3]

        sinr_cosp = 2 * (w * x + y * z)
        cosr_cosp = 1 - 2 * (x * x + y * y)
        roll = np.arctan2(sinr_cosp, cosr_cosp)

        sinp = 2 * (w * y - z * x)
        pitch = np.arcsin(sinp)

        siny_cosp = 2 * (w * z + x * y)
        cosy_cosp = 1 - 2 * (y * y + z * z)
        yaw = np.arctan2(siny_cosp, cosy_cosp)

        return roll, pitch, yaw
  • Este es el método definido en el timer y contiene la estrategia de control explicada anteriormente. Se ejecuta cada 0.1s y actualiza la velocidad del robot de acuerdo con la posición estimada.
   def timer_callback(self):

        if (self.index < len(self.posiciones)): 
            self.pos_x_d, self.pos_y_d = self.posiciones[self.index]
 
            if (self.fase == 1):
                # Calcular ángulo de rotación
                self.angulo_d = math.atan2(self.pos_y_d - self.pos_y, self.pos_x_d - self.pos_x) 
                if ( abs(self.yaw - self.angulo_d) > self.angular_error ):
                    self.vel.linear.x = 0.0
                    self.vel.angular.z = self.vel_angular_z if self.yaw < self.angulo_d else -self.vel_angular_z 
                else: 
                    self.vel.linear.x = 0.0 
                    self.vel.angular.z = 0.0 
                    self.fase += 1 
            if (self.fase == 2): 
                if ( math.sqrt((self.pos_x - self.pos_x_d)**2 + (self.pos_y - self.pos_y_d)**2) > self.linear_error ):
                    self.vel.linear.x = self.vel_linear_x
                    if ( abs(self.yaw - self.angulo_d) > self.angular_error):
                        self.vel.angular.z = self.vel_angular_z if self.yaw < self.angulo_d else -self.vel_angular_z
                    else:
                        self.vel.angular.z = 0.0
                else:
                    self.vel.linear.x = 0.0
                    self.vel.angular.z = 0.0
                    self.fase = 1
                    # Siguiente posición deseada
                    self.index += 1

        self.publisher_.publish(self.vel)

Creación del launch

Una vez que el nodo esta listo creamos el archivo launch con lo siguiente:

cd ~/ros2_ws/src/turtlebot3_tutorial
mkdir launch
cd launch
touch turtlebot3_launch.launch.py
chmod +x turtlebot3_launch.launch.py

Copiamos el siguiente código dentro del archivo que acabamos de crear:

from launch import LaunchDescription
from launch_ros.actions import Node
​
def generate_launch_description():
    return LaunchDescription([
        Node(
            package='turtlebot3_tutorial',
            executable='turtlebot3_node',
            output='screen'),
    ])

Una vez hecho lo anterior, editamos el archivo setup.py para añadir nuestro nodo.

from setuptools import setup
import os # <-- Añadir
from glob import glob # <-- Añadir
​
package_name = 'turtlebot3_tutorial'
​
setup(
    name=package_name,
    version='0.0.0',
    packages=[package_name],
    data_files=[
        ('share/ament_index/resource_index/packages',
            ['resource/' + package_name]),
        ('share/' + package_name, ['package.xml']),
        (os.path.join('share', package_name), glob('launch/*.launch.py')) # <-- Añadir
    ],
    install_requires=['setuptools'],
    zip_safe=True,
    maintainer='user',
    maintainer_email='user@todo.todo',
    description='TODO: Package description',
    license='TODO: License declaration',
    tests_require=['pytest'],
    entry_points={
        'console_scripts': [
            'turtlebot3_node = turtlebot3_tutorial.turtlebot3_node:main' # <-- Añadir de acuerdo al nombre de tu paquete y script
        ],
    },
)

Compilación del paquete

Por último, compilamos el paquete antes de ejecutar la simulación.

cd ~/ros2_ws
pip install setuptools==58.2.0
colcon build --packages-select turtlebot3_tutorial
source install/setup.bash

PASO 3: Simulando en Gazebo

Finalmente simularemos el comportamiento en gazebo, para ello primero iniciamos la simulación del Turtlebot3, cuyo paquete ya esta en este rosject. Esta se puede obtener desde este link.

export TURTLEBOT3_MODEL=waffle
ros2 launch turtlebot3_gazebo empty_world.launch.py

Simulación en Gazebo del Turtlebot3 moviendose

Una vez que se inicio la simulación, ya podemos iniciar el nodo que creamos.

ros2 launch turtlebot3_tutorial turtlebot3_launch.launch.py

Y listo! Ya podemos observar al robot moviendose por todas las posiciones deseadas en el orden especificado.

Simulación en Gazebo del Turtlebot3 moviendose

 

Video Tutorial

How to Install a USB Camera in TurtleBot3 – ROS Q&A #220

How to Install a USB Camera in TurtleBot3 – ROS Q&A #220

What we are going to learn

  1. How to install the ROS driver cv_camera
  2. How to add Camera link to TurtleBot3 transform frame tree

List of resources used in this post

  1. ROS Development Studio (ROSDS) —▸ http://rosds.online
  2. This post answers the following question: https://answers.ros.org/question/329102/which-camera-for-turtle3-burger-and-how-to-plug-it/
  3. cv_camera package: https://wiki.ros.org/cv_camera
  4. Turtlebo3 e-manual: https://emanual.robotis.com/docs/en/platform/turtlebot3/overview/
  5. Turtlebot3 repository: https://github.com/ROBOTIS-GIT/turtlebot3
  6. ROS2 Tutorials –▸
    1. ROS2 Basics in 5 Days (C++): https://app.theconstructsim.com/#/Course/61
    2. ROS2 Basics in 5 Days (Python): ROS2 Basics for Python

Installing ROS OpenCV camera driver

In order to install the cv_camera driver, the first thing you have to do is to connect to your real robot.

If you are reading this tutorial, then we assume you already know how to connect to your own robot using SSH.

After you have connected, you can install the cv_camera package either by directly compiling the https://github.com/OTL/cv_camera package or by using apt-get, which is what we are going to do.

Important things about the cv_camera package:

  • It publishes the images on the ~image_raw topic (sensor_msgs/Image)
  • It uses camera as the default value of the ~frame_id parameter.

Ok, let’s finally install the cv_camera package. The commands would be the following:

sudo apt update 

sudo apt install ros-[YOUR-ROS-DISTRO]-cv-camera

Remember to replace [YOUR-ROS-DISTRO] with your ROS Distro, like melodic, or noetic, for example.

Once you have this installed in your TurtleBot3, you can plug it in a USB port and test it running this node:

source /opt/ros/[YOUR-ROS-DISTRO]/setup.bash

rosrun cv_camera cv_camera_node

 

If it works, then create a launch file for the node and also a static transform publisher from base_link to camera_link, indicating the approximate distance to where you end up placing your camera. The file would look something like this:

<launch>
  <node pkg="cv_camera" type="cv_camera_node" name="cv_camera" output="screen"/>
  <node pkg="tf" type="static_transform_publisher" name="camera_frames_pub" args="0.05 0.0 0.1 0 0 0 /base_link /camera 35"/>
</launch>

After launching the launch file we just created, using roslaunch your_package_name_here your_launch_file.launch, you should be able to list the topics by using rostopic list. Among other topics, the output of rostopic list should show the following topic:

/cv_camera

 

If you now open RViz, you should be able to see the camera.

If you want a step-by-step walk-through of the instructions above, you can check out this video below.

Youtube video

So this is the post for today. Remember that we have the live version of this post on YouTube. If you liked the content, please consider subscribing to our youtube channel. We are publishing new content ~every day.

Keep pushing your ROS Learning.

Related Courses & Training

If you want to learn more about ROS and ROS2, we recommend the following courses:

Pin It on Pinterest