这篇文章整理了ROS1的使用,包括各个模块的解释。如果已经有人写了很好的说明,就直接使用别人写好的东西。示例代码可以参考这里。这是我写的一个ros工程,这个工程的目的是为了介绍ros1的机制,没有特意实现某个具体功能。其中的“intro”功能包是为了理解ros机制创建的,里面和ros机制相关的文件都有十分详细的注释。其它包则是基础的ros机制和简单的功能实现。
第一章 运行机制
这一部分介绍了ros的结构,以及ros是如何运行的。 查看ros版本的命令如下
echo $ROS_DISTRO
or
rosversion -d
第一节 初始化和文件结构
我们写的ros的相关程序都是在工作空间之内运行的。因此首先创建和初始化一个工作空间。工作空间需要位于一个文件夹内,现在进入一个文件夹来创建工作空间。创建工作空间的命令如下
catkin_init_workspace
现在已经得到初始化后的工作空间。
“build”、“devel”、“install”是编译后产生的文件夹,编译结果保存在这几个文件夹。cmake
和catkin_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.txt
和package.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.xml
和CMakeLists.txt
这两个文件。在package.xml
中,需要添加依赖的库,否则会在编译时报错。xml
文件有不同的版本,对应的语法有所不同。ros使用CMake
编译,CMakeLists.txt
定义了编译过程,具体使用可以参考CMake教程。同时ros提供了一个封装后的编译工具catkin_make
2. 选择性编译
- 使用以下命令可以选择性编译特定的包。
catkin_make -DCMAKE_WHITELIST_PACKAGES="packagename1;packagename2"
如果设置编译白名单后,之后再使用
catkin_make
有可能也只会编译白名单中的包。如果出现这种情况,可以使用以下命令,之后会默认编译所有包。catkin_make -DCMAKE_WHITELIST_PACKAGES=""
- 不编译特定包
在不需要编译的包的根目录中添加名为
CATKIN_IGNORE
的文件夹。在编译的时候就不会再编译这个包。 - 清理编译
catkin_make clean
: 清理所有包的编译catkin clean package_name
: 清除某一个包的编译- 手动删除“build”、“devel”、“install”文件夹,也可视作清理所有包的编译
3. 编译流程
如果要使用自定义的消息和服务,需要在package.xml
和CMakeLists.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文件
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中的坐标系分为link
和joint
。link
的结构如下
<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
播放
- 正常播放
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
: 循环播放
- 话题映射
把包中的话题映射为/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>
查看信息
- ”–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); // 返回本节点订阅的话题
- 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
- roscpp 提供的函数
ros::init(); // 解析传入的ros参数 ros::NodeHandle; // 和话题、服务、参数等交互的公共接口 ros::master; // 包含从master查询信息的函数 ros::this_node; // 包含查询这个进程(node)的哈桑农户 ros::service; // 包含查询服务的函数 ros::param; // 包含查询参数服务器的函数,而不需要用到NodeHandle ros::names; // 包含处理ros图资源名称的函数
- 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运行的端口
- 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
,要求字段名称和数据类型完全对应,例如消息中点的字段ring
为int
,但点云的点的字段ring
为uint8
,会提示没有对应的字段。
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::Bag
和rosbag::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
错误使得程序退出。