Record, replay, and analyze ROS 2 launch executions with resource monitoring and interactive management.
Requires Ubuntu 22.04+ with ROS 2 (Humble or Jazzy), Python 3.10+, and:
sudo apt install libz3-dev
sudo apt install ros-${ROS_DISTRO}-rclcpp-components ros-${ROS_DISTRO}-class-loaderInstall from PyPI:
pip install play_launchOptional: Enable I/O monitoring (requires sudo):
play_launch setcap-io-helperLaunch any ROS 2 package with monitoring and Web UI enabled by default:
play_launch launch demo_nodes_cpp talker_listener.launch.pyAccess Web UI at http://127.0.0.1:8080 for real-time node management and log streaming.
The Rust parser is used by default for speed. For maximum compatibility, use --parser python.
Replace ros2 launch with play_launch launch:
play_launch launch <package> <launch_file> [arguments...]Replace ros2 run with play_launch run:
play_launch run <package> <executable> [arguments...]Record first, replay multiple times:
# Record
play_launch dump launch <package> <launch_file> [arguments...]
# Replay
play_launch replayAll features enabled by default:
- Resource monitoring: CPU, memory, I/O, GPU (2s interval)
- Diagnostic monitoring:
/diagnosticsand/diagnostics_aggtopics - Web UI: Interactive management at
http://127.0.0.1:8080 - Container isolation: Composable nodes run in isolated processes via fork+exec (default)
Disable specific features:
play_launch launch <package> <launch_file> --disable-monitoring
play_launch launch <package> <launch_file> --disable-diagnostics
play_launch launch <package> <launch_file> --disable-web-ui
play_launch launch <package> <launch_file> --disable-allControl how composable nodes are managed (default: isolated):
# Isolated: fork+exec per-node process isolation (default)
play_launch launch <pkg> <file> --container-mode isolated
# Observable: ComponentEvent publishing, shared process
play_launch launch <pkg> <file> --container-mode observable
# Stock: use original container from launch file, no override
play_launch launch <pkg> <file> --container-mode stockChange sampling interval (default: 2000ms):
play_launch launch <package> <launch_file> --monitor-interval-ms 500Change address or port (default: 127.0.0.1:8080):
play_launch launch <package> <launch_file> --web-addr 0.0.0.0:8080Use YAML for advanced control:
# config.yaml
monitoring:
enabled: true
sample_interval_ms: 2000
processes:
- node_pattern: "NODE 'rclcpp_components/component_container*"
cpu_affinity: [0, 1]
nice: 5Apply configuration:
play_launch replay --config config.yamlGenerate interactive plots from monitoring data:
# Plot latest execution
play_launch plot
# Plot specific log directory
play_launch plot --log-dir play_log/2025-10-28_16-17-56
# Plot specific metrics
play_launch plot --metrics cpu memory
# List available metrics
play_launch plot --list-metricsOutput saved to play_log/<timestamp>/plot/:
cpu_timeline.html- CPU usage over timememory_timeline.html- Memory usage over timeio_timeline.html- I/O read/write ratescpu_distribution.html- CPU distribution box plotmemory_distribution.html- Memory distribution box plotstatistics.txt- Top 10 rankings for all metrics
- Node management: Start/Stop/Restart individual or all nodes
- Container controls: Load/Unload composable nodes
- Real-time logs: Stream stdout/stderr with log level coloring and filtering
- Diagnostics panel: View
/diagnosticsmessages with level filtering - Status monitoring: Color-coded node states
- Auto-restart: Per-node automatic restart configuration
- Search & filter: Find nodes in large deployments
play_log/<timestamp>/
├── node/<node_name>/
│ ├── metadata.json
│ ├── metrics.csv # Resource metrics (when enabled)
│ ├── out/err # Process logs
│ ├── pid/status/cmdline
│ └── params_files/ # ROS parameter files
├── load_node/<name>/
│ └── out/err # Per-composable-node logs (isolated mode)
├── system_stats.csv # System-wide metrics
├── diagnostics.csv # Diagnostic messages (when enabled)
└── plot/ # Generated visualizations
# Launch (all features enabled by default)
play_launch launch <package> <launch_file> [args...]
play_launch run <package> <executable> [args...]
# Dump and replay
play_launch dump launch <package> <launch_file> [args...]
play_launch replay [--input-file record.json]
# Parser selection (Rust is default)
play_launch launch <pkg> <file> --parser rust # Default, fast
play_launch launch <pkg> <file> --parser python # Maximum compatibility
# Container mode
play_launch launch <pkg> <file> --container-mode isolated # Default
play_launch launch <pkg> <file> --container-mode observable
play_launch launch <pkg> <file> --container-mode stock
# Disable features
play_launch launch <pkg> <file> --disable-monitoring
play_launch launch <pkg> <file> --disable-diagnostics
play_launch launch <pkg> <file> --disable-web-ui
play_launch launch <pkg> <file> --disable-all
play_launch launch <pkg> <file> --disable-respawn
# Enable only specific features
play_launch launch <pkg> <file> --enable monitoring
play_launch launch <pkg> <file> --enable web-ui --enable diagnostics
# Adjust settings
play_launch launch <pkg> <file> --monitor-interval-ms 500
play_launch launch <pkg> <file> --web-addr 0.0.0.0:8080
play_launch launch <pkg> <file> --config config.yaml
# Logging
play_launch launch <pkg> <file> --verbose # Enable INFO level
RUST_LOG=play_launch=debug play_launch launch <pkg> <file> # DEBUG level
# Visualization
play_launch plot
play_launch plot --log-dir <dir>
play_launch plot --metrics cpu memory io gpu
play_launch plot --list-metrics- ROS 2 (Humble on Ubuntu 22.04, Jazzy on 24.04)
- Rust toolchain (stable)
- Python 3.10+
- just command runner
- cargo-nextest test runner
- uv Python package manager
just install-depsThis installs colcon-cargo-ros2 and runs rosdep install for all ROS dependencies.
just build # Full build: colcon + interception .so + wheel
just build-cpp # C++ only (msgs + container)
just build-rust # Rust only (assumes C++ install/ exists)
just build-wheel # Bundle + wheel only (no colcon rebuild)Optional: enable I/O monitoring (requires sudo):
just setcap-io-helperjust run launch <package> <launch_file> [arguments...]
just run run <package> <executable> [arguments...]just test # Parser unit + fast integration (~3s)
just test-all # Parser unit + all integration (~30s)
just test-unit # Parser unit tests only
just test-integration # All integration tests
just test-autoware # Autoware integration testsjust check # clippy + rustfmt + ruff + cpplint + clang-format
just format # Auto-format Rust + Python + C++
just quality # Format then checkjust install-wheelMIT License. See LICENSE.txt.
