核心ROS教程(一)
1. 查看ROS的设置
2. ROS环境变量都自动添加到bash会话中(只在本次终端中有效)
3.ROS环境变量都自动添加到bash会话中(一直有效)
4. 创建catkin_ws工作空间
5. 创建和构建ROS包
6. 了解ROS节点
7. 了解ROS主题
8. 了解ROS服务
9. 使用rqt_console和roslaunch
10. 使用rosed编辑ROS中的文件
11. 创建ROS消息msg和服务srv
12. 编写简单的发布者和订阅者(C ++)
13. 编写简单的发布者和订阅者(Python)
14. 编写简单的服务和客户端(C ++)
15. 编写简单的服务和客户端(Python)
16. 录制和播放数据
17. roswtf
参考wiki.ros
- 1. 查看ROS的设置
- 2. ROS环境变量都自动添加到bash会话中(只在本次终端中有效)
- 3. ROS环境变量都自动添加到bash会话中(一直有效)
- 4. 创建catkin_ws工作空间
- 5. 创建和构建ROS包
- 6. 了解ROS节点
- 7. 了解ROS主题
- 8. 了解ROS服务
- 9. 使用rqt_console和roslaunch
- 10. 使用rosed编辑ROS中的文件
- 11. 创建ROS消息msg和服务srv
- 12. 编写简单的发布者和订阅者(C ++)
- 13. 编写简单的发布者和订阅者(Python)
- 14. 编写简单的服务和客户端(C ++)
- 15. 编写简单的服务和客户端(Python)
- 16. 录制和播放数据
- 17. roswtf
1. 查看ROS的设置
printenv | grep ROS
2. ROS环境变量都自动添加到bash会话中(只在本次终端中有效)
source /opt/ros/indigo/setup.bash
3.ROS环境变量都自动添加到bash会话中(一直有效)
echo“source /opt/ros/indigo/setup.bash”>>〜/ .bashrc
source~ / .bashrc
4. 创建catkin_ws工作空间
mkdir -p ~ / catkin_ws / src
cd ~ / catkin_ws / src
catkin_init_workspace
cd~ / catkin_ws /
catkin_make
source devel / setup.bash # 或者加上下面两句使得工作空间新打开终端依旧有效
gedit ~/.bashrc # gedit的方式打开bashc文件
source ~/catkin_ws/devel/setup.bash # 末尾加上这句
echo $ROS_PACKAGE_PATH
5. 创建和构建ROS包
# 创建catkin包
cd ~ / catkin_ws / src
catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
# 构建catkin包
cd ~ / catkin_ws
catkin_make --pkg beginner_tutorials #构建工作空间之后,它在devel子文件夹(catkin_ws/devel)中创建了一个类似的结构,就像您在/ opt / ros / $ ROSDISTRO_NAME下找到的一样
6. 了解ROS节点
roscore # roscore是使用ROS时应该运行的第一件事
# 用rosnode来看看正在运行的roscore做了什么.
rosnode list
rosnode info /rosout
rosrun turtlesim turtlesim_node # 使用rosrun来启动另一个节点,允许您使用包名直接在包中运行节点(无需知道包路径),用法:rosrun [package_name] [node_name]
rosnode list # 查看当前节点多了一个turtlesim
rosrun turtlesim turtlesim_node __name:= my_turtle # 使用remapping argument来更改节点turtlesim为my_turtle
rosnode list # 会发现当前节点turtlesim变成了my_turtle
rosnode ping my_turtle # rosnode命令ping来测试它是否正常
7. 了解ROS主题
roscore # roscore是使用ROS时应该运行的第一件事
rosrun turtlesim turtlesim_node # 启动乌龟节点
rosrun turtlesim turtle_teleop_key # 启动键盘驱动节点
rosrun rqt_graph rqt_graph
rostopic -h # 使用rostopic的help选项获取rostopic的可用子命令
rostopic list # 打印有关活动主题的信息
rostopic echo /turtle1/cmd_vel # 显示了在主题/turtle1/cmd_vel上发布的数据,按rqt_graph左上角的刷新按钮显示新节点
rostopic list -h # 查看list子命令需要什么参数
rostopic list -v # 列出有关每个主题的完整详细信息
rostopic list -p # 仅列出发布者
rostopic list -s # 仅列出订阅者
rostopic type /turtle1/cmd_vel # 返回正在发布的/turtle1/cmd_vel 主题的消息类型,得到geometry_msgs/Twist
rosmsg show geometry_msgs/Twist # 查看消息geometry_msgs/Twist的详细信息
rostopic pub -1 /turtle1/cmd_vel geometry_msgs/Twist -- '[2.0, 0.0, 0.0]' '[0.0, 0.0, 1.8]' #rostopic pub [topic] [msg_type] [args]发布有关当前/turtle1/cmd_vel主题的数据。-1:仅发布一条消息然后退出;/ turtle1 / cmd_vel:要发布到的主题的名称;geometry_msgs/Twist:发布到主题时要使用的消息类型;双破折号--:告诉选项解析器以下任何参数都不是一个选项;geometry_msgs / Twist msg具有两个向量,每个向量包含三个浮点元素:线性和角度。在这种情况下,'[2.0,0.0,0.0]'变为线性值,x = 2.0,y = 0.0,z = 0.0,'[0.0,0.0,1.8]'是x = 0.0的角度值,y = 0.0,z = 1.8
rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 - '[2.0,0.0,0.0]' '[0.0,0.0,-1.8]' # rostopic pub -r命令发布稳定的命令流,该命令表示在速度主题上以1 Hz的速率发布速度命令
rostopic list
rostopic echo / turtle1 / pose #查看我们的turtlesim发布的数据
rostopic hz / turtle1 / pose # rostopic hz [主题];该命令看看turtlesim_node发布的速度有多快
rosrun rqt_plot rqt_plot # rqt_plot显示主题上发布的数据的滚动时间图,将左上角的主题添加进来,键入/ turtle1 / pose / x,用来显示乌龟的x位置
8. 了解ROS服务
roscore # roscore是使用ROS时应该运行的第一件事
rosrun turtlesim turtlesim_node # 启动乌龟节点
rosrun turtlesim turtle_teleop_key # 启动键盘驱动节点
rosservice list # 打印有关活动服务的信息,得到/clear /kill /reset /rosout/get_loggers /rosout/set_logger_level /spawn /teleop_turtle/get_loggers /teleop_turtle/set_logger_level /turtle1/set_pen /turtle1/teleport_absolute /turtle1/teleport_relative /turtlesim/get_loggers /turtlesim/set_logger_level
rosservice type /clear # rosservice type [service],打印服务/clear类型,得到std_srvs/Empty,此服务为空,这意味着在进行服务调用时它不带参数(即,在发出请求时不发送数据,在接收响应时不接收数据)
rosservice call /clear # rosservice call [service] [args],这里我们将调用没有参数,因为服务的类型为empty,将会清除了turtlesim_node的背景。
rosservice type /spawn | rossrv show # 通过查看服务spawn的信息来查看服务具有参数的情况,d得到
rosservice call /spawn 2 2 0.2 "" # 在给定的位置和方向产生一只新的乌龟。name字段是可选的,这里没有取名,让turtlesim为我们创建一个,服务调用返回新创建的乌龟的名称
rosservice find # 按服务类型查找服务
rosservice uri # 打印服务
rosparam set set parameter
rosparam get get parameter
rosparam load load parameters from file
rosparam dump dump parameters to file
rosparam delete delete parameter
rosparam list # 看到turtlesim节点在param服务器上有三个参数用于背景颜色,得到/ background_b / background_g / background_r / rosdistro / roslaunch /的URI / host_57aea0986fef__34309 / rosversion / run_id
rosparam set /background_r 150 # rosparam set [param_name],更改背景颜色的红色通道,这会更改参数值,但是背景颜色还未改变
rosservice call /clear # 调用clear服务才能使参数更改生效,此时背景变成红色
rosparam get / background_g # 看一下param服务器上其他参数/ background_g的值。让我们得到绿色背景的值:
rosparam get / # 查看整个Parameter Server的内容
rosparam dump params.yaml #rosparam dump [file_name] [namespace],将所有参数写入文件params.yaml
rosparam load params.yaml copy #将这些yaml文件加载到新的命名空间copy中
rosparam get /copy/background_b # 获取copy中的参数/copy/background_b
9. 使用rqt_console和roslaunch
sudo apt-get install ros-indigo-rqt ros-indigo-rqt-common-plugins ros-indigo-turtlesim # 安装rqt和turtlesim包
rosrun rqt_console rqt_console # 启动rqt_console并附加到ROS的日志框架以显示节点的输出
rosrun rqt_logger_level rqt_logger_level # 启动rqt_logger_level它允许我们在节点运行时更改详细级别(DEBUG,WARN,INFO和ERROR)
rosrun turtlesim turtlesim_node # 在一个新的终端开始turtlesim,默认记录器级别为INFO,您将看到turtlesim在启动时发布的任何信息,这时可以将选择rqt_logger_level的Warn按钮
rostopic pub /turtle1/cmd_vel geometry_msgs/Twist -r 1 -- '{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0,y: 0.0,z: 0.0}}' # 将乌龟带到墙上,查看rqt_console中显示红色警告,记录级别按以下顺序排列优先级:Fatal(致命) Error(错误) Warn(警告) Info(信息) Debug(调试)
roscd beginner_tutorials # 转到我们之前创建和构建的beginner_tutorials包:
mkdir launch # 在beginner_tutorials下创建一个launch启动目录,roslaunch命令自动查看传递的包并检测可用的启动文件
cd launch # 进入launch文件夹下,创建一个名为turtlemimic.launch的启动文件并粘贴以下内容:
<launch>
<group ns="turtlesim1">
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
</group>
<group ns="turtlesim2">
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
</group>
<node pkg="turtlesim" name="mimic" type="mimic">
<remap from="input" to="turtlesim1/turtle1"/>
<remap from="output" to="turtlesim2/turtle1"/>
</node>
</launch>
roslaunch beginner_tutorials turtlemimic.launch # 启动启动文件,roslaunch [package] [filename.launch]
rostopic pub / turtlesim1 / turtle1 / cmd_vel geometry_msgs / Twist -r 1 - '[2.0,0.0,0.0]''[0.0,0.0,-1.8]' # 发布命令仅发送到turtlesim1,您也会看到两个turtlesims开始移动
rqt_graph # 启动rqt更好地理解我们的启动文件的作用
9.1 launch启动文件说明
<launch> //打破启动xml,这里使用启动标记启动启动文件,以便将文件标识为启动文件
//启动两个组,名称空间标签为turtlesim1、turtlesim2为turtlesim节点,名称为sim,这允许我们在没有名称冲突的情况下启动两个模拟器
<group ns="turtlesim1">
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
</group>
<group ns="turtlesim2">
<node pkg="turtlesim" name="sim" type="turtlesim_node"/>
</group>
//启动模拟节点,主题输入和输出重命名为turtlesim1和turtlesim2,这个重命名会使得turtlesim2模仿turtlesim1
<node pkg="turtlesim" name="mimic" type="mimic">
<remap from="input" to="turtlesim1/turtle1"/>
<remap from="output" to="turtlesim2/turtle1"/>
</node>
</launch>//关闭启动文件的xml标记
10. 使用rosed编辑ROS中的文件
rosed roscpp Logger.msg # rosed [package_name] [filename],允许您使用包名roscpp直接编辑包roscpp中的文件Logger.msg,而不必键入包的整个路径
export EDITOR ='nano -w' # rosed的默认编辑器是vim,通过编辑〜/ .bashrc文件,将默认编辑器设置为nano, .bashrc中的更改仅对新终端生效。已经打开的终端将看不到新的环境变量
export EDITOR ='emacs -nw' # 编辑〜/ .bashrc文件,将默认编辑器设置为emacs
echo $ EDITOR # 打开一个新终端,看看是否定义了EDITOR,得到应该是你更改后的编辑器nano -w或emacs -nw
11. 创建ROS消息msg和服务srv
-
11.1 创建ROS消息msg
roscd beginner_tutorials # 进入上一个教程中创建的包beginner_tutorials
mkdir msg # 在包beginner_tutorials中定义一个新的msg
# 在msg文件夹下新建Num.msg文件,加入以下一行内容:
int64 num
#为了确保将msg文件转换为C ++,Python和其他语言的源代码,打开package.xml,确保下面两行都在其中并取消注释,构建时,我们需要“message_generation”,而在运行时,我们只需要“message_runtime”:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
# 打开CMakeLists.txt ,添加下面四项(有的是添加,有的是取消注释)内容,主要是添加message_generation,以便生成消息;添加message_runtime,确保导出消息运行时依赖项;添加 Num.msg,通过手动添加.msg文件,确保CMake知道在添加其他.msg文件后何时必须重新配置项目;取消注释generate_messages:确保调用generate_messages()函数:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
add_message_files(
FILES
Num.msg
)
generate_messages(
DEPENDENCIES
std_msgs
)
rosmsg show beginner_tutorials/Num # 前面已经完成了准备好从msg定义生成源文件,也是创建一个消息所需要做的一切。rosmsg show [message type]用来查看Num消息的内容,也可以直接用rosmsg show Num
-
11.2 创建ROS服务srv
roscd beginner_tutorials # 同样进入上一个教程中创建的包beginner_tutorials
mkdir srv # 创建srv文件夹
roscp rospy_tutorials AddTwoInts.srv srv/AddTwoInts.srv # 将 rospy_tutorials功能包里面的AddTwoInts.srv服务文件复制到当前功能包下的srv文件夹下面。roscp [package_name] [file_to_copy_path] [copy_path]
#为了确保将srv文件转换为C ++,Python和其他语言的源代码,打开package.xml,确保下面两行都在其中并取消注释,同样,构建时,我们需要“message_generation”,而在运行时,我们只需要“message_runtime”:
<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>
# 打开CMakeLists.txt ,添加下面四项(有的是添加,有的是取消注释)内容,主要是添加message_generation(适用于msg和srv),以便生成服务;添加message_runtime,确保导出服务运行时依赖项;添加 AddTwoInts.srv,通过手动添加.srv文件,确保CMake知道在添加其他.srv文件后何时必须重新配置项目;取消注释generate_messages:确保调用generate_messages()函数:
find_package(catkin REQUIRED COMPONENTS
roscpp
rospy
std_msgs
message_generation
)
catkin_package(
...
CATKIN_DEPENDS message_runtime ...
...)
add_service_files(
FILES
AddTwoInts.srv
)
generate_messages(
DEPENDENCIES
std_msgs
)
rossrv show beginner_tutorials/AddTwoInts # 前面已经完成了准备好从srv定义生成源文件。rossrv show <service type>,与rosmsg类似,您可以在不指定包名的情况下找到这样的服务文件rossrv show AddTwoInts,显示了两个服务,第一个是你刚刚在beginner_tutorials包中创建的那个,第二个是rospy_tutorials包中预先存在的那个。
-
11.3 msg和srv简介
msg文件是描述ROS消息字段的简单文本文件。它们用于生成不同语言的消息的源代码
srv文件描述服务。它由两部分组成:请求和响应。
msg文件存储在包的msg目录中,srv文件存储在srv目录中。
msgs只是简单的文本文件,每行有一个字段类型和字段名称。您可以使用的字段类型是:
int8, int16, int32, int64 (plus uint*)
float32, float64
string
time, duration
other msg files
variable-length array[] and fixed-length array[C]
ROS中还有一种特殊类型:标题,标题包含ROS中常用的时间戳和坐标框架信息。您经常会看到msg文件中的第一行有Header头。以下是使用标头,字符串基元和另外两个消息的消息的示例:
Header header
string child_frame_id
geometry_msgs/PoseWithCovariance pose
geometry_msgs/TwistWithCovariance twist
srv文件就像msg文件一样,除了它们包含两部分:请求和响应。这两部分用'---'线分开。以下是srv文件的示例(A和B是请求,Sum是响应):
int64 A
int64 B
---
int64 Sum
-
11.4 msg和srv的常用步骤
# 更改CMakeLists.txt(取消注释并添加您依赖的任何包)
generate_messages(
DEPENDENCIES
std_msgs
)
# 现在我们已经发了一些新消息,我们需要再次制作我们的包执行下面四行操作
roscd beginner_tutorials # 回到 beginner_tutorials包下
cd ../.. # 回到工作空间catkin_ws下
catkin_make install # 再次制作
cd - # 回到 beginner_tutorials包下
#执行完上面的操作后,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'文件夹。如果要构建使用新消息的C ++节点,则还需要在节点和消息之间声明依赖关系,参见:http://docs.ros.org/latest/api/catkin/html/howto/format2/building_msgs.html
-
11.5获取帮助
rosmsg -h #查看rosmsg子命令的列表
rosmsg show -h #获得子命令的帮助
12. 编写简单的发布者和订阅者(C ++)
-
12.1 编写简单的发布者节点
roscd beginner_tutorials # 回到功能包beginner_tutorials下
mkdir -p src # -p表示确定目录名称beginner_tutorials存在,如果目录不存在的就新创建一个beginner_tutorials
# 在src下新建一个talker.cpp文件,代码如下
#include "ros/ros.h"
#include "std_msgs/String.h"
#include <sstream>
/**
* This tutorial demonstrates simple sending of messages over the ROS system.
*/
int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line. For programmatic
* remappings you can use a different version of init() which takes remappings
* directly, but for most command-line programs, passing argc and argv is the easiest
* way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "talker");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* The advertise() function is how you tell ROS that you want to
* publish on a given topic name. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. After this advertise() call is made, the master
* node will notify anyone who is trying to subscribe to this topic name,
* and they will in turn negotiate a peer-to-peer connection with this
* node. advertise() returns a Publisher object which allows you to
* publish messages on that topic through a call to publish(). Once
* all copies of the returned Publisher object are destroyed, the topic
* will be automatically unadvertised.
*
* The second parameter to advertise() is the size of the message queue
* used for publishing messages. If messages are published more quickly
* than we can send them, the number here specifies how many messages to
* buffer up before throwing some away.
*/
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000);
ros::Rate loop_rate(10);
/**
* A count of how many messages we have sent. This is used to create
* a unique string for each message.
*/
int count = 0;
while (ros::ok())
{
/**
* This is a message object. You stuff it with data, and then publish it.
*/
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count;
msg.data = ss.str();
ROS_INFO("%s", msg.data.c_str());
/**
* The publish() function is how you send messages. The parameter
* is the message object. The type of this object must agree with the type
* given as a template parameter to the advertise<>() call, as was done
* in the constructor above.
*/
chatter_pub.publish(msg);
ros::spinOnce();
loop_rate.sleep();
++count;
}
return 0;
}
-
12.1.1 talker.cpp代码分析
#include "ros/ros.h" //包括使用ROS系统最常见的公共部分所需的所有标题
#include "std_msgs/String.h" //包括std_msgs/String消息,该消息在std_msgs包中
#include <sstream>
int main(int argc, char **argv)
{
ros::init(argc, argv, "talker"); //初始化ROS。这允许ROS通过命令行进行名称重映射 - 现在不重要。这也是我们指定节点名称的地方。节点名称在运行的系统中必须是唯一的
ros::NodeHandle n; //为此进程的节点创建句柄。创建的第一个NodeHandle实际上将执行节点的初始化,最后一个被破坏的将清除节点正在使用的任何资源
ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); //告诉master我们将在主题聊天中发布类型为std_mags/String的消息,这让master告诉任何监听聊天节点的节点,我们将发布关于该主题的数据。第二个参数是我们的发布队列的大小。在这种情况下,如果我们发布得太快,它将在开始丢弃旧消息之前缓冲最多1000条消息。
ros::Rate loop_rate(10); //使用ros :: Rate对象可以指定要循环的频率。它将跟踪自上次调用Rate :: sleep()以来的持续时间,并在正确的时间内休眠。这里我们告诉它我们想要以10Hz运行
int count = 0;
while (ros::ok())
{
std_msgs::String msg;
std::stringstream ss;
ss << "hello world " << count; //发布消息,通常从msg文件生成
msg.data = ss.str(); //使用标准的String消息,它有一个成员:“data”
ROS_INFO("%s", msg.data.c_str()); //ROS_INFO类似于printf / cout
chatter_pub.publish(msg); //将消息广播给任何已连接的人
ros::spinOnce(); //这个简单程序不需要调用ros :: spinOnce(),因为我们没有收到任何回调。但是,如果您要在此应用程序中添加订阅,并且此处没有ros :: spinOnce(),则永远不会调用您的回调。所以,添加它是为了衡量
loop_rate.sleep(); //使用ros :: Rate对象在剩余的时间内休眠,让我们达到10Hz的发布速度
++count;
}
return 0;
}
-
12.2 编写简单的订阅者节点
roscd beginner_tutorials # 回到功能包beginner_tutorials下
mkdir -p src # -p表示确定目录名称beginner_tutorials存在,如果目录不存在的就新创建一个beginner_tutorials
# 在src下新建一个listener.cpp文件,代码如下
#include "ros/ros.h"
#include "std_msgs/String.h"
/**
* This tutorial demonstrates simple receipt of messages over the ROS system.
*/
void chatterCallback(const std_msgs::String::ConstPtr& msg)
{
ROS_INFO("I heard: [%s]", msg->data.c_str());
}
int main(int argc, char **argv)
{
/**
* The ros::init() function needs to see argc and argv so that it can perform
* any ROS arguments and name remapping that were provided at the command line.
* For programmatic remappings you can use a different version of init() which takes
* remappings directly, but for most command-line programs, passing argc and argv is
* the easiest way to do it. The third argument to init() is the name of the node.
*
* You must call one of the versions of ros::init() before using any other
* part of the ROS system.
*/
ros::init(argc, argv, "listener");
/**
* NodeHandle is the main access point to communications with the ROS system.
* The first NodeHandle constructed will fully initialize this node, and the last
* NodeHandle destructed will close down the node.
*/
ros::NodeHandle n;
/**
* The subscribe() call is how you tell ROS that you want to receive messages
* on a given topic. This invokes a call to the ROS
* master node, which keeps a registry of who is publishing and who
* is subscribing. Messages are passed to a callback function, here
* called chatterCallback. subscribe() returns a Subscriber object that you
* must hold on to until you want to unsubscribe. When all copies of the Subscriber
* object go out of scope, this callback will automatically be unsubscribed from
* this topic.
*
* The second parameter to the subscribe() function is the size of the message
* queue. If messages are arriving faster than they are being processed, this
* is the number of messages that will be buffered up before beginning to throw
* away the oldest ones.
*/
ros::Subscriber sub = n.subscribe("chatter", 1000, chatterCallback);
/**
* ros::spin() will enter a loop, pumping callbacks. With this version, all
* callbacks will be called from within this thread (the main one). ros::spin()
* will exit when Ctrl-C is pressed, or the node is shutdown by the master.
*/
ros::spin();
return 0;
}
-
12.2.1listener.cpp代码分析
#include "ros/ros.h"
#include "std_msgs/String.h"
void chatterCallback(const std_msgs::String::ConstPtr& msg) //这是一个回调函数,当一个新的消息到达chatter主题时将被调用,消息在boost shared_ptr中传递,这意味着您可以根据需要将其存储起来,而不必担心它会被删除,并且不会复制基础数据
{
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); //与master订阅聊天主题。每当有新消息到达时,ROS都会调用chatterCallback()函数。第二个参数是队列大小,以防我们无法足够快地处理消息。在这种情况下,如果队列达到1000条消息,我们将在新消息到达时开始丢弃旧消息
ros::spin(); //ros :: spin()进入一个循环,尽可能快地调用消息回调。不过不用担心,如果没有什么可以做的话就不会占用太多的CPU。 ros :: spin()将退出一次ros :: ok()返回false,这意味着已经调用了ros :: shutdown(),默认的Ctrl-C处理程序,master告诉我们关闭,或者手动调用它
return 0;
}
-
12.3 构建节点
# 将这几行添加到CMakeLists.txt最底部(这将创建两个可执行文件,talker和listener,默认情况下位于〜/ catkin_ws / devel / lib / <package name>。)
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)
# 生成的CMakeLists.txt如下所示:
cmake_minimum_required(VERSION 2.8.3)
project(beginner_tutorials)
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg) ##找到catkin和任何catkin包
add_message_files(FILES Num.msg)##声明ROS消息
add_service_files(FILES AddTwoInts.srv) ##声明ROS服务
generate_messages(DEPENDENCIES std_msgs)##生成添加的消息和服务
catkin_package()##宣告一个catkin包
include_directories(include ${catkin_INCLUDE_DIRS}) ##建立talker和listener
add_executable(talker src/talker.cpp)
target_link_libraries(talker ${catkin_LIBRARIES}) ##如果您使用catkin工作区内其他包的消息,使用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 --pkg beginner_tutorials
-
12.4 检查简单的Publisher和Subscriber
roscore #启动master
cd ~/catkin_ws
source ./devel/setup.bash
rosrun beginner_tutorials talker #运行发布者(c++)
rosrun beginner_tutorials listener #运行订阅者接收来自发布者的消息(C++)
13. 编写简单的发布者和订阅者(Python)
-
13.1 编写发布者节点
roscd beginner_tutorials # 回到功能包beginner_tutorials下
mkdir scripts # 创建一个'scripts'文件夹来存储我们的Python脚本
cd scripts
wget https://raw.github.com/ros/ros_tutorials/kinetic-devel/rospy_tutorials/001_talker_listener/talker.py # 将示例脚本talker.py下载到新脚本scripts目录
chmod +x talker.py # 使示例脚本talker.py可执行
rosed beginner_tutorials talker.py # 可以执行此句查看和编辑该文件
-
13.1.1 talker.py代码分析
#!/usr/bin/env python # 每个Python ROS节点都会在顶部显示此声明。第一行确保您的脚本作为Python脚本执行
# license removed for brevity
import rospy # 导入rospy
from std_msgs.msg import String # std_msgs.msg使我们可以重新使用std_msgs /String用于发布消息类型(一个简单的字符串容器)
def talker():
pub = rospy.Publisher('chatter', String, queue_size=10) #这部分代码定义了talker与其余ROS的接口,声明Publisher节点使用消息类型String发布到chatter主题。这里的字符串实际上是类std_msgs.msg.String。该的queue_size参数是限制排队的消息的数量,如果任何用户不足够快地接收他们
rospy.init_node('talker', anonymous=True) #告诉rospy节点的名称为talker,直到rospy有这个信息,它才能开始与ROS Master通信,nonymous = True通过在NAME末尾添加随机数来确保您的节点具有唯一名称
rate = rospy.Rate(10) # 创建Rate对象速率。借助其方法sleep(),它提供了一种以所需速率循环的便捷方式。它的参数为10,我们应该期望每秒循环10次(只要我们的处理时间不超过1/10秒!)
while not rospy.is_shutdown(): # 这个循环是一个相当标准的rospy构造:检查rospy.is_shutdown()标志然后做工作,你必须检查is_shutdown()以检查你的程序是否应该退出(例如,如果有Ctrl-C或其他)
hello_str = "hello world %s" % rospy.get_time()
rospy.loginfo(hello_str) # 这个循环还调用rospy.loginfo(str),它执行三重任务:消息被打印到屏幕,它被写入Node的日志文件,并被写入rosout,对于调试非常方便:您可以使用rqt_console来提取消息,而不必使用Node的输出找到控制台窗口
pub.publish(hello_str)
rate.sleep() # 循环调用rate.sleep(),它睡眠的时间足够长,可以通过循环保持所需的速率
if __name__ == '__main__': #检查标准的Python __main__,这还会捕获一个rospy.ROSInterruptException异常,当按下Ctrl-C或者你的节点关闭时,rospy.sleep()和rospy.Rate.sleep()方法可以抛出该异常。引发此异常的原因是,您不会在sleep()之后意外地继续执行代码。
try:
talker()
except rospy.ROSInterruptException: # 捕获一个rospy.ROSInterruptException异常
pass
-
13.2 编写简单的订阅者节点
roscd beginner_tutorials/scripts /
wget https://raw.github.com/ros/ros_tutorials/kinetic-devel/rospy_tutorials/001_talker_listener/listener.py # 将示例脚本listener.py下载到新脚本scripts目录
chmod +x listener.py # 使示例脚本listener.py可执行
-
13.2.1 listener.py代码分析
#!/usr/bin/env python # listener.py的代码类似于talker.py,除了我们引入了一个新的基于回调的机制来订阅消息
import rospy
from std_msgs.msg import String
def callback(data):
rospy.loginfo(rospy.get_caller_id() + 'I heard %s', data.data)
def listener():
rospy.init_node('listener', anonymous=True) # 添加了anonymous = True关键字参数。ROS要求每个节点都有唯一的名称。如果出现具有相同名称的节点,则会突然显示前一个节点。这样可以很容易地将故障节点从网络中踢出。anonymous=True告诉rospy为节点生成一个唯一的名称,以便可以有多个listener.py节点轻松运行。
rospy.Subscriber('chatter', String, callback) # 节点订阅的话题类型是std_msgs.msgs.String,收到新消息时,将调用回调,并将消息作为第一个参数
rospy.spin() #spin()只是让python节点退出,直到该节点停止。与roscpp不同,rospy.spin()不会影响订阅者回调函数,因为它们有自己的线程。
if __name__ == '__main__':
listener()
-
13.3 构建节点
cd ~/catkin_ws
catkin_make --pkg beginner_tutorials
-
13.4 检查简单的发布者和订阅者
roscore #启动master
cd ~/catkin_ws
source ./devel/setup.bash
rosrun beginner_tutorials talker.py #运行发布者(python)
rosrun beginner_tutorials listener.py #运行订阅者接收来自发布者的消息(Python)
14. 编写简单的服务和客户端(C ++)
-
14.1 编写简单的服务端节点
roscd 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;
}
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;
}
-
14.1.1 add_two_ints_server.cpp代码分析
#include "ros/ros.h"
#include "beginner_tutorials/AddTwoInts.h" # beginner_tutorials / AddTwoInts.h是从我们之前创建的srv文件生成的头文件
bool add(beginner_tutorials::AddTwoInts::Request &req,
beginner_tutorials::AddTwoInts::Response &res) # add函数提供添加两个int的服务,它接受srv文件中定义的请求和响应类型并返回一个布尔值。
{
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; #最后,服务在完成后返回true
}
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创建和订阅的
ROS_INFO("Ready to add two ints.");
ros::spin();
return 0;
}
-
14.2 编写客户端节点
#在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");
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;
}
-
14.2.1 add_two_ints_client.cpp代码分析
ros::ServiceClient client = n.serviceClient<beginner_tutorials::AddTwoInts>("add_two_ints"); # 为add_two_ints服务创建一个客户端。该ROS :: ServiceClient对象用于以后调用服务
beginner_tutorials::AddTwoInts srv; # 实例化一个自动生成的服务类,并将值分配给它的请求成员。服务类包含两个成员,即请求和响应。它还包含两个类定义,Request和Response
srv.request.a = atoll(argv[1]);
srv.request.b = atoll(argv[2]);
if (client.call(srv)) # 这实际上是调用服务。由于服务调用是阻塞的,因此一旦调用完成,它将返回。如果服务调用成功,则call()将返回true,并且srv.response中的值将有效。如果调用未成功,则call()将返回false,并且srv.response中的值将无效
-
14.3 构建节点
# 编辑位于〜/ catkin_ws / src / beginner_tutorials / CMakeLists.txt的beginner_tutorials CMakeLists.txt,并在最后添加以下内容(创建两个可执行文件add_two_ints_server和add_two_ints_client,默认情况下位于〜/ catkin_ws / devel / lib / <package name>。您可以直接调用可执行文件,也可以使用rosrun来调用它们。它们不会放在'<prefix> / bin'中,因为在将软件包安装到系统时会污染PATH。如果您希望在安装时将可执行文件放在PATH上,可以设置安装目标,请参阅:http://wiki.ros.org/catkin/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 --pkg beginner_tutorials
-
14.4 运行节点
roscore # 启动master
rosrun beginner_tutorials add_two_ints_server # 运行服务器
rosrun beginner_tutorials add_two_ints_client 1 3 # 运行客户端
15. 编写简单的服务和客户端(Python)
-
15.1 编写服务端节点
roscd beginner_tutorials
# 在beginner_tutorials包中创建scripts / add_two_ints_server.py文件,代码如下:
#!/usr/bin/env python
from beginner_tutorials.srv import *
import rospy
def handle_add_two_ints(req):
print "Returning [%s + %s = %s]"%(req.a, req.b, (req.a + req.b))
return AddTwoIntsResponse(req.a + req.b)
def add_two_ints_server():
rospy.init_node('add_two_ints_server')
s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints)
print "Ready to add two ints."
rospy.spin()
if __name__ == "__main__":
add_two_ints_server()
chmod +x add_two_ints_server.py # 使示例脚本add_two_ints_server.py可执行
-
15.1.1 add_two_ints_server.py代码分析
s = rospy.Service('add_two_ints', AddTwoInts, handle_add_two_ints) # 使用AddTwoInts服务类型声明一个名为add_two_ints的新服务。所有请求都传递给handle_add_two_ints函数。handle_add_two_ints被调用的情况下AddTwoIntsRequest和返回的实例AddTwoIntsResponse。就像订阅者示例一样,rospy.spin()使代码不会退出,直到服务关闭。
-
15.2 编写客户端节点
# 在beginner_tutorials包中创建scripts / add_two_ints_client.py文件
#!/usr/bin/env python
import sys
import rospy
from beginner_tutorials.srv import *
def add_two_ints_client(x, y):
rospy.wait_for_service('add_two_ints')
try:
add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts)
resp1 = add_two_ints(x, y)
return resp1.sum
except rospy.ServiceException, e:
print "Service call failed: %s"%e
def usage():
return "%s [x y]"%sys.argv[0]
if __name__ == "__main__":
if len(sys.argv) == 3:
x = int(sys.argv[1])
y = int(sys.argv[2])
else:
print usage()
sys.exit(1)
print "Requesting %s+%s"%(x, y)
print "%s + %s = %s"%(x, y, add_two_ints_client(x, y))
cd scripts
chmod +x scripts/add_two_ints_client.py # 使示例脚本add_two_ints_client.py可执行
-
15.2.1 add_two_ints_client.py代码分析
rospy.wait_for_service('add_two_ints') # 对于客户端,不必调用init_node()
add_two_ints = rospy.ServiceProxy('add_two_ints', AddTwoInts) # 创建一个用于调用服务的句柄
resp1 = add_two_ints(x, y) # 使用这个句柄并调用它
return resp1.sum #因为已经将服务的类型声明为AddTwoInts,所以它会生成AddTwoIntsRequest对象(您可以自由传递)。返回值是AddTwoIntsResponse对象。如果调用失败,可能会抛出rospy.ServiceException
-
15.3 构建节点
# 回到工作空间
cd ~/catkin_ws
catkin_make --pkg beginner_tutorials
-
15.4 运行节点
roscore # 启动master
rosrun beginner_tutorials add_two_ints_server.py # 运行服务器
rosrun beginner_tutorials add_two_ints_client.py 1 3 # 运行客户端
16. 录制和播放数据
-
16.1 记录所有主题数据
roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
rostopic list -v # 检查系统中当前正在运行的每个主题的完整详细信息
mkdir ~/bagfiles # 创建一个临时目录bagfiles来记录数据
cd ~/bagfiles
rosbag record -a # 使用选项-a 运行rosbag记录,记录了所有节点发布的所有消息。
rosbag info <your bagfile> # 检查bag文件的内容而不回放
rosbag play <your bagfile> # 重播bag文件以重现正在运行的系统中的行为
rosbag play -r 2 <your bagfile> # r选项,它允许您按指定因子更改发布速率(发出的键盘命令速度提高了两倍)
-
16.2 记录感兴趣的主题数据
roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
rosbag record -O subset /turtle1/cmd_vel /turtle1/pose # 只记录/turtle1/cmd_vel和/turtle1/pose这两个主题数据到subset.bag文件中
rosbag info subset.bag # 检查包subset.bag 文件的内容
rosbag play subset.bag # 重播subset.bag文件以重现行为
-
16.3 rosbag记录/播放的局限性
乌龟的路径可能没有完全映射到原始键盘输入 - 粗糙的形状应该是相同的,但乌龟可能没有完全跟踪相同的路径。这样做的原因是turtlesim跟踪的路径对系统中的时间的微小变化非常敏感,并且rosbag在能够根据rosrecord记录和处理消息时精确复制正在运行的系统的行为的能力受到限制。 ,以及使用rosplay时生成和处理消息的时间。对于像turtlesim这样的节点,当处理命令消息时微小的时序变化可以巧妙地改变行为,用户不应该期望完全模仿行为。
17. roswtf
-
17.1 roswtf简单介绍
可以检查您的ROS设置,比如环境变量,并查找配置问题。如果您有在线ROS系统,它会查看它并检查是否存在任何潜在问题。
ps -ef | grep -i rosmaster # 该命令用来查看roscore是否在运行,如果存在某个路径中存在roscore,则表示roscore正在运行
# 下面两行命令用来检查安装,如果安装正确运行,则输出no error啥的
roscd rosmaster
roswtf
# 下面进行在线尝试,需要master运行,发现结果有错误
roscore
roscd
roswtf
-
17.2 roswtf工具
roswtf # 检查当前包或堆栈的问题
roswtf yourfile.launch # 检查给定的启动文件是否存在任何潜在问题,例如尚未正确构建的软件包
1. 检查文件系统问题:查看您的环境变量,包配置,堆栈配置等。它还可以接收roslaunch文件并尝试在其中查找任何潜在的配置问题,例如尚未正确构建的软件包。
2. 检查在线问题:检查您当前图表的状态,并尝试查找任何潜在问题。这些问题可能是无响应的节点,节点之间缺少连接,或者是roslaunch潜在机器配置问题