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

动手学ROS

install

ros

# Set Up Sources and Keys
sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt update
sudo apt install curl
curl -s https://raw.githubusercontent.com/ros/rosdistro/master/ros.key | sudo apt-key add -
sudo apt update

# Install ROS Noetic
sudo apt install ros-noetic-desktop-full

# Initialize rosdep
sudo rosdep init
rosdep update

# Set Up Environment
echo "source /opt/ros/noetic/setup.bash" >> ~/.bashrc
source ~/.bashrc
# Verify Installation
roscore

tf

install tf

sudo apt install ros-noetic-tf

install tf2

sudo apt install ros-noetic-tf2 ros-noetic-tf2-ros ros-noetic-tf2-tools

check installation

roscd tf
roscd tf2_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);




Chapter 4 Other

4.1 command

cmd meaning
rospack find nmea_msgs This should return the path to the nmea_msgs package if it is installed.

终章 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

或者

rm -rf devel build # 删除编译产生的所有文件
catkin_make # 重新编译
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后进程会转而执行ros::spin()后面的程序,但是如果在一定时间内程序没有执行完毕,进程会强制退出,并抛出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错误使得程序退出。

34 每次运行结果不一致

相同的程序和数据,使用roslaunch启动,然后播放相同的数据包,每次的运行结果存在差异。分析后认为,是因为ros消息的缓存和处理机制存在一定的随机性,会导致每次处理的消息数量或者顺序并不完全一致。如果存在多个话题,不同话题的处理顺序也会存在差异。因此会有一定的随机性。正常情况下,缓存和延迟等因素的影响有限,因此往往只会有几帧的顺序或数量的差异。如果多次运行,运行结果会收敛在固定几个状态。

35

cannot launch node of type [rviz/rviz]: rviz

This means rviz is not found by ros. Install it to solve it. If you iinstall with sudo apt install rviz, ROS can’t find it automatically, either. Therefore, it’s recommended to install with sudo apt install ros-noetic-rviz.