Atlas C1 — Robot Mecanum 2 máy tính, SLAM & Nav2 trên ROS2 Humble

Robot di động 4 bánh Mecanum (holonomic), chạy ROS2 Humble trên kiến trúc 2 máy tính: NUC đặt ngay trên robot chỉ chạy phần cứng thời gian thực (LiDAR, cầu nối serial tới ESP32, động học mecanum), một PC riêng chạy toàn bộ SLAM (slam_toolbox), định vị (AMCL) và điều hướng (Nav2 — MPPI controller). ESP32 khép kín vòng PID tốc độ từng bánh cục bộ dựa trên encoder đọc qua bộ đếm xung phần cứng (PCNT), giao tiếp với PC qua khung serial nhị phân riêng — không dùng bus CAN thật dù tên class gợi nhớ CAN.
Xem mã nguồn trên GitHubHình ảnh dự án




Hình ảnh minh họa Version 2


Video demo
Demo Mapping & Nav2 — SLAM và điều hướng tự động trên bánh Mecanum
Video demo Version 2
Video demo Version 2
Hậu trường
Quá trình lắp ráp phần cứng Atlas C1
Lưu ý: Đây là sản phẩm mô hình phục vụ mục đích nghiên cứu, dùng để kiểm chứng kiến trúc phần cứng/phần mềm của một robot di động tự hành — không phải sản phẩm thương mại hoàn thiện. README gốc của dự án ghi rõ tài liệu mô tả đúng trạng thái code hiện tại, kể cả những phần chưa hoàn chỉnh — nội dung dưới đây giữ nguyên tinh thần đó thay vì chỉ trình bày phiên bản lý tưởng hoá.
Atlas C1 khác gì so với các dự án khác
Robot Mecanum đã chứng minh kiến trúc bánh Mecanum + ESP32 + Raspberry Pi 4 + Cartographer hoạt động được. Atlas C1 dùng cùng loại bánh và cùng dòng LiDAR (YDLidar X3 Pro), nhưng thay đổi ba điểm quan trọng: máy tính onboard chuyển sang NUC (mạnh hơn Raspberry Pi 4), tách rõ 2 máy tính — robot chỉ chạy phần cứng thời gian thực, mọi tính toán SLAM/Nav2 dồn sang một PC riêng giao tiếp qua cùng ROS domain — và vòng điều khiển tốc độ bánh chuyển thành PID cục bộ trên ESP32 dựa trên encoder đọc bằng bộ đếm xung phần cứng (PCNT), thay vì chỉ xuất PWM open-loop.
So với Atlas A2 (Jetson Orin Nano, YOLOv8, auto-docking, REST API), Atlas C1 đơn giản hơn nhiều — không có nhận diện vật thể, không có docking tự động, không có tầng dịch vụ REST/WebSocket. Đổi lại, Atlas C1 bổ sung một nguồn odometry mà cả hai dự án trước không có: rf2o_laser_odometry, ước lượng vận tốc trực tiếp từ hai lần quét LiDAR liên tiếp bằng range-flow scan matching — hữu ích đúng lúc encoder bánh xe kém tin cậy nhất (trượt bánh Mecanum).
Kiến trúc hệ thống
Hai máy tính giao tiếp qua chung một ROS domain (Wi-Fi/LAN) — không có message-bridge riêng, toàn bộ là topic/service ROS2 thông thường:
- Robot (NUC, onboard) —
ydlidar_ros2_driver_nodeđọc LiDAR qua/dev/ydlidar(symlink udev cố định theo cổng USB vật lý), publish/scan;kinematic.pynhận/cmd_vel, tính động học nghịch mecanum ra RPM 4 bánh;ros_serial_bridge.pyđóng gói RPM thành khung nhị phân (0x2a+ id 4 byte + 8 byte dữ liệu +0x23) gửi cho ESP32 qua/dev/esp32. - PC (điều khiển) —
robot_state_publisher/mecanum_joint_publisherdựng mô hình robot cho RViz2;scan_relaychuyển tiếp/scansang/atlas/scan_filteredchorf2o_laser_odometrytính/atlas/odom; joystick quatwist_mux(ưu tiên lateral > joystick > Nav2);atlas_slamchạyslam_toolbox/AMCL/Nav2.
Tóm lại: Ranh giới giữa 2 máy trùng đúng ranh giới thời gian thực/không thời gian thực đã thấy ở các dự án khác — robot chỉ giữ lại phần bắt buộc phải nhanh (đọc LiDAR, đóng khung serial, watchdog
/cmd_vel), mọi thứ nặng tính toán (SLAM, Nav2, odometry từ scan matching) đẩy hết sang PC.
Động học nghịch mecanum và vòng PID cục bộ trên ESP32
kinematic.py áp dụng đúng ma trận động học nghịch 4×3 đã trình bày ở bài Differential Drive/Mecanum trong AMR Drive System: [1,−1,−k; 1,1,k; 1,1,−k; 1,−1,k] với k = lx+ly, chuyển Twist (vx, vy, ω) thành RPM từng bánh, kèm watchdog tự đưa RPM về 0 nếu không nhận /cmd_vel quá 1 giây.
Khác với việc chỉ xuất PWM tỉ lệ thuận với RPM mong muốn (open-loop), firmware ESP32 (main.cpp, PlatformIO) đọc encoder 4 bánh bằng PCNT (pulse counter) — ngoại vi đếm xung phần cứng của ESP32, không tốn CPU polling — rồi chạy một vòng PID tốc độ cục bộ cho từng bánh, so target RPM nhận từ ROS với tốc độ đo được, xuất PWM 2 kênh/động cơ ra driver H-bridge. Toàn bộ vòng PID này chạy kín hoàn toàn trên vi điều khiển, đúng nguyên tắc phân tầng thời gian thực đã học ở Motor Control cho AMR — ROS không cần (và không nên) tham gia vào chu kỳ điều khiển tần số cao này.
SLAM, AMCL, và Nav2 — vẫn bộ ba quen thuộc, thêm một giới hạn đáng chú ý
atlas_slam dùng đúng bộ ba đã thấy ở các dự án khác: slam_toolbox (chế độ mapping, pose-graph dựa trên Karto, có loop closing) để dựng bản đồ, AMCL để định vị trên bản đồ tĩnh đã lưu, và Nav2 với planner_server chạy NavFn và controller_server chạy MPPI. Costmap cục bộ kết hợp obstacle_layer + voxel_layer (từ LiDAR) + inflation_layer; costmap toàn cục dùng static_layer + speed_filter.
Điểm đáng chú ý: dù phần cứng là bánh Mecanum (holonomic, đi ngang được) và lateral_teleop_node.py phía joystick tận dụng đúng khả năng đó, cấu hình Nav2 (atlas_nav2_mppi.yaml) lại đặt motion_model: DiffDrive và khoá vy_max = 0 — nghĩa là Nav2 hiện điều khiển Atlas C1 như một robot 2 bánh vi sai, chưa khai thác khả năng đi ngang khi tự động điều hướng. Khả năng holonomic chỉ thực sự dùng được qua 2 nút bấm strafe thủ công trên joystick.
Vấn đề đã biết / hạn chế
Danh sách dưới trích từ chính tài liệu kỹ thuật của dự án — phản ánh đúng trạng thái code thật tại thời điểm viết bài, không phải danh sách rủi ro lý thuyết:
| Vấn đề | Chi tiết |
|---|---|
twist_mux không nhận lệnh Nav2 | Nav2/collision_monitor xuất thẳng /cmd_vel, trong khi twist_mux lắng nghe nhánh "navigation" ở cmd_vel_nav — không node nào publish topic đó. Chạy đồng thời Nav2 và joystick có thể khiến cả hai ghi đè trực tiếp /cmd_vel. |
| Không có phản hồi tốc độ bánh thật về ROS | Velinfo.msg và đoạn gửi encoder-tick trong firmware ESP32 đều tồn tại nhưng bị comment — PID chạy kín hoàn toàn cục bộ, ROS không biết tốc độ bánh thực tế. |
| Thông số động học không khớp giữa các package | kinematic.py dùng r=0.035, lx=0.085, ly=0.11; mecanum_joint_publisher.py (chỉ phục vụ hiển thị RViz) dùng R=0.049, Lx=0.11, Ly=0.15 — cần đối chiếu lại với kích thước robot thật để thống nhất. |
| Nav2 chạy như diff-drive | Đã nêu ở mục trên — motion_model: DiffDrive và vy_max=0 dù phần cứng holonomic. |
| Tham số localization sai tên file | Launch khai atlas_localization.yaml nhưng file thật trên đĩa tên atlas_localization_c1.yaml — chạy với tên cũ sẽ không load được tham số như ý; map mặc định cũng trỏ tới đường dẫn không tồn tại. |
| Cảm biến RealSense được cấu hình nhưng chưa lắp | Costmap toàn cục và collision monitor khai báo nguồn pointcloud từ /intel_realsense_r200_depth/points, nhưng URDF hiện tại không có camera nào — phần cứng dự kiến cho tương lai. |
| Code thừa/chưa dùng | Package c1_platform rỗng; submodule src/atlas_base chưa checkout; ESP32_code còn nhiều file CAN-bus/RFID/UDP-debug từ template dự án khác không được main.cpp include; atlas_cartographer_real.launch.py và công cụ route_manager.py/route_tool.py (GUI PyQt5 vẽ waypoint) là thử nghiệm, chưa nằm trong quy trình vận hành chính thức. |
Phần cứng thực tế
Từ ảnh chụp thực tế: khung robot 2 tầng bằng mica/gỗ ép và kim loại, 4 bánh Mecanum, LiDAR gắn trên giá đỡ in 3D ở trung tâm tầng trên, NUC đặt cạnh trong hộp đế in 3D riêng, một access point Wi-Fi 2 ăng-ten dùng làm mạng nội bộ giữa robot và PC điều khiển, và màn hình hiển thị dung lượng/điện áp pin gắn lộ ở mép khung — thuận tiện kiểm tra nhanh mà không cần mở nắp hay kết nối phần mềm.
| Thành phần | Model / Chi tiết | Kết nối |
|---|---|---|
| Khung & bánh | Mica/gỗ ép 2 tầng, 4 bánh Mecanum | — |
| Máy tính onboard | NUC | — |
| Vi điều khiển | ESP32 (PlatformIO firmware) | Serial 115200 baud (/dev/esp32) |
| LiDAR | YDLidar X3 Pro — 0,1–12m, 10Hz | Serial 115200 baud (/dev/ydlidar) |
| Driver động cơ | H-bridge, 2 kênh PWM/động cơ | GPIO ESP32 |
| Encoder | Đọc bằng PCNT (pulse counter) của ESP32 | — |
| Mạng | Access point Wi-Fi 2 ăng-ten | Robot ↔ PC (chung ROS domain) |
| Nguồn | Pin, kèm màn hình hiển thị dung lượng/điện áp | — |
Version 2 — khung mica, thu gọn kích thước
Bên cạnh phiên bản mô tả ở các mục trên, dự án có thêm Version 2 (hình ảnh minh hoạ, video demo và bảng so sánh thông số kỹ thuật đầy đủ ở đầu/cuối trang) — giữ nguyên toàn bộ kiến trúc phần mềm 2 máy tính và vòng PID cục bộ trên ESP32 đã trình bày, nhưng thay đổi đáng kể phần khung cơ khí và một vài linh kiện: khung chuyển gần như hoàn toàn sang mica (acrylic) kết hợp các chi tiết in 3D (đế gắn LiDAR, hộp đỡ máy tính onboard), thu gọn kích thước tổng thể còn 32 × 26 × 23cm, bánh Mecanum đường kính 6,8cm gắn trực tiếp với động cơ JGA25, 130rpm, kèm encoder trục.
Khác với bản đầu, encoder ở Version 2 tích hợp sẵn theo cụm động cơ thay vì lắp rời — nhưng vẫn đọc qua PCNT trên ESP32 và phục vụ đúng vòng PID tốc độ cục bộ đã mô tả ở phần trên, không cần thay đổi gì ở tầng firmware.
Máy tính onboard không còn cố định là NUC mà có thể lắp mini PC hoặc Raspberry Pi tuỳ cấu hình — đánh đổi hiệu năng lấy chi phí/kích thước thấp hơn, phù hợp với hướng thu gọn khung của Version 2. Mạng giữa robot và PC điều khiển cũng chuyển từ access point 2 ăng-ten sang router Wi-Fi Tenda thông thường.
Tóm lại: Version 2 là một bài toán cơ khí nhiều hơn là phần mềm — thu gọn khung, đổi vật liệu, đổi động cơ có encoder tích hợp — trong khi kiến trúc ROS2, vòng PID trên ESP32, và bộ ba SLAM/AMCL/Nav2 giữ nguyên. Điểm cần lưu ý khi triển khai thực tế: đường kính bánh 6,8cm và khung nhỏ hơn kéo theo bán kính bánh
rvà khoảng cách tâm bánhlx, lytrongkinematic.pyphải đo và hiệu chỉnh lại theo kích thước khung mới — dùng nguyên thông số động học cũ từ bản đầu sẽ sai tỉ lệ, giống đúng loại lỗi thông số không khớp đã nêu ở mục "Vấn đề đã biết / hạn chế" phía trên.
Tổng kết và hướng phát triển
Tổng kết
Atlas C1 là một biến thể kiến trúc đáng chú ý trong cùng dòng nghiên cứu robot Mecanum của dự án: giữ nguyên bài toán cốt lõi (SLAM + AMCL + Nav2 trên ROS2 Humble), nhưng thử nghiệm 2 thay đổi cụ thể — tách hẳn tính toán thời gian thực (robot/NUC) khỏi tính toán SLAM/Nav2 (PC riêng) thay vì gộp chung một máy như Robot Mecanum, và khép kín vòng điều khiển tốc độ bánh bằng PID cục bộ trên ESP32 thay vì open-loop. Tài liệu kỹ thuật của dự án đặc biệt minh bạch về các điểm chưa hoàn thiện — một thực hành tốt hiếm gặp, giúp bất kỳ ai đọc code sau này biết chính xác nên tin phần nào và cần sửa phần nào trước khi dựa vào.
Hướng phát triển
- Sửa
twist_mux— thêm node publishcmd_vel_navtừ output Nav2 thay vì để Nav2 ghi thẳng/cmd_vel, khôi phục đúng cơ chế phân giải ưu tiên đã thiết kế. - Bật khả năng holonomic trong Nav2 — đổi
motion_modeltừDiffDrivesang cấu hình omnidirectional, mở khoávy_maxđể Nav2 thực sự tận dụng được bánh Mecanum khi tự động điều hướng, không chỉ khi lái tay. - Khôi phục phản hồi tốc độ bánh — bật lại đoạn gửi encoder-tick trong firmware ESP32 và publish
Velinfo.msg, cho phép ROS giám sát tốc độ bánh thực tế thay vì tin tưởng mù quáng vòng PID cục bộ. - Thống nhất thông số động học — đối chiếu kích thước robot thật, đồng bộ
r, lx, lygiữakinematic.pyvàmecanum_joint_publisher.pyđể mô hình RViz khớp đúng chuyển động thật. - Dọn code thừa — gỡ các file CAN-bus/RFID/UDP-debug không dùng trong
ESP32_code, quyết định giữ hay bỏ packagec1_platformrỗng và submoduleatlas_basechưa checkout.
Thông số kỹ thuật
| Bản chính | Version 2 | |
|---|---|---|
| Kiểu di chuyển | 4 bánh Mecanum (holonomic) | 4 bánh Mecanum (holonomic) |
| Khung | Mica/gỗ ép 2 tầng | Mica (acrylic) + chi tiết in 3D |
| Kích thước tổng thể | Chưa đo/không công bố | 32 × 26 × 23cm |
| Bánh Mecanum | Không nêu đường kính | Đường kính 6,8cm |
| Động cơ | Không nêu model cụ thể | JGA25, 130rpm, kèm encoder trục |
| Vi điều khiển | ESP32 — PID tốc độ bánh cục bộ (encoder qua PCNT) | ESP32 — PID tốc độ bánh cục bộ (encoder qua PCNT) |
| Máy tính onboard | NUC — LiDAR + cầu nối serial ESP32 + động học mecanum | Mini PC hoặc Raspberry Pi (tuỳ cấu hình lắp) — LiDAR + cầu nối serial ESP32 + động học mecanum |
| Máy tính điều khiển | PC riêng — SLAM/AMCL/Nav2, RViz2, joystick | PC riêng — SLAM/AMCL/Nav2, RViz2, joystick |
| Cảm biến LiDAR | YDLidar X3 Pro — 0,1–12m, 10Hz, serial 115200 baud | YDLidar X3 Pro — 0,1–12m, 10Hz, serial 115200 baud |
| Driver động cơ | H-bridge, 2 kênh PWM/động cơ | H-bridge, 2 kênh PWM/động cơ |
| Giao tiếp ESP32 ↔ PC | Serial 115200 baud, khung nhị phân 0x2a…0x23 | Serial 115200 baud, khung nhị phân 0x2a…0x23 |
| Odometry bổ sung | rf2o_laser_odometry — range-flow scan matching từ LiDAR | rf2o_laser_odometry — range-flow scan matching từ LiDAR |
| Mạng | Access point Wi-Fi 2 ăng-ten | Router Wi-Fi Tenda |
| Hệ điều hành | Ubuntu 22.04 LTS | Ubuntu 22.04 LTS |
| Framework | ROS2 Humble | ROS2 Humble |
| SLAM | slam_toolbox (pose-graph, Karto, loop closing) | slam_toolbox (pose-graph, Karto, loop closing) |
| Định vị | AMCL | AMCL |
| Điều hướng | Nav2 — planner NavFn, controller MPPI, behavior tree | Nav2 — planner NavFn, controller MPPI, behavior tree |
| Điều khiển thủ công | Joystick qua twist_mux (ưu tiên: lateral > joystick > nav) | Joystick qua twist_mux (ưu tiên: lateral > joystick > nav) |