ROS Beginner
文章目录
引言
本文是适用于ROS初学者的笔记,包含从基本概念到尝试创建服务端和客户端的内容。
参考网站:http://wiki.ros.org/cn/ROS/Tutorials
1.创建一个catkin工作空间
mkdir -p ~/catkin_ws/src
cd ~/catkin_ws/
catkin_make(编译)
source devel/setup.bash
2.文件系统
软件包:Packages,ROS代码的软件组织单元
Mainifests:package.xml
清单是对软件包的描述,定义软件包之间的依赖关系和元信息
roscd
:允许直接切换目录到某个软件包或者软件包集当中,或者子目录中
roscd log
:进入存储ROS日志文件的目录
rosls [locationname[/subdir]]
:直接按照软件包的名称执行ls命令
3.创建软件包
软件包的规范:
1)必须有一个package.xml
文件,提供有关软件包的元信息
2)必须有一个CMakeLists.txt
文件
3)必须有自己的目录(意味着在同一个目录下不能有嵌套的或者多个软件包存在?
cd ~/catkin_ws/src
catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
(catkin_create_pkg <package_name> [depend1] [depend2] [depend3] ...)
cd ~/catkin_ws
catkin_make
source ~/catkin_ws/devel/setup.bash(将这个工作空间添加到ROS环境中,似乎只对该终端生效)
rospack查看依赖关系:(rosdep有问题 = =
rospack depends1 beginner_tutorials(查看一级依赖
rospack depends1 rospy(依赖包自己的依赖关系
rospack depends beginner_tutorials(递归的检查出所有依赖关系
4.构建ROS软件包
用catkin_make
进行构建
cd ~/catkin_ws/
catkin_make(得到的build目录是构建空间的默认位置,devel是开发空间的默认位置,可以存放可执行文件和库
5.理解ROS节点
计算图(Computation Graph)是一个由ROS进程组成的点对点网络,它们能够共同处理数据
节点(Nodes):节点是一个可执行文件,它可以通过ROS(使用客户端库)来与其他节点进行通信。
消息(Messages):订阅或发布话题时所使用的ROS数据类型。
话题(Topics):节点可以将消息发布到话题,或通过订阅话题来接收消息。
主节点(Master):ROS的命名服务,例如帮助节点发现彼此。
rosout
:在ROS中相当于stdout/stderr
(标准输出/标准错误)
roscore
:主节点 + rosout
+ 参数服务器
rosnode
:获取节点信息,rosnode list
看当下节点,用rosnode info /rosout
查看某节点信息(此处为/rosout
rosrun
可以用包名直接运行某节点,如rosrun turtlesim turtlesim_node
重新分配节点名称:rosrun turtlesim turtlesim_node __name:=my_turtle
6.理解ROS话题
首先打开小乌龟和它的键盘控制:
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
turtlesim_node
节点和turtle_teleop_key
节点之间是通过一个ROS话题来相互通信的:turtle_teleop_key
在话题上发布键盘按下的消息,turtlesim
则订阅该话题以接收消息
用rqt_graph
查看当前运行的节点和话题:rosrun rqt_graph rqt_graph
rostopic
:获取ROS话题的信息
rostopic echo [topic]
显示某个话题发布的数据(此时rostopic echo
也订阅了该话题
rostopic list
列出当前已被订阅和发布的所有话题
消息:为了使发布者和订阅者进行通信,必须接受和发送相同类型的消息,即话题的类型是由发布在它上面消息的类型决定的
rostopic type [topic](查看话题的消息类型
rosmsg show [消息类型](查看消息的详细信息
rostopic pub [topic] [msg_type] [args](把数据发布到当前某个正在广播的话题上
例如:rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]'
-1表示只发出一条消息,(-r表示循环)后面依次是话题名称,消息类型,YAML语法的参数消息
rostopic hz [topic]
:报告数据发布的速率
rqt_plot
:在滚动时间图上显示发布到某个话题上的数据(rosrun rqt_plot rqt_plot
7.理解ROS服务和参数
Services服务:节点间通讯的一种方式。节点可以发送一个请求并且获得一个响应
rosservice list
:显示活跃服务的信息
rosservice type [service]
:查看服务的类型
rosservice call [service] [args]
:调用服务
rosservice type /spawn | rossrv show
:显示产卵服务的信息(后面加的可以使显示参数的信息?
rosparam
:让我们在ROS参数服务器(Parameter Server)上存储和操作数据(使用YAML语法)
数据类型:整型(integer)、浮点型(float)、布尔(boolean)、字典(dictionaries)和列表(list)
rosparam list
:列出参数名
rosparam set [param_name]
:设置参数
rosparam get [param_name]
:获取参数
rosparam load [file_name] [namespace]
:从文件中加载参数
rosparam dump [file_name] [namespace]
:向文件中转储参数
rosparam delete
:删除参数
8.rqt_console和roslaunch
rqt_console
连接到了ROS的日志框架,以显示节点的输出信息
rqt_logger_level
允许我们在节点运行时改变输出信息的详细级别,包括Debug、Info、Warn和Error
rosrun rqt_console rqt_console
rosrun rqt_logger_level rqt_logger_level
rosrun turtlesim turtlesim_node(启动turulesim
日志记录级别:
Fatal (致命)
Error (错误)
Warn (警告)
Info (信息)
Debug (调试)
使用roslaunch
:
roscd beginner_tutorials(切换道之前构建的软件包目录下
mkdir launch
cd launch
p.s.存放launch文件的目录不一定非要命名为launch,事实上都不用非得放在目录中,roslaunch命令会自动查找经过的包并检测可用的启动文件。然而,这种推荐的标准做法被认为是最佳实践
创建一个名为turtlemimic.launch
的launch文件并复制粘贴以下内容:
<launch>(标签)
<group ns="turtlesim1">(ns:namespace)
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
</group>(分组1)
<group ns="turtlesim2">
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
</group>(分组2)(两个分组节点名相同sim,可以同时启动两个turtlesim模拟器,避免命名冲突
<node pkg="turtlesim" name="mimic" type="mimic">
<remap from="input" to="turtlesim1/turtle1"/>
<remap from="output" to="turtlesim2/turtle1"/>
</node>(启动模仿节点,让turtlesim2模仿turtlesim1)
</launch>(使得launch文件的XML标签闭合)
运行launch文件:roslaunch beginner_tutorials turtlemimic.launch
rostopic pub /turtlesim1/turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, -1.8]'
(给两个turtlesim发布命令,可以看到两个都在转转转)
此时可以通过rqt_graph
更好的理解launch文件做的事情(或者运行rqt
并在主窗口中选择Plugins > Introspection > Node
)
9.使用rosed
rosed [package_name] [filename]
:直接通过软件包名编辑包中的文件,而无需键入完整路径
更改编辑器:在.bashrc
文件中加入export EDITOR='emacs -nw'
或者export EDITOR='nano -w'
10.创建消息和服务
msg
(消息):msg文件就是文本文件,用于描述ROS消息的字段。它们用于为不同编程语言编写的消息生成源代码,放在软件包的msg
目录下
srv
(服务):一个srv
文件描述一个服务。它由两部分组成:请求(request)和响应(response),中间用------线隔开,存放在软件包的srv
目录下。
roscd beginner_tutorials
mkdir msg
echo "int64 num" > msg/Num.msg(创建了一个只有一行的msg文件)
打开package.xml, 确保它包含以下两行且没有被注释
<build_depend>message_generation</build_depend>(构建需要)
<exec_depend>message_runtime</exec_depend>(运行需要)
在CMakeLists.txt文件中,为已经存在里面的find_package
调用添加message_generation
依赖项:(message_generation
对msg
和srv
都适用)
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
注意:有时即使没有使用全部依赖项调用find_package
,项目也可以构建。这是因为catkin把你所有的项目整合在了一起,因此如果之前的项目调用了find_package
,你的依赖关系也被配置成了一样的值。但是,忘记调用意味着你的项目在单独构建时很容易崩溃。
确保导出消息时的依赖关系:
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
找到代码块:
# add_message_files(
# FILES
# Message1.msg
# Message2.msg
# )
取消注释,并且改成这样:
add_message_files(
FILES
Num.msg
)
取消下面几行注释:(确保CMake知道何时需要重新配置项目)
# generate_messages(
# DEPENDENCIES
# std_msgs
# )
以上完成了创建消息,现在通过rosmsg show
命令看看ROS能否识别它
rosmsg show [message type]
即:rosmsg show beginner_tutorials/Num
(包名可省)
创建src
:
roscd beginner_tutorials
mkdir srv
roscp:将文件从一个包复制到另一个包
roscp [package_name] [file_to_copy_path] [copy_path]
roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv(从rospy_tutorials包中复制一个服务)
注意CMakeLists.txt文件中为find_package
调用添加message_generation
依赖项
将
# add_service_files(
# FILES
# Service1.srv
# Service2.srv
# )
变成:
add_service_files(
FILES
AddTwoInts.srv
)
看能否识别它:rossrv show <service type>
即:rossrv show beginner_tutorials/AddTwoInts
(也可以不指定包命)
重新编译一下软件包:
roscd beginner_tutorials
cd ../..
catkin_make
cd -
注意:msg目录中的任何.msg文件都将生成所有支持语言的代码。
C++消息的头文件将生成在~/catkin_ws/devel/include/beginner_tutorials/。
Python脚本将创建在~/catkin_ws/devel/lib/python2.7/dist-packages/beginner_tutorials/msg。
而Lisp文件则出现在~/catkin_ws/devel/share/common-lisp/ros/beginner_tutorials/msg/。
类似地,srv目录中的任何.srv文件都将生成支持语言的代码。对于C++,头文件将生成在消息的头文件的同一目录中。对于Python和Lisp,会在msg目录旁边的srv目录中。
11.编写简单的发布者和订阅者
C++
talker节点(发布者):不断广播消息
roscd beginner_tutorials
mkdir src
创建talker.cpp文件,文本如下:
#include "ros/ros.h"(一个头文件,包含了ROS系统中常见的公共部分所需的头文件)
#include "std_msgs/String.h"(引用了std_msgs中的std_msg/String消息)
#include <sstream>
int main(int argc, char **argv){
ros::init(argc, argv, "talker");(初始化ROS,使得ROS可以通过命令行进行名称重映射,给节点指定名称)(名称必须是基本名称,不能有/)
ros::NodeHandle n;(为这个进程的节点创建句柄。创建的第一个NodeHandle实际上将执行节点的初始化,而最后一个被销毁的NodeHandle将清除节点所使用的任何资源。)
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);(告诉主节点我们要在chatter的话题上发布一个类型是std_msgs/String的消息,主节点会告诉订阅了chatter1的节点,第二个参数表示缓存队列大小为1000,越界则会丢弃旧消息)
(NodeHandle::advertise()返回一个ros::Publisher对象,它有2个目的:其一,它包含一个publish()方法,可以将消息发布到创建它的话题上;其二,当超出范围时,它将自动取消这一宣告操作)
ros::Rate loop_rate(10);(指定循环的频率,记录上次调用sleep到现在有多长时间,并且休眠正确的时间)
int count = 0;
while (ros::ok()){
(ros:ok返回false的情况:
收到SIGINT信号(Ctrl+C)
被另一个同名的节点踢出了网络
ros::shutdown()被程序的另一部分调用
所有的ros::NodeHandles都已被销毁 )
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
chatter_pub.publish(msg);(把这个信息广播给了任何已连接的节点
ros::spinOnce();(回调)
loop_rate.sleep();(使用ros::Rate在剩下的时间内睡眠,以让我们达到10Hz的发布速率??)
++count;
}
return 0;
}
订阅者节点:
创建一个listener.cpp文件,写入以下内容
#include "ros/ros.h"
#include "std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr& msg){
ROS_INFO("I heard: [%s]", msg->data.c_str());
}(回调函数,智能指针传递消息)
int main(int argc, char **argv){
ros::init(argc, argv, "listener");
ros::NodeHandle n;
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);(通过主节点订阅话题,第二个参数是队列大小,该对象被析构时自动取消订阅)
ros::spin();(启动了一个自循环,它会尽可能快地调用消息回调函数)
return 0;
}
在CMakeLists.txt文件底部加上这些内容:
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)(为可执行目标添加依赖项到消息生成目标,确保在使用此包之前生成了该包的消息头)
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)
最终长这样:
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)
## Find catkin and any catkin packages
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg)
## Declare ROS messages and services
add_message_files(FILES Num.msg)
add_service_files(FILES AddTwoInts.srv)
## Generate added messages and services
generate_messages(DEPENDENCIES std_msgs)
## Declare a catkin package
catkin_package()
## Build talker and listener
include_directories(include ${catkin_INCLUDE_DIRS})
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES})
add_dependencies(talker beginner_tutorials_generate_messages_cpp)
add_executable(listener src/listener.cpp)
target_link_libraries(listener ${catkin_LIBRARIES})
add_dependencies(listener beginner_tutorials_generate_messages_cpp)
进行编译:
cd ~/catkin_ws
catkin_make
12.测试发布者和订阅者
运行发布者:
roscore
cd ~/catkin_ws
source ./devel/setup.bash(请确保调用catkin_make后已经source过工作空间的setup.*sh文件)
rosrun beginner_tutorials talker # (C++)
运行订阅者:
rosrun beginner_tutorials listener # (C++)
13.编写简单的服务和客户端
编写服务节点:
创建简单的服务(Service)节点add_two_ints_server,该节点将接收两个整数,并返回它们的和。
roscd beginner_tutorials
在beginner_tutorials包中创建src/add_two_ints_server.cpp文件并粘贴以下内容进去:
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
bool add(beginner_tutorials::AddTwoInts::Request &req,
beginner_tutorials::AddTwoInts::Response &res){
res.sum = req.a + req.b;
ROS_INFO("request: x=%ld, y=%ld", (long int)req.a, (long int)req.b);
ROS_INFO("sending back response: [%ld]", (long int)res.sum);
return true;
}(该函数提供了AddTwoInts服务,它接受srv文件中定义的请求(request)和响应(response)类型,并返回一个布尔值)
int main(int argc, char **argv)
{
ros::init(argc, argv, "add_two_ints_server");
ros::NodeHandle n;
ros::ServiceServer service = n.advertiseService("add_two_ints", add);
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
编写客户端节点:
在beginner_tutorials包中创建src/add_two_ints_client.cpp文件并粘贴以下内容进去:
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h"
#include <cstdlib>
int main(int argc, char **argv){
ros::init(argc, argv, "add_two_ints_client");
if (argc != 3){
ROS_INFO("usage: add_two_ints_client X Y");
return 1;
}
ros::NodeHandle n;
ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints");(为add_two_ints服务创建一个客户端。ros::ServiceClient对象的作用是在稍后调用服务)
beginner_tutorials::AddTwoInts srv;(实例化一个服务类)
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);(为两个成员赋值)
if (client.call(srv)){
ROS_INFO("Sum: %ld", (long int)srv.response.sum);
}
else{
ROS_ERROR("Failed to call service add_two_ints");
return 1;
}
return 0;
}
更改CMakeLists.txt(添加代码):
add_executable(add_two_ints_server src/add_two_ints_server.cpp)
target_link_libraries(add_two_ints_server ${catkin_LIBRARIES})
add_dependencies(add_two_ints_server beginner_tutorials_gencpp)
add_executable(add_two_ints_client src/add_two_ints_client.cpp)
target_link_libraries(add_two_ints_client ${catkin_LIBRARIES})
add_dependencies(add_two_ints_client beginner_tutorials_gencpp)
编译:
cd ~/catkin_ws
catkin_make
14.检验简单的服务与客户端
运行服务:
rosrun beginner_tutorials add_two_ints_server
运行客户端:
rosrun beginner_tutorials add_two_ints_client 1 3
15.录制和回放数据
将正在运行的ROS系统中的数据记录到一个bag文件中,然后通过回放这些数据来来重现相似的运行过程
录制数据:
roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key(又打开了小乌龟)
mkdir ~/bagfiles
cd ~/bagfiles
rosbag record -a(-a表明所有发布的话题都积累在一个bag文件中)
随便控制小乌龟移动一会儿
回放:
rosbag info <your bagfile>(查看bag中的记录,bag名字似乎和时间有关)
rosbag play <your bagfile>(先结束key的进程)
rosbag play -r 2 <your bagfile>(-r可以改变消息发布的频率)(-s可以指定开始时间,使不从头开始)
录制数据子集:
rosbag record命令支持只录制特定的话题到bag文件中,这样就可以只录制用户感兴趣的话题
rosbag record -O subset /turtle1/cmd_vel /turtle1/pose
(-O参数告诉rosbag record将数据记录到名为subset.bag的文件中,而后面的topic参数告诉rosbag record只能订阅这两个指定的话题)
p.s.无法完美模仿,精度不够
16.从bag文件中读取消息
注:命令前面都有一个time,这样做可以同时输出执行每个命令花费的时间
立即回放消息并在多个终端中查看输出:
time rosbag info demo.bag (手动检查所有已发布的话题,以及向每个话题发布了多少消息)
rostopic echo /obs1/gps/fix | tee topic1.yaml(订阅/obs1/gps/fix话题并复读该话题上发布的所有内容,同时用tee命令转储到一个yaml格式的文件中以便之后查看)
订阅另一个话题:rostopic echo /diagnostics_agg | tee topic2.yaml
time rosbag play --immediate demo.bag --topics /topic1 /topic2 /topic3 /topicN
(回放,–immediate尽可能快)
即:time rosbag play --immediate demo.bag --topics /obs1/gps/fix /diagnostics_agg
可以使用ros_readbagfile
脚本提取感兴趣的话题
17.roswtf入门
安装检查:
roswtf可以检查系统并尝试发现问题(首先确保ros没有运行)
roscd rosmaster
roswtf
在线检查:
roscd
roswtf
错误:
roscd
ROS_PACKAGE_PATH=bad:$ROS_PACKAGE_PATH roswtf
最后:在vscode中配置ROS环境:
在workspace中添加文件(devel里面):catkin_make -DCMAKE_EXPORT_COMPILE_COMMANDS=Yes
更改c_cpp_propertries.json为:
{
"configurations": [
{
"browse": {
"databaseFilename": "",
"limitSymbolsToIncludedHeaders": true
},
"includePath": [
"/home/nova/catkin_ws/devel/include/**",
"/opt/ros/kinetic/include/**",
"/home/nova/catkin_ws/src/beginner_tutorials/include/**",
"/home/nova/catkin_ws/src/try0/include/**",
"/usr/include/**"
],
"name": "ROS",
"intelliSenseMode": "gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "c11",
"cppStandard": "c++17",
"compileCommands": "${workspaceFolder}/build/compile_commands.json"
}
],
"version": 4
}
就可以找到ros.h等头文件啦!