Khi robot Unitree G1 của bạn làm điều gì đó kỳ lạ — chân trái run run lúc đứng yên, IMU liên tục báo roll lệch 2 độ, hay một khớp khuỷu tay có torque cao bất thường — câu hỏi đầu tiên luôn là: dữ liệu thật sự đang nói gì? PlotJuggler là công cụ trả lời câu hỏi đó nhanh nhất. Bài này hướng dẫn bạn đi từ terminal trống đến việc stream live 23 khớp cùng IMU của G1 vào PlotJuggler, tất cả trong vòng 15 phút.
Roadmap series — 5 bài
| # | Bài | Nội dung |
|---|---|---|
| 1 | Cài đặt & kết nối live (bài này) | Cài PlotJuggler, CycloneDDS, subscribe /lowstate và /sportmodestate |
| 2 | Layout 23 khớp + ghi MCAP | Nhóm panel theo limb, đặt layout template, ghi file .mcap chuẩn |
| 3 | Debug IMU, Quaternion & FFT | Phân tích tư thế, phát hiện rung động cơ học qua FFT |
| 4 | Lua Transforms — custom metric | Viết script Lua tính power per joint, slip index, derived signal |
| 5 | ZMQ Publisher + video overlay sim2real | Cầu nối ZMQ từ MuJoCo sang PlotJuggler, overlay camera feed, so sánh sim2real |
Tại sao PlotJuggler thay vì RViz hay rqt_plot?
rqt_plot chỉ vẽ được 4–5 topic đồng thời, lag rõ ràng, và không thể phân tích dữ liệu đã ghi sau này. RViz giỏi về không gian 3D nhưng hoàn toàn không phải công cụ time-series. ros2 topic echo đúng là... đọc không nổi khi có 29 trường motor đang thay đổi 500 lần mỗi giây.
PlotJuggler giải quyết ba vấn đề cùng lúc:
- Layout linh hoạt — kéo thả field vào 10+ panel đồng thời, đồng bộ trục thời gian, zoom bất cứ đâu
- Live streaming và internal buffer — vừa xem real-time vừa giữ buffer 60 giây để scrub lại
- Mở file .mcap trực tiếp — không cần
ros2 bag play, file tự chứa schema
Với G1, bạn sẽ cần cả ba. Live streaming để xem robot đang làm gì ngay lúc chạy thử, internal buffer để tua lại đúng khoảnh khắc robot vấp ngã, và file .mcap để so sánh nhiều run với nhau sau buổi test.
Yêu cầu trước khi bắt đầu
- Ubuntu 22.04 trên workstation của bạn
- ROS2 Humble, Iron, hoặc Jazzy đã cài đặt và source
- Unitree G1 đang bật, kết nối Ethernet trực tiếp với PC bằng cáp
- Biết dùng terminal:
ip addr,ros2 topic list,ping
Bài này dùng ROS2 Humble cho tất cả lệnh mẫu. Thay humble thành iron hoặc jazzy nếu bạn dùng distro mới hơn — mọi thứ khác giữ nguyên.
Bước 1: Cài đặt PlotJuggler và plugin ROS2
Một lệnh duy nhất cài cả PlotJuggler core lẫn toàn bộ ROS2 plugin (Topic Subscriber, Bag Reader, và Re-publisher):
sudo apt install ros-humble-plotjuggler-ros
Iron/Jazzy: Thay
humblebằngironhoặcjazzy. Tên package giữ nguyênplotjuggler-ros.
Kiểm tra cài đặt thành công:
source /opt/ros/humble/setup.bash
ros2 run plotjuggler plotjuggler --version
# Kết quả: PlotJuggler version 3.x.x
Package ros-humble-plotjuggler-ros gộp sẵn tất cả plugin — bạn không cần cài riêng plotjuggler và plotjuggler-ros-plugins như hướng dẫn build-from-source cũ.
Bước 2: Kết nối vật lý và cấu hình mạng
G1 giao tiếp với PC qua Ethernet. Robot có IP cố định 192.168.123.10 — PC của bạn phải ở cùng subnet 192.168.123.x.
Xác định tên interface Ethernet:
ip addr show
# hoặc: ifconfig
Tìm interface có trạng thái UP kết nối cổng Ethernet (thường là eth0, enp3s0, eno1, enx...). Ghi nhớ tên này — bạn sẽ dùng lại ở bước CycloneDDS.
Đặt IP tĩnh qua nmcli (terminal):
# Thay "Wired connection 1" bằng tên kết nối và "enp3s0" bằng interface thực tế
sudo nmcli connection modify "Wired connection 1" \
ipv4.method manual \
ipv4.addresses "192.168.123.99/24" \
ipv4.gateway "" \
connection.autoconnect yes
sudo nmcli connection up "Wired connection 1"
Hoặc qua Ubuntu Network Settings (GUI):
- Mở Settings → Network → Cổng Ethernet kết nối G1 → cài đặt (⚙)
- IPv4 Method: Manual
- Address:
192.168.123.99, Netmask:255.255.255.0, Gateway: (để trống) - Nhấn Apply → tắt/bật connection
Kiểm tra kết nối:
ping -c 4 192.168.123.10
# Bình thường: 64 bytes from 192.168.123.10: icmp_seq=1 ttl=64 time=0.4 ms
# Nếu "Request timeout": cáp chưa cắm, hoặc IP chưa đặt đúng
Bước 3: Cấu hình CycloneDDS — bước không được bỏ qua
Đây là điểm gây nhầm lẫn nhiều nhất. ROS2 Humble mặc định dùng FastDDS làm middleware, nhưng Unitree G1 chạy CycloneDDS. Nếu hai bên không đồng nhất DDS implementation, ros2 topic list sẽ trống hoàn toàn dù G1 đang broadcast dữ liệu.
Cài CycloneDDS middleware cho ROS2 Humble:
sudo apt install ros-humble-rmw-cyclonedds-cpp
Tạo file cấu hình ~/cyclonedds_g1.xml:
<CycloneDDS>
<Domain>
<General>
<Interfaces>
<!-- Thay "enp3s0" bằng tên interface Ethernet của bạn kết nối G1 -->
<NetworkInterface name="enp3s0" priority="default" multicast="default" />
</Interfaces>
</General>
</Domain>
</CycloneDDS>
Quan trọng: Điền đúng tên interface vật lý (ví dụ
enp3s0,eth0,eno1). Không dùnglo(loopback) trừ khi đang test local với Mujoco simulator. CycloneDDS cần biết đúng interface để gửi multicast discovery đến đúng subnet.
Export biến môi trường:
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI=file:///home/$USER/cyclonedds_g1.xml
Tự động load khi mở terminal mới:
echo 'export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp' >> ~/.bashrc
echo "export CYCLONEDDS_URI=file:///home/$USER/cyclonedds_g1.xml" >> ~/.bashrc
source ~/.bashrc
Mẹo cho người dùng nhiều robot: Nếu workstation của bạn cũng kết nối các robot khác không dùng CycloneDDS, đừng hardcode vào
.bashrc. Tạo alias thay thế:# Thêm vào ~/.bashrc alias ros-g1='export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp && \ export CYCLONEDDS_URI=file:///home/$USER/cyclonedds_g1.xml && \ echo "CycloneDDS G1 mode active"'Gõ
ros-g1trước khi làm việc với G1. Terminal khác không bị ảnh hưởng.
Bước 4: Xác nhận kết nối ROS2 với G1
Với G1 đang bật và CycloneDDS đã cấu hình:
source /opt/ros/humble/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI=file:///home/$USER/cyclonedds_g1.xml
ros2 topic list
Bạn sẽ thấy danh sách topic, trong đó có:
/lowstate
/sportmodestate
/lowcmd
/wirelesscontroller
/utlidar/cloud
/lf/lowstate
/lf/sportmodestate
Prefix /lf/ là "low frequency" — cùng dữ liệu nhưng publish ở tần số thấp hơn, phù hợp khi bạn không cần full 500 Hz.
Kiểm tra nhanh dữ liệu IMU:
# Xem roll/pitch/yaw thời gian thực
ros2 topic echo /lowstate --field imu_state.rpy
# Kiểm tra tần số publish
ros2 topic hz /lowstate
# G1 publish /lowstate ở 500 Hz (2 ms/lần)
ros2 topic hz /sportmodestate
# /sportmodestate thường ở 50–100 Hz
Nếu ros2 topic list vẫn trống hoặc chỉ có /parameter_events:
| Vấn đề | Cách kiểm tra |
|---|---|
| Network vật lý | ping 192.168.123.10 → nếu fail, vấn đề ở cáp/IP |
| Tên interface sai | ip addr show enp3s0 → thay đúng tên vào XML |
| CycloneDDS chưa load | echo $RMW_IMPLEMENTATION → phải là rmw_cyclonedds_cpp |
| Firewall chặn | sudo ufw disable rồi thử lại (tái bật sau: sudo ufw enable) |
| G1 chưa bật hẳn | Chờ ~30s sau khi bật, hoặc kiểm tra đèn LED trạng thái |
Bước 5: Launch PlotJuggler và subscribe topics G1
ros2 run plotjuggler plotjuggler
Giao diện PlotJuggler mở ra với ba vùng chính:
- Trái — Signal List: Tất cả topic fields xuất hiện ở đây sau khi subscribe
- Giữa — Plot Area: Kéo thả field vào đây để vẽ đồ thị
- Phải — Time Panel: Điều chỉnh cửa sổ thời gian hiển thị (cuộn, zoom)
Quy trình subscribe topics G1:
- Nhấn nút Streaming trên toolbar (biểu tượng sóng radio) — hoặc vào menu Streaming → Start
- Dropdown xuất hiện → chọn "ROS2 Topic Subscriber"
- Nhấn nút Start màu xanh lá
- Dialog hiện ra với danh sách tất cả topic đang broadcast trong mạng ROS2
- Tìm và tích chọn:
- ☑
/lowstate - ☑
/sportmodestate
- ☑
- Nhấn OK
Sau 2–3 giây, Signal List bên trái expand thành cây nhiều cấp. Cấu trúc điển hình của /lowstate:
/lowstate
├─ imu_state
│ ├─ quaternion[0] ← w (phần thực)
│ ├─ quaternion[1] ← x
│ ├─ quaternion[2] ← y
│ ├─ quaternion[3] ← z
│ ├─ rpy[0] ← roll (rad)
│ ├─ rpy[1] ← pitch (rad)
│ ├─ rpy[2] ← yaw (rad)
│ ├─ accelerometer[0..2] ← m/s²
│ └─ gyroscope[0..2] ← rad/s
└─ motor_state[0..28]
├─ q ← góc khớp (rad)
├─ dq ← vận tốc khớp (rad/s)
├─ tau_est ← torque ước tính (Nm)
└─ temperature ← nhiệt độ motor (°C)
Vẽ plot đầu tiên:
Kéo /lowstate/imu_state/rpy[0] (roll) từ Signal List thả vào vùng plot trống. Đường roll xuất hiện và cập nhật real-time. Kéo thêm rpy[1] (pitch) vào cùng panel bằng cách giữ Ctrl khi thả — hai đường vẽ chung trục để so sánh.
Bước 6: Giải phẫu /lowstate và /sportmodestate
/lowstate — dữ liệu thô từ firmware
Topic /lowstate publish ở 500 Hz (2 ms/lần), dùng message type unitree_hg::msg::LowState — đặc thù cho humanoid Unitree, khác với unitree_go::msg::LowState của Go2/B2/B1 (quadruped).
Các field quan trọng:
| Field | Kiểu | Ý nghĩa |
|---|---|---|
motor_state[0..28] |
array[29] | 29 motor của G1 |
motor_state[i].q |
float32 | Góc khớp (radian) |
motor_state[i].dq |
float32 | Vận tốc khớp (rad/s) |
motor_state[i].tau_est |
float32 | Torque ước tính (Nm) |
motor_state[i].temperature |
float32 | Nhiệt độ motor (°C) |
imu_state.quaternion[4] |
float32[4] | Tư thế (w, x, y, z) |
imu_state.rpy[3] |
float32[3] | Roll, Pitch, Yaw (radian) |
imu_state.accelerometer[3] |
float32[3] | Gia tốc pelvis (m/s²) |
imu_state.gyroscope[3] |
float32[3] | Vận tốc góc pelvis (rad/s) |
foot_force[4] |
int16[4] | Lực tiếp xúc chân (đơn vị tương đối) |
23 khớp body vs 29 motor — sự khác biệt:
G1 có tổng cộng 29 motor:
- Chân (Legs): 12 motor (6 mỗi chân — hip roll, hip pitch, hip yaw, knee, ankle pitch, ankle roll)
- Eo (Waist): 3 motor (yaw, roll, pitch)
- Tay (Arms): 14 motor (7 mỗi tay — shoulder pitch, shoulder roll, shoulder yaw, elbow, wrist roll, wrist pitch, gripper nếu có DEX3)
Khi stream vào PlotJuggler, bạn thấy motor_state[0] đến motor_state[28]. Mapping index → tên khớp cụ thể có trong tài liệu chính thức của unitree_ros2 — bài 2 của series sẽ đi sâu vào việc gán nhãn từng motor.
/sportmodestate — trạng thái high-level
Topic /sportmodestate publish ở 50–100 Hz và chứa dữ liệu sau khi G1's onboard controller đã xử lý và tích hợp:
| Field | Ý nghĩa |
|---|---|
position[3] |
Vị trí ước tính (x, y, z) trong world frame (m) |
velocity[3] |
Vận tốc tịnh tiến (m/s) |
yaw_speed |
Vận tốc xoay quanh trục z (rad/s) |
gait_type |
Loại dáng đi (integer enum) |
foot_force[4] |
Lực chân ước tính |
mode |
Chế độ hoạt động của controller |
body_height |
Chiều cao thân ước tính (m) |
Khi nào dùng topic nào:
- Dùng
/lowstatekhi debug phần cứng và firmware: motor nào bị quá nhiệt? IMU có đọc đúng không? Khớp nào có rung động bất thường? - Dùng
/sportmodestatekhi debug hành vi cấp cao: robot có đang di chuyển đúng hướng không? Vận tốc thực có khớp command không? Body height có ổn định không?
PlotJuggler Record vs ros2 bag record — khi nào dùng gì?
Câu hỏi thực tế mà nhiều người nhầm lẫn khi bắt đầu.
PlotJuggler internal buffer
Khi streaming trong PlotJuggler, tool tự động giữ một buffer trong RAM (mặc định 60 giây, có thể điều chỉnh). Bạn có thể pause streaming và scrub lại timeline để xem đúng khoảnh khắc robot gặp sự cố.
Giới hạn: Buffer mất hoàn toàn khi đóng PlotJuggler. Không lưu ra file.
Dùng khi: Debug nhanh trong buổi test, muốn xem ngay vấn đề đang xảy ra, session ngắn dưới vài phút.
ros2 bag record với định dạng MCAP
# Cài MCAP storage plugin cho Humble (Iron/Jazzy đã có sẵn)
sudo apt install ros-humble-rosbag2-storage-mcap
# Ghi /lowstate và /sportmodestate ra file MCAP
ros2 bag record -s mcap -o g1_session_$(date +%Y%m%d_%H%M) /lowstate /sportmodestate
# Ctrl+C để dừng ghi
File .mcap tự chứa message schema bên trong — PlotJuggler mở trực tiếp mà không cần cài package message type:
# Mở file MCAP để phân tích offline
ros2 run plotjuggler plotjuggler -d g1_session_20260615_1430/*.mcap
# hoặc: File → Load Data File trong giao diện PlotJuggler
Humble mặc định ghi
.db3(SQLite3): Format này không tự chứa schema, khiến PlotJuggler gặp khó khăn khi parse một số message type của Unitree. Luôn thêm flag-s mcapkhi record trên Humble.
Bảng so sánh nhanh
| Tiêu chí | PlotJuggler Live Buffer | ros2 bag record (MCAP) |
|---|---|---|
| Xem ngay real-time | ✅ | ❌ (cần play lại sau) |
| Lưu vĩnh viễn | ❌ (mất khi đóng) | ✅ |
| Chia sẻ với đồng nghiệp | ❌ | ✅ |
| Session dài (>5 phút) | ❌ (RAM giới hạn) | ✅ (disk) |
| Mở lại bất cứ lúc nào | ❌ | ✅ |
Cần ros2 bag play để xem |
❌ (stream trực tiếp) | ❌ (PlotJuggler mở .mcap trực tiếp) |
Chiến lược thực tế tốt nhất: Bật ros2 bag record chạy nền ngay khi bắt đầu test robot, đồng thời dùng PlotJuggler để monitor live. Nếu robot làm điều gì lạ, bạn đã có file để mở lại và phân tích chi tiết.
# Terminal 1: Ghi bag nền
ros2 bag record -s mcap -o session_$(date +%Y%m%d_%H%M) /lowstate /sportmodestate &
# Terminal 2: Monitor live
ros2 run plotjuggler plotjuggler
Checklist 15 phút — từ đầu đến stream live
# === TERMINAL 1: Setup môi trường ===
source /opt/ros/humble/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI=file:///home/$USER/cyclonedds_g1.xml
# Verify kết nối vật lý
ping -c 3 192.168.123.10
# Verify ROS2 thấy G1
ros2 topic list | grep -E "lowstate|sportmode"
# Kỳ vọng: /lowstate và /sportmodestate xuất hiện
# Kiểm tra tần số
ros2 topic hz /lowstate
# Kỳ vọng: ~500 Hz
# === TERMINAL 2: (Tùy chọn) Ghi bag nền ===
source /opt/ros/humble/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI=file:///home/$USER/cyclonedds_g1.xml
ros2 bag record -s mcap -o session_$(date +%Y%m%d_%H%M) /lowstate /sportmodestate
# === TERMINAL 3: Launch PlotJuggler ===
source /opt/ros/humble/setup.bash
export RMW_IMPLEMENTATION=rmw_cyclonedds_cpp
export CYCLONEDDS_URI=file:///home/$USER/cyclonedds_g1.xml
ros2 run plotjuggler plotjuggler
# Trong PlotJuggler: Streaming → Start → ROS2 Topic Subscriber → tick /lowstate + /sportmodestate → OK
Kết luận
Sau bài này, bạn đã có pipeline hoàn chỉnh để xem dữ liệu live từ Unitree G1:
- PlotJuggler cài trong một lệnh
apt install ros-humble-plotjuggler-ros - CycloneDDS cấu hình đúng interface Ethernet — bước không được bỏ qua
/lowstate(500 Hz, 29 motor + IMU pelvis) và/sportmodestate(50 Hz, state ước tính high-level) đều đang stream vào PlotJuggler- Biết khi nào dùng live buffer và khi nào record
.mcap
Bài tiếp theo trong series sẽ đi sâu vào việc tổ chức layout 23 khớp — cách nhóm panel theo từng limb (left leg, right leg, left arm, right arm, waist), đặt layout template để tái dùng giữa các session, và workflow ghi .mcap chuẩn để phân tích sim2real sau này.
Bài viết liên quan
- Bài 2: Layout 23 khớp + ghi MCAP — Tổ chức panel theo limb, template layout, ghi
.mcapchuẩn cho phân tích offline - Bài 3: Debug IMU, Quaternion và FFT rung động — Phân tích tư thế từ quaternion, dùng FFT phát hiện rung động cơ học
- Debug MPC/WBID G1 bằng PlotJuggler trong Mujoco — Series anh em: PlotJuggler với G1 trong môi trường simulation MuJoCo



