POIROT is a comprehensive profiling and monitoring framework for ROS 2 applications that provides detailed insights into function performance, resource consumption, energy usage and CO2 emissions. With its htop-like terminal interface, real-time data visualization and CSV recording capabilities, POIROT enables developers to optimize their robotic systems for performance and sustainability, acting as a keen investigator of code inefficiencies.
- Core Profiling Library (poirot): Automatic system detection, comprehensive metrics (timing, memory, I/O, energy, CO2), low overhead, ROS 2 integration.
- Terminal User Interface (poirot_tui): Real-time table and graphs, interactive controls.
- CSV Recorder (poirot_recorder): Records profiling data to CSV for analysis.
- Demo Applications (poirot_demos): Examples for ROS 2 profiling.
- Message Definitions (poirot_msgs): Custom ROS 2 messages for profiling data.
cd ~/ros2_ws/src
git clone https://github.com/mgonzs13/poirot
cd ~/ros2_ws
rosdep install --from-paths src --ignore-src -r -y
colcon buildPOIROT uses the Ember Climate API to fetch real CO2 emission factors for energy consumption calculations. To enable CO2 monitoring, you need to obtain a free API key from Ember and set it as an environment variable:
export EMBER_KEY="your_api_key_here"Without this key, POIROT will use a global average CO2 factor as fallback.
POIROT uses RAPL (Running Average Power Limit) to monitor CPU energy consumption by reading from /sys/class/powercap/intel-rapl/*/energy_uj files. These files typically require special permissions to access. To resolve permission issues, create a rapl group and configure the appropriate permissions:
# Create rapl group
sudo groupadd rapl
# Add your user to the rapl group
sudo usermod -a -G rapl $USER
# Install sysfsutils
sudo apt install sysfsutils -y
# Run the following script to add the required lines to /etc/sysfs.conf
for rapl_dir in /sys/class/powercap/intel-rapl*; do
if [ -f "$rapl_dir/energy_uj" ]; then
domain=$(basename "$rapl_dir")
sudo echo "mode class/powercap/$domain/energy_uj = 0440" | sudo tee -a /etc/sysfs.conf
sudo echo "owner class/powercap/$domain/energy_uj = root:rapl" | sudo tee -a /etc/sysfs.conf
fi
done
# Restart the sysfsutils service.
sudo systemctl restart sysfsutils
# Reboot or log out and back in for group changes to take effect
# You can also run the following command to activate the changes to groups:
newgrp raplAfter following these steps, POIROT should be able to read energy consumption data without permission errors.
POIROT's GPU monitor uses nvidia-smi for NVIDIA GPUs and rocm-smi for AMD GPUs to collect GPU metrics such as power consumption and utilization. Ensure these tools are installed on your system. Without these tools, GPU monitoring will not be available.
-
Add dependency in
package.xml:<depend>poirot</depend>
-
Update
CMakeLists.txt:find_package(poirot REQUIRED) add_executable(your_node src/your_node.cpp) target_link_libraries(your_node poirot::poirot )
-
Instrument your C++ code:
#include "rclcpp/rclcpp.hpp" #include "poirot/poirot.hpp" using namespace poirot; class MyNode : public rclcpp::Node { public: MyNode() : Node("my_node") { // Create timer, subscribers, etc. timer_ = create_wall_timer( std::chrono::seconds(1), std::bind(&MyNode::timer_callback, this)); } private: void timer_callback() { PROFILE_FUNCTION(); // Automatically profiles this function // Your function logic here... } rclcpp::TimerBase::SharedPtr timer_; }; int main(int argc, char* argv[]) { rclcpp::init(argc, argv); auto node = std::make_shared<MyNode>(); rclcpp::spin(node); rclcpp::shutdown(); return 0; }
-
Instrument your Python code:
import rclpy
from rclpy.node import Node
from poirot import profile_function
class MyNode(Node):
def __init__(self) -> None:
super().__init__("my_node")
# Create a timer that fires every 1 second
self.timer = self.create_timer(1.0, self.timer_callback)
@profile_function # Automatically profiles this function
def timer_callback(self) -> None:
# Your function logic here...
def main() -> None:
rclpy.init()
node = MyNode()
executor = rclpy.executors.SingleThreadedExecutor()
try:
rclpy.spin(node)
except KeyboardInterrupt:
pass
finally:
executor.shutdown()
publisher_node.destroy_node()
subscriber_node.destroy_node()
if __name__ == "__main__":
main()The fastest way to see POIROT in action is to run the demo application with the TUI monitor:
Terminal 1 - Run the C++ demo node with profiling:
ros2 run poirot_demos demo_nodeOr for the Python demo node with profiling:
ros2 run poirot_demos demo_node.pyTerminal 2 - Run the TUI monitor:
ros2 run poirot_tui poirot_tuiYou should see a live table displaying profiling metrics for the demo node's timer and subscription callbacks, including wall time, CPU usage, memory consumption, energy and CO2 emissions.
poirot_1.mp4
The TUI provides multiple views and interactive controls:
General Navigation:
Q/ESC- QuitTab/Shift+Tab- Next/Previous tab←/→- Previous/Next tab1-0- Jump to specific tabC- Clear all dataMouse- Click to interact
Table View:
↑/K/↓/J- Navigate rowsPgUp/PgDn- Page up/downHome/G- First rowEnd/Shift+G- Last rowH/L- Scroll table left/right horizontallyS- Cycle sort columnR/O- Toggle sort order
Graph Views:
↑/K/↓/J- Navigate function listSpace/Enter- Toggle selected function in graphA- Enable all functionsN- Disable all functions
- Table - Main table view with all profiling data
- Wall - Wall time graph over time
- CPU - CPU time graph over time
- Mem - Memory usage graph over time
- IO-R - I/O read bytes graph over time
- IO-W - I/O write bytes graph over time
- Ctx - Context switches graph over time
- CPU Enrg - Energy consumption graph over time by the CPU
- GPU Enrg - Energy consumption graph over time by the GPU
- Enrg - Energy consumption graph over time
- CO2 - CO2 emissions graph over time
To record profiling data for offline analysis:
Terminal 1 - Run your ROS 2 application:
ros2 run poirot_demos demo_nodeTerminal 2 - Start the recorder:
ros2 run poirot_recorder poirot_recorder_node --ros-args -p csv_file_path:=/tmp/profiling_data.csvThe CSV file will contain comprehensive profiling data including timestamps, function names, call counts, timing metrics, memory usage, I/O statistics, energy consumption, CO2 emissions and system information.
