Publisher trong ROS2 là gì?
Atlasbot · 16/8/2026 · 10 phút đọc
Một node muốn "nói" ra dữ liệu — vị trí robot, ảnh camera, trạng thái pin — cần tạo ra một Publisher: đối tượng đại diện cho quyền gửi dữ liệu lên một topic cụ thể.
Khái niệm chính
Publisher được tạo bên trong một node, gắn với tên topic và kiểu message cố định. Sau khi tạo, node chỉ cần gọi .publish(msg) bất cứ khi nào có dữ liệu mới cần gửi — thường đặt trong một hàm chạy định kỳ nhờ Timer của ROS2 (ví dụ publish mỗi 0,1 giây = tần suất 10Hz).
Publisher không chờ phản hồi, không biết có subscriber nào đang lắng nghe hay không — nó chỉ đẩy dữ liệu vào hệ thống, giao cho DDS bên dưới lo việc chuyển tới các subscriber đã đăng ký (xem thêm bài Topic và DDS).
Tóm lại: Tạo publisher không đồng nghĩa dữ liệu sẽ tự động được gửi — phải chủ động gọi
.publish()mỗi lần có dữ liệu mới, thường thông qua một Timer chạy đều đặn.
Nguyên lý hoạt động
Publisher tối thiểu trong một node Python, gửi vận tốc cố định mỗi 0,5 giây:
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import Twist
class VelocityPublisher(Node):
def __init__(self):
super().__init__('velocity_publisher')
self.publisher_ = self.create_publisher(Twist, '/cmd_vel', 10) # 10 = độ sâu hàng đợi (QoS)
self.timer = self.create_timer(0.5, self.timer_callback) # gọi lại mỗi 0.5s
def timer_callback(self):
msg = Twist()
msg.linear.x = 0.2 # 0.2 m/s tiến thẳng
self.publisher_.publish(msg)
self.get_logger().info(f'Đã gửi: linear.x = {msg.linear.x}')
Tham số 10 trong create_publisher(Twist, '/cmd_vel', 10) là độ sâu hàng đợi tin nhắn (một phần của QoS — Quality of Service, xem bài riêng) — số lượng message tối đa được giữ trong bộ đệm nếu bên nhận xử lý chưa kịp.
Kiến thức kỹ thuật
Công thức
Chu kỳ Timer và tần suất publish liên hệ nghịch đảo với nhau — cần tính đúng khi yêu cầu là "publish ở X Hz":
Chu kỳ Timer (giây) = 1 / Tần suất mong muốn (Hz)
VD: cần publish odometry ở 50Hz
Chu kỳ Timer = 1 / 50 = 0.02 giây → create_timer(0.02, callback)
Thông số
Tần suất publish hợp lý khác nhau đáng kể tuỳ loại dữ liệu — publish quá nhanh lãng phí băng thông, quá chậm khiến dữ liệu "cũ" ảnh hưởng vòng điều khiển phía sau:
| Loại dữ liệu | Tần suất publish điển hình |
|---|---|
Odometry (/odom) | 20 – 50Hz |
LiDAR scan (/scan) | 10 – 15Hz |
| Camera image | 15 – 30fps |
| Trạng thái pin/chẩn đoán | 1Hz hoặc chỉ khi thay đổi (event-driven) |
Ứng dụng thực tế
Không phải mọi publisher đều publish theo Timer cố định — dữ liệu ít thay đổi như trạng thái pin hay cờ lỗi phần cứng thường publish theo kiểu event-driven (chỉ gửi khi giá trị thực sự đổi, hoặc publish chậm hơn nhiều so với dữ liệu cảm biến chính) để tiết kiệm băng thông và tránh làm "ngập" các topic khác đang cần băng thông cho dữ liệu tần suất cao như LiDAR/camera. Thiết kế đúng tần suất cho từng loại publisher là quyết định kiến trúc quan trọng khi nhiều node cùng chạy trên 1 mạng nội bộ robot.
Ví dụ thực tế
Publisher tương đương viết bằng C++ (rclcpp), thường dùng khi cần hiệu năng cao hơn Python:
#include "rclcpp/rclcpp.hpp"
#include "geometry_msgs/msg/twist.hpp"
class VelocityPublisher : public rclcpp::Node {
public:
VelocityPublisher() : Node("velocity_publisher") {
publisher_ = create_publisher<geometry_msgs::msg::Twist>("/cmd_vel", 10);
timer_ = create_wall_timer(std::chrono::milliseconds(500),
std::bind(&VelocityPublisher::timer_callback, this));
}
private:
void timer_callback() {
auto msg = geometry_msgs::msg::Twist();
msg.linear.x = 0.2;
publisher_->publish(msg);
}
rclcpp::Publisher<geometry_msgs::msg::Twist>::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr timer_;
};
$ ros2 topic echo /cmd_vel
Output:
linear:
x: 0.2
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 0.0
---
Cấu trúc logic giống hệt bản Python — tạo publisher, tạo timer, publish trong callback — chỉ khác cú pháp ngôn ngữ, minh chứng cho khả năng đa ngôn ngữ đã nói ở bài ROS2 là gì.
Lỗi thường gặp
- Tạo publisher nhưng quên gọi
.publish()— publisher tồn tại, node chạy bình thường, nhưng không có message nào thực sự được gửi đi — kiểm tra bằngros2 topic echođể xác nhận thực sự có dữ liệu chảy qua, không chỉ tin vào việc code "chạy không lỗi". - Độ sâu hàng đợi (queue depth) quá nhỏ so với tốc độ xử lý của subscriber — nếu subscriber xử lý chậm hơn tốc độ publish, message cũ trong hàng đợi bị loại bỏ khi hàng đợi đầy — với dữ liệu cần đầy đủ không được mất (VD: log sự kiện quan trọng), cần tăng queue depth hoặc đổi chính sách QoS phù hợp.
- Publish ngay khi node vừa khởi tạo, trước khi discovery hoàn tất — DDS cần một khoảng thời gian ngắn để publisher/subscriber "tìm thấy nhau" qua discovery — vài message đầu tiên gửi ngay lúc node vừa start có thể bị mất vì subscriber chưa kịp kết nối, không phải lỗi code.
- Tái sử dụng cùng 1 object message giữa các lần publish mà quên reset field — nếu chỉ cập nhật 1 vài field rồi publish lại object cũ, các field không được cập nhật vẫn giữ giá trị từ lần trước, có thể vô tình gửi dữ liệu "cũ" lẫn vào dữ liệu mới.
Kết luận
Publisher là nửa "gửi" của mô hình pub-sub trong ROS2 — nắm vững cách tạo publisher, chọn đúng tần suất publish theo loại dữ liệu, và hiểu độ sâu hàng đợi ảnh hưởng thế nào tới việc mất dữ liệu là kỹ năng cơ bản khi viết bất kỳ node ROS2 nào. Bài tiếp theo trong chuyên mục nói về Subscriber — nửa còn lại, phía nhận dữ liệu từ topic.