这篇文章整理了ROS1的使用,包括各个模块的解释。如果已经有人写了很好的说明,就直接使用别人写好的东西。示例代码可以参考这里。这是我写的一个ros工程,这个工程的目的是为了介绍ros1的机制,没有特意实现某个具体功能。其中的“intro”功能包是为了理解ros机制创建的,里面和ros机制相关的文件都有十分详细的注释。其它包则是基础的ros机制和简单的功能实现。

动手学ROS

第一章 运行机制

这一部分介绍了ros的结构,以及ros是如何运行的。 查看ros版本的命令如下

echo $ROS_DISTRO
or
rosversion -d

第一节 初始化和文件结构

我们写的ros的相关程序都是在工作空间之内运行的。因此首先创建和初始化一个工作空间。工作空间需要位于一个文件夹内,现在进入一个文件夹来创建工作空间。创建工作空间的命令如下

catkin_init_workspace

现在已经得到初始化后的工作空间。 工作空间文件结构

“build”、“devel”、“install”是编译后产生的文件夹,编译结果保存在这几个文件夹。cmakecatkin_make在编译过程中产生的缓存和中间文件保存在build文件夹,编译结果放在devel文件夹。devel文件夹中没有每个包中的config文件夹中的配置文件和launch文件夹中的launch文件等配置文件。如果在编译时使用install选项,则会把所有运行的相关文件都放在install文件夹。
我们写的所有相关代码和文件都会保存在src文件夹下。一个系统(例如机器人和无人驾驶)往往包含很多功能模块(例如定位、控制),在ros中功能模块被称为package,每个package都有一个自己的文件夹。创建文件夹的命令如下。

cd ~/catkin_ws/src
catkin_create_pkg <package_name> [depend1] [depend2] [depend3]

一个包文件夹中一定会有CMakeLists.txtpackage.xml这两个文件。“package.xml”定义了这个包的信息,依赖哪些库(即上述命令中的depend),包的维护者,等等。源文件需要经过编译产生可执行文件,“CMakeLists.txt”这个文件是用于控制编译的,例如哪些源文件需要编译在一起,可执行文件需要链接哪些库,编译时使用c++11还是c++17。这些与编译相关的东西都在这个文件里。
  功能模块之间有信息交流,这个功能在ros中使用话题topic消息message实现。话题是一个字符串,用于确定接收什么信息(例如是定位信息还是控制信号)。信息在传输时需要使用固定的格式,这个格式通常保存在msg文件夹的*.msg文件中,该文件会在编译后产生一个头文件。
  config通常保存配置文件,参数都保存在配置文件里。srv通常保存自定义的用于生成“服务”的文件。include通常保存头文件。“src”通常保存“cpp”之类的源文件。
  ros是以package为单位启动的,launch是启动package的一种方法。在launch文件中定义了启动过程,例如启动时传入参数、是否需要启动其它package等等。更多关于launch文件的介绍可以看这里。


