Tạo package C++ trong ROS2 từng bước
Atlasbot · 17/8/2026 · 10 phút đọc
Bài Package trong ROS2 là gì? đã giới thiệu khái niệm ament_cmake. Bài này đi vào thao tác cụ thể: tạo một package C++ hoàn chỉnh, từ ros2 pkg create đến chạy được node đầu tiên bằng ros2 run.
Khái niệm chính
Đứng trong thư mục src/ của workspace, dùng ros2 pkg create với --build-type ament_cmake:
cd ~/ros2_ws/src
ros2 pkg create --build-type ament_cmake my_cpp_pkg \
--dependencies rclcpp std_msgs
Cờ --dependencies không chỉ ghi chú — nó tự động điền các dòng find_package() vào CMakeLists.txt và <depend> vào package.xml tương ứng, đỡ phải sửa tay hai file này ngay từ đầu.
Kết quả sinh ra:
my_cpp_pkg/
├── CMakeLists.txt ← khai cách biên dịch (xem bài riêng)
├── package.xml ← khai dependency (xem bài riêng)
├── include/my_cpp_pkg/ ← header .hpp nếu tách riêng
└── src/ ← file .cpp thực tế
Nguyên lý hoạt động
Tạo src/my_cpp_pkg/src/hello_node.cpp:
#include "rclcpp/rclcpp.hpp"
int main(int argc, char **argv) {
rclcpp::init(argc, argv);
auto node = std::make_shared<rclcpp::Node>("hello_node");
RCLCPP_INFO(node->get_logger(), "hello_node đã khởi động");
rclcpp::spin(node);
rclcpp::shutdown();
return 0;
}
Khác với Python (khai trong setup.py), package C++ khai executable trực tiếp trong CMakeLists.txt:
add_executable(hello_node src/hello_node.cpp)
ament_target_dependencies(hello_node rclcpp)
install(TARGETS hello_node
DESTINATION lib/${PROJECT_NAME})
ament_target_dependencies() là hàm tiện ích của ament_cmake — tự link đúng thư viện, đúng include path cho từng dependency đã khai ở find_package(), thay vì phải tự viết target_link_libraries() thủ công cho từng gói.
Build và chạy:
cd ~/ros2_ws
colcon build --packages-select my_cpp_pkg
source install/setup.bash
ros2 run my_cpp_pkg hello_node
Tóm lại: Với C++, "khai executable" nằm ở
CMakeLists.txt(add_executable+install), khác hẳn Python nơi việc này nằm ởsetup.py(entry_points) — đây là khác biệt lớn nhất giữa hai loại package khi thao tác thực tế.
Kiến thức kỹ thuật
Thông số
Một số yêu cầu/thông số thực tế khi build package C++ trong ROS2:
| Thông số | Giá trị |
|---|---|
Chuẩn C++ tối thiểu rclcpp yêu cầu | C++14 (khuyến nghị C++17 trở lên) |
| Thời gian build package nhỏ (vài node đơn giản) | Vài giây |
| Thời gian build package phụ thuộc thư viện nặng (OpenCV, PCL) | Có thể vài phút mỗi lần build sạch |
| Tăng tốc build lặp lại | Dùng ccache (cache kết quả biên dịch) giảm đáng kể thời gian build lại |
Ứng dụng thực tế
Chọn C++ thay vì Python cho một node cụ thể thường dựa trên yêu cầu hiệu năng — node driver đọc dữ liệu cảm biến tần suất rất cao (LiDAR, IMU ở hàng trăm Hz) hoặc vòng lặp điều khiển thời gian thực (PID chạy ở kHz) thường viết bằng C++ để tránh overhead của trình thông dịch Python và giới hạn GIL (Global Interpreter Lock) — trong khi logic điều phối cấp cao ít nhạy cảm với độ trễ (VD: node ra quyết định hành vi robot) thường viết Python cho tốc độ phát triển nhanh hơn. Nhiều dự án robot thực tế trộn cả 2 ngôn ngữ trong cùng workspace, mỗi node dùng ngôn ngữ phù hợp nhất với vai trò của nó — điều này hoàn toàn khả thi vì cả 2 loại package đều giao tiếp qua cùng lớp DDS/Topic tiêu chuẩn, node C++ và node Python không hề "biết" hay quan tâm ngôn ngữ của node bên kia.
Trong package ament_cmake, một package cũng có thể chứa nhiều executable độc lập dùng chung 1 thư viện lõi — mẫu hình phổ biến cho driver phần cứng có cả chương trình chính lẫn 1-2 công cụ phụ trợ (VD: motor_driver_node chạy chính và motor_calibration_tool dùng riêng lúc hiệu chỉnh, không chạy thường xuyên) — cả 2 cùng nằm trong 1 package, chia sẻ chung code đọc/ghi thanh ghi driver qua add_library(), tránh phải tách thành 2 package riêng chỉ vì có 2 điểm vào (entry point) khác nhau.
Ví dụ thực tế
$ colcon build --packages-select my_cpp_pkg
Output:
Starting >>> my_cpp_pkg
Finished <<< my_cpp_pkg [4.12s]
Summary: 1 package finished [4.35s]
$ ros2 run my_cpp_pkg hello_node
Output:
[INFO] [1723000000.123456789] [hello_node]: hello_node đã khởi động
Thời gian build 4.12s cho 1 node đơn giản là điển hình — nếu build package C++ mất hàng chục giây trở lên cho code rất đơn giản, kiểm tra lại có đang vô tình build lại toàn bộ dependency nặng không cần thiết hay không.
Lỗi thường gặp
- Quên
ament_target_dependencies()cho executable mới thêm — code biên dịch báo lỗi linker (undefined reference) dù#includeđã đúng — mỗiadd_executablemới đều cần dòngament_target_dependenciesriêng liệt kê đủ dependency nó cần dùng. - Quên khối
install(TARGETS ...)— package build thành công (không lỗi biên dịch) nhưngros2 runbáo không tìm thấy executable — vì file build ra chỉ nằm trongbuild/, chưa được cài đặt (install) vàoinstall/lib/<package_name>/nơiros2 runtìm kiếm. - Include header sai đường dẫn khi tách
.hppriêng — quên thêminclude/${PROJECT_NAME}vàotarget_include_directoriestrongCMakeLists.txtkhi có file header tự viết đặt trong thư mụcinclude/. - Không rebuild sau khi sửa
CMakeLists.txt— thay đổi cấu hình build (thêm dependency, đổi tên executable) chỉ có hiệu lực sau khicolcon buildlại, không tự động áp dụng như code Python có thể dùng--symlink-install.
Kết luận
Quy trình tạo package C++ xoay quanh việc khai executable và dependency trong CMakeLists.txt/package.xml — khác biệt căn bản với package Python ở chỗ cần biên dịch trước khi chạy được. Nắm vững ament_target_dependencies và khối install(TARGETS...) là đủ để tránh phần lớn lỗi build/link thường gặp. Bài tiếp theo trong chuyên mục nói về tạo package Python — quy trình tương đương nhưng đơn giản hơn nhiều vì không cần biên dịch.