Hướng dẫn cách tạo một topic publisher trong ROS2 – Vietnamese ROS Tutorial

Hướng dẫn cách tạo một topic publisher trong ROS2 – Vietnamese ROS Tutorial

Xin chào các bạn! Trong bài viết này mình sẽ hướng dẫn các bạn cách tạo một topic publisher cơ bản trong ROS2 trên nền tảng The Construct.

Nguồn tham khảo

  1. Nền tảng học ROS trực tuyến The Construct
  2. Khóa học ROS2 cơ bản (Python) ROS2 Basics in 5 Days

Tạo một package chứa publisher

Đầu tiên chúng ta sẽ tạo một package có tên là publisher_pkg trong không gian làm việc ros2_ws bằng lệnh sau:

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

Tạo một file publisher

Tiếp theo, chúng ta sẽ tạo một file tên là simple_publisher.py trong thư mục publisher_pkg ở trong package publisher_pkg mà chúng ta vừa tạo. Các bạn lưu ý là file này phải nằm trong thư mục cùng tên nhưng nằm trong package vừa tạo nhé. Cụ thể là đường dẫn tới file simple_publisher.py~/ros2_ws/src/publisher_pkg/publisher_pkg/simple_publisher.py

Tiếp theo, các bạn hãy copy những dòng code sau vào file simple_publisher.py 

import rclpy
# import the ROS2 python libraries
from rclpy.node import Node
# import the Twist interface from the geometry_msgs package
from geometry_msgs.msg import Twist

class SimplePublisher(Node):

    def __init__(self):
        # Here you have the class constructor
        # call super() in the constructor to initialize the Node object
        # the parameter you pass is the node name
        super().__init__('simple_publisher')
        # create the publisher object
        # in this case, the publisher will publish on /cmd_vel Topic with a queue size of 10 messages.
        # use the Twist module for /cmd_vel Topic
        self.publisher_ = self.create_publisher(Twist, 'cmd_vel', 10)
        # define the timer period for 0.5 seconds
        timer_period = 0.5
        # create a timer sending two parameters:
        # - the duration between 2 callbacks (0.5 seconds)
        # - the timer function (timer_callback)
        self.timer = self.create_timer(timer_period, self.timer_callback)

    def timer_callback(self):
        # Here you have the callback method
        # create a Twist message
        msg = Twist()
        # define the linear x-axis velocity of /cmd_vel Topic parameter to 0.5
        msg.linear.x = 0.5
        # define the angular z-axis velocity of /cmd_vel Topic parameter to 0.5
        msg.angular.z = 0.5
        # Publish the message to the Topic
        self.publisher_.publish(msg)
        # Display the message on the console
        self.get_logger().info('Publishing: "%s"' % msg)

def main(args=None):
    # initialize the ROS communication
    rclpy.init(args=args)
    # declare the node constructor
    simple_publisher = SimplePublisher()
    # pause the program execution, waits for a request to kill the node (ctrl+c)
    rclpy.spin(simple_publisher)
    # Explicity destroys the node
    simple_publisher.destroy_node()
    # shutdown the ROS communication
    rclpy.shutdown()

if __name__ == '__main__':
    main()

File simple_publisher.py này chứa nội dung của một topic publisher cơ bản. Hiện tại thì các bạn chưa cần hiểu những dòng code này, mình sẽ giải thích ở phần sau. Chúng ta sẽ tiếp tục các bước tiếp theo để chạy thử code xem nó làm được gì trước nhé.

Tạo file launch

Đầu tiên, chúng ta sẽ tạo một thư mục launch ở trong package đang làm việc:

cd ~/ros2_ws/src/publisher_pkg
mkdir launch

Tiếp theo, ta sẽ tạo một file launch để chạy node publisher tên là publisher_pkg_launch_file.launch.py

cd ~/ros2_ws/src/publisher_pkg/launch
touch publisher_pkg_launch_file.launch.py
chmod +x publisher_pkg_launch_file.launch.py

Các bạn copy các dòng code sau vào trong file launch vừa mới tạo:

from launch import LaunchDescription
from launch_ros.actions import Node

def generate_launch_description():
    return LaunchDescription([
        Node(
            package='publisher_pkg',
            executable='simple_publisher',
            output='screen'),
])

Chỉnh sửa file setup.py

Tiếp theo, các bạn hãy chỉnh sửa file setup.py để thiết lập các file vừa tạo với hệ thống package:

