ROS中rosrun可生成CSV存储文件但roslaunch无法生成的问题排查及树莓派4适配方案咨询
roslaunch (And How to Fix It) Let's break down exactly what's going on here, fix your current code, and share some Raspberry Pi-friendly data storage alternatives tailored for ROS launch scenarios.
Core Problem: Working Directory Differences
When you run nodes with rosrun, each node uses the current working directory of your terminal (like your ROS package folder or workspace root). That's why your storage_csv.csv shows up exactly where you expect it.
But roslaunch has a default behavior: it sets each node's working directory to a hidden ROS log folder (usually ~/.ros/log/<unique-launch-ID>/). Your listener node is writing the CSV file—it's just hiding in that log directory, not where you're looking. If the log folder has weird permissions, it might even fail to create the file entirely.
Secondary Code Pitfalls
Your listener code has a couple of issues that make this problem worse (and could cause silent data loss even when it works):
- Frequent file open/close: You're opening and closing the CSV file every time a message arrives. This is inefficient, and if messages come faster than the file can open/close, you'll get unreported failures.
- No error checking: You never verify if the file actually opened successfully. If there's a path or permission issue, your code just skips writing data without telling you.
Let's fix the path issue first, then clean up the file handling.
Step 1: Use Absolute Paths (or ROS Parameters)
Ditch relative paths—instead, use an absolute path to your desired storage location, or make it flexible with ROS parameters. Here's the updated listener code:
//header #include "ros/ros.h" #include "std_msgs/String.h" #include <sstream> #include <fstream> std::ofstream MyFile; // Keep global, but open once at startup //callback function void storage_callback(const std_msgs::String::ConstPtr& msg) { if (MyFile.is_open()) { // Always check if the file is usable first MyFile << msg->data << "\n"; ROS_INFO("I recieve: [%s]", msg->data.c_str()); } else { ROS_ERROR("Failed to write data—file is not open!"); } } //main int main(int argc, char **argv) { ros::init(argc, argv, "listener_node"); ros::NodeHandle n; ROS_INFO("Storage code initialized"); // Get file path from ROS params (fallback to a Pi-friendly absolute path) std::string file_path; n.param<std::string>("storage_file_path", file_path, "/home/pi/catkin_ws/src/storage/storage_csv.csv"); // Open the file once at startup (append mode to avoid overwriting old data) MyFile.open(file_path, std::ios::out | std::ios::app); if (!MyFile.is_open()) { ROS_FATAL("Could not open file at %s! Check permissions or path.", file_path.c_str()); return 1; // Exit immediately if we can't open the file } MyFile << "listener data.\n"; ros::Subscriber sub = n.subscribe("/chatter", 1, storage_callback); ros::spin(); // Close the file only when the node exits MyFile.close(); return 0; }
Step 2: Fix the Launch File Typo
You have a small typo (takler_node instead of talker_node—it might work, but let's clean it up and add the optional path parameter):
<launch> <node name="talker_node" pkg="storage" type="talker1_executable" output="screen"/> <node name="listener_node" pkg="storage" type="listener1_executable" output="screen"> <!-- Optional: Override the file path here --> <param name="storage_file_path" value="/home/pi/my_data/storage_csv.csv" /> </node> </launch>
Since the Pi has limited resources (CPU, SD card speed), here are better options than raw CSV writes:
1. Use rosbag (Simplest, Most ROS-Native)
rosbag is ROS's built-in tool for recording and replaying topic data. It's perfect for the Pi because it's lightweight, requires no custom code, and stores data in a compact, standardized format.
Add a bag recorder to your launch file:
<launch> <node name="talker_node" pkg="storage" type="talker1_executable" output="screen"/> <node name="listener_node" pkg="storage" type="listener1_executable" output="screen"/> <!-- Record the /chatter topic to a specific file --> <node name="bag_recorder" pkg="rosbag" type="record" args="/chatter -O /home/pi/rosbags/chatter_log.bag" output="screen"/> </launch>
- To replay the data later:
rosbag play /home/pi/rosbags/chatter_log.bag - Pros: No code changes, supports multiple topics, easy to analyze later with tools like
rqt_bag.
2. SQLite for Structured Storage
If you need to query or organize data (not just log it), SQLite is a great embedded database for the Pi—no separate server needed, just a local file.
Install the ROS SQLite package first:
sudo apt-get install ros-<your-ros-version>-sqlite3
Then modify your listener node to write to a SQLite database instead of CSV. This is more efficient than frequent CSV writes, especially on slow SD cards.
3. Batch-Write CSV (If You Must Stick to CSV)
If CSV is non-negotiable, optimize it by buffering data and writing in batches:
- Collect 10-100 messages in a queue, then write all of them to the file at once.
- Enable file buffering with
MyFile.rdbuf()->pubsetbuf(0, 8192);to reduce SD card wear.
内容的提问来源于stack exchange,提问作者kartikeya singh

