Kết nối ROS 2 Humble giữa hai mạng LAN bằng Tailscale và Fast DDS Discovery Server
Khi hai node ROS 2 nằm trong cùng một mạng LAN, việc truyền topic thường diễn ra khá tự nhiên. Một máy chạy publisher, máy khác chạy subscriber, hai bên tự tìm thấy nhau qua DDS discovery. Vấn đề bắt đầu xuất hiện khi robot ở một mạng LAN, còn laptop điều khiển hoặc máy xử lý nằm ở mạng khác.
Tailscale giúp hai thiết bị nhìn thấy nhau bằng IP riêng trong cùng một tailnet. Ví dụ, máy chạy robot có thể có địa chỉ 100.89.17.36, còn laptop ở nơi khác có một địa chỉ 100.x.x.x. Hai máy có thể ping nhau qua Tailscale, nhưng ROS 2 vẫn chưa chắc tự thấy node của nhau. Lý do là DDS discovery mặc định thường dựa nhiều vào multicast trong mạng cục bộ, trong khi kết nối qua Tailscale không hành xử như một LAN quảng bá multicast thông thường.
Fast DDS Discovery Server giải quyết đúng phần này. Thay vì để các node tự tìm nhau bằng multicast, ta đặt một server discovery cố định. Các node ROS 2 ở hai phía cùng khai báo server này. Khi cần tìm publisher, subscriber, service hoặc action, chúng trao đổi thông tin discovery qua địa chỉ server bằng unicast. Sau khi discovery xong, dữ liệu DDS thực tế có thể truyền qua đường Tailscale giữa các máy.
Trong bài này, cấu hình minh họa dùng:
- ROS 2 Humble
- Fast DDS qua
rmw_fastrtps_cpp - Discovery Server tại
100.89.17.36:11811 ROS_DOMAIN_ID=10- Workspace ROS 2 tại
~/ros_ws
Luồng kết nối
Ta có ba lớp cần tách rõ.
Lớp đầu tiên là kết nối mạng. Tailscale tạo một đường IP riêng giữa hai thiết bị, dù chúng không nằm chung LAN vật lý. Nếu tầng này lỗi, ROS 2 không có cơ hội hoạt động. Vì vậy, trước khi nhìn vào topic, cần chắc chắn hai máy thấy nhau qua Tailscale.
Lớp thứ hai là DDS discovery. Đây là bước các ROS 2 node công bố “tôi có topic này”, “tôi muốn subscribe topic kia”, “tôi có service này”. Trong LAN, multicast thường giúp bước này diễn ra tự động. Qua hai mạng khác nhau, ta chuyển sang Fast DDS Discovery Server để discovery đi qua một điểm hẹn rõ ràng.
Lớp thứ ba là dữ liệu ROS 2 thật: /scan, /odom, /tf, /cmd_vel, /map, hoặc các topic custom. Discovery Server không nén dữ liệu và không làm topic nhẹ hơn. Nó chỉ giúp các endpoint tìm thấy nhau. Nếu topic quá nặng hoặc firewall chặn user data, discovery có thể đúng nhưng dữ liệu vẫn không chạy như mong muốn.
Điều kiện ban đầu
Hai máy cần cùng một nền cấu hình trước khi thử ROS 2 qua Tailscale:
- Đã cài ROS 2 Humble.
- Đã đăng nhập Tailscale và cùng có quyền truy cập trong tailnet.
- Dùng Fast DDS RMW.
- Dùng cùng
ROS_DOMAIN_ID. - Nếu topic dùng custom message, cả hai máy đều phải có package chứa message đó.
Kiểm tra Tailscale:
tailscale status
tailscale ip -4Từ máy client, kiểm tra tới máy sẽ chạy Discovery Server:
tailscale ping 100.89.17.36
ping -c 4 100.89.17.36Nếu bước này chưa thành công, chưa nên debug ROS 2. Cần xử lý Tailscale trước: thiết bị có online không, tailnet policy có cho phép hai máy nói chuyện không, IP có đúng không.
Gói cần cài
Trên cả hai máy, cài Fast DDS RMW, demo node và công cụ fastdds:
sudo apt update
sudo apt install -y \
ros-humble-rmw-fastrtps-cpp \
ros-humble-demo-nodes-cpp \
fastdds-toolsSource ROS 2:
source /opt/ros/humble/setup.bashNếu dùng workspace riêng:
source ~/ros_ws/install/setup.bashKiểm tra middleware và Fast DDS CLI:
ros2 doctor --report | grep -i middleware
fastdds --helpỞ giai đoạn này, mục tiêu chưa phải là chạy topic ngay. Mục tiêu chỉ là chắc chắn hai máy có cùng nền ROS 2 và cùng công cụ Fast DDS.
Discovery Server
Chọn một máy làm Discovery Server. Trong ví dụ này, máy đó có IP Tailscale:
100.89.17.36Trên máy này, mở một terminal riêng và chạy:
source /opt/ros/humble/setup.bash
fastdds discovery \
-i 0 \
-l 100.89.17.36 \
-p 11811Các tham số chính:
| Tham số | Vai trò |
|---|---|
-i 0 | ID của Discovery Server |
-l 100.89.17.36 | IP Tailscale mà server lắng nghe |
-p 11811 | Cổng UDP cho discovery |
Nếu chạy đúng, terminal sẽ báo server đang hoạt động và lắng nghe ở UDPv4:[100.89.17.36]:11811. Giữ terminal này mở trong suốt quá trình test.
Kiểm tra cổng:
ss -lunp | grep 11811Một lỗi hay gặp là bind nhầm IP. Tham số -l phải là IP Tailscale của chính máy đang chạy server, không phải IP của máy client.
Môi trường ROS 2
Trên mỗi terminal chạy ROS 2 node, cần khai báo cùng một bộ biến môi trường. Không dùng terminal đang chạy Discovery Server để chạy node; mở terminal mới.
Trên máy server:
source /opt/ros/humble/setup.bash
source ~/ros_ws/install/setup.bash
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export ROS_DOMAIN_ID=10
export ROS_LOCALHOST_ONLY=0
export ROS_DISCOVERY_SERVER=100.89.17.36:11811
ros2 daemon stopTrên máy client:
source /opt/ros/humble/setup.bash
source ~/ros_ws/install/setup.bash
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export ROS_DOMAIN_ID=10
export ROS_LOCALHOST_ONLY=0
export ROS_DISCOVERY_SERVER=100.89.17.36:11811
ros2 daemon stopBốn biến này là lõi của cấu hình:
RMW_IMPLEMENTATION=rmw_fastrtps_cpp
ROS_DOMAIN_ID=10
ROS_LOCALHOST_ONLY=0
ROS_DISCOVERY_SERVER=100.89.17.36:11811RMW_IMPLEMENTATION buộc ROS 2 dùng Fast DDS RMW. ROS_DOMAIN_ID tách graph ROS 2 theo domain; hai máy muốn thấy nhau phải cùng domain. ROS_LOCALHOST_ONLY=0 cho phép ROS 2 dùng giao tiếp ngoài localhost. ROS_DISCOVERY_SERVER chỉ node tới server discovery đã chạy qua Tailscale.
Sau khi đổi các biến liên quan tới discovery, nên dừng ROS daemon cũ. Nếu daemon được tạo từ cấu hình cũ, các lệnh CLI có thể cho kết quả gây nhiễu.
Talker và listener
Kiểm tra bằng demo node trước khi đưa topic robot thật vào. Trên máy server:
ros2 run demo_nodes_cpp talkerKết quả sẽ có dạng:
Publishing: 'Hello World: 1'
Publishing: 'Hello World: 2'Trên máy client:
ros2 run demo_nodes_cpp listenerNếu listener nhận được:
I heard: [Hello World: 1]
I heard: [Hello World: 2]thì ba tầng cơ bản đã chạy: Tailscale routing hoạt động, Discovery Server hoạt động, và dữ liệu DDS có thể đi giữa hai máy.
Nếu talker/listener chưa chạy, chưa nên kiểm tra /scan, /map hay /tf. Demo node là bài test nhỏ nhất để cô lập lỗi.
ROS 2 CLI
Fast DDS Discovery Server có thể làm các lệnh quan sát graph như ros2 topic list hoặc ros2 node list không luôn hiển thị giống multicast discovery trong LAN. Một node thường chỉ cần biết những endpoint liên quan tới nó, còn CLI introspection lại muốn nhìn toàn bộ graph.
Cách kiểm tra đơn giản là dùng --no-daemon:
ros2 topic list --no-daemon
ros2 node list --no-daemon
ros2 topic info /chatter --verbose --no-daemonMột số lệnh cần thêm thời gian discovery:
ros2 node info /talker --no-daemon --spin-time 3Với terminal dùng để giám sát graph, có thể bật Super Client:
export ROS_SUPER_CLIENT=TRUE
ros2 daemon stopSau đó thử:
ros2 topic list
ros2 node listKhông nhất thiết bật ROS_SUPER_CLIENT=TRUE cho mọi node vận hành. Nó phù hợp hơn với terminal quản trị vì nhận nhiều thông tin discovery hơn client thông thường.
Topic robot
Khi demo node đã chạy, mới chuyển sang topic robot thật. Với mỗi topic, cần kiểm tra ba thứ: topic có xuất hiện không, QoS có tương thích không, và dữ liệu có thật sự chảy không.
Với LaserScan:
ros2 topic info /scan --verbose
ros2 topic hz /scan
ros2 topic echo /scan --qos-reliability best_effortNhiều driver cảm biến phát với QoS BEST_EFFORT. Nếu subscriber yêu cầu RELIABLE, hai bên có thể không match.
Với map:
ros2 topic info /map --verbose
ros2 topic echo /mapVới odometry:
ros2 topic echo /odomVới TF:
ros2 topic hz /tf
ros2 topic hz /tf_static
ros2 run tf2_tools view_framesVới điều khiển vận tốc:
ros2 topic pub -r 5 /cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 0.1}, angular: {z: 0.0}}"Không gửi /cmd_vel tới robot thật khi chưa có giới hạn vận tốc và cơ chế dừng khẩn cấp. Kết nối mạng chạy được không đồng nghĩa hệ điều khiển đã an toàn.
Custom message
Nếu publisher dùng custom message, máy nhận cũng phải có cùng định nghĩa message. Ví dụ topic dùng:
my_robot_msgs/msg/RobotStateMáy client cũng cần build hoặc cài package chứa interface đó:
cd ~/ros_ws
colcon build --symlink-install
source install/setup.bashKiểm tra:
ros2 interface show my_robot_msgs/msg/RobotStateNếu thiếu interface, discovery có thể vẫn thấy topic nhưng subscriber hoặc ROS CLI không giải mã được dữ liệu. Đây là lỗi dễ nhầm với lỗi mạng, nhưng bản chất là hai workspace không cùng định nghĩa message.
Cấu hình dùng lại
Để không phải export thủ công ở mỗi terminal, tạo file:
nano ~/ros2_tailscale_env.shNội dung:
#!/usr/bin/env bash
source /opt/ros/humble/setup.bash
if [ -f "$HOME/ros_ws/install/setup.bash" ]; then
source "$HOME/ros_ws/install/setup.bash"
fi
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export ROS_DOMAIN_ID=10
export ROS_LOCALHOST_ONLY=0
export ROS_DISCOVERY_SERVER=100.89.17.36:11811Cấp quyền:
chmod +x ~/ros2_tailscale_env.shMỗi lần mở terminal:
source ~/ros2_tailscale_env.sh
ros2 daemon stopNếu muốn tự động source trong .bashrc:
echo 'source "$HOME/ros2_tailscale_env.sh"' >> ~/.bashrc
source ~/.bashrcSystemd cho Discovery Server
Nếu Discovery Server cần chạy ổn định sau mỗi lần khởi động máy, dùng systemd trên máy 100.89.17.36.
Kiểm tra đường dẫn fastdds:
which fastddsTạo service:
sudo nano /etc/systemd/system/ros2-discovery-server.serviceNội dung:
[Unit]
Description=ROS 2 Fast DDS Discovery Server over Tailscale
Wants=network-online.target tailscaled.service
After=network-online.target tailscaled.service
[Service]
Type=simple
User=dhcn-1
WorkingDirectory=/home/dhcn-1
ExecStartPre=/bin/bash -lc 'until ip -4 addr show tailscale0 | grep -q "100.89.17.36"; do sleep 1; done'
ExecStart=/bin/bash -lc 'source /opt/ros/humble/setup.bash && exec fastdds discovery -i 0 -l 100.89.17.36 -p 11811'
Restart=on-failure
RestartSec=3
[Install]
WantedBy=multi-user.targetNếu username không phải dhcn-1, thay User và WorkingDirectory theo tài khoản thực tế.
Kích hoạt service:
sudo systemctl daemon-reload
sudo systemctl enable --now ros2-discovery-server.serviceKiểm tra:
systemctl status ros2-discovery-server.service
journalctl -u ros2-discovery-server.service -f
ss -lunp | grep 11811Khởi động lại khi cần:
sudo systemctl restart ros2-discovery-server.serviceCấu hình cốt lõi
Trên máy chạy Discovery Server:
source /opt/ros/humble/setup.bash
fastdds discovery \
-i 0 \
-l 100.89.17.36 \
-p 11811Trên mọi terminal chạy node ROS 2:
source /opt/ros/humble/setup.bash
source ~/ros_ws/install/setup.bash
export RMW_IMPLEMENTATION=rmw_fastrtps_cpp
export ROS_DOMAIN_ID=10
export ROS_LOCALHOST_ONLY=0
export ROS_DISCOVERY_SERVER=100.89.17.36:11811
ros2 daemon stopTest nhỏ nhất vẫn là:
# Máy server
ros2 run demo_nodes_cpp talker
# Máy client
ros2 run demo_nodes_cpp listenerKhi test này chạy ổn, các vấn đề còn lại thường nằm ở QoS, custom message, firewall, băng thông hoặc cấu hình riêng của topic robot.