from setuptools import setup
import os
from glob import glob

package_name = 'publisher_pkg'

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'))
    ],
    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': [
            'simple_publisher = publisher_pkg.simple_publisher:main'
        ],
    },
)

Biên dịch package

Tiếp theo chúng ta sẽ biên dịch (compile) package bằng các lệnh sau:

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

Chạy publisher node trong Terminal

ros2 launch publisher_pkg publisher_pkg_launch_file.launch.py

Trên Terminal sẽ hiển thị các nội dung message mà node mình vừa tạo đang publish lên topic /cmd_vel. Đồng thời robot cũng bắt đầu di chuyển thành vòng tròn.

Để kiểm tra thông tin đang ở trên topic /cmd_vel, các bạn chạy lệnh :

ros2 topic echo /cmd_vel

Giải thích code tạo publisher

Quy trình hoạt động

def main(args=None):
 # initialize the ROS communication
 rclpy.init(args=args)
 # declare the node constructor
 simple_publisher = SimplePublisher()
 # pause the program execution, waits for a request to kill the node (ctrl+c)
 rclpy.spin(simple_publisher)
 # Explicity destroys the node
 simple_publisher.destroy_node()
 # shutdown the ROS communication
 rclpy.shutdown()

Đầu tiên hệ thống sẽ khởi tạo giao tiếp ROS2 bằng lệnh

rclpy.init(args=args)

Khai báo và khởi tạo node publisher

simple_publisher = SimplePublisher()

Tạm dừng việc thực thi chương trình, chờ yêu cầu hủy node (ctrl+c)

rclpy.spin(simple_publisher)

Hủy node

simple_publisher.destroy_node()

Tắt hệ thống giao tiếp ROS2

rclpy.shutdown()

Tạo Class publisher

Phần code này cũng tương tự như khi tạo một class trong python thông thường với hàm khởi tạo __init__ và các hàm chức năng khác. Trong đó quan trọng nhất là các lệnh sau để thực hiện các chức năng liên quan tới ROS:

Khởi tạo một node có tên là 'simple_publisher':

super().__init__('simple_publisher')

Tạo một publisher cho topic /cmd_vel sử dụng Twist message với kích thước hàng đợi là 10:

self.publisher_ = self.create_publisher(Twist, 'cmd_vel', 10)

Tạo một timer để kích hoạt hàm timer_callback sau mỗi time_period=0.5

self.timer = self.create_timer(timer_period, self.timer_callback)

Xuất bản nội dung của biến msg lên topic /cmd_vel

self.publisher_.publish(msg)

Vậy là trong bài viết này mình đã hướng dẫn các bạn cách tạo một topic publisher cơ bản và các bước chạy thử. Chúc các bạn thực hành thành công.

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

Cách tạo gói ROS2 vững chắc – Vietnamese ROS Tutorial

Cách tạo gói ROS2 vững chắc – Vietnamese ROS Tutorial

This tutorial is created by Rosbotics Ambassador 018 Tung

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

Những mục chúng ta sẽ học

  1. Cách tạo package trong ROS2
  2. Cách compile package trong ROS2

Các tài nguyên được sử dụng trong bài viết này

  1. Rosject: https://app.theconstructsim.com/l/5e2dd3a5/
  2. The Construct: https://app.theconstructsim.com/
  3. Khóa học ROS2: ROS2 Basics in 5 Days

 

Tạo package trong ROS2

Để hệ thống nhận diện được các câu lệnh của ROS2 thì chúng ta cần source file setup.bash của ROS2 trước.

Source file setup của ROS2 trên Terminal 1:
source /opt/ros/humble/setup.bash

Để tạo package ROS2, chúng ta cần có một không gian làm việc ROS2.

Cùng tạo một không gian làm việc ROS2 tên là ros2_ws bằng cách tạo folder ros2_ws và một thư mục nguồn src chứa code của các package bằng các lệnh sau:
mkdir ros_ws
cd ros_ws
mkdir src

Tạo một package tên là my_package ở trong thư mục src của không gian làm việc vừa tạo bằng trình tạo package tự động của ROS2:
cd ~/ros_ws/src
ros2 pkg create --build-type ament_python my_package --dependencies rclpy

Một câu lệnh tạo package có cú pháp như sau:
ros2 pkg create --build-type ament_python <package_name> --dependencies <package_dependency_1> <package_dependency_2>

Trong đó:

  • ros2: câu lệnh của ROS2
  • pkg: (package) thực hiện các hành động liên quan tới package
  • create: tạo một package mới
  • –build-type: truyền tham số về cách build package
  • ament_python: package sẽ được build dựa trên python
  • <package_name>: tên của package muốn tạo
  • –dependencies: truyền tham số tên các package mà package muốn tạo phụ thuộc
  • <package_dependency_1>: tên của package phụ thuộc

Compile package trong ROS2

Sau khi tạo xong package, bạn nên build package vừa tạo. Đây là cách nhanh nhất để xác định xem các package phụ thuộc mà bạn đã liệt kê có hoạt động hay không và kiểm tra xem dữ liệu đã nhập có sai sót gì không. Thực hiện các lệnh sau để build các package trong không gian làm việc ros_ws:
cd ~/ros_ws
colcon build

Để build từng package riêng lẻ thì ta dùng lệnh sau:

cd ~/ros_ws
colcon build --packages-select <package_name>

Sau mỗi lần build xong package, hãy tạo thói quen source file setup.bash từ thư mục install để ROS2 có thể tìm thấy các package trong không gian làm việc

source ~/ros_ws/install/setup.bash

Để xác nhận rằng package đã được tạo thành công và đã được tích hợp vào hệ thống ROS2, hãy liệt kê các package đã được khai báo với ROS2.
ros2 pkg list

Nếu package vừa tạo có tên trong danh sách thì chúng ta đã thành công.

Một cách nhanh hơn là ta có thể sử dụng lệnh grep trong Terminal để tìm nhanh tên package vừa tạo:

ros2 pkg list | grep my_package

Video Tutorial

ROS2 PYTHON Script Pamoja Na Mchapishaji na Majiri – Swahili ROS Tutorial

ROS2 PYTHON Script Pamoja Na Mchapishaji na Majiri – Swahili ROS Tutorial

This tutorial is created by Rosbotics Ambassador 021 Kevin

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


Mambo tunaenda kujifunza

  1. Jinsi ya kutengeneza rosject
  2. Jinsi ya kutengeneza kifurushi ya ROS2.
  3. Jinsi ya kuunda hati ya python..
  4. Jinsi ya kutengeneza mchapishaji.
  5. Jinsi ya kutengeneza mteja.
  6. Jinsi ya kujenga nafasi ya kazi ya ROS2..

Orodha ya rasilimali zilizotumiwa katika chapisho hili

  1. The Construct: https://app.theconstructsim.com/
  2. ROS2 Courses –▸
    1. ROS2 Basics in 5 Days Humble (Python): https://app.theconstructsim.com/Course/132

Muhtasari.

ROS (Mfumo wa Uendeshaji wa Roboti) unakuwa “mfumo” wa kawaida wa roboti za kutengeneza programu. Katika chapisho hili, hebu tujifunze jinsi ya kuunda kifurushi cha ROS2, muhimu kwa kutoa maagizo kwa roboti, kwa kutumia amri ya ros2.

Kutengeneza rosject

Ili kufuata mafunzo haya, tunahitaji kuwa na ROS2 iliyosakinishwa katika mfumo wetu, na kwa hakika ros2_ws (ROS2 Workspace). Ili kurahisisha maisha yako, tutaunda mpango wa kubofya kiungo hiki: https://app.theconstructsim.com/ 

Hii inakuleta kwenye ukurasa wa kutua wa kuunda, hapo tutaenda kwa myrosjects na kisha kuunda rosject mpya, hatua zimeangaziwa katika miraba ya bluu.

 

Baada ya kuunda rosject mpya, weka usambazaji ambao ungependa kutumia kwa kesi yetu Humble na jina la rosject na maelezo mafupi.

 

Baada ya kubonyeza kitufe cha Unda, unapaswa kuwa na rosject kwenye rosjects zangu Bonyeza na ubonyeze RUN.

 

Kuunda kifurushi cha ros2.

Ili kuunda kifurushi cha ROS2, tunahitaji kuwa na Nafasi ya Kazi ya ROS2, na kwa hiyo, tunahitaji terminal.

Wacha tufungue terminal kwa kubofya kitufe cha Fungua terminal mpya.

Mara tu ndani ya terminal tumia amri kuona vitu vilivyopo kwenye saraka ya sasa:

ls

Kisha nenda kwenye saraka ifuatayo ambapo tutaunda kifurushi:

cd ros2_ws/src

Tumia amri ifuatayo kuunda kifurushi cha python:
ros2 pkg create --build-type ament_python --license Apache-2.0 py_pubsub

Pamoja na hayo umeunda kifurushi cha python ROS2.

 

 

Kuunda hati ya python.

Fungua kihariri cha msimbo kulia na uunde hati kwenye py_pubsub fille inayoitwa py_pubsub.py. Hii inafanywa kwa kubofya kulia kwenye py_pubsub na kubofya kuunda faili mpya

 

Nakili na ubandike yafuatayo na hati ya python ambayo ina mchapishaji na aliyejiandikisha kwenye hati yetu

import rclpy

import math
from rclpy.node import Node
from std_msgs.msg import Float32

class MyNode(Node):
    def __init__(self):
        super().__init__(‘my_node’)
        self.subscription = self.create_subscription(Float32, ‘input_topic’, self.listener_callback, 10)
        self.publisher = self.create_publisher(Float32, ‘output_topic’, 10)

    def listener_callback(self, msg):
        radians = msg.data
        degrees = radians * 180 / math.pi
        self.publisher.publish(Float32(data=degrees))

def main(args=None):
    rclpy.init(args=args)
    node = MyNode()
    rclpy.spin(node)
    node.destroy_node()
    rclpy.shutdown()

if __name__ == ‘__main__’:
    main()

 

Kisha ongeza laini ifuatayo kwenye setup.py kwenye sehemu ya kuingilia.

‘talker = py_pubsub.py_pubsub:main’
Faili ya stepup.py inapaswa kuonekana kama ifuatavyo:

 

from setuptools import setup

package_name = ‘py_pubsub’

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’]),
    ],
    install_requires=[‘setuptools’],
    zip_safe=True,
    maintainer=’user’,
    maintainer_email=’user@todo.todo’,
    description=’TODO: Package description’,
    license=’Apache-2.0′,
    tests_require=[‘pytest’],
    entry_points={
        ‘console_scripts’: [
        ‘talker = py_pubsub.pypubsub:main’,
        ],
    },
)

 

Kuunda hati ya pili ya python

Hati hii mpya tunayopaswa kuunda itatumika kujaribu ya awali ili kuhakikisha kwamba mchapishaji na anayejisajili wanaendesha vyema.

Unda faili mpya kama hapo awali iliitwa test.py

Nakili na ubandike msimbo ufuatao

import rclpy
from rclpy.node import Node
import math

from std_msgs.msg import Float32


class MinimalPublisher(Node):

    def __init__(self):
        super().__init__(‘minimal_publisher’)
        self.publisher_ = self.create_publisher(Float32, ‘input_topic’, 10)
        timer_period = 0.5  # seconds
        self.timer = self.create_timer(timer_period, self.timer_callback)
        self.i = 0.0

    def timer_callback(self):
        msg = Float32()
        msg.data =  self.i
        self.publisher_.publish(msg)
        self.get_logger().info(‘Publishing: “%s”‘ % msg.data)
        if(self.i > math.pi):
            self.i = 0
        self.i += 0.01


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()

Hati hii hutumia kipima muda baada ya kila sekunde 0.5 ili kuchapisha ujumbe kwa digrii kwenye mada inayoitwa input_topic.

Ongeza amri ifuatayo kwa setup.py yako

‘test =  py_pubsub.test:main’,

 

Faili yako ya setup.py sasa inapaswa kuonekana kama hii

from setuptools import setup

package_name = ‘py_pubsub’

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’]),
    ],
    install_requires=[‘setuptools’],
    zip_safe=True,
    maintainer=’user’,
    maintainer_email=’user@todo.todo’,
    description=’TODO: Package description’,
    license=’Apache-2.0′,
    tests_require=[‘pytest’],
    entry_points={
        ‘console_scripts’: [
        ‘talker = py_pubsub.py_pubsub:main’
        ‘test =  py_pubsub.test:main’,
        ],
    },
)

 

Ongeza mabadiliko yafuatayo kwenye package.xml yako

 

  <exec_depend>rclpy</exec_depend>
  <exec_depend>std_msgs</exec_depend>

 

Sasa kifurushi chako.xml kinapaswa kuwa kama ifuatavyo

<?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>py_pubsub</name>
  <version>0.0.0</version>
  <description>TODO: Package description</description>
  <maintainer email=“user@todo.todo”>user</maintainer>
  <license>Apache-2.0</license>

  <test_depend>ament_copyright</test_depend>
  <test_depend>ament_flake8</test_depend>
  <test_depend>ament_pep257</test_depend>
  <test_depend>python3-pytest</test_depend>
  <exec_depend>rclpy</exec_depend>
  <exec_depend>std_msgs</exec_depend>

  <export>
    <build_type>ament_python</build_type>
  </export>
</package>

 

Kuunda vifurushi vyetu vya ros2

Ingiza amri ifuatayo kwenye terminal

cd
cd ros2_ws


Ingiza amri zifuatazo

colcon build
source install/setup.bash

Kujaribu nodi mbili

Katika terminal ya kwanza kukimbia

ros2 run py_pubsub talker

 

 

Katika terminal ya pili endesha amri zifuatazo

cd ros2_ws
source install/setup.bash
ros2 run py_pubsub test


Unaweza basi katika kukimbia kwa terminal ya tatu 

ros2 topic list
ros2 topic echo /output_topic


Pamoja na hayo, tumethibitisha kwamba nodi mbili zinafanya kazi, Hapa kuna kiunga cha rosject https://app.theconstructsim.com/Desktop 

 

 

Kozi & Mafunzo Husika

Ikiwa unataka kujifunza zaidi kuhusu ROS na ROS2, tunapendekeza kozi zifuatazo:

Video Tutorial

Wie erstellt man ein ROS2 Launch File mit Python – German ROS Tutorial

Wie erstellt man ein ROS2 Launch File mit Python – German ROS Tutorial

This tutorial is created by RA 015 Frederick

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

 

Was wir lernen werden:

  1. Wie erstellt man ein ROS2 Launch File in einem ROS2 Paket
  2. Welche Anpassungen man machen muss
  3. Wie man Nodes in das Launch File hinzufügt
  4. Wie man das Aufnehmen eines ROS-Bags in einem Launch-File hinzufügt
  5. Welche Vorteile es hat, ein Launch File in Python zu schreiben

 

Verwendete Ressourcen:

  1. Verwende dieses rosject oder ein eigenes ROS2 Python Paket https://app.theconstructsim.com/l/5e5aaf01/
  2. The Construct: The Construct: Where Your Robotics Career Happens (theconstructsim.com)
  3. ROS2 Kurse bei The Construct:
    1. ROS2 Basics in 5 Days (Python) | Robotics & ROS Online Courses | The Construct (theconstructsim.com)
    2. Linux for Robotics | Robotics & ROS Online Courses | The Construct (theconstructsim.com)

Überblick:

Mehrere Nodes können gleichzeitig über ein gemeinsames Launch-File gestartet werden. Durch die Verwendung von Python für diesen Start können einfach komplexe Algorithmen implementiert werden.

Öffne das Rosject

Über RUN kann das rosject gestartet werden.

Als nächstes erstellen wir den Ordner „launch“ in dem Package „start“, in welchem wir unsere Launch Files speichern werden.


In diesem erstellen wir das Launch File „general_launch.py“.

Anschließend passen wir die setup.py an, damit das Launch File gestartet werden kann.

 

Als nächsten programmieren wir das eigentliche Launch File. In diesem wird die Funktion „generate_launch_description()“ verwendet, deshalb definieren wird diese und geben für einen ersten Test erstmal eine leere Liste zurück.

Anschließend starten wir das Launch File, indem wir erst in den Workspace gehen, dann den Bauprozess durchlaufen und danach mit „ros2 launch start general_launch.py“ das Ganze starten. Tipp: durch die Verwendung von TAB kann man sich viel Tipparbeit und Zeit sparen.

 

Das sollte erfolgreich keine Nodes starten, da unsere Launch Description aktuell leer ist.

Um dies zu ändern, füllen wir die Launch Description mit zwei Nodes und nehmen einen Prozess zum ROS-Bag aufnehmen auf.

Nun können wir das ganze erneut bauen und starten. Wir erhalten:

Wir haben nun mit einem ROS2 Launch File mehrere Nodes und Prozesse gestartet.
Dies kann nun beliebig erweitert werden.

Wir hoffen, dass dieser Post helfen konnte und Dir gefallen hat.

 

Video Tutorial

 

Pin It on Pinterest