前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ROS Beginner(长文预警!

ROS Beginner(长文预警!

作者头像
From Zero
发布2021-12-07 17:55:02
8260
发布2021-12-07 17:55:02
举报
文章被收录于专栏:C语言C语言

ROS Beginner

文章目录

引言

本文是适用于ROS初学者的笔记,包含从基本概念到尝试创建服务端和客户端的内容。

参考网站:http://wiki.ros.org/cn/ROS/Tutorials

1.创建一个catkin工作空间

代码语言:javascript
复制
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)必须有自己的目录(意味着在同一个目录下不能有嵌套的或者多个软件包存在?

代码语言:javascript
复制
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进行构建

代码语言:javascript
复制
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话题

首先打开小乌龟和它的键盘控制:

代码语言:javascript
复制
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列出当前已被订阅和发布的所有话题 消息:为了使发布者和订阅者进行通信,必须接受和发送相同类型的消息,即话题的类型是由发布在它上面消息的类型决定的

代码语言:javascript
复制
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

代码语言:javascript
复制
rosrun rqt_console rqt_console
rosrun rqt_logger_level rqt_logger_level
rosrun turtlesim turtlesim_node(启动turulesim

日志记录级别: Fatal (致命) Error (错误) Warn (警告) Info (信息) Debug (调试) 使用roslaunch:

代码语言:javascript
复制
roscd beginner_tutorials(切换道之前构建的软件包目录下
mkdir launch
cd launch

p.s.存放launch文件的目录不一定非要命名为launch,事实上都不用非得放在目录中,roslaunch命令会自动查找经过的包并检测可用的启动文件。然而,这种推荐的标准做法被认为是最佳实践 创建一个名为turtlemimic.launch的launch文件并复制粘贴以下内容:

代码语言:javascript
复制
<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目录下。

代码语言:javascript
复制
roscd beginner_tutorials
mkdir msg
echo "int64 num" > msg/Num.msg(创建了一个只有一行的msg文件)

打开package.xml, 确保它包含以下两行且没有被注释

代码语言:javascript
复制
<build_depend>message_generation</build_depend>(构建需要)
<exec_depend>message_runtime</exec_depend>(运行需要)

在CMakeLists.txt文件中,为已经存在里面的find_package调用添加message_generation依赖项:(message_generationmsgsrv都适用)

代码语言:javascript
复制
find_package(catkin REQUIRED COMPONENTS
   roscpp
   rospy
   std_msgs
   message_generation
)

注意:有时即使没有使用全部依赖项调用find_package,项目也可以构建。这是因为catkin把你所有的项目整合在了一起,因此如果之前的项目调用了find_package,你的依赖关系也被配置成了一样的值。但是,忘记调用意味着你的项目在单独构建时很容易崩溃。 确保导出消息时的依赖关系:

代码语言:javascript
复制
catkin_package(
  ...
  CATKIN_DEPENDS message_runtime ...
  ...)

找到代码块:

代码语言:javascript
复制
# add_message_files(

#   FILES

#   Message1.msg

#   Message2.msg

# )

取消注释,并且改成这样:

代码语言:javascript
复制
add_message_files(
  FILES
  Num.msg
)

取消下面几行注释:(确保CMake知道何时需要重新配置项目)

代码语言:javascript
复制
# generate_messages(

#   DEPENDENCIES

#   std_msgs

# )

以上完成了创建消息,现在通过rosmsg show命令看看ROS能否识别它 rosmsg show [message type] 即:rosmsg show beginner_tutorials/Num(包名可省) 创建src

代码语言:javascript
复制
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依赖项 将

代码语言:javascript
复制
# add_service_files(

#   FILES

#   Service1.srv

#   Service2.srv

# )

变成:

代码语言:javascript
复制
add_service_files(
  FILES
  AddTwoInts.srv
)

看能否识别它:rossrv show <service type> 即:rossrv show beginner_tutorials/AddTwoInts(也可以不指定包命) 重新编译一下软件包:

代码语言:javascript
复制
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节点(发布者):不断广播消息

代码语言:javascript
复制
roscd beginner_tutorials
mkdir src

创建talker.cpp文件,文本如下:

代码语言:javascript
复制
#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文件,写入以下内容

代码语言:javascript
复制
#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文件底部加上这些内容:

代码语言:javascript
复制
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)

最终长这样:

代码语言:javascript
复制
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)

进行编译:

代码语言:javascript
复制
cd ~/catkin_ws
catkin_make

12.测试发布者和订阅者

运行发布者:

代码语言:javascript
复制
roscore
cd ~/catkin_ws
source ./devel/setup.bash(请确保调用catkin_make后已经source过工作空间的setup.*sh文件)
rosrun beginner_tutorials talker      # (C++)

运行订阅者:

代码语言:javascript
复制
 rosrun beginner_tutorials listener     # (C++)

13.编写简单的服务和客户端

编写服务节点: 创建简单的服务(Service)节点add_two_ints_server,该节点将接收两个整数,并返回它们的和。

代码语言:javascript
复制
roscd beginner_tutorials

在beginner_tutorials包中创建src/add_two_ints_server.cpp文件并粘贴以下内容进去:

代码语言:javascript
复制
#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文件并粘贴以下内容进去:

代码语言:javascript
复制
#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(添加代码):

代码语言:javascript
复制
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)

编译:

代码语言:javascript
复制
cd ~/catkin_ws
catkin_make

14.检验简单的服务与客户端

运行服务:

代码语言:javascript
复制
rosrun beginner_tutorials add_two_ints_server

运行客户端:

代码语言:javascript
复制
rosrun beginner_tutorials add_two_ints_client 1 3

15.录制和回放数据

将正在运行的ROS系统中的数据记录到一个bag文件中,然后通过回放这些数据来来重现相似的运行过程 录制数据:

代码语言:javascript
复制
roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key(又打开了小乌龟)
mkdir ~/bagfiles
cd ~/bagfiles
rosbag record -a(-a表明所有发布的话题都积累在一个bag文件中)

随便控制小乌龟移动一会儿 回放:

代码语言:javascript
复制
rosbag info <your bagfile>(查看bag中的记录,bag名字似乎和时间有关)
rosbag play <your bagfile>(先结束key的进程)
rosbag play -r 2 <your bagfile>(-r可以改变消息发布的频率)(-s可以指定开始时间,使不从头开始)

录制数据子集:

代码语言:javascript
复制
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,这样做可以同时输出执行每个命令花费的时间 立即回放消息并在多个终端中查看输出:

代码语言:javascript
复制
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入门

安装检查:

代码语言:javascript
复制
roswtf可以检查系统并尝试发现问题(首先确保ros没有运行)
roscd rosmaster
roswtf

在线检查:

代码语言:javascript
复制
roscd
roswtf

错误:

代码语言:javascript
复制
roscd
ROS_PACKAGE_PATH=bad:$ROS_PACKAGE_PATH roswtf

最后:在vscode中配置ROS环境:

在workspace中添加文件(devel里面):catkin_make -DCMAKE_EXPORT_COMPILE_COMMANDS=Yes 更改c_cpp_propertries.json为:

代码语言:javascript
复制
{
  "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等头文件啦!

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-07-23 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ROS Beginner
    • 文章目录
      • 引言
        • 1.创建一个catkin工作空间
          • 2.文件系统
            • 3.创建软件包
              • 4.构建ROS软件包
                • 5.理解ROS节点
                  • 6.理解ROS话题
                    • 7.理解ROS服务和参数
                      • 8.rqt_console和roslaunch
                        • 9.使用rosed
                          • 10.创建消息和服务
                            • 11.编写简单的发布者和订阅者
                              • 12.测试发布者和订阅者
                                • 13.编写简单的服务和客户端
                                  • 14.检验简单的服务与客户端
                                    • 15.录制和回放数据
                                      • 16.从bag文件中读取消息
                                        • 17.roswtf入门
                                          • 最后:在vscode中配置ROS环境:
                                          领券
                                          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档