Khái niệm về Node

Hãy xét một robot tự hành trong kho. LiDAR liên tục tạo dữ liệu quét, khối localization ước lượng pose, planner tìm đường, còn bộ điều khiển bánh xe biến vận tốc mong muốn thành tín hiệu cho động cơ. Có thể đặt toàn bộ logic này trong một chương trình duy nhất, nhưng khi localization bị lỗi hoặc driver LiDAR cần thay thế, mọi thành phần còn lại cũng bị kéo vào cùng một vòng sửa đổi và kiểm thử.

ROS 2 giải quyết vấn đề đó bằng cách biểu diễn hệ thống như một tập các thành phần có trách nhiệm riêng, trao đổi dữ liệu qua những interface đã định nghĩa. Mỗi thành phần logic như vậy thường được hiện thực thành một node. Node không chỉ là một đoạn chương trình đang chạy; nó là đơn vị tham gia ROS graph, sở hữu các đầu giao tiếp và nhận công việc thông qua callback.

Phân rã hệ thống robot

Trong chương trình nguyên khối, các hàm đọc cảm biến, ước lượng pose, lập kế hoạch và điều khiển có thể gọi trực tiếp lẫn nhau. Cách này đơn giản khi hệ thống còn nhỏ vì dữ liệu nằm trong cùng bộ nhớ và luồng thực thi dễ nhìn thấy. Vấn đề xuất hiện khi từng phần có nhịp hoạt động, yêu cầu tài nguyên và vòng đời khác nhau.

Driver LiDAR có thể phát dữ liệu ở 20 Hz. Localization phải phản ứng theo từng bản quét. Planner chỉ cần chạy khi mục tiêu hoặc bản đồ thay đổi. Bộ điều khiển vận tốc lại cần chu kỳ ổn định và độ trễ thấp. Ép tất cả vào một khối làm ranh giới trách nhiệm mờ đi: một callback chậm có thể ảnh hưởng vòng điều khiển, lỗi phần cứng có thể làm sập cả hệ thống, còn việc thay một thuật toán localization buộc ta hiểu quá nhiều trạng thái nội bộ không liên quan.

Một cách phân rã hợp lý cho robot kho là:

  • lidar_driver: giao tiếp với LiDAR và phát bản quét;
  • localization: nhận bản quét cùng odometry để ước lượng pose;
  • planner: nhận pose, bản đồ và mục tiêu để tạo quỹ đạo;
  • base_controller: nhận lệnh vận tốc và điều khiển bánh xe.

Mỗi node nên đảm nhiệm một mục đích logic có thể mô tả rõ. Đây không phải quy tắc “mỗi hàm là một node”. Ranh giới node chỉ có ích khi nó cô lập được một trách nhiệm, một failure mode, một vòng đời hoặc một chính sách thực thi có ý nghĩa. Tài liệu ROS 2 cũng mô tả node là một participant trong ROS graph và thường là đơn vị tính toán thực hiện một công việc logic (ROS 2 Nodes (opens in a new tab)).

Tách được trách nhiệm mới chỉ trả lời hệ thống gồm những khối nào. Các khối đó vẫn cần tìm thấy nhau và thống nhất cách trao đổi dữ liệu. Đây là vai trò của ROS graph.

Node và ROS graph

Tại thời điểm chạy, có thể xem ROS graph như một đồ thị động:

G(t)=(V(t), E(t)),\mathcal{G}(t) = \bigl(\mathcal{V}(t),\, \mathcal{E}(t)\bigr),

trong đó:

  • V(t)\mathcal{V}(t) là tập các node đang hiện diện tại thời điểm tt;
  • E(t)\mathcal{E}(t) là tập quan hệ giao tiếp đang có thể hình thành giữa các endpoint của chúng;
  • tt nhấn mạnh rằng node có thể xuất hiện, biến mất hoặc thay đổi kết nối trong lúc hệ thống vận hành.

Một node không cần giữ danh sách địa chỉ IP của tất cả node khác. Khi khởi động, các endpoint do node tạo ra tham gia cơ chế discovery của middleware. Những publisher và subscriber phù hợp có thể tìm thấy nhau dựa trên tên interface, kiểu dữ liệu và các chính sách tương thích. Vì discovery mang tính phân tán, các node có thể giao tiếp khi nằm trong cùng process, khác process hoặc thậm chí khác máy, miễn là cấu hình mạng và ROS domain cho phép. Đây là lý do graph là mô hình tốt hơn một cây gọi hàm cố định (ROS 2 Discovery (opens in a new tab)).

Giả sử lidar_driver tạo một publisher cho topic /scan, còn localization tạo một subscription cho cùng topic. Quan hệ quan trọng trong graph không phải là “localization gọi hàm của lidar_driver”, mà là hai endpoint cùng tham gia một contract truyền thông. Nếu sau này LiDAR thật được thay bằng simulator nhưng simulator vẫn cung cấp contract phù hợp, localization không cần biết nguồn dữ liệu đã đổi.

Node vì thế là nơi tập hợp các endpoint và trạng thái logic của một thành phần. Muốn hiểu node làm gì, ta phải nhìn vào các interface mà nó cung cấp và sử dụng.

Topic, service, action và parameter

Một node thực tế thường đồng thời có nhiều publisher, subscription, service client/server, action client/server và parameter. Chúng không phải các lựa chọn loại trừ nhau; mỗi interface diễn đạt một kiểu quan hệ khác nhau (ROS 2 Interfaces (opens in a new tab)).

InterfaceQuan hệTrường hợp phù hợp trên robot kho
TopicLuồng dữ liệu bất đồng bộLiDAR scan, pose, odometry, velocity command
ServiceRequest/response ngắnXóa trạng thái, truy vấn cấu hình, reset một thành phần
ActionTác vụ kéo dài có feedback và khả năng hủyDi chuyển robot đến một pose đích
ParameterCấu hình gắn với từng nodeTần số cập nhật, frame name, ngưỡng lọc

Topic phù hợp với dữ liệu liên tục vì publisher không cần biết cụ thể subscriber nào đang nhận. Với /scan, có thể đồng thời có localization, obstacle detector và công cụ ghi dữ liệu. Việc thêm một subscriber mới không buộc sửa lidar_driver. Tuy nhiên, trùng tên và kiểu message vẫn chưa đủ: publisher và subscription phải có QoS tương thích thì kết nối mới truyền được dữ liệu. Vì vậy, trường hợp “nhìn thấy node nhưng không nhận được message” có thể là lỗi contract truyền thông chứ không phải node đã chết (ROS 2 QoS (opens in a new tab)).

Service phù hợp khi một node cần câu trả lời cho một yêu cầu ngắn. Action mở rộng ý tưởng request/response cho công việc kéo dài: client gửi goal, server phát feedback trong quá trình xử lý, rồi trả result; goal cũng có thể bị hủy. Parameter không vận chuyển dữ liệu giữa các thuật toán như topic, mà cung cấp cấu hình có tên và gắn vòng đời của cấu hình đó với node (ROS 2 Parameters (opens in a new tab)).

Với localization, subscription nhận /scan và /odom; publisher phát pose; service có thể reset estimate; parameter điều chỉnh noise model và update rate. Tập interface này mô tả biên của node, nhưng chưa cho biết lúc message đến thì đoạn xử lý nào thực sự được chạy. Cần thêm một lớp điều phối thực thi.

Callback và executor

Khi tạo subscription, timer, service hoặc action, node đăng ký các callback tương ứng. Callback mô tả việc cần làm khi sự kiện xảy ra: xử lý bản quét mới, chạy một bước cập nhật theo timer hoặc trả lời service request. Bản thân node không đồng nghĩa với một thread tự chạy liên tục.

Executor theo dõi các sự kiện sẵn sàng rồi gọi callback bằng một hay nhiều thread của hệ điều hành. Một SingleThreadedExecutor có thể phục vụ nhiều node trong cùng một thread; một MultiThreadedExecutor có thể xử lý nhiều callback song song nếu cách tổ chức callback group cho phép. Do đó, số node, số process và số thread là ba quyết định khác nhau. Tài liệu executor của ROS 2 mô tả rõ executor là thành phần dùng thread để gọi callback của subscription, timer, service và action (ROS 2 Executors (opens in a new tab)).

Điểm này quan trọng với robot kho. Nếu callback localization mất 80 ms và dùng chung single-threaded executor với timer điều khiển cần chạy mỗi 10 ms, timer có thể phải chờ. Tách localization và controller thành hai node làm kiến trúc rõ hơn, nhưng chưa tự động bảo đảm chúng chạy song song. Nếu cả hai node vẫn được gắn vào cùng một single-threaded executor, nút thắt thực thi vẫn còn nguyên.

Ngược lại, chuyển ngay sang nhiều thread cũng không tự động giải quyết vấn đề. Hai callback cùng sửa một trạng thái có thể tạo race condition; callback group MutuallyExclusive ngăn các callback trong cùng nhóm chạy song song, còn Reentrant cho phép điều đó. Ranh giới node xác định mô-đun logic, trong khi executor và callback group xác định chính sách lập lịch ở mức ứng dụng. Sự tách biệt này dẫn đến một nhầm lẫn phổ biến khác: node không phải là process.

Node, executable, process và package

Các khái niệm này thường trùng nhau trong ví dụ nhỏ nên dễ bị đồng nhất:

Khái niệmÝ nghĩa
NodeParticipant logic trong ROS graph, sở hữu interface và callback
ExecutableArtifact chương trình có thể được khởi chạy
ProcessInstance đang chạy do hệ điều hành quản lý
PackageĐơn vị tổ chức source, dependency, build và phân phối
ComponentCách đóng gói một node để có thể nạp vào container process

Một executable có thể tạo một node, nhưng cũng có thể tạo nhiều node. Một process có thể chứa nhiều node; ngược lại, các node của hệ thống có thể nằm trên nhiều process và nhiều máy. Package cũng có thể chứa nhiều executable, library, message definition và file cấu hình. Vì vậy, nhìn thấy bốn node trong ROS graph không cho phép kết luận hệ điều hành đang chạy bốn process.

ROS 2 hỗ trợ composition để giữ ranh giới node ở mức thiết kế nhưng nạp nhiều node vào cùng một process. Cách này giảm overhead và có thể cho phép truyền dữ liệu nội process hiệu quả hơn. Chạy mỗi node trong process riêng lại có lợi cho fault isolation và việc debug từng thành phần. ROS 2 xem process layout là một quyết định triển khai: cùng thiết kế node có thể được bố trí khác nhau tùy yêu cầu hệ thống (ROS 2 Composition (opens in a new tab)).

Trên robot kho, camera driver và image preprocessor có thể cần composition vì ảnh lớn, tần số cao và chi phí copy đáng kể. Trong khi đó, safety monitor có thể đáng được đặt ở process riêng để lỗi của perception không làm mất cơ chế dừng an toàn. Không có một ánh xạ “mỗi node một process” tối ưu cho mọi trường hợp.

Ranh giới node

Nguyên tắc “mỗi node làm một việc” chỉ hữu ích khi “một việc” được xác định theo kiến trúc, không theo số lượng hàm. Một ranh giới node tốt thường xuất hiện khi có ít nhất một trong các khác biệt sau:

  • vòng đời phần cứng hoặc phần mềm độc lập;
  • failure mode cần được cô lập;
  • nhịp callback hoặc yêu cầu latency khác biệt;
  • nhu cầu triển khai trên máy khác;
  • tài nguyên CPU, GPU hoặc quyền truy cập thiết bị khác nhau;
  • interface đủ ổn định để thay thế implementation;
  • nhu cầu quan sát, restart hoặc cấu hình độc lập.

Tách quá ít tạo ra mega-node: nhiều trách nhiệm chia sẻ trạng thái ngầm, khó kiểm thử và khó thay thế. Tách quá nhiều tạo ra một graph vụn: số interface tăng, serialization và discovery tốn tài nguyên hơn, cấu hình QoS phức tạp hơn, còn causal chain của dữ liệu khó theo dõi. Composition giúp giảm một phần chi phí runtime nhưng không xóa chi phí thiết kế interface.

Các node điều khiển phần cứng còn gặp vấn đề thứ tự khởi động và dừng. Camera không nên phát dữ liệu trước khi cấu hình hoàn tất; motor driver không nên nhận command khi hệ thống safety chưa sẵn sàng. Với trường hợp này, lifecycle node bổ sung các trạng thái được quản lý để việc configure, activate, deactivate và cleanup diễn ra có kiểm soát (ROS 2 Managed Nodes (opens in a new tab)). Lifecycle không thay định nghĩa node; nó làm vòng đời của node trở nên tường minh hơn.

Node vì thế không phải mục tiêu cuối cùng của thiết kế ROS 2. Nó là ranh giới logic để hệ thống có thể được quan sát, thay thế, cấu hình và triển khai linh hoạt. Một graph có nhiều node vẫn có thể hoạt động kém nếu interface chọn sai, QoS không tương thích, callback chặn executor hoặc ranh giới trách nhiệm đặt không hợp lý. Khi thiết kế robot kho, câu hỏi tốt không phải “cần bao nhiêu node”, mà là “trách nhiệm nào cần một vòng đời, contract giao tiếp và chính sách thực thi độc lập”.

Bình luận & Cảm xúc