\[workspace=\left\{ \begin{matrix} build \\ devel \\ install \\ src \end{matrix} \right.\] \[src=\left\{ \begin{matrix} package1 \\ package2 \\ folder1 \end{matrix} \right.\] \[folder1=\left\{ \begin{matrix} package3 \\ package4 \\ folder2 \end{matrix} \right.\] \[package1=\left\{ \begin{matrix} config \\ include \\ launch \\ msg \\ srv \\ src \\ CMakeLists.txt \\ package.xml \end{matrix} \right.\]

“src”中保存包(package)。上述的文件夹“packagex”里面保存的就是包的相关文件。“folderx”可以是任意文件夹,不一定要保存包。上述的“folder1”中保存了两个包文件夹和一个非包文件夹“folder2” 。


第二节 编译

1. 编译所需文件和解析

编译需要正确设置package.xmlCMakeLists.txt这两个文件。在package.xml中,需要添加依赖的库,否则会在编译时报错。xml文件有不同的版本,对应的语法有所不同。ros使用CMake编译,CMakeLists.txt定义了编译过程,具体使用可以参考CMake教程。同时ros提供了一个封装后的编译工具catkin_make

2. 选择性编译

  1. 使用以下命令可以选择性编译特定的包。
    catkin_make -DCMAKE_WHITELIST_PACKAGES="packagename1;packagename2"
    

    如果设置编译白名单后,之后再使用catkin_make有可能也只会编译白名单中的包。如果出现这种情况,可以使用以下命令,之后会默认编译所有包。

    catkin_make -DCMAKE_WHITELIST_PACKAGES=""
    
  2. 不编译特定包 在不需要编译的包的根目录中添加名为CATKIN_IGNORE的文件夹。在编译的时候就不会再编译这个包。
  3. 清理编译
    • catkin_make clean: 清理所有包的编译
    • catkin clean package_name: 清除某一个包的编译
    • 手动删除“build”、“devel”、“install”文件夹,也可视作清理所有包的编译

3. 编译流程

如果要使用自定义的消息和服务,需要在package.xmlCMakeLists.txt添加相应语句,在编译过程中生成对应的头文件。如果编译源文件的时候,这两个文件还没有编译成功,就无法找到对应的头文件。在package.xml文件中,需要添加编译自定义消息和服务所需要的依赖包

<run_depend>message_generation</run_depend>
<build_depend>message_runtime</build_depend>

CMakeLists.txt中添加编译选项

find_package(catkin REQUIRED COMPONENTS  roscpp  rospy  std_msgs  message_generation)

catkin_package(  INCLUDE_DIRS include  CATKIN_DEPENDS  std_msgs  message_runtime  message_generation)

add_message_files(  DIRECTORY msg  FILES  cloud_info.msg)

add_service_files(  DIRECTORY srv  FILES  save_map.srv)

generate_messages(DEPENDENCIES  std_msgs ) # 自定义的消息使用到的其它消息类型需要写在这里,例如geometry_msgs或nav_msgs等

第三节 启动和初始化

1. launch文件

launch文件说明

2. 启动方式

3. 隐藏的启动过程

4. 节点内的初始化

/* 这个函数会初始化该节点与ros相关的部分,例如解析ros参数。
   argc和argv是main函数的传入参数。如果main函数没有传入参数,应该去掉这两项。最后的那个字符串是该节点在ros的节点网络中的节点名称
*/
ros::init(argc, argv, "node_name"); 

第四节 信息的传递

1. 话题和消息

1. 常用消息

geometry_msgs/TransformStamped.msg: 标准header,子frame的id,平移用xyz表示,旋转使用四元数表示。

2. 自定义消息

3. 消息传递方式

ros::TransportHints() // 确定传输层的作用话题的方式
                    .reliable() 
                    .unreliable() // reliable()和unreliable()是指定要接收的訊息是透過TCP還是UDP傳輸的;
                    .maxDatagramSize(1000) // 限制收到的訊息大小
                    .tcpNoDelay(); // 允许指定hints到roscpp的传输层,这里使用没延迟的TCP

// <msg>(类型为string的topic, num of cached msg, callback function, 指定传输方式)
subImu= nh.subscribe<sensor_msgs::Imu>(imuTopic, 2000,imuHandler,this, ros::TransportHints().tcpNoDelay());
// <msg>(topic, num of cached msg)
pubImuOdometry = nh.advertise<nav_msgs::Odometry> (odomTopic+"_incremental", 2000);

4. rostopic

net speed of topic: rostopic bw topicname

2. 参数

参数读取

nh.getParam("localization/lidar/numberOfCores", numberOfCores); // 读取成功返回true,否则返回false
double a;
nh.param<std::vector<double>>("localization/lidar/front_lidar/internalTrans", a, 0.1);
std::vector<double> interTransTemp; // 读取一串的值
lnh.param<std::vector<double>>("localization/lidar/front_lidar/internalTrans", interTransTemp, std::vector<double>());

参数会在读取后被保存在参数服务器中。这是参数服务器的介绍

3. 服务

五、话题和服务的回调机制

spin创建了线程给回调函数去执行它, 这样多线程就不会影响其他的作业。

  • 最简单的单线程自旋, 它会一直调用直到结束;调用该函数后不会再返回,之后的程序也不会再执行了;一般写在主函数的最后;是消息订阅函数的必要前提函数
    int main()
    {
    // other statement;
    ros::spin();  
    std::cout << "get here" << std::endl; // 这个节点被关闭时才会退出spin,然后执行这个语句
    return 0;
    }
    
  • 定期调用等待在那个点上的所有回调;要注意消息接收频率、消息池大小、调用频率,消息处理不及时会导致部分数据未处理就从消息队列删除丢失,频率低可能会使消息延迟
    int main()
    {
    // other statement;
    ros::Rate hz(20); // 执行回调函数的频率
    while(ros::ok())
    {
      ros::spinOnce();
      hz.sleep();
    }
    std::cout << "get here" << std::endl; // ros结束时才会退出spin,然后执行这个语句
    return 0;
    }
    
  • 防阻塞多线程回调函数;可以在它的构造函数中指定线程数量, 但如果不指定或者设为0, 它会根据你的CPU内核数创建线程.
    ros::MultiThreadedSpinner spinner(4); // Use 4 threads
    spinner.spin(); // spin() will not return until the node has been shutdown
    
  • 防阻塞多线程回调函数;有start() 和stop() 函数,并且在销毁的时候会自动停止;下面的用法等价于上面的MultiThreadedSpinner例子。
    ros::AsyncSpinner spinner(4); // Use 4 threads
    spinner.start();
    spinner.stop();
    ros::waitForShutdown();
    

第六节 tf和坐标系

ros中的坐标系分为linkjointlink的结构如下

 <link name="my_link">
   <inertial> <!-- 惯性属性 -->
     <origin xyz="0 0 0.5" rpy="0 0 0"/> <!-- 相对link的坐标 -->
     <mass value="1"/> <!-- 质量 -->
     <inertia ixx="100"  ixy="0"  ixz="0" iyy="100" iyz="0" izz="100" /> <!-- 转动惯量 -->
   </inertial>

   <visual>  <!-- 用于可视化的视觉属性 -->
     <origin xyz="0 0 0" rpy="0 0 0" /> <!-- 相对link的坐标 -->
     <geometry> <!-- 形状 -->
       <box size="1 1 1" /> 
     </geometry>
     <material name="Cyan"> <!-- 材质 -->
       <color rgba="0 1.0 1.0 1.0"/> 
     </material>
   </visual>

   <collision> <!-- 碰撞属性 -->
     <origin xyz="0 0 0" rpy="0 0 0"/> <!-- 相对link的坐标 -->
     <geometry> <!-- 形状 -->
       <cylinder radius="1" length="0.5"/>
     </geometry>
   </collision>
 </link>

joint的结构如下

<joint name="my_joint" type="floating">
  <origin xyz="0 0 1" rpy="0 0 3.1416"/> <!-- 从父link到子link的转换 -->
  <parent link="link1"/> <!-- 父link -->
  <child link="link2"/> <!-- 子link -->
  <axis>  <!-- 关节轴 -->
  <calibration rising="0.0"/> <!-- 视觉属性 -->
  <dynamics damping="0.0" friction="0.0"/> <!-- 动力学参数 -->
  <limit effort="30" velocity="1.0" lower="-2.2" upper="0.7" /> <!-- 关节限位 -->
  <safety_controller k_velocity="10" k_position="15" soft_lower_limit="-2.0" soft_upper_limit="0.5" /> <!-- 碰撞属性 -->
</joint>

第二章 功能详解

第一节 数据记录和使用

1. bag

录制

  • 录制指定的topic: rosbag record topic1 topic2
  • 录制所有的topic: rosbag record -a
  • 录制完保存 bag 包名称为 “session1 + 时间戳.bag” 格式:rosbag record -o session1 /chatter
  • 录制完保存为指定文件名 “session2-090210.bag” 格式: rosbag record -O session2-090210.bag /chatter
  • 录制该主题1000个消息限制: rosbag record -l 1000 /chatter
  • 持续30s录制持续30s录制: rosbag record --duration=30 /chatter

播放

  1. 正常播放
    rosbag play 0027.bag --pause -r 0.8 --clock --topic /velodyne_points /imu/data -s 10 -u 5 -l `
    
    • --pause : 文件读取完成后不播放,按空格键开始播放
    • -r 0.8 : in specified speed;
    • --clock : use timestamp in bag file;
    • --topic : /velodyne_points /imu/data, play these two topics;
    • -s 10 : 参数用于指定从10秒开始
    • -u 5 : 表示仅播放包的5秒
    • -l : 循环播放
  2. 话题映射
    把包中的话题映射为/points_raw
    rosbag play bagName.bag /YourPointCloud2Topic:=/points_raw
    

信息截取

  • 截取一段时间 : rosbag filter input.bag output.bag "t.to_sec() <= 1284703931 and t.to_sec()>=1284703935"
  • 过滤单个topic : rosbag filter input.bag output.bag "topic == '/tf'"
  • 只保留/velodyne_point_cloud和/visensor/imu,使用or和== : rosbag filter input.bag output.bag "topic == '/velodyne_point_cloud' or topic =='/visensor/imu' "
  • 过滤两个topic:/velodyne_point_cloud和/visensor/imu,使用and和!= : rosbag filter input.bag output.bag "topic != '/velodyne_point_cloud' and topic !='/visensor/imu' "

提取图片
将图片从bag文件中提取出来并放在该命令所在目录

rosrun image_view extract_images _sec_per_frame:=0.01 image:=<IMAGETOPICINBAGFILE>
rosbag  play xxx.bag

提取点云文件
解析bag文件得到带时间戳的pcd点云数据文件

rosrun pcl_ros bag_to_pcd <*.bag> <topic> <output_directory>

查看信息

  1. ”–freq”: 显示话题的频率
    rosbag info *.bag --freq
    

    2. 日志

    ros日志的介绍参考这里。 在实际中,可以根据需要对某个节点的日志级别进行设置,使用下面的命令:rosservice call/node-name/set_logger_level ros.package-name level

第n节 其它函数

void getAdvertisedTopics(V_string &topics); // 返回本节点发布的话题
const string &getName(); // 返回当前节点的名称
const string &getNamespace(); // 返回当前节点的命名空间
void getSubscribedTopics (V_string &topics); // 返回本节点订阅的话题
  1. ros::NodeHandle 类提供的函数
    ros::Publisher advertise(); // create publisher for topic 
    ros::Subscriber subscriber(); // create subscriber for topic 
    ros::ServiceServe advertiseService(); // create server for service
    ros::ServiceClient serviceClient(); // create client for service
    bool gerParam(); // query value of param
    bool setParam(); // set value of param
    
  2. roscpp 提供的函数
    ros::init(); // 解析传入的ros参数
    ros::NodeHandle; // 和话题、服务、参数等交互的公共接口
    ros::master; // 包含从master查询信息的函数
    ros::this_node; // 包含查询这个进程(node)的哈桑农户
    ros::service; // 包含查询服务的函数
    ros::param; // 包含查询参数服务器的函数,而不需要用到NodeHandle
    ros::names; // 包含处理ros图资源名称的函数
    
  3. ros::master 命名空间提供的函数
    bool check(); // 检查master是否启动
    const string& getHost(); // 返回master所处的hostname
    bool getNode(V_string &nodes); // 从master返回已知的node名称列表
    bool getTopics(V_TopicInfo &topics); // 返回所有正在被发布的topic列表
    bool getURI(); // 返回master的URI地址
    uint32_t getPort(); // 返回master运行的端口
    
  4. ros::names 命名空间提供的函数

第三章 常用函数和类

##

回调函数

回调函数多参数传递
消息松同步

订阅多个话题,然后用一个回调函数同时处理多个话题的消息。例如订阅了一个图像话题和一个激光点云话题,回调函数的传入参数同时有图像消息和激光点云消息。

tf

数据类型

向量: tf::Vector3 点:tf::Point 四元数:tf::Quaternion 旋转矩阵:tf::Matrix3x3 位姿:tf::Pose 变换:tf::Transform 带时间戳的以上类型:tf::Stamped<T> 带时间戳的变换:tf::StampedTransform

位姿

1. 球面插值
tf::Quaternion transformQuaternion;
double rollMid, pitchMid, yawMid;

// slerp roll
transformQuaternion.setRPY(roll, 0, 0);
imuQuaternion.setRPY(cloudInfo.imuRollInit, 0, 0);
tf::Matrix3x3(transformQuaternion.slerp(imuQuaternion, imuWeight)).getRPY(rollMid, pitchMid, yawMid);
2. 位姿转换
#include <tf/transform_broadcaster.h>
// 把 tf变换 转换为 Eigen格式 的矩阵
Eigen::Matrix4f mat = tf2::transformToEigen(transform_stamped.transform).matrix().cast<float>();
3. tf发布坐标
#include <tf/transform_broadcaster.h>
tf::TransformBroadcaster br; // 定义一个广播,相当于发布话题时定义一个发布器
tf::Transform transform; // 定义存放转换信息(平动,转动)的变量;这里transform为一个类

// (存储变换关系的变量,广播tf使用的时间戳,父坐标系的名称,子坐标系的名称);sendTransform函数有很多重载
br.sendTransform(tf::StampedTransform(transform, ros::Time::now(), "map", ”base_link“));

//
tf::createQuaternionMsgFromRollPitchYaw(roll, pitch, yaw);
tf::poseMsgToTF(laserOdometry.pose.pose, tCur);

// 用于返回两个坐标系的变换,返回的变换的方向将从target_frame到source_frame
tf::lookupTransform (target_frame, source_frame, time, transform)

四、时间

sensor_msgs::Imu x;
double time;
time = x.header.stamp.toSec(); // ros时间戳变为浮点数,单位为秒
x.stamp = ros::Time().fromSec(currentImuState.time); // 浮点数变为ros时间戳
2. 转换点云消息的坐标系
#include <pcl_ros/point_cloud.h>
#include <pcl_ros/transforms.h>

// 点云消息转换坐标系
sensor_msgs::PointCloud2::ConstPtr in_cloud_ptr, out_cloud_ptr;
Eigen::Matrix4f mat = tf2::transformToEigen(transform_stamped.transform).matrix().cast<float>();
pcl_ros::transformPointCloud(mat, *in_cloud_ptr, *out_cloud_ptr);




终章 q&a

这一部分整理了RO使用过程中遇到的问题以及对应的解决方案。这些方案并不总是能解决问题,只是提供一个排查问题的方向。覆盖范围包括ROS生态内的软件,比如 rqt,rviz,yaml等等。

1

运行以下命令时:

rosdep update

出现错误提示:

ERROR: unable to process source [https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/osx-homebrew.yaml]:<urlopen error [Errno -2] Name or service not known> (https://raw.githubusercontent.com/ros/rosdistro/master/rosdep/osx-homebrew.yaml)

可以尝试以下命令:

sudo apt-get update && sudo apt-get install python-rosdep

然后再重复之前的输入——rosdep update——即可。最好不要以root用户运行”rosdep update“。

2

运行以下命令

rqt_plot 

错误提示

qt_gui_main() found no plugin matching "rqt_plot.plot.Plot" 

输入以下命令解决

rqt_plot --force-discover 
3

错误提示如下

Error: package 'rqt_graph' not found

输入以下命令解决

sudo apt-get install ros-melodic-rqt-graph
4

错误提示如下

/usr/bin/rosrun: line 56: rospack: command not found 

输入以下命令获取root权限后解决

sudo su 

对于部分需要root权限的命令,使用非root时会提示命令不存在。如果该命令本该以非root用户安装,但却以root用户安装,可能出现该问题。

5

运行以下命令

roslaunch mws turtlemimic.launch  

错误提示如下

RLException: [turtlemimic.launch] is neither a launch file in package [mws] nor is [mws] a launch file name. The traceback for the exception was written to the log file 

原因分析:编译后没有执行source,或者包名文件名拼写错误

输入以下命令解决

source devel/setup.bash
6

运行以下命令

rosed roscpp Logger.msg

错误提示如下

rosed: command not found

尝试输入以下命令之一来解决

sudo apt-get install ros-melodic-rosbash

或者

source /opt/ros/melodic/setup.bash
7

错误提示如下

[ERROR] [1619511862.358210519]: [registerPublisher] Failed to contact master at [localhost:11311].  Retrying...

原因分析:必须先启动roscore,然后才能运行其它ros节点。可以输入以下命令解决

roscore
8

运行以下命令

rosservice call /optimize_map

错误提示如下

ERROR: Unable to load type [lidar_localization/optimizeMap]. Have you typed 'make' in [lidar_localization]?

输入以下命令解决

source ./devel/setup.bash
9. exit code 134

gazebo 在运行时闪退,错误提示如下

[gazebo-2] process has died [pid 2074, exit code 134, cmd /opt/ros/kinetic/lib/gazebo_ros/gzserver -e ode /opt/ros/kinetic/share/turtlebot_gazebo/worlds/playground.world __name:=gazebo __log:=/home/kim/.ros/log/fa7d7a88-3b3a-11ea-8115-54bf64a35529/gazebo-2.log]

查看发现gazebo的版本是 7.0.0。更新gazebo的版本后,问题解决

10

错误程序如下

ros::init(argc, argv, " listener");

编译错误提示如下: 当前的首字母为无效字符

terminate called after throwing an instance of 'ros::InvalidNameException'
  what():  Character [ ] is not valid as the first character in Graph Resource Name [ listener].  Valid characters are a-z, A-Z, / and in some cases ~.
Aborted (core dumped)

解决方法:改为以下程序

ros::init(argc, argv, "listener");// first character in "" can't be space(空格)
11

移动工作空间后,执行catkin_make

catkin_make

编译错误提示如下

The source directory "/home/nnnn/Documents/mr/03/src" does not exist.
Specify --help for usage, or press the help button on the CMake GUI.
Makefile:1438: recipe for target 'cmake_check_build_system' failed
make: *** [cmake_check_build_system] Error 1

尝试输入以下方法之一解决

catkin_make -DCATKIN_WHITELIST_PACKAGES="package1;package2" //in the root of your workspace

或者

catkin_make -DCATKIN_WHITELIST_PACKAGES=""

或者

cmake ../src -DCMAKE_INSTALL_PREFIX=../install -DCATKIN_DEVEL_PREFIX=../devel //指定src文件夹,命令在bulid文件夹中
make

或者

delete folder devel and build and re-compile;// 删除编译产生的所有文件并重新编译
12

运行以下命令

catkin_make

错误提示如下

Error in cmake code at /home/nnnn/Documents/my/li/src/frame/CMakeLists.txt:239: Parse error.  Function missing ending ")". End of file reached.

解决方案:在文件中检查一下括号是否成对。虽然提示出错的地方在文件末尾,但实际错误处可能在cmakelists文件的任何地方。

13

错误程序如下

ros::init(argc, argv, "scanRegistration");

编译错误提示如下

argc’ was not declared in this scope

原因分析:ros::init的参数应与main函数一致。

14

运行以下命令

catkin_make

错误提示如下

/usr/local/include/ceres/internal/integer_sequence_algorithm.h:64:16: error: ‘integer_sequence’ is not a member of ‘std’
 struct SumImpl<std::integer_sequence<T, N, Ns...>> 

解决方案如下

replace " set(CMAKE_CXX_FLAGS "-std=c++11") " with " set(CMAKE_CXX_STANDARD 14) " % In the new version of cmake, the latter method is used to set C + + standard %
15

注意区分 ROS::ok()ROS::OK()

16

程序运行中崩溃,错误提示如下

terminate called after throwing an instance of 'YAML::InvalidNode'

原因分析:yaml文件中的变量名和程序中的变量名不一致

17

if remove cmake, when command roscore, there will be something wrong with poython-roslaunch. you can also meet it when you make ros but didn’t source it.

18

异常代码如下:

while(ros::ok()) { return;}  // ros只循环了一次,没有继续循环调用

解决方案如下:

while(ros::ok()) { continue;}
19

提示如下:

Warning: TF_OLD_DATA ignoring data from the past for frame base_link at time 0 according to authority unknown_publisher Possible reasons are list at

原因分析:TF没有设置更新频率,使得tf一直没有更新

20

异常情况:运行ros节点,只开启了部分线程 原因分析:.join() 函数应该在

ros::MultiThreadSpinner spinner(9); spinner.spin(); 

之后,因为.join()函数表示,在该线程结束后再继续执行该函数后的语句。如果前面的线程是一个订阅和回调的循环,那么就无法进入后面的线程

21

异常代码如下:

tf::TransformBroadcaster br; br.sendTransform(trans_map_to_truck);

错误提示如下

For frame [map_link]: No transform to fixed frame [base_link].  TF error: [Lookup would require extrapolation at time 1652258510.336753588, but only time 1652258489.731681437 is in the buffer, when looking up transform from frame [map_link] to frame [base_link]]

解决方案如下:

static tf::TransformBroadcaster br; br.sendTransform(trans_map_to_truck);
22

安装ros时出现如下错误

Failed to connect to raw.githubusercontent.com

可参照这个方案解决。

23

在打开终端后就出现以下错误。

bash: /opt/ros/kinetic/setup.bash: No such file or directory

在打开终端时,会根据.bashrc文件把环境变量加载到终端。此时我们把ros直接加载到终端。但在加载时系统没有找到这个文件,于是出现了这个错误。首先检查ros是否正确安装,如果已经正确安装,则只需要添加这个文件即可。最简单的办法就是运行这个sudo apt-get install ros-kinetic-turtlesim命令,安装小海龟就自动装好了这个文件。

24

异常代码如下:

pcl::moveFromROSMsg(cloudMsg, cloudIn);

错误提示如下

Failed to find match for field 'time'.

原因分析:Date type of field should be same. double in msg, float in point.time.

25

运行ros节点执行ctrl+c后进程会转而执行rosspin()后面的程序,但是如果在一定时间内程序没有执行完毕,进程会强制退出,并抛出”escalating to SIGTERM“错误,为了解决此问题,可以修改/opt/ros/kinetic/lib/python2.7/dist-packages/roslaunch路径下的nodeprocess.py文件,将该文件第48行的“_TIMEOUT_SIGINT = 15.0 #seconds”中的15秒改为您期望运行的最大时间。

26

pcl::moveFromRosMsg,要求字段名称和数据类型完全对应,例如消息中点的字段ringint,但点云的点的字段ringuint8,会提示没有对应的字段。

27

启动ros的launch文件时出现如下错误

syntax error near unexpected token `newline'

经检查发现,本应该用命令roslaunch,结果使用了rosrun

28

rviz中接收图像显示时,无法显示且出现这个警告Unsupported image encoding。发布图片的代码如下

cv::Mat img_cur;
cv_bridge::CvImage cv_image;
cv_image.image    = img_cur;
pub_image.publish(cv_image.toImageMsg( ));

opencv默认的编码格式无法识别,需要添加合适的编码格式。改为如下代码即可解决。

cv::Mat img_cur;
cv_bridge::CvImage cv_image;
cv_image.encoding = "bgr8"; // 在此处添加opencv的图片编码格式
cv_image.image    = img_cur;
pub_image.publish(cv_image.toImageMsg( ));
29
class A;
int main()
{
  A a;
  while(ros::ok())
  {
    // pass 
  }
  return;
}

对于如上程序,使用roslaunch启动节点,按ctrl+c停止程序后,可能对象a还没有完成析构,整个程序就被终止了。

class A;
int main()
{
  A a;
  while(ros::ok())
  {
    // pass 
  }
  a.~a();
  return;
}

改为手动析构,则对象a通常会在程序被终止前完成析构。如果析构过程过长,导致达到系统设定的最长等待时间还没有结束,则程序会被强制结束,同时也能看到终端给出的错误提示。

30

运行的时候发现,一个频率为50hz的消息,在程序中远低于50hz,只有10hz左右。经研究发现,ros::spinOnce()的运行频率通过ros::Rate被控制在10hz,也就是说,任何消息,不论其原始频率多高,最多只能以10hz的频率被处理。

31

程序改完后,编译正常执行,但运行程序时发现使用的是旧程序。经研究发现,在.bashrc设定了source的文件,这个文件是在原来的路径。我因为实际需要把程序复制到了其它路径,之后在新的路径中编辑和编译程序,在编译完成后也没有重新source新的路径。于是在运行时根据source的结果执行了旧的程序。

32

无法正确读取launch文件中的参数。<node> </node>中用param的参数是局部参数,所对应的作用域是按照cpp文件划分的。在cmakelists文件中链接的cpp文件才算作用域。如果一个类的声明定义及其成员函数的定义都在一个头文件中,使用这个类时只需要引用头文件,不需要在cmakelists中链接任何cpp文件,那么这个类就不能从参数服务器中读取该局部参数。

空文件夹CATKIN_IGNORE的作用范围所在的目录,如果放在工作空间的src路径下,所有包都不会被编译。

33

现有包body中的自定义的消息hand.msg,其定义如下

uint8 finger1

使用这个消息录制了一个数据包1.bag。之后修改该消息如下

uint8 finger1
uint8 finger2

在程序中使用该消息时都是用了指针body::handConstPtr。如果启动程序,然后播放数据包,会出现类似wants topic /hand to have datatype/md5sum [hand/18536fa9c0ea251df93729f54dc4cff3], but our version has [hand/ed5bab078835f4955671fdea48d7e675]. Dropping connection.的错误提示,不会进入程序中的相关回调函数。如果使用rosbag::Bagrosbag::View的方式解析,例如如下代码

rosbag::Bag i_bag;
i_bag.open(bag_file, rosbag::bagmode::Read);
rosbag::View view(i_bag);
for (auto m : view)
{
  if (topic == vcu_topic)
  {
    body::handConstPtr hand_msg = m.instantiate<body::hand>(); // statement1
    handHandler(hand_msg); // statement2
    continue;
  }
}
i_bag.close();

变更消息类型可以编译通过,语句statement1可以正常执行,但得到的是空指针nullptr。如果在语句statement2中对该指针有任何操作,都会导致exit code -6错误使得程序退出。