ros和c++使用时的技巧和踩过的坑

1. ros的技巧命令

  1. rosbag暂停播放rosbag play --clock --pause bagfile.bag ,通过空格来开始暂停和继续。rosbag play --pause bagfile.bag ,一帧帧点云播放,按s键播放下一帧,通过空格来暂停和继续。rosbag play bagfile.bag -r 0.1,慢速播放,后面的0.1即为播放的速度,如果改为10,即为10倍播放速度。
  2. roslaunch重定向,方便调试代码roslaunch lio_sam run.launch 2>&1 | tee log.txt, 该命令既能输出到屏幕,也可以打印信息到文件中。
  3. 清理ros磁盘: rosclean purge
  4. 调用tf树rosrun rqt_tf_tree rqt_tf_tree
  5. 播放一个包的部分话题rosbag play record.bag --topics /topic1 /topic2
    rosbag包的使用相关命令可以参考:链接: link 和链接: link ,我播放我的比赛数据包, rosbag play xiamengang_Truck3_918_20230830160116_sunny_zhongzai_2.bag --topic /minibus/front/lslidar_packets /minibus/rear/lslidar_packets /imu /minibus/gpsposition /minibus/insposition
  6. 调用plotjuggler: 可以看数据中的时间戳是否是一致的,rosrun plotjuggler plotjuggler
  7. 警告信息Warning: TF_OLD_DATA: Warning: TF_OLD_DATA ignoring data from the past for frame stm32_imu at time 1.6203e+09 according to authority unknown_publisher Possible reasons are listed at http://wiki.ros.org/tf/Errors%20explained at line 277 in /tmp/binarydeb/ros-kinetic-tf2-0.5.20/src/buffer_core.cpp ,执行rosparam set /use_sim_time true ,然后在播放bag数据时使用参数--clock
  8. evo的使用技巧: 画轨迹图evo_traj tum --ref lio_sam_traj.txt fast_lio_traj.txt --align_origin -ap --plot_mode=xy 其中–align_origin 是原点对齐,–plot_mode=xy为画图显示xy两个方向的结果, -ap是画图,也可以用evo_traj tum --ref GNSS_traj.txt Wgs84Xyz_quaternion_Tum.txt -ap评定绝对误差evo_ape tum GNSS_traj.txt Wgs84Xyz_quaternion_Tum.txt -ap;评定相对误差,evo_rpe tum GNSS_traj.txt Wgs84Xyz_quaternion_Tum.txt -ap
  9. 保存LIO-SAM-6AIXS的结果rosservice call /lio_sam_6axis/save_map
  10. roabag数据包转换成pcd的命令: rosrun pcl_ros bag_to_pcd ../40m_RsLidar.bag /velodyne_points ./
  11. rosbag包分割成两部分: 链接: 分割rosbag包
  12. 查看ubuntu系统:一般在下载ubuntu的安装包时,会让选择是arm64还是x86,可以通过命令uname -m查看系统版本,我的系统是x86_64,x64也被称为x86_64,因为x64是从x86延伸过来的。

2. catkin_make问题记录与方案

  1. 全局查找linux中的文件sudo find / -name Python3.framework 或者 locate Eigen进行模糊查询
  2. catkin_make报错:找不到分享的so文件,具体为Linux error while loading shared libraries: cannot open shared object file: No such file or directory: 参考链接: linksudo find / -name the_name_of_the_file.so , echo $LD_LIBRARY_PATH export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/my_library/
  3. catkin_make报错:ymbol lookup error: /lib/x86_64-linux-gnu/libp11-kit.so.0: undefined symbol: ffi_type_pointer, version LIBFFI_BASE_7.0: 参考链接:link
  4. catkin_make报错error: curl: /usr/local/lib/libcurl.so.4: no version information available (required by curl),主要是软链接错误:参考链接:linklocate libcurl.so.4 ,ls -l /softwares/anaconda3/lib/libcurl.so.4rm /softwares/anaconda3/lib/libcurl.so.4ln -s /snap/curl/1679/lib/libcurl.so.4.8.0 /softwares/anaconda3/lib/libcurl.so.4ls -l /softwares/anaconda3/lib/libcurl.so.4
  5. 打印环境变量echo $PATH,echo $LD_LIBRARY_PATHecho $CPLUS_INCLUDE_PATH
  6. 进入到Conda,用c++画图:进入环境conda activate, 退出环境conda deactivate,进入forcppEnvs画图环境,conda activate forcppEnvs
  7. matplotlibcpp.h: No such file or directory:先找到matplotlib-cpp,命令为sudo find / -name matplotlib-cpp,接下来将找到的路径放到环境变量中,也就是主目录的.bashrc文件中,如下***/matplotlib-cpp,之后source下,source ~/.bashrc,重新编译即可。
    插入matplotlib-cpp的路径主要思路就是catkin在编译的时候找不到头文件,将头文件给到编译的文件即可,可以在环境变量中添加,也可以在Cmakelists添加,我直接在环境变量中添加,以后写新的代码,就不用再麻烦添加了。

3. C++的技巧

  1. lio-sam输出轨迹:
std::ofstream ofsTraj;  // Trajectory文件
ofsTraj.open(MO.saveTrajDirectory, ios::out);
// 输出的技巧:
ofsTraj << std::fixed << setprecision(9)  << laserOdometryROS.header.stamp  
                << std::fixed << setprecision(3) << std::right << setw(9)  
                << laserOdometryROS.pose.pose.position.x
                << std::fixed << setprecision(3) << std::right << setw(9)  
                << laserOdometryROS.pose.pose.position.y  
                << std::fixed << setprecision(3) << std::right << setw(9)  
                << laserOdometryROS.pose.pose.position.z << std::endl;

4. C++踩过的坑

4.1. VectorXd和Vector3d的使用区别

Eigen::Vector3d first_point(p.x, p.y, p.z);
Eigen::Vector3d lla_point;
geo_converter.Reverse(first_point[0], first_point[1], first_point[2], lla_point[0], lla_point[1],
                      lla_point[2]);
lla_vec.push_back(lla_point);

// 存储WGS84下的XYZ和四元数
Eigen::VectorXd wgs84Xyz_orientation = Eigen::VectorXd::Zero(7);
earth.Forward(lla_point[0], lla_point[1], lla_point[2], wgs84Xyz_orientation[0], wgs84Xyz_orientation[1],
                   wgs84Xyz_orientation[2]);
wgs84Xyz_orientation[3] = laserOdometryROS.pose.pose.orientation.x;
wgs84Xyz_orientation[4] = laserOdometryROS.pose.pose.orientation.y;
wgs84Xyz_orientation[5] = laserOdometryROS.pose.pose.orientation.z;
wgs84Xyz_orientation[6] = laserOdometryROS.pose.pose.orientation.w;
wgs84Xyz_quaternion_vec.push_back(wgs84Xyz_orientation);

上面的代码中lla_point没有初始化,也可以放在geo_converter.Reverse中进行使用,可是wgs84Xyz_orientation就没有这么幸运了,需要初始化才能在earth.Forward中使用。
也就是说想使用代码Eigen::VectorXd 中的变量,必须先初始化,才能使用wgs84Xyz_orientation[0]。而Vector3d不需要初始化。可以参考:链接: link

4.2. GeographicLib::Geocentric earth

GeographicLib::Geocentric earth的定义不能用下面代码块中的注释赋值,这一步我不知道为什么,最后使用上面这句话就调试成功了,现在我还是不明白,下面注释的代码中提供了构造函数,不知道为什么构造不出来。

    GeographicLib::Geocentric earth = GeographicLib::Geocentric(GeographicLib::Constants::WGS84_a(), GeographicLib::Constants::WGS84_f());
    // GeographicLib::Geocentric earth1(GeographicLib::Constants::WGS84_a(), GeographicLib::Constants::WGS84_f());

4.3. vector中的erase函数

使用erase函数,会删除掉对应元素,此时for循环中的i++就可能会跳过到下一个元素,因此这里删除之后,增加了一个isMerged变量,如果是false,那么就i++,否则出现删除的情况,就可以继续下次循环了。

    vector<GroupProp> mergedGroups;
    for (int i = 0; i < carGroup.size(); ) {
        bool isMerged = false;
        for (int j = 0; j < unknownGroup.size(); ) {
            double deltaX = abs(carGroup[i]._center.x - unknownGroup[j]._center.x);
            double deltaY = abs(carGroup[i]._center.y - unknownGroup[j]._center.y);
            double diagonal = sqrt(deltaX * deltaX + deltaY * deltaY);
            if (deltaX <= 4.68 && deltaY <= 1.7 && diagonal <= 4.9) {
                // 合并group
                GroupProp mergeGroup = carGroup[i] + unknownGroup[j];
                mergeGroup._object = Car;
                mergedGroups.push_back(mergeGroup);
                // 合并后删除carGroup[i]中的元素和unknownGroup[j]中的元素
                carGroup.erase(carGroup.begin() + i);
                unknownGroup.erase(unknownGroup.begin() + j);
                
                mergeNum++;
                std::cout << "mergeNum is %d  " << mergeNum << std::endl;
                
                isMerged = true;
                break;
            } else {
                j++;
            }
        }
        if (!isMerged) {
            i++;
        }
    }

4.4 extern和static

二者的使用过程中,声明和定义均要分开,要不然无法使用。这两个关键字我都踩了两次坑,都是将声明和定义写在了一起,然后就报错。

4.4.1 extern

extern的作用域可见extern

//stdafx.h
enum BoxType{AABB,OBB}; // 边界框类型
extern BoxType boundingBox;
//stdafx.cpp
BoxType boundingBox = AABB;

然后再在别的cpp使用,此时使用如下:

// main.cpp
#include "stdafx.h"
extern BoxType boundingBox;
int main(int argc, char **argv) {
  // other Codes
  boundingBox = boundingType == 0 ? AABB : OBB;
  // ***
}

4.4.2 static

定义和声明,要分来写

// obs_cluster.h
class CTrackersCenter{
  public:
  static std::vector<Tracker> sPastTracker;
}
// obs_cluster.cpp
std::vector<Tracker> CTrackersCenter::sPastTracker = {};

4.4.3 声明与定义

变量的声明用于向程序表明变量的类型和名字,当对一个变量进行声明时,编译器不会为其分配内存空间。可以通过使用extern关键字声明变量名而不定义它。不定义的变量的声明包括声明的对象类型,对象名和关键字extern。
变量的定义用于为变量分配存储空间,还可以为变量指定初始值,在一个程序中只能定义一次,出现多次编译器会报错,变量的定义也是声明,当定义一个变量的时声明了它的类型和名字。
注意:声明可以多次,定义在整个程序中只能有一次,定义的同时会为变量分配内存空间

extern int i; //声明,但是没有定义 
int i; //声明,也是定义 

5. Ubuntu踩过的坑

我都不知道什么时候给我ubuntu 下面的.bashrc文件改掉了,而且还加了几行代码,

export  CPLUS_INCLUDE_PATH=$CPLUS_INCLUDE_PATH:/usr/include/pcl-1.10:/usr/include/eigen3:/usr/include/vtk-7.1:/home/zhao/softwares/LAStools-master:/media/zhao/ZhaoZhibo/Codes/RoadsideNotes/RoadsideNotes_add_Carto/catkin_ws/src/cartographer:/media/zhao/ZhaoZhibo/Codes/RoadsideNotes/RoadsideNotes_add_Carto/catkin_ws/src/cartographer_ros:/usr/include/lua5.3/:/usr/lib/x86_64-linux-gnu/
export CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH:/media/zhao/ZhaoZhibo/Codes/RoadsideNotes/RoadsideNotes_add_Carto/catkin_ws/build_isolated/cartographer/install_isolated

我说为什么catkin_make挂掉了,而且还去访问我的移动硬盘上的路径,不知道啥时候自动给我添加了一个路径。然后直接注释上述代码,而是source一下这个文件就好了。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用IMU和激光雷达进行机器人定位,可以采用扩展卡尔曼滤波(Extended Kalman Filter,EKF)算法。 首先,需要在ROS环境中安装`robot_localization`功能包,可以通过以下命令进行安装: ``` sudo apt-get install ros-<distro>-robot-localization ``` 其中,`<distro>`是ROS版本号,例如`kinetic`、`melodic`等。 接着,在ROS节点中引入`robot_localization`的头文件,并创建一个`ros::NodeHandle`对象: ``` #include <ros/ros.h> #include <robot_localization/ekf_localization_node.hpp> ... ros::NodeHandle nh("~"); ``` 然后,需要设置EKF的参数,例如状态量、传感器数据类型等: ``` std::vector<std::string> state_vars = {"x", "y", "z", "roll", "pitch", "yaw", "xd", "yd", "zd", "rolld", "pitchd", "yawd"}; // 状态量 std::vector<std::string> odom_vars = {"x", "y", "z", "roll", "pitch", "yaw"}; // 里程计数据 std::vector<std::string> imu_vars = {"roll", "pitch", "yaw", "rolld", "pitchd", "yawd"}; // IMU数据 std::vector<std::string> laser_vars = {"x", "y", "z"}; // 激光雷达数据 robot_localization::EkfLocalizationNode::EkfConfig config; config.set_state_vars(state_vars); config.set_odom_vars(odom_vars); config.set_imu_vars(imu_vars); config.set_laser_vars(laser_vars); ``` 接着,可以通过以下代码创建EKF节点: ``` robot_localization::EkfLocalizationNode ekf_node(config); ekf_node.setNodeHandle(&nh); ekf_node.init(); ``` 最后,可以在`ros::spin()`循环中调用EKF节点的定位函数,例如: ``` while (ros::ok()) { ekf_node.correct(); // 使用IMU和激光雷达数据进行校正 pose = ekf_node.getRobotPose(); // 获取机器人位姿 ... ros::spinOnce(); } ``` 这样就可以使用C++编写ROS机器人使用IMU和激光雷达进行定位了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值