ros2 topic echo /odometry/filtered --once
# Look for:
# - pose.pose.position: (x, y, z) in meters
# - pose.pose.orientation: Quaternion
# - twist.twist.linear: Linear velocity
# - twist.twist.angular: Angular velocity
# - covariance: Uncertainty estimates
# Questions:
# 1. Where is robot? (x, y position)
# 2. Which direction facing? (orientation)
# 3. How fast moving? (linear.x)
ros2 interface show geometry_msgs/msg/Twist
# Output:
# Vector3 linear
# float64 x
# float64 y
# float64 z
# Vector3 angular
# float64 x
# float64 y
# float64 z
ros2 topic pub --once /cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 0.3, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"
# Robot ignores single command due to timeout!
# First, ARM the robot
ros2 service call /lyra/arm std_srvs/srv/Trigger
# Then publish continuously at 10 Hz
ros2 topic pub --rate 10 /cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 0.3, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"
# Robot moves forward at 0.3 m/s
# Press Ctrl+C to stop
# ARM robot
ros2 service call /lyra/arm std_srvs/srv/Trigger
# Drive forward 0.3 m/s
ros2 topic pub --rate 10 /cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 0.3, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"
# Let it run for 3 seconds, then Ctrl+C
# Robot should travel ~0.9 meters
# ARM robot (if needed)
ros2 service call /lyra/arm std_srvs/srv/Trigger
# Rotate in place (1.0 rad/s β 57Β°/s)
ros2 topic pub --rate 10 /cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 0.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.0}}"
# Let it run for 1.57 seconds for 90Β° turn
# Then Ctrl+C
# ARM robot
ros2 service call /lyra/arm std_srvs/srv/Trigger
# Drive for exactly 2 seconds then auto-stop
timeout 2 ros2 topic pub --rate 10 /cmd_vel geometry_msgs/msg/Twist \
"{linear: {x: 0.5, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 0.0}}"
# No need for Ctrl+C, stops automatically
ros2 service list | grep lyra
# Output:
# /lyra/arm
# /lyra/disarm
# /lyra/emergency_stop
# ARM robot (enable motors)
ros2 service call /lyra/arm std_srvs/srv/Trigger
# Output:
# waiting for service to become available...
# requester: making request: std_srvs.srv.Trigger_Request()
#
# response:
# std_srvs.srv.Trigger_Response(success=True, message='Motors armed')
# DISARM robot (disable motors)
ros2 service call /lyra/disarm std_srvs/srv/Trigger
# Output:
# response:
# std_srvs.srv.Trigger_Response(success=True, message='Motors disarmed')
# Emergency stop
ros2 service call /lyra/emergency_stop std_srvs/srv/Trigger
# Output:
# response:
# std_srvs.srv.Trigger_Response(success=True, message='Emergency stop activated')
# Get service type
ros2 service type /lyra/arm
# Output: std_srvs/srv/Trigger
# See interface definition
ros2 interface show std_srvs/srv/Trigger
# Output:
# ---
# bool success # indicate successful run of triggered service
# string message # informational, e.g. for error messages
# In rqt_graph:
# 1. Find /joy_node (reads USB controller)
# 2. Follow arrow to /joy topic
# 3. Follow to /teleop_twist_joy_node (converts joy β velocity)
# 4. Follow to /cmd_vel_joy topic
# 5. Follow to /cmd_vel_gate (safety checks)
# 6. Follow to /cmd_vel topic
# 7. Follow to /lyra_bridge (sends to STM32)
# Questions:
# 1. How many nodes process joystick input before reaching motors?
# 2. What happens if /cmd_vel_gate node crashes?
# 3. Why separate /cmd_vel_joy and /cmd_vel topics?
# Record LiDAR and odometry
ros2 bag record -o test_data /scan /odometry/filtered
# Output:
# [INFO] [rosbag2_storage]: Opened database 'test_data/test_data_0.db3' for writing.
# [INFO] [rosbag2_transport]: Listening for topics...
# [INFO] [rosbag2_transport]: Subscribed to topic '/scan'
# [INFO] [rosbag2_transport]: Subscribed to topic '/odometry/filtered'
# [INFO] [rosbag2_transport]: All requested topics are subscribed. Stopping discovery...
# Drive robot around for 30 seconds
# Press Ctrl+C to stop recording
# Record everything (β οΈ large file!)
ros2 bag record -a -o full_session
# Not recommended for long sessions (GBs of data)
# Compress on-the-fly (smaller files)
ros2 bag record --compression-mode file --compression-format zstd \
-o compressed_data /scan /odometry/filtered /imu/data
# Info about bag
ros2 bag info test_data
# Output:
# Files: test_data_0.db3
# Bag size: 15.2 MiB
# Storage id: sqlite3
# Duration: 30.5s
# Start: Jan 5 2026 14:30:00.123
# End: Jan 5 2026 14:30:30.654
# Messages: 3050
# Topic information: Topic: /scan | Type: sensor_msgs/msg/LaserScan | Count: 305 | Serialization Format: cdr
# Topic: /odometry/filtered | Type: nav_msgs/msg/Odometry | Count: 915 | Serialization Format: cdr
# Play bag (default speed)
ros2 bag play test_data
# Play at 2x speed
ros2 bag play test_data --rate 2.0
# Play at 0.5x speed (slow motion)
ros2 bag play test_data --rate 0.5
# Play in loop
ros2 bag play test_data --loop
# Play starting from specific time
ros2 bag play test_data --start-offset 5.0 # Skip first 5 seconds
# Record sensors while driving
ros2 bag record -o driving_session \
/scan /odometry/filtered /imu/data /cmd_vel_joy /battery_voltage
# Drive robot:
# - Forward 2m
# - Turn 90Β° left
# - Forward 2m
# - Turn 90Β° left
# - Continue until back at start (square pattern)
# Stop recording (Ctrl+C)
# Get bag info
ros2 bag info driving_session
# Play back and visualize
# Terminal 1: Play bag
ros2 bag play driving_session
# Terminal 2: Echo odometry
ros2 topic echo /odometry/filtered --field pose.pose.position
# Terminal 3: Echo velocity commands
ros2 topic echo /cmd_vel_joy
# Questions:
# 1. What was max speed achieved?
# 2. How far did robot drift from start?
# 3. What was battery voltage change?
rqt_plot
Topic field 1: /odometry/filtered/pose/pose/position/x
Topic field 2: /odometry/filtered/pose/pose/position/y
Topic field 3: /battery_voltage/data
# See position and battery change over time
rqt_image_view
# Dropdown: Select /pi_camera/image_raw/compressed
# Camera feed appears
# 1. Check if topic exists
ros2 topic list | grep scan
# 2. Check which nodes publish it
ros2 topic info /scan
# Should show Publisher count > 0
# 3. Check if node is running
ros2 node list | grep sllidar
# 4. Check for errors in logs
ros2 topic echo /rosout | grep -i error
# 5. Power cycle robot
# β‘ This fixes 90% of issues
# 1. Check if ARMED
ros2 topic echo /lyra/armed --once
# If false, ARM it:
ros2 service call /lyra/arm std_srvs/srv/Trigger
# 2. Verify publishing continuously (not --once)
# β Wrong:
ros2 topic pub --once /cmd_vel ...
# β Right:
ros2 topic pub --rate 10 /cmd_vel ...
# 3. Check command timeout (500ms)
# Must publish at least 2 Hz
# 4. Check if another node controlling
ros2 topic info /cmd_vel
# Multiple publishers? One might override
# 5. Check for errors
ros2 topic echo /rosout | grep cmd_vel
# 1. Check service exists
ros2 service list | grep lyra/arm
# 2. Check node providing service
ros2 service find std_srvs/srv/Trigger
# 3. Check if node alive
ros2 node list | grep lyra_bridge
# 4. Try with timeout
timeout 5 ros2 service call /lyra/arm std_srvs/srv/Trigger
# 5. Restart node or power cycle
# --- Topics ---
ros2 topic list # List all topics
ros2 topic type /cmd_vel # Get message type
ros2 topic hz /scan # Check publish rate
ros2 topic echo /odometry/filtered --once # View single message
ros2 topic pub --rate 10 /cmd_vel ... # Publish continuously
# --- Services ---
ros2 service list | grep lyra # List robot services
ros2 service type /lyra/arm # Get service type
ros2 service call /lyra/arm std_srvs/srv/Trigger # Call service
# --- Nodes ---
ros2 node list # List all nodes
ros2 node info /lyra_bridge # Node details
# --- Recording ---
ros2 bag record -o name /topic1 /topic2 # Record topics
ros2 bag info name # Bag statistics
ros2 bag play name # Playback
# --- Visualization ---
rqt_graph --force-discover # Node graph
rqt_plot # Time series
rqt_console # Logs
rqt_image_view # Camera
# --- Interfaces ---
ros2 interface show geometry_msgs/msg/Twist # Message structure
ros2 interface show std_srvs/srv/Trigger # Service structure