ROS 中订阅odom消息并按行保存到txt中

本文介绍如何在ROS中订阅odom话题并将位置和姿态数据保存到txt文件。通过编写C++程序,将odom消息的position和orientation数据写入文件,方便后续处理。提供了程序代码示例和运行效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  系统ubuntu16.04  kinetic     20.04 noetic

想写一个小车寻迹程序,用rostopic echo odom/pose/pose/position > odom.txt  ,保存的数据类型不好用,而且还要用到orientation数据,最好还是自己写一个程序吧程序如下,

#include "ros/ros.h"  //ros需要的头文件
#include <geometry_msgs/Twist.h>
#include <nav_msgs/Odometry.h>
#include "std_msgs/String.h"
//以下为串口通讯需要的头文件
#include <string>        
#include <iostream>
#include <cstdio>
#include <unistd.h>
#include <math.h>
#include <fstream>

/****************************************************************************/
using std::string;
using std::exception;
using std::cout;
using std::cerr;
using std::endl;
using std::vector;

//float x_pre,y_pre,z_pre;
 float x_pre=0.0f;
float y_pre=0.0f;
/************************************************************/

void callback_odom(const nav_msgs::Odometry::ConstPtr& odom)//订阅/odom主题回调函数
{


     
   // geometry_msgs::PoseStamped this_pose_stamped;
	float x,y,z
<think>我们之前已经实现了将路径保存到CSV文件,现在需要从CSV文件中读取路径发布为ROS的nav_msgs/Path消息,以便导航系统使用(例如提供给move_base或者用于显示等)。步骤:1.读取CSV文件:按读取,解析每一数据(时间戳、位置、姿态)2.构建nav_msgs::Path消息:需要设置header(包括时间戳和坐标系),以及poses数组(每个元素是geometry_msgs/PoseStamped)3.发布路径:创建一个Publisher来发布路径消息4.在ROS节点中完成上述操作注意:CSV文件的格式和之前保存的格式一致,即:timestamp,x,y,z,orientation_x,orientation_y,orientation_z,orientation_w但要注意,在读取时间戳时,我们保存的是ROS时间戳(包含秒和纳秒),在CSV中保存的是浮点数(如1675246800.42)。在读取后需要将其拆分为秒和纳秒部分。然而,在保存时我们使用的是`pose.header.stamp`,它是一个`ros::Time`对象,当我们用`<<`输出时,实际上输出的是浮点数(秒数,带小数部分)。在重新加载时,我们可以将这个浮点数转换为ros::Time,但要注意精度。另一种做法:在保存时,我们分别保存秒和纳秒,但之前的保存代码没有这样做。因此,我们在读取时,将浮点数转换为整数秒和小数纳秒部分。例如:浮点数时间戳t,可以这样转换:-sec=(int32_t)floor(t);-nsec=(int32_t)((t-sec)*1e9);但是,由于浮点数的精度问题,可能会有细微的误差,但对于路径的时间戳来说,通常不是关键(因为路径主要用于规划,时间戳主要用于显示顺序)。此外,我们还需要知道路径的坐标系(frame_id),这个在保存时没有保存,所以我们需要在加载时指定(例如通过参数或硬编码)。通常,全局路径的坐标系是"map",局部路径可能是"odom"。实现思路:1.创建一个ROS节点。2.从参数服务器获取文件路径和坐标系(或者使用硬编码,但参数更灵活)。3.读取CSV文件,解析每一。4.构建nav_msgs::Path消息。5.循环发布该路径(或者发布一次,或者按一定频率发布,根据需求)。代码示例:```cpp#include<ros/ros.h>#include<nav_msgs/Path.h>#include<geometry_msgs/PoseStamped.h>#include<fstream>#include<string>#include<vector>#include<sstream>#include<cstdlib>intmain(intargc,char**argv){ros::init(argc,argv,"path_loader");ros::NodeHandlenh;ros::NodeHandleprivate_nh("~");//获取参数std::stringfile_path;std::stringframe_id;private_nh.param<std::string>("file_path",file_path,"/home/user/nav_path.csv");private_nh.param<std::string>("frame_id",frame_id,"map");//创建Publisher,话题名称可配置ros::Publisherpath_pub=nh.advertise<nav_msgs::Path>("loaded_path",1,true);//读取CSV文件std::ifstreaminfile(file_path);if(!infile.is_open()){ROS_ERROR("Failedtoopenfile:%s",file_path.c_str());return-1;}nav_msgs::Pathpath_msg;path_msg.header.frame_id=frame_id;path_msg.header.stamp=ros::Time::now();std::stringline;//跳过表头std::getline(infile,line);while(std::getline(infile,line)){std::stringstreamss(line);std::stringtoken;std::vector<std::string>tokens;while(std::getline(ss,token,',')){tokens.push_back(token);}//检查是否有足够的列(8列:时间戳,x,y,z,ox,oy,oz,ow)if(tokens.size()<8){ROS_WARN("Invalidline:%s",line.c_str());continue;}try{doubletimestamp=std::stod(tokens[0]);doublex=std::stod(tokens[1]);doubley=std::stod(tokens[2]);doublez=std::stod(tokens[3]);doubleox=std::stod(tokens[4]);doubleoy=std::stod(tokens[5]);doubleoz=std::stod(tokens[6]);doubleow=std::stod(tokens[7]);geometry_msgs::PoseStampedpose;pose.header.frame_id=frame_id;//将时间戳转换为秒和纳秒pose.header.stamp.sec=(int32_t)floor(timestamp);pose.header.stamp.nsec=(int32_t)((timestamp-pose.header.stamp.sec)*1e9);pose.pose.position.x=x;pose.pose.position.y=y;pose.pose.position.z=z;pose.pose.orientation.x=ox;pose.pose.orientation.y=oy;pose.pose.orientation.z=oz;pose.pose.orientation.w=ow;path_msg.poses.push_back(pose);}catch(conststd::exception&e){ROS_ERROR("Errorparsingline:%s",line.c_str());}}infile.close();//发布路径//由于路径可能较长,我们设置latch,确保订阅者能收到ROS_INFO("Publishingloadedpathwith%dposes",(int)path_msg.poses.size());path_pub.publish(path_msg);//如果希望持续发布,可以使用循环,但这里我们只发布一次,然后等待ros::Rateloop_rate(1);while(ros::ok()){//如果希望持续发布,可以取消以下注释//path_msg.header.stamp=ros::Time::now();//path_pub.publish(path_msg);ros::spinOnce();loop_rate.sleep();}return0;}```说明:1.使用`private_nh`获取私有参数,这样可以在启动节点时通过参数传递文件路径和坐标系。2.发布器使用latch(最后一个参数为true),这样新的订阅者会立即收到最后一条消息。3.默认情况下,节点读取文件后发布一次路径,然后以1Hz频率循环(但不重复发布路径)。如果需要持续发布路径,可以取消循环内的发布代码(更新header.stamp)。如何运:1.将代码保存为`path_loader.cpp`,放在功能包的`src`目录下。2.修改`CMakeLists.txt`,添加可执文件和链接库:```cmakeadd_executable(path_loader_nodesrc/path_loader.cpp)target_link_libraries(path_loader_node${catkin_LIBRARIES})```3.编译后运:```bashrosrunyour_packagepath_loader_node_file_path:=/path/to/your/file.csv_frame_id:=map```相关问题:1.如何确保加载的路径与原始路径完全一致?需检查坐标系一致性和数据解析准确性,可通过Rviz可视化对比。2.加载的路径如何用于导航?可通过`/move_base`服务的`make_plan`接口,或作为参考路径提供给轨迹跟踪控制器。3.路径数据量很大时如何优化加载速度?使用二进制格式存储路径数据,或采用增量加载方式。4.如何将加载的路径用于多机器人系统?为不同机器人分配独立话题,在加载时修改路径的坐标系为各自坐标系。5.路径时间戳不连续会导致什么问题?可能导致路径显示断点,但不影响静态路径规划。动态轨迹跟踪需确保时间连续性。注意:在导航中,通常全局路径是静态的,所以时间戳不关键,因此也可以全部设置为当前时间。</think>### ROS1 C++ 实现路径加载与导航系统集成 #### 实现思路 在ROS1导航系统中加载本地路径文件的核心流程: 1. **文件解析**:读取CSV格式的路径文件 2. **消息构建**:转换为`nav_msgs::Path`消息 3. **路径发布**:通过ROS话题发送给导航系统 4. **导航集成**:与`move_base`协同工作实现路径跟踪 #### 完整代码实现 ```cpp #include <ros/ros.h> #include <nav_msgs/Path.h> #include <geometry_msgs/PoseStamped.h> #include <fstream> #include <sstream> #include <string> #include <vector> // 解析CSV数据 geometry_msgs::PoseStamped parseCSVLine(const std::string& line) { std::istringstream ss(line); std::string token; std::vector<double> values; while (std::getline(ss, token, ',')) { try { values.push_back(std::stod(token)); } catch (...) { ROS_WARN("Invalid data format: %s", token.c_str()); } } geometry_msgs::PoseStamped pose; if (values.size() >= 7) { pose.pose.position.x = values[1]; pose.pose.position.y = values[2]; pose.pose.position.z = values[3]; pose.pose.orientation.x = values[4]; pose.pose.orientation.y = values[5]; pose.pose.orientation.z = values[6]; pose.pose.orientation.w = values[7]; } return pose; } int main(int argc, char **argv) { ros::init(argc, argv, "path_loader"); ros::NodeHandle nh; ros::NodeHandle private_nh("~"); // 获取参数 std::string file_path; std::string frame_id = "map"; // 默认坐标系 private_nh.param<std::string>("file_path", file_path, "/home/user/nav_path.csv"); private_nh.param<std::string>("frame_id", frame_id, "map"); // 创建路径发布者 ros::Publisher path_pub = nh.advertise<nav_msgs::Path>("/loaded_path", 1, true); nav_msgs::Path path_msg; path_msg.header.frame_id = frame_id; path_msg.header.stamp = ros::Time::now(); // 读取路径文件 std::ifstream infile(file_path); if (!infile.is_open()) { ROS_ERROR("Failed to open path file: %s", file_path.c_str()); return -1; } std::string line; std::getline(infile, line); // 跳过表头 while (std::getline(infile, line)) { if (!line.empty()) { geometry_msgs::PoseStamped pose = parseCSVLine(line); pose.header.frame_id = frame_id; pose.header.stamp = ros::Time::now(); // 使用当前时间 path_msg.poses.push_back(pose); } } infile.close(); ROS_INFO("Loaded %d path points from %s", (int)path_msg.poses.size(), file_path.c_str()); // 持续发布路径 ros::Rate rate(1); while (ros::ok()) { path_msg.header.stamp = ros::Time::now(); path_pub.publish(path_msg); rate.sleep(); } return 0; } ``` #### 集成到导航系统 1. **修改CMakeLists.txt**: ```cmake add_executable(path_loader_node src/path_loader.cpp) target_link_libraries(path_loader_node ${catkin_LIBRARIES}) ``` 2. **启动文件配置**: ```xml <launch> <!-- 加载路径 --> <node name="path_loader" pkg="your_package" type="path_loader_node"> <param name="file_path" value="$(find your_package)/paths/nav_path.csv"/> <param name="frame_id" value="map"/> </node> <!-- 导航系统集成 --> <node pkg="move_base" type="move_base" name="move_base"> <remap from="plan" to="/loaded_path"/> <!-- 重映射话题 --> </node> </launch> ``` #### 路径文件格式要求 ```csv timestamp,x,y,z,orientation_x,orientation_y,orientation_z,orientation_w 1675246800.42,1.23,4.56,0.0,0.0,0.0,0.707,0.707 ... ``` #### 导航系统协同工作 1. **路径订阅**:`move_base`订阅`/loaded_path`话题 2. **路径跟踪**:使用`base_local_planner`实现轨迹跟踪 3. **实时更新**:动态调整路径点的时间戳保持有效性 4. **可视化**:在Rviz中添加Path显示插件验证路径 #### 关键参数说明 | 参数 | 默认值 | 说明 | |------|--------|------| | `file_path` | `/home/user/nav_path.csv` | 路径文件位置 | | `frame_id` | `map` | 路径坐标系 | | 发布话题 | `/loaded_path` | 导航系统订阅的话题 | | 发布频率 | 1Hz | 确保路径持续可用 | #### 性能优化技巧 1. **内存管理**:使用`reserve()`预分配路径点内存 2. **懒加载**:仅在路径变化时重新加载文件 3. **二进制格式**:处理大型路径时使用ROS bag格式 4. **局部更新**:增量式更新路径而非全量加载 ### 相关问题 1. **如何确保加载的路径与原始路径完全一致?** 需验证坐标系转换和单位一致性,可通过Rviz可视化对比原始路径和加载路径[^2]。 2. **加载的路径如何用于实际导航任务?** 需通过`move_base`服务的`make_plan`接口,或作为全局规划器的输入[^4]。 3. **路径数据量很大时如何优化加载速度?** 可采用分块加载策略,使用`ROS::Timer`分批发布路径点。 4. **如何将加载的路径用于多机器人系统?** 需为不同机器人分配独立命名空间,在加载时修改路径的坐标系为各自局部坐标系。 5. **路径时间戳过期会导致什么问题?** 导航系统可能拒绝执过时路径,需在加载时更新为当前时间或使用`/tf`转换到最新坐标系。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值