Trước khi đi vào nội dung chính, nếu bạn nào chưa biết Service trong ROS2 là gì thì mình khuyên các bạn nên ghé qua video mình hướng dẫn các câu lệnh cơ bản với Dịch vụ trong ROS2 trước ở link này nhé:
1. Tạo một package
Đầu tiên, chúng ta sẽ tạo một package chứa code của Client cần tạo.
Tạo một package mới tên là client_pkg trong thư mục ~/ros2_ws/src bằng các lệnh sau:
Chúng ta sẽ cần package std_srvs để giao tiếp với các Dịch vụ /moving và /stop như trong video trước mình đã hướng dẫn.
2. Tạo một tệp chứa nội dung code của Client
Tiếp theo, trong thư mục client_pkg của package vừa tạo, ta sẽ tạo một tệp mới tên là service_client.py chứa phần code chính của Service Client mà chúng ta muốn tạo. Các bạn hãy copy nội dung phần code sau vào trong file service_client.py vừa tạo:
# import the empty module from std_servs Service interface
from std_srvs.srv import Empty
# import the ROS2 Python client libraries
import rclpy
from rclpy.node import Node
class ClientAsync(Node):
def __init__(self):
# Here you have the class constructor
# call the class constructor to initialize the node as service_client
super().__init__('service_client')
# create the Service Client object
# defines the name and type of the Service Server you will work with.
self.client = self.create_client(Empty, 'moving')
# checks once per second if a Service matching the type and name of the Client is available.
while not self.client.wait_for_service(timeout_sec=1.0):
# if it is not available, a message is displayed
self.get_logger().info('service not available, waiting again...')
# create an Empty request
self.req = Empty.Request()
def send_request(self):
# send the request
self.future = self.client.call_async(self.req)
def main(args=None):
# initialize the ROS communication
rclpy.init(args=args)
# declare the node constructor
client = ClientAsync()
# run the send_request() method
client.send_request()
while rclpy.ok():
# pause the program execution, waits for a request to kill the node (ctrl+c)
rclpy.spin_once(client)
if client.future.done():
try:
# checks the future for a response from the Service
# while the system is running.
# If the Service has sent a response, the result will be written
# to a log message.
response = client.future.result()
except Exception as e:
# Display the message on the console
client.get_logger().info(
'Service call failed %r' % (e,))
else:
# Display the message on the console
client.get_logger().info(
'the robot is moving' )
break
client.destroy_node()
# shutdown the ROS communication
rclpy.shutdown()
if __name__ == '__main__':
main()
Mình sẽ giải thích qua đoạn code này nhé. Phần code trong file này cũng đi theo cấu trúc chung của một node trong ROS2. Ở trong hàm main(), sau khi khởi tạo ROS2, chương trình sẽ khai báo object ClientAsync() mà chúng ta đã định nghĩa ở phần trên. Trong phần constructor của class ClientAsync(), lệnh để tạo Client là:
self.client = self.create_client(Empty, 'moving')
Lệnh này sẽ tạo một Client sử dụng kiểu dữ liệu Empty và kết nối tới một Service tên là /moving.
Tiếp theo, vòng lặp while được sử dụng để đảm bảo rằng Service Server (/moving ) đang hoạt động.
while not self.client.wait_for_service(timeout_sec=1.0):
Quay trở lại với hàm main(). Sau khi khởi tạo xong, yêu cầu sẽ được gửi tới server bằng hàmsend_request(). Hàm này rất quan trọng và có nội dung là:
self.future = self.client.call_async(self.req)
Câu lệnh này sẽ gửi một yêu cầu không đồng bộ (asynchronous request) tới Service Server bằng hàm call_async(). Sau đó, lưu phản hồi từ Server về biến self.future. Biến này cũng rất quan trọng. Sau khi tạo yêu cầu, Server sẽ ngay lập tức trả về future cho biết việc gọi và phản hồi được thực hiện (nhưng nó không chứa nội dung của phản hồi).
Cuối cùng, trong hàm main() là vòng lặp while:
while rclpy.ok():
# pause the program execution, waits for a request to kill the node (ctrl+c)
rclpy.spin_once(client)
if client.future.done():
try:
# checks the future for a response from the Service
# while the system is running.
# If the Service has sent a response, the result will be written
# to a log message.
response = client.future.result()
except Exception as e:
# Display the message on the console
client.get_logger().info(
'Service call failed %r' % (e,))
else:
# Display the message on the console
client.get_logger().info(
'the robot is moving' )
break
Khi chương trình đang chạy (rclpy.ok()), nó sẽ liên tục kiểm tra xem phản hồi từ dịch vụ đã hoàn thành hay chưa:
if client.future.done():
Nếu nó đã hoàn thành thì chương trình sẽ nhận được giá trị của phản hồi từ Server:
response = client.future.result()
Trong trường hợp này, vì chúng ta đang làm việc với một phản hồi kiểu Empty nên chúng ta sẽ không làm gì cả.
Trong khi đợi phản hồi hoàn thành của Dịch vụ, chương trình sẽ in ra các thông tin vào nhật ký node bằng lệnh:
client.get_logger().info('the robot is moving' )
Và cuối cùng phải kể đến dòng lệnh sau:
rclpy.spin_once(client)
Dòng lệnh này tương tự như lệnh rclpy.spin() nhưng chỉ quay vòng node đó một lần.
3. Tạo một tệp launch
Tiếp theo, chúng ta sẽ tạo một file launch để khởi tạo client này.
Tạo một file launch tên là service_client_launch_file.launch.py bằng các lệnh sau:
cd ~/ros2_ws/src/client_pkg
mkdir launch
cd launch
touch service_client_launch_file.launch.py
chmod +x service_client_launch_file.launch.py
Mở file launch vừa tạo và thêm những dòng sau vào:
from launch import LaunchDescription
from launch_ros.actions import Node
def generate_launch_description():
return LaunchDescription([
Node(
package='client_pkg',
executable='service_client',
output='screen'),
])
4. Sửa tệp setup.py
Sau khi tạo xong file launch, chúng ta sẽ thêm một số dòng code vào file setup.py để cài đặt tệp launch mà chúng ta vừa mới tạo và thêm entry points vào executable cho script service_client.py
Sau khi dòng code này chạy, chúng ta sẽ thấy robot bắt đầu di chuyển ngẫu nhiên. Điều này chứng tỏ Service Server /moving đã nhận được yêu cầu từ Client và thực hiện yêu cầu đó.
Đầu tiên chúng ta sẽ làm quen với câu lệnh giúp liệt kê tất cả các Dịch vụ đang sẵn sàng hoạt động trên hệ thống ROS2 bằng lệnh sau:
source /opt/ros/humble/setup.sh
ros2 service list
Sau khi nhập lệnh trên, Terminal sẽ hiện ra danh sách đầy đủ các Dịch vụ đang sẵn sàng hoạt động. Trong bài hôm nay, chúng ta sẽ làm việc với hai Dịch vụ rất đơn giản là /moving và /stop . Khi gọi Dịch vụ /moving thì robot sẽ tự động di chuyển ngẫu nhiên, còn khi gọi Dịch vụ /stop thì robot sẽ dừng di chuyển.
Thông tin chi tiết về một Dịch vụ
Sau khi đã liệt kê các Dịch vụ đang sẵn sàng hoạt động, chúng ta cùng tìm hiểu các câu lệnh để xem thông tin chi tiết về chúng. Để biết một Dịch vụ có kiểu dữ liệu là gì, ta sử dụng câu lệnh ros2 service type . Các bạn hãy thử câu lệnh này với Dịch vụ /moving nhé:
ros2 service type /moving
Terminal hiện ra kiểu dữ liệu của Dịch vụ /moving là std_srvs/srv/Empty . Hi vọng các bạn còn nhớ cách để xem thông tin chi tiết của một interface mà mình đã hướng dẫn trong các bài trước. Ở đây để xem thông tin chi tiết của kiểu dữ liệu std_srvs/srv/Empty mình sử dụng lệnh liên quan tới interface như sau:
ros2 interface show std_srvs/srv/Empty
Terminal hiện lên ba dấu gạch ngang. Một kiểu dữ liệu Dịch vụ sẽ gồm hai phần là Yêu cầu (Request) ở trước ba dấu gạch ngang và phần Phản hồi (Response) ở sau ba dấu gạch ngang. Tuy nhiên, trong trường hợp này ta không thấy thông tin gì hiện lên ngoài ba dấu gạch ngang. Đây là do kiểu dữ liệu std_srvs/srv/Empty không yêu cầu truyền dữ liệu gì cả, mà nó thường được dùng để chỉ kích hoạt một hành động nào đó của robot mà không tốn băng thông truyền tải dữ liệu.
Hầu hết các kiểu dữ liệu Dịch vụ nào cũng sẽ có quy định cụ thể yêu cầu và phản hồi là gì. Ta cùng thử một loại kiểu dữ liệu Dịch vụ khác để thấy rõ hơn:
ros2 interface show std_srvs/srv/SetBool
Như các bạn thấy trên Terminal, Dịch vụ này chỉ nhận Yêu cầu là một dữ liệu dạng bool và sẽ phản hồi hai lại hai thông tin. Một thông tin là tín hiệu xác nhận Dịch vụ đã được kích hoạt hay chưa, cũng thuộc dạng bool. Thông tin còn lại là một thông báo chi tiết định dạng string.
Gọi một Dịch vụ
Sau khi đã liệt kê các Dịch vụ đang sẵn sàng hoạt động và thông tin chi tiết của chúng, chúng ta sẽ tiếp tục tìm hiểu cách gọi các Dịch vụ mà chúng ta cần. Câu lệnh để gọi một Dịch vụ là ros2 service call. Câu lệnh này có cú pháp như sau:
ros2 service call <service_name> <service_type> <value>
Trong đó:
<service_name>: tên của Dịch vụ
<service_type>: kiểu dữ liệu của Dịch vụ
<value>: giá trị muốn truyền tới Dịch vụ trong Yêu cầu
Trong bài hôm nay, chúng ta sẽ thực hành gọi một Dịch vụ cụ thể /moving với câu lệnh như sau:
ros2 service call /moving std_srvs/srv/Empty
Như mình đã nói ở trên, do kiểu dữ liệu của Dịch vụ std_srvs/srv/Empty không yêu cầu dữ liệu truyền vào nên phần Giá trị <value> của câu lệnh được bỏ trống. Khi nhập câu lệnh này, hệ thống sẽ thực hiện tuần tự các hoạt động như sau:
Tìm một Dịch vụ trên hệ thống có tên là /moving
Tạo một Client (Khách hàng) trên hệ thống với một Yêu cầu được gửi tới Dịch vụ tương ứng
Đợi Server trả lời và thông báo kết quả
Đồng thời các bạn sẽ thấy là trong phần mô phỏng Gazebo thì robot cũng bắt đầu chạy sau khi mình gọi Dịch vụ /moving .
Bây giờ mình sẽ gửi yêu cầu gọi tới Dịch vụ /stop để dừng robot lại. Cách làm cũng hoàn toàn tương tự như khi gọi Dịch vụ /moving .
Ta cùng xem kiểu dữ liệu của Dịch vụ /stop bằng lệnh:
ros2 service type /stop
Vậy là kiểu dữ liệu của Dịch vụ /stop cũng giống như Dịch vụ /moving, đều là std_srvs/srv/Empty. Tiếp theo, ta sẽ gọi Dịch vụ /stop này để dừng robot lại vơi cách làm tương tự như khi gọi Dịch vụ /moving:
ros2 service call /stop std_srvs/srv/Empty
Sau khi Dịch vụ /stop được gọi thì robot cũng đã dừng lại trên phần mô phỏng.
Vậy là trong bài viết này mình đã hướng dẫn các bạn các câu lệnh cơ bản để thao tác với Dịch vụ (Service) ở trong ROS2. Chúc các bạn thực hành thành công!
Cách khai báo tọa độ cho robot để di chuyển trong không gian Gazebo và RViz
Cách tạo câu lệnh để điều khiển robot di chuyển với gói Nav2
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/
Ở 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:
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:
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:
Ở đâ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
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.
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.
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:
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.
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.
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.
Đế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é.
Nếu các bạn chưa biết cách tạo package trong ros2 như thế nào thì các bạn có thể xem lại video hướng dẫn tạo package của mình trước đây nhé.
Tạo một file subscriber
Tiếp theo, chúng ta sẽ tạo một file tên là simple_subscriber.py trong thư mục subscriber_pkg ở trong package subscriber_pkg mà chúng ta vừa tạo.
Tiếp theo, các bạn hãy copy những dòng code sau vào file simple_subscriber.py
import rclpy
# import the ROS2 python libraries
from rclpy.node import Node
# import the LaserScan module from sensor_msgs interface
from sensor_msgs.msg import LaserScan
# import Quality of Service library, to set the correct profile and reliability to read sensor data.
from rclpy.qos import ReliabilityPolicy, QoSProfile
class SimpleSubscriber(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_subscriber')
# create the subscriber object
# in this case, the subscriptor will be subscribed on /scan topic with a queue size of 10 messages.
# use the LaserScan module for /scan topic
# send the received info to the listener_callback method.
self.subscriber = self.create_subscription(
LaserScan,
'/scan',
self.listener_callback,
QoSProfile(depth=10, reliability=ReliabilityPolicy.RELIABLE)) # is the most used to read LaserScan data and some sensor data.
def listener_callback(self, msg):
# print the log info in the terminal
self.get_logger().info('I receive: "%s"' % str(msg))
def main(args=None):
# initialize the ROS communication
rclpy.init(args=args)
# declare the node constructor
simple_subscriber = SimpleSubscriber()
# pause the program execution, waits for a request to kill the node (ctrl+c)
rclpy.spin(simple_subscriber)
# Explicity destroy the node
simple_subscriber.destroy_node()
# shutdown the ROS communication
rclpy.shutdown()
if __name__ == '__main__':
main()
File simple_subscriber.py này chứa nội dung của một node subscriber đơn giản. 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 subscriber_pkg đang làm việc bằng lệnh sau:
cd ~/ros2_ws/src/subscriber_pkg
mkdir launch
Tiếp theo, ta sẽ tạo một file launch tên là subscriber_pkg_launch_file.launch.py và chuyển nó sang chế độ executable để chạy node subscriber:
cd ~/ros2_ws/src/subscriber_pkg/launch
touch subscriber_pkg_launch_file.launch.py
chmod +x subscriber_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='subscriber_pkg',
executable='simple_subscriber',
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:
Nếu node hoạt động bình thường thì Terminal sẽ hiển thị nội dung message mà subscriber đọc được.
Giải thích code tạo subscriber
Quy trình hoạt động
def main(args=None):
# initialize the ROS communication
rclpy.init(args=args)
# declare the node constructor
simple_subscriber = SimpleSubscriber()
# pause the program execution, waits for a request to kill the node (ctrl+c)
rclpy.spin(simple_subscriber)
# Explicity destroy the node
simple_subscriber.destroy_node()
# shutdown the ROS communication
rclpy.shutdown()
Đầu tiên, hệ thống sẽ khởi tạo giao tiếp ROS2
rclpy.init(args=args)
Khai báo và khởi tạo node subscriber
simple_subscriber = SimpleSubscriber()
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_subscriber)
Hủy node
simple_subscriber.destroy_node()
Tắt hệ thống giao tiếp ROS2
rclpy.shutdown()
Tạo class SimpleSubscriber
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_subscriber'
super().__init__('simple_subscriber')
Phần quan trọng nhất của code là mục tạo subscriber cho topic /scan sử dụng LaserScan message:
Ngoài ra, trong lệnh này chúng ta cũng thực hiện hai việc:
Khai báo hàm callback self.listener_callback cho subscriber. Mỗi lần subscriber này đọc được một message mới từ topic /scan thì hàm callback này sẽ được thực hiện. Hàm self.listener_callback trong node chúng ta tạo chỉ thực hiện một hành động đơn giản là in nội dung message mà subscriber đọc được ra Terminal.
Định nghĩa QoSProfile – Quality of Service của topic. Hiện tại thì các bạn chưa cần hiểu QoS là gì và chỉ cần biết rằng các thiết lập QoS của topic publisher và subscriber phải phù hợp với nhau. Nếu chúng không phù hợp thì giao tiếp giữa subscriber và puplisher sẽ không hoạt động.
Các bạn có thể xem QoSProfile của topic /scan bằng lệnh sau:
ros2 topic info /scan -v
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 subscriber 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.
This tutorial is created by Robotics Ambassador Eric
Những điều bạn sẽ được biết trong blog này:
Cách cài đặt các packages cần thiết để mô phỏng nhiều robots ở ROS2 Humble
Cách tạo launch file để mô phỏng nhiều robot trên gazebo
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/
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:
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:
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:
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.
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
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.
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.
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:
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é.
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.
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
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
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.
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.
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
Đế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é.
Trong bài toán định vị (localization) ở robot, chúng ta cần sự trợ giúp từ các cảm biến như IMU, GPS, và Odometry. Bằng cách kết hợp các tín hiệu từ các cảm biến lại với nhau(sensor fusion) một lượng thông tin bổ ích sẽ giúp cho robot định vị được tọa độ chính xác trong các môi trường khác nhau. Trong project này, mình sẽ giới thiệu tới các bạn Robot Localization package một công cụ hữu ích để tích hợp sensor fusion cùng với turtlebot 3. Ở project này, mức độ đòi hỏi các bạn cần có một số kiến thức cơ bản như urdf, frames,… để có thể chỉnh sửa và thay đổi các file sẵn có. Hy vọng blog này sẽ mang lại cái nhìn tổng quan về chủ đề sensor fusion và localization ở robotics.
Khởi động rosject
The Construct đã tạo ra một công cụ rất hữu ích là rosject, 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:
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:
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:
Sau bước này thì các bạn sẽ cần một ít thời gian để có thể cài đặt thành công các gói cần thiết cho chương trình.
Tiếp theo sử dụng câu lệnh sau để build môi trường catkin
colcon build --symlink-install
Mình sẽ giải thích lý do tại sao các bạn lại phải sử dụng đường link github phía bên trên. Vì ở trong project này mình muốn tổng hợp và sử dụng những packages cần thiết và chỉnh sửa một số nội dung file nên mình đã gom thành một package lớn bao gồm các packages nhỏ để tiện cho việc chạy chương trình.
Mô phỏng trên Gazebo cùng robot
Sau khi đã cài đặt đầy đủ các package cần thiết. Bước tiếp theo bạn cần chọn model robot cho project của bạn. mình sử dụng waffle trong project này:
source ~/ros2_ws/install/setup.bash
export TURTLEBOT3_MODEL=waffle
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:
Cửa sổ Gazebo sẽ xuất hiện như trên cùng với robot waffle.
Chạy Robot Localization package
Như bạn thấy môi trường và robot đã có, điểm quan trọng trong project lần này là biết cách áp dụng các cảm biến như IMU, Odometry, GPS,.. cùng với robot trong nhiệm vụ tổng hợp các tiến hiệu từ các cảm biến khác để giải quyết bài toán định vị cho robot. Ở cửa sổ thứ 2, các bạn thực hiện câu lệnh sau đây:
source ~/ros2_ws/install/setup.bash
ros2 launch robot_localization ekf.launch.py
Tạo và mô phỏng robot cùng cảm biến trên Rviz
Sau khi đã có môi trường cho robot hoạt động là gazebo, có các cảm biến được tích hợp trên robot, nhiệm vụ là làm sao để có thể xác định và sử dụng các cảm biến để phục vụ cho bài toán định vị trên robot. Ở đây trên Rviz sẽ mô phỏng và cho thấy các trục tọa độ của từng thành phần được gắn vào robot. Trong các thư mục được cài đặt mình cũng đã chỉnh sửa phần mô phỏng để cho thấy sự thay đổi khi sử dụng gói Robot Localization:
Các bạn lưu ý là ở đây odom sẽ là frame chính của chúng ta, sau khi mô phỏng trên RViz thành công bây giờ các bjan có thể tận dụng các cảm biến đưuọc cài đặt sẵn trên robot và phục vụ cho project của các bạn như SLAM,…
Thêm một chút gợi ý nho nhỏ cho các bạn để kiểm tra rằng mình đã có các topic và frame được liên kết với nhau chưa. Đầu tiên mình sử dụng:ros2 topic list và kết quả xuất hiện các topic như GPS, IMU, Odometry.
Bên cạnh đó, ROS cung cấp câu lệnh để kiểm tra sự kết nối giữa các frame được khai báo: ros2 run tf2_tools view_frames.py
Sau đó file sẽ được lưu dưới dạng pdf và bạn chỉ việc mở lên để kiểm tra bằng câu lệnh sau:
evince frames.pdf
Để có được kết quả như trên, thì chủ yếu mình đã chỉnh sửa và thêm vào IMU và GPS tại urdf file. Các bạn có thể mở gói turtlebot3_description để chỉnh sửa tùy theo project của mình.
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é.