欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

ROS入门篇

最编程 2024-03-08 20:48:54
...

ROS安装

我们这里使用的是Ubuntu 20.04系统来进行安装。

  • 添加ROS软件源
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-key adv --keyserver 'hkp://keyserver.ubuntu.com:80' --recv-key C1CF6E31E6BADE8868B172B4F42ED6FBAB17C654
  • 安装ROS
sudo apt-get update
sudo apt install ros-noetic-desktop-full

如果是Ubuntu 18.04,可以使用

sudo apt install ros-melodic-desktop-full

进行安装,此次安装的时间会比较长。

添加环境变量

sudo vim /etc/profile

内容如下

source /opt/ros/noetic/setup.bash
export ROS_PACKAGE_PATH=${ROS_PACKAGE_PATH}:~/ros_ws/src

如果是Ubuntu 18.04,则为

source /opt/ros/melodic/setup.bash
export ROS_PACKAGE_PATH=${ROS_PACKAGE_PATH}:~/ros_ws/src

执行

source /etc/profile
sudo apt-get install python3-rosdep2
sudo vim /etc/hosts

如果是Ubuntu 18.04,则为

source /etc/profile
sudo apt-get install python3-rosdep
sudo vim /etc/hosts

添加内容

199.232.28.133 raw.githubusercontent.com

执行

sudo rm /etc/ros/rosdep/sources.list.d/20-default.list
sudo rosdep init
rosdep update

如果是Ubuntu 18.04,则为

sudo rosdep init
rosdep update
  • 安装rosinstall
sudo apt install python3-rosinstall python3-rosinstall-generator python3-wstool
sudo apt-get install ros-noetic-roslaunch

如果是Ubuntu 18.04,则为

sudo apt install python3-catkin-pkg
sudo apt install python3-rosinstall python3-rosinstall-generator python3-wstool
sudo apt-get install ros-melodic-roslaunch

安装完成后输入

roscore

输出

... logging to /home/guanjian/.ros/log/cb18d28e-35fe-11ee-b678-03d041231f43/roslaunch-guanjian-X99-38074.log
Checking log directory for disk usage. This may take a while.
Press Ctrl-C to interrupt
Done checking log file disk usage. Usage is <1GB.

started roslaunch server http://guanjian-X99:34311/
ros_comm version 1.16.0


SUMMARY
========

PARAMETERS
 * /rosdistro: noetic
 * /rosversion: 1.16.0

NODES

auto-starting new master
process[master]: started with pid [38083]
ROS_MASTER_URI=http://guanjian-X99:11311/

setting /run_id to cb18d28e-35fe-11ee-b678-03d041231f43
process[rosout-1]: started with pid [38093]
started core service [/rosout]

表示安装成功

  • 测试
sudo apt install ros-noetic-rosbash
sudo apt install ros-noetic-catkin
sudo apt-get install ros-noetic-turtlesim

如果是Ubuntu 18.04,则为

sudo apt install ros-melodic-rosbash
sudo apt install ros-melodic-catkin
sudo apt-get install ros-melodic-turtlesim

执行

rosrun turtlesim turtlesim_node

会出现一个小海龟的画面

执行

rosrun turtlesim turtle_teleop_key

这样就可以使用键盘的方向键来控制这个小海龟了。

  • 安装rqt工具

ros rqt工具是使用qt开发的一系列直观可视化工具

sudo apt-get install ros-noetic-rqt
sudo apt-get install ros-noetic-rqt-graph
sudo apt-get install ros-noetic-rqt-common-plugins

如果是Ubuntu 18.04,则为

sudo apt-get install ros-melodic-rqt
sudo apt-get install ros-melodic-rqt-graph
sudo apt-get install ros-melodic-rqt-common-plugins
  • 安装图像处理以及传输工具
sudo apt-get install ros-noetic-cv-bridge
sudo apt-get install ros-noetic-image-transport

如果是Ubuntu 18.04,则为

sudo apt-get install ros-melodic-cv-bridge
sudo apt-get install ros-melodic-image-transport

配置jetson nano opencv的目录(这里只针对18.04)

cd /opt/ros/melodic/share/cv_bridge/cmake/
sudo vim cv_bridgeConfig.cmake

将94行和96行的两个opencv改成opencv4

ROS的核心概念

通讯机制

ROS是一个松耦合的分布式软件框架。上图中有很多的Node(节点),每个节点是在机器人系统中完成一个具体功能的进程,比方说有的Node是完成图像识别,有的Node是完成一个图像的驱动,它们之间会有一系列图像的传输。节点与节点之间位置也不是固定的,比如上图中有两台电脑,一个A,一个B,有些节点可以在A当中,有些节点可以在B当中,它们之间可以通过一系列的传输方式来完成通讯。每个节点的编程语言也不是固定的,可以使用Python,也可以使用C++。

上图中有三个节点,分别为摄像头节点、图像处理节点、图像显示节点。

  • 节点(Node)——执行单元
  1. 执行具体任务的进程,独立运行的可执行文件;
  2. 不同节点可使用不同的编程语言,可分布式运行在不同的主机;
  3. 节点在系统中的名称必须是唯一的。
  • 节点管理器(ROS Master)——控制中心
  1. 为节点提供命名和注册服务;
  2. 跟踪和记录话题/服务通信,辅助节点相互查找,建立连接;
  3. 提供参数服务器,节点使用此服务器存储和检索运行时参数。

节点之间的通讯有两种核心的通讯方式——话题、服务

话题

  • 话题(Topic)——异步通信机制
  1. 节点间用来传输数据的重要总线,它是一个数据管道;
  2. 使用发布/订阅模型,数据由发布者传输到订阅者,同一个话题的订阅者或发布者可以不唯一。在上图中有一个发布节点(Publisher,比如一个摄像头节点,它会发布图像数据给别人做处理)以及两个订阅节点(Subscriber,需要处理摄像头发布的图像数据的节点,需要去订阅摄像头节点)。数据流向是单向的,从发布者到订阅者。
  • 消息(Message)——话题数据
  1. 具有一定类型和数据结构,包括ROS提供的标准类型和用户自定义类型;
  2. 使用编程语言无关的.msg文件定义,编译过程中生成对应的代码文件。

服务

  • 服务(Service)——同步通信机制
  1. 使用客户端/服务器(C/S)模型,客户端发送请求数据,服务器完成处理后返回应答数据,它是一种双向通讯;
  2. 使用编程语言无关的.srv文件定义请求和应答数据结构,编译过程中生成对应的代码文件。

话题与服务的区别

  话题 服务
同步性 异步 同步
通信模型 发布/订阅 服务器/客户端
底层协议 ROSTCP/ROSUDP ROSTCP/ROSUDP
反馈机制
缓冲区
实时性
节点关系 多对多 一对多(一个server)
适用场景 数据传输 逻辑处理

参数

它是在ROS Master里面维护一个参数服务器,所有节点都可以通过网络访问。

  • 参数(Parameter)——全局共享字典
  1. 可通过网络访问的共享、多变量字典
  2. 节点使用此服务器来存储和检索运行时的参数
  3. 适合存储静态、非二进制的配置参数,不适合存储动态配置的数据。

文件系统

  • 功能包(Package)

ROS软件中的基本单元,包含节点源码、配置文件、数据定义等

  • 功能包清单(Package manifest)

记录功能包的基本信息,包含作者信息、许可信息、依赖选项、编译标志等

  • 元功能包(Meta Packages)

组织多个用于同一目的的功能包

ROS命令行工具

启动ROS Master

roscore

运行一个节点

rosrun turtlesim turtlesim_node

rosrun一般会有两个参数,第一个参数turtlesim是功能包名,第二个参数turtlesim_node是节点,它是一个仿真器节点,就是之前看到的小海龟。

运行另一个节点

rosrun turtlesim turtle_teleop_key

该节点是用键盘上下左右键来控制小海龟移动的节点。

  • 分析工具
rqt_graph

这是一个显示系统计算图的工具,ROS的核心通讯机制就是一个计算图,通过图可以很快的了解整个系统的全貌。

该界面列出了当前系统中的节点(椭圆部分),这里为2个节点,一个turtlesim小海龟仿真器节点,一个teleop_turtle键盘控制节点。两个节点存在着数据通讯,它们中间存在着一个话题/turtle1/cmd_vel,专门用来通讯的。

  • 无图形界面的节点查看命令
rosnode

此时会返回它后面的参数以及参数的含义

rosnode is a command-line tool for printing information about ROS Nodes.

Commands:
	rosnode ping	test connectivity to node
	rosnode list	list active nodes
	rosnode info	print information about node
	rosnode machine	list nodes running on a particular machine or list machines
	rosnode kill	kill a running node
	rosnode cleanup	purge registration information of unreachable nodes

Type rosnode <command> -h for more detailed usage, e.g. 'rosnode ping -h'

此时我们使用

rosnode list

会返回所有活动的节点

/rosout
/teleop_turtle
/turtlesim

其中的后面两个节点是我们启动的小海龟仿真器以及键盘控制器,而/rosout是ROS默认的话题,它是采集所有节点的日志信息用来提交给后面的界面做显示的,一般不用关心。

查看某一个节点具体的信息

rosnode info /turtlesim

此时会返回

Node [/turtlesim]
Publications: 
 * /rosout [rosgraph_msgs/Log]
 * /turtle1/color_sensor [turtlesim/Color]
 * /turtle1/pose [turtlesim/Pose]

Subscriptions: 
 * /turtle1/cmd_vel [geometry_msgs/Twist]

Services: 
 * /clear
 * /kill
 * /reset
 * /spawn
 * /turtle1/set_pen
 * /turtle1/teleport_absolute
 * /turtle1/teleport_relative
 * /turtlesim/get_loggers
 * /turtlesim/set_logger_level


contacting node http://guanjian-X99:46261/ ...
Pid: 4769
Connections:
 * topic: /rosout
    * to: /rosout
    * direction: outbound (57873 - 127.0.0.1:56794) [27]
    * transport: TCPROS
 * topic: /turtle1/cmd_vel
    * to: /teleop_turtle (http://guanjian-X99:40209/)
    * direction: inbound (46338 - guanjian-X99:43423) [29]
    * transport: TCPROS

其中Publications是该节点发布的信息,比如说/turtle1/pose [turtlesim/Pose]表示小海龟的位置。Subscriptions是该节点订阅的信息,这里它订阅了一个/turtle1/cmd_vel [geometry_msgs/Twist],表示通过键盘指令发过去的数据,通过它订阅的数据控制小海龟的移动。Services是该节点发布的服务,通过其中的服务来完成一些配置。最下面的是一些Id号以及底层通讯机制。

  • 话题工具
rostopic

此时会返回它后面的参数以及参数的含义

rostopic is a command-line tool for printing information about ROS Topics.

Commands:
	rostopic bw	display bandwidth used by topic
	rostopic delay	display delay of topic from timestamp in header
	rostopic echo	print messages to screen
	rostopic find	find topics by type
	rostopic hz	display publishing rate of topic    
	rostopic info	print information about active topic
	rostopic list	list active topics
	rostopic pub	publish data to topic
	rostopic type	print topic or field type

Type rostopic <command> -h for more detailed usage, e.g. 'rostopic echo -h'

查看系统当前所有的话题列表

rostopic list

此时返回

/rosout
/rosout_agg
/turtle1/cmd_vel
/turtle1/color_sensor
/turtle1/pose

其中/turtle1/cmd_vel就为为小海龟发布控制指令的话题,键盘控制节点和小海龟控制节点就是通过该话题来通讯的。

发布指令让海龟运动

rostopic pub /turtle1/cmd_vel geometry_msgs/Twist '[1.0,0.0,0.0]' '[0.0,0.0,0.0]'

这里的'[1.0,0.0,0.0]'指的是线速度linear,'[0.0,0.0,0.0]'指的是角速度angular。geometry_msgs/Twist指的是消息的数据结构。

该指令会让小海龟动起来,但是动了一小段之后会停下来,这是因为pub指令只会发布一次,为了让海龟一直动,需要给它一个循环

rostopic pub -r 10 /turtle1/cmd_vel geometry_msgs/Twist '[1.0,0.0,0.0]' '[0.0,0.0,0.0]'

这里-r表示频率,以多少的频率去发布消息内容,-r 10表示10Hz,一秒钟发布10次。

查看消息数据结构的内容

rosmsg show geometry_msgs/Twist

这里查看的是geometry_msgs/Twist的数据结构,返回

geometry_msgs/Vector3 linear
  float64 x
  float64 y
  float64 z
geometry_msgs/Vector3 angular
  float64 x
  float64 y
  float64 z
  • 服务工具
rosservice

此时会返回它后面的参数以及参数的含义

Commands:
	rosservice args	print service arguments
	rosservice call	call the service with the provided args
	rosservice find	find services by service type
	rosservice info	print information about service
	rosservice list	list active services
	rosservice type	print service type
	rosservice uri	print service ROSRPC uri

Type rosservice <command> -h for more detailed usage, e.g. 'rosservice call -h'

查看当前的所有服务

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 call /spawn "x: 2.0
y: 2.0
theta: 0.0
name: 'turtle2'" 
  • 话题记录
rosbag record -a -O cmd_record

这里的record表示记录的意思,-a为保存所有的记录,-O表示将数据保存成压缩包,cmd_record为一个保存的名字

返回

[ INFO] [1691623907.802537745]: Recording to 'cmd_record.bag'.
[ INFO] [1691623907.803795811]: Subscribing to /rosout_agg
[ INFO] [1691623907.806439843]: Subscribing to /rosout
[ INFO] [1691623907.809232127]: Subscribing to /turtle1/pose
[ INFO] [1691623907.811861515]: Subscribing to /turtle1/color_sensor
[ INFO] [1691623907.815194941]: Subscribing to /turtle1/cmd_vel

现在我们可以来操作小海龟做运动,运动结束之后在rosbag命令行窗口按下Ctrl-C,这样记录结果就保存下来了,保存的路径就是/home/user下,user为你自己的用户名,保存的文件名为cmd_record.bag。

  • 话题复现

话题复现,假设我们有一个无人机需要调试,但是不可能每次都让飞机飞起来去调试,我们会做一次飞行,使用rosbag record将所有的数据都保存下来,回到实验室中将数据复现出来再去做实验。现在我们来复现刚才小海龟移动的数据。

先将之前的终端全部关闭,重新打开两个终端,启动ROS Master以及小海龟仿真器节点。

rosbag play cmd_record.bag

这样我们就可以看到小海龟走的路径跟记录时候是一样的了。

创建工作空间与功能包
  • 工作空间(workspace)是一个存放工程开发相关文件的文件夹,其下可分为
  1. src:代码空间(Source Space),放置所有功能包的代码,配置文件
  2. build:编译空间(Build Space),放置在编译过程中所产生的中间文件,无需关注
  3. devel:开发空间(Development Space),放置编译生成的可执行文件,库和脚本
  4. install:安装空间(Install Space),放置安装位置,内容与开发空间有一定重复

指令集

  • 创建工作空间(我这里是在Documents下)
cd Documents/
mkdir -p catkin_ws/src
cd catkin_ws/src/
catkin_init_workspace

此时在工作空间中没有任何的代码,但依然可以编译。

  • 编译工作空间
cd ..
catkin_make install

此时会产生另外三个文件夹——build、devel、install

  • 设置环境变量
source devel/setup.bash
  • 创建功能包
cd src
catkin_create_pkg test_pkg std_msgs rospy roscpp

这里test_pkg为功能包的名称,后面的项都是ROS的依赖,std_msgs为ROS定义的标准的消息结构,rospy为ROS的Python调用,roscpp为ROS的C++调用。

在同一个工作空间下,不允许存在同名功能包;不同工作空间下,允许存在同名功能包。

在test_pkg下会有

CMakeLists.txt  include  package.xml  src

其中include和src是文件夹,include是放置C++头文件的,src是放置.cpp代码的。

  • 编译功能包

回到catkin_ws文件夹

cd ..
catkin_make
  • 设置环境变量
source devel/setup.bash
  •  查看工作空间环境变量
echo $ROS_PACKAGE_PATH

此时返回

/home/user/Documents/catkin_ws/src:/opt/ros/noetic/share

它表示ROS能够找到我们的功能包的位置(user为你自己的用户名)

  • 功能包中的两个重要文件

在test_pkg中会有两个重要文件——CMakeLists.txt和package.xml,我们来看一下package.xml的内容

<?xml version="1.0"?>
<package format="2">
  <name>test_pkg</name>
  <version>0.0.0</version>
  <description>The test_pkg package</description>

  <!-- One maintainer tag required, multiple allowed, one person per tag -->
  <!-- Example:  -->
  <!-- <maintainer email="jane.doe@example.com">Jane Doe</maintainer> -->
  <maintainer email="guanjian@todo.todo">guanjian</maintainer>


  <!-- One license tag required, multiple allowed, one license per tag -->
  <!-- Commonly used license strings: -->
  <!--   BSD, MIT, Boost Software License, GPLv2, GPLv3, LGPLv2.1, LGPLv3 -->
  <license>TODO</license>


  <!-- Url tags are optional, but multiple are allowed, one per tag -->
  <!-- Optional attribute type can be: website, bugtracker, or repository -->
  <!-- Example: -->
  <!-- <url type="website">http://wiki.ros.org/test_pkg</url> -->


  <!-- Author tags are optional, multiple are allowed, one per tag -->
  <!-- Authors do not have to be maintainers, but could be -->
  <!-- Example: -->
  <!-- <author email="jane.doe@example.com">Jane Doe</author> -->


  <!-- The *depend tags are used to specify dependencies -->
  <!-- Dependencies can be catkin packages or system dependencies -->
  <!-- Examples: -->
  <!-- Use depend as a shortcut for packages that are both build and exec dependencies -->
  <!--   <depend>roscpp</depend> -->
  <!--   Note that this is equivalent to the following: -->
  <!--   <build_depend>roscpp</build_depend> -->
  <!--   <exec_depend>roscpp</exec_depend> -->
  <!-- Use build_depend for packages you need at compile time: -->
  <!--   <build_depend>message_generation</build_depend> -->
  <!-- Use build_export_depend for packages you need in order to build against this package: -->
  <!--   <build_export_depend>message_generation</build_export_depend> -->
  <!-- Use buildtool_depend for build tool packages: -->
  <!--   <buildtool_depend>catkin</buildtool_depend> -->
  <!-- Use exec_depend for packages you need at runtime: -->
  <!--   <exec_depend>message_runtime</exec_depend> -->
  <!-- Use test_depend for packages you need only for testing: -->
  <!--   <test_depend>gtest</test_depend> -->
  <!-- Use doc_depend for packages you need only for building documentation: -->
  <!--   <doc_depend>doxygen</doc_depend> -->
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_export_depend>roscpp</build_export_depend>
  <build_export_depend>rospy</build_export_depend>
  <build_export_depend>std_msgs</build_export_depend>
  <exec_depend>roscpp</exec_depend>
  <exec_depend>rospy</exec_depend>
  <exec_depend>std_msgs</exec_depend>


  <!-- The export tag contains other, unspecified, tags -->
  <export>
    <!-- Other tools can request additional information be placed here -->

  </export>
</package>

它会包含一些基本信息:功能包的名称,版本号,描述,作者以及作者的email地址,还有下面的功能包的ROS依赖。之后我们也可以手动在这里添加依赖项。

然后是CMakeLists.txt中的内容

cmake_minimum_required(VERSION 3.0.2)
project(test_pkg)

## Compile as C++11, supported in ROS Kinetic and newer
# add_compile_options(-std=c++11)

## Find catkin macros and libraries
## if COMPONENTS list like find_package(catkin REQUIRED COMPONENTS xyz)
## is used, also find other catkin packages
find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
)

## System dependencies are found with CMake's conventions
# find_package(Boost REQUIRED COMPONENTS system)


## Uncomment this if the package has a setup.py. This macro ensures
## modules and global scripts declared therein get installed
## See http://ros.org/doc/api/catkin/html/user_guide/setup_dot_py.html
# catkin_python_setup()

################################################
## Declare ROS messages, services and actions ##
################################################

## To declare and build messages, services or actions from within this
## package, follow these steps:
## * Let MSG_DEP_SET be the set of packages whose message types you use in
##   your messages/services/actions (e.g. std_msgs, actionlib_msgs, ...).
## * In the file package.xml:
##   * add a build_depend tag for "message_generation"
##   * add a build_depend and a exec_depend tag for each package in MSG_DEP_SET
##   * If MSG_DEP_SET isn't empty the following dependency has been pulled in
##     but can be declared for certainty nonetheless:
##     * add a exec_depend tag for "message_runtime"
## * In this file (CMakeLists.txt):
##   * add "message_generation" and every package in MSG_DEP_SET to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * add "message_runtime" and every package in MSG_DEP_SET to
##     catkin_package(CATKIN_DEPENDS ...)
##   * uncomment the add_*_files sections below as needed
##     and list every .msg/.srv/.action file to be processed
##   * uncomment the generate_messages entry below
##   * add every package in MSG_DEP_SET to generate_messages(DEPENDENCIES ...)

## Generate messages in the 'msg' folder
# add_message_files(
#   FILES
#   Message1.msg
#   Message2.msg
# )

## Generate services in the 'srv' folder
# add_service_files(
#   FILES
#   Service1.srv
#   Service2.srv
# )

## Generate actions in the 'action' folder
# add_action_files(
#   FILES
#   Action1.action
#   Action2.action
# )

## Generate added messages and services with any dependencies listed here
# generate_messages(
#   DEPENDENCIES
#   std_msgs
# )

################################################
## Declare ROS dynamic reconfigure parameters ##
################################################

## To declare and build dynamic reconfigure parameters within this
## package, follow these steps:
## * In the file package.xml:
##   * add a build_depend and a exec_depend tag for "dynamic_reconfigure"
## * In this file (CMakeLists.txt):
##   * add "dynamic_reconfigure" to
##     find_package(catkin REQUIRED COMPONENTS ...)
##   * uncomment the "generate_dynamic_reconfigure_options" section below
##     and list every .cfg file to be processed

## Generate dynamic reconfigure parameters in the 'cfg' folder
# generate_dynamic_reconfigure_options(
#   cfg/DynReconf1.cfg
#   cfg/DynReconf2.cfg
# )

###################################
## catkin specific configuration ##
###################################
## The catkin_package macro generates cmake config files for your package
## Declare things to be passed to dependent projects
## INCLUDE_DIRS: uncomment this if your package contains header files
## LIBRARIES: libraries you create in this project that dependent projects also need
## CATKIN_DEPENDS: catkin_packages dependent projects also need
## DEPENDS: system dependencies of this project that dependent projects also need
catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES test_pkg
#  CATKIN_DEPENDS roscpp rospy std_msgs
#  DEPENDS system_lib
)

###########
## Build ##
###########

## Specify additional locations of header files
## Your package locations should be listed before other locations
include_directories(
# include
  ${catkin_INCLUDE_DIRS}
)

## Declare a C++ library
# add_library(${PROJECT_NAME}
#   src/${PROJECT_NAME}/test_pkg.cpp
# )

## Add cmake target dependencies of the library
## as an example, code may need to be generated before libraries
## either from message generation or dynamic reconfigure
# add_dependencies(${PROJECT_NAME} ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Declare a C++ executable
## With catkin_make all packages are built within a single CMake context
## The recommended prefix ensures that target names across packages don't collide
# add_executable(${PROJECT_NAME}_node src/test_pkg_node.cpp)

## Rename C++ executable without prefix
## The above recommended prefix causes long target names, the following renames the
## target back to the shorter version for ease of user use
## e.g. "rosrun someones_pkg node" instead of "rosrun someones_pkg someones_pkg_node"
# set_target_properties(${PROJECT_NAME}_node PROPERTIES OUTPUT_NAME node PREFIX "")

## Add cmake target dependencies of the executable
## same as for the library above
# add_dependencies(${PROJECT_NAME}_node ${${PROJECT_NAME}_EXPORTED_TARGETS} ${catkin_EXPORTED_TARGETS})

## Specify libraries to link a library or executable target against
# target_link_libraries(${PROJECT_NAME}_node
#   ${catkin_LIBRARIES}
# )

#############
## Install ##
#############

# all install targets should use catkin DESTINATION variables
# See http://ros.org/doc/api/catkin/html/adv_user_guide/variables.html

## Mark executable scripts (Python etc.) for installation
## in contrast to setup.py, you can choose the destination
# catkin_install_python(PROGRAMS
#   scripts/my_python_script
#   DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark executables for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_executables.html
# install(TARGETS ${PROJECT_NAME}_node
#   RUNTIME DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
# )

## Mark libraries for installation
## See http://docs.ros.org/melodic/api/catkin/html/howto/format1/building_libraries.html
# install(TARGETS ${PROJECT_NAME}
#   ARCHIVE DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   LIBRARY DESTINATION ${CATKIN_PACKAGE_LIB_DESTINATION}
#   RUNTIME DESTINATION ${CATKIN_GLOBAL_BIN_DESTINATION}
# )

## Mark cpp header files for installation
# install(DIRECTORY include/${PROJECT_NAME}/
#   DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
#   FILES_MATCHING PATTERN "*.h"
#   PATTERN ".svn" EXCLUDE
# )

## Mark other files for installation (e.g. launch and bag files, etc.)
# install(FILES
#   # myfile1
#   # myfile2
#   DESTINATION ${CATKIN_PACKAGE_SHARE_DESTINATION}
# )

#############
## Testing ##
#############

## Add gtest based cpp test target and link libraries
# catkin_add_gtest(${PROJECT_NAME}-test test/test_test_pkg.cpp)
# if(TARGET ${PROJECT_NAME}-test)
#   target_link_libraries(${PROJECT_NAME}-test ${PROJECT_NAME})
# endif()

## Add folders to be run by python nosetests
# catkin_add_nosetests(test)

有关CMakeLists.txt的介绍可以参考C++基础整理 中的CMake 的使用

发布者Publisher编程实现

C++实现

进入工作空间

cd Documents/catkin_ws/src

创建一个新的话题功能包

catkin_create_pkg learning_topic roscpp rospy std_msgs geometry_msgs turtlesim

进入到该功能包的src目录

cd learning_topic/src

创建我们需要的C++文件

vim velocity_publisher.cpp

内容如下

/*
 *  该程序将发布turtle1/cmd_vel话题,消息类型geometry_msgs::Twist
*/

#include <ros/ros.h>
#include <geometry_msgs/Twist.h>

using namespace ros;

int main(int argc, char **argv) {
    //ROS节点初始化
    init(argc, argv, "velocity_publisher");
    //创建节点句柄
    NodeHandle n;
    //创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10
    Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);
    //设置循环的频率
    Rate loop_rate(10);

    int count = 0;

    while(ok()) {
        //初始化geometry_msgs::Twist类型的消息
	geometry_msgs::Twist vel_msg;
        //设置小海龟的线动量
	vel_msg.linear.x = 0.5;
        //设置小海龟的角动量
	vel_msg.angular.z = 0.2;
        //发布消息
	turtle_vel_pub.publish(vel_msg);
	ROS_INFO("Publish turtle velocity command[%0.2f m/s, %0.2f rad/s]", vel_msg.linear.x, vel_msg.angular.z);
        //按照循环频率延时
	loop_rate.sleep();
    }

    return 0;
}

关于队列Publisher turtle_vel_pub = n.advertise<geometry_msgs::Twist>("/turtle1/cmd_vel", 10);以上图为例,如果Publisher发布的非常快,比如10000次/秒,底层的以太网可能发不出去,这里会有一个队列,先将10000次消息存到队列中,再根据实际发送的能力从队列(缓存)中去发送数据。这里有一个问题就是队列满了怎么办,我们这里设置的是10,如果底层能力太弱,ROS会默认把时间戳最老的数据给抛弃,先入队的数据会抛出,它会永远保存10个数据是最新的数据。此时会发生一些掉数据掉帧的情况。

回到learning_topic文件夹,修改CMakelists.txt。

cd ..
vim CMakelists.txt

添加内容如下

add_executable(velocity_publisher src/velocity_publisher.cpp)
target_link_libraries(velocity_publisher ${catkin_LIBRARIES})

回到catkin_ws文件夹,开始编译

cd ../..
catkin_make

编译完成后设置环境变量

source devel/setup.bash

如果我们不想每次编译后都去设置一次环境变量的话,可以直接修改/etc/profile

sudo vim /etc/profile

添加内容

source /home/user/Documents/catkin_ws/devel/setup.bash

其中user改成你自己的用户名

source /etc/profile

执行该程序,在不同的终端窗口执行

roscore
rosrun turtlesim turtlesim_node
rosrun learning_topic velocity_publisher

这样我们就可以看到小海龟在绕着一个圈运动。

Python实现

进入包文件夹

cd Documents/catkin_ws/src/learning_topic

新建脚本文件夹

mkdir scripts
cd scripts

创建我们需要的 Python文件

vim velocity_publisher.py

内容如下

#!/home/user/anaconda3/envs/py39/bin/python
# -*- coding: utf-8 -*-
# 该程序将发布turtle1/cmd_vel话题,消息类型geometry_msgs::Twist

import rospy
from geometry_msgs.msg import Twist

def velocity_publisher():
    # ROS节点初始化
    rospy.init_node('velocity_publisher', anonymous=True)
    # 创建一个Publisher,发布名为/turtle1/cmd_vel的topic,消息类型为geometry_msgs::Twist,队列长度10
    turtle_vel_pub = rospy.Publisher('/turtle1/cmd_vel', Twist, queue_size=10)
    # 设置循环的频率
    rate = rospy.Rate(10)

    while not rospy.is_shutdown():
        # 初始化geometry_msgs::Twist类型的消息
        vel_msg = Twist()
        # 设置小海龟的线动量
        vel_msg.linear.x = 0.5
        # 设置小海龟的角动量
        vel_msg.angular.z = 0.2
        # 发布消息
        turtle_vel_pub.publish(vel_msg)
        rospy.loginfo("Publish turtle velocity command[%0.2f m/s, %0.2f rad/s]", vel_msg.linear.x, vel_msg.angular.z)
        # 按照循环频率延时
        rate.sleep()

if __name__ == '__main__':

    try:
        velocity_publisher()
    except rospy.ROSInterruptException:
        pass

设置可执行权限

chmod 777 velocity_publisher.py

这里我们需要Python的运行环境,具体可以参考乌班图安装Pytorch、Tensorflow Cuda环境 中的安装 Anaconda。

安装两个需要的包

conda activate py39
pip install pyyaml
pip install rospkg

执行命令(不同终端窗口)

roscore
rosrun turtlesim turtlesim_node
rosrun learning_topic velocity_publisher.py

这样就跟之前使用C++的效果是一样的了。

订阅者Subscriber的编程实现

C++实现

进入learning_topic功能包中

cd Documents/catkin_ws/src/learning_topic/src

创建我们需要的 C++ 文件

vim pose_subscriber.cpp

内容如下

/*
 *  该例程将订阅/turtle1/pose话题,消息类型turtlesim::Pose
*/

#include <ros/ros.h>
#include "turtlesim/Pose.h"

using namespace ros;

//接受到订阅的消息后,会进入消息回调函数
void poseCallback(const turtlesim::Pose::ConstPtr& msg) {
    //将接收的消息打印出来
    ROS_INFO("Turtle pose: x:%0.6f, y:%0.6f", msg->x, msg->y);
}

int main(int argc, char **argv) {
    //初始化ROS节点
    init(argc, argv, "pose_subscriber");
    //创建节点句柄
    NodeHandle n;
    //创建一个Subscriber,订阅名为/turtle1/pose的topic,注册回调函数poseCallback
    Subscriber pose_sub = n.subscribe("/turtle1/pose", 10, poseCallback);
    //循环等待回调函数
    spin();

    return 0;
}

回到 learning_topic 文件夹,修改 CMakelists.txt。

cd ..
vim CMakelists.txt

添加内容如下

add_executable(pose_subscriber src/pose_subscriber.cpp)
target_link_libraries(pose_subscriber ${catkin_LIBRARIES})

回到 catkin_ws 文件夹,开始编译

cd ../..
catkin_make

执行该程序,在不同的终端窗口执行

roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
rosrun learning_topic pose_subscriber

此时我们会看见,我们使用键盘来移动小海龟,pose_subscriber中会实时返回小海龟的坐标

[ INFO] [1691889920.721039829]: Turtle pose: x:7.258318, y:6.718236
[ INFO] [1691889920.736960560]: Turtle pose: x:7.226329, y:6.717391
[ INFO] [1691889920.752859056]: Turtle pose: x:7.194340, y:6.716546
[ INFO] [1691889920.768800015]: Turtle pose: x:7.162352, y:6.715701
[ INFO] [1691889920.784756857]: Turtle pose: x:7.130363, y:6.714856
[ INFO] [1691889920.800640019]: Turtle pose: x:7.098374, y:6.714011
[ INFO] [1691889920.816949030]: Turtle pose: x:7.066385, y:6.713166
[ INFO] [1691889920.832867584]: Turtle pose: x:7.034396, y:6.712321
[ INFO] [1691889920.848822764]: Turtle pose: x:7.002407, y:6.711476
[ INFO] [1691889920.864732401]: Turtle pose: x:6.970418, y:6.710631
[ INFO] [1691889920.880662398]: Turtle pose: x:6.938430, y:6.709786
[ INFO] [1691889920.896609434]: Turtle pose: x:6.906441, y:6.708941
[ INFO] [1691889920.912486155]: Turtle pose: x:6.874452, y:6.708097
[ INFO] [1691889920.928392700]: Turtle pose: x:6.842463, y:6.707252
[ INFO] [1691889920.944258964]: Turtle pose: x:6.810474, y:6.706407
[ INFO] [1691889920.961082117]: Turtle pose: x:6.778485, y:6.705562
[ INFO] [1691889920.976929758]: Turtle pose: x:6.746497, y:6.704717
[ INFO] [1691889920.992809891]: Turtle pose: x:6.714508, y:6.703872
[ INFO] [1691889921.008716570]: Turtle pose: x:6.682519, y:6.703027
[ INFO] [1691889921.024568443]: Turtle pose: x:6.650530, y:6.702182
[ INFO] [1691889921.040418382]: Turtle pose: x:6.618541, y:6.701337
[ INFO] [1691889921.056245800]: Turtle pose: x:6.586552, y:6.700492
[ INFO] [1691889921.072179865]: Turtle pose: x:6.554564, y:6.699647
[ INFO] [1691889921.088081910]: Turtle pose: x:6.522575, y:6.698802
[ INFO] [1691889921.104950901]: Turtle pose: x:6.490586, y:6.697958
[ INFO] [1691889921.120758048]: Turtle pose: x:6.458597, y:6.697113
[ INFO] [1691889921.136643736]: Turtle pose: x:6.426608, y:6.696268
[ INFO] [1691889921.152551417]: Turtle pose: x:6.394619, y:6.695423
[ INFO] [1691889921.168359967]: Turtle pose: x:6.362630, y:6.694578
[ INFO] [1691889921.184151132]: Turtle pose: x:6.330642, y:6.693733
[ INFO] [1691889921.200989473]: Turtle pose: x:6.298653, y:6.692888
[ INFO] [1691889921.216881443]: Turtle pose: x:6.266664, y:6.692043
[ INFO] [1691889921.232730165]: Turtle pose: x:6.234675, y:6.691198
[ INFO] [1691889921.248579035]: Turtle pose: x:6.202686, y:6.690353
[ INFO] [1691889921.264415767]: Turtle pose: x:6.170697, y:6.689508
[ INFO] [1691889921.280488459]: Turtle pose: x:6.138709, y:6.688663
[ INFO] [1691889921.296428648]: Turtle pose: x:6.106719, y:6.687818
[ INFO] [1691889921.312561137]: Turtle pose: x:6.074731, y:6.686973
[ INFO] [1691889921.328623851]: Turtle pose: x:6.042742, y:6.686129
[ INFO] [1691889921.344572117]: Turtle pose: x:6.010753, y:6.685284
[ INFO] [1691889921.360503389]: Turtle pose: x:5.978765, y:6.684439
[ INFO] [1691889921.376510552]: Turtle pose: x:5.946775, y:6.683594
[ INFO] [1691889921.392376821]: Turtle pose: x:5.914787, y:6.682749
[ INFO] [1691889921.408252255]: Turtle pose: x:5.882798, y:6.681904
[ INFO] [1691889921.424182561]: Turtle pose: x:5.850809, y:6.681059
[ INFO] [1691889921.440997168]: Turtle pose: x:5.818820, y:6.680214
[ INFO] [1691889921.456974244]: Turtle pose: x:5.786831, y:6.679369
[ INFO] [1691889921.472901624]: Turtle pose: x:5.754842, y:6.678524
[ INFO] [1691889921.488752417]: Turtle pose: x:5.722854, y:6.677679
[ INFO] [1691889921.504541853]: Turtle pose: x:5.690865, y:6.676834
[ INFO] [1691889921.520481420]: Turtle pose: x:5.658876, y:6.675989
[ INFO] [1691889921.536294508]: Turtle pose: x:5.626887, y:6.675144
[ INFO] [1691889921.552146408]: Turtle pose: x:5.594898, y:6.674299
[ INFO] [1691889921.569002808]: Turtle pose: x:5.562910, y:6.673454
[ INFO] [1691889921.584837421]: Turtle pose: x:5.530921, y:6.672609
[ INFO] [1691889921.600731891]: Turtle pose: x:5.498932, y:6.671764
[ INFO] [1691889921.616585402]: Turtle pose: x:5.466943, y:6.670919
[ INFO] [1691889921.632283348]: Turtle pose: x:5.434954, y:6.670074
[ INFO] [1691889921.648109869]: Turtle pose: x:5.402965, y:6.669230
[ INFO] [1691889921.664930356]: Turtle pose: x:5.370976, y:6.668385
[ INFO] [1691889921.680713380]: Turtle pose: x:5.338987, y:6.667540
[ INFO] [1691889921.696535478]: Turtle pose: x:5.306999, y:6.666695
[ INFO] [1691889921.712416030]: Turtle pose: x:5.275010, y:6.665850
[ INFO] [1691889921.728232197]: Turtle pose: x:5.243021, y:6.665005
[ INFO] [1691889921.745038074]: Turtle pose: x:5.211032, y:6.664160
[ INFO] [1691889921.760931901]: Turtle pose: x:5.179043, y:6.663315
[ INFO] [1691889921.776834055]: Turtle pose: x:5.147054, y:6.662470
[ INFO] [1691889921.792767539]: Turtle pose: x:5.115066, y:6.661625
[ INFO] [1691889921.808459123]: Turtle pose: x:5.083077, y:6.660780
[ INFO] [1691889921.824286895]: Turtle pose: x:5.051088, y:6.659935
[ INFO] [1691889921.840047529]: Turtle pose: x:5.019099, y:6.659091
[ INFO] [1691889921.856934123]: Turtle pose: x:4.987110, y:6.658246
[ INFO] [1691889921.872885179]: Turtle pose: x:4.955122, y:6.657401
[ INFO] [1691889921.888850674]: Turtle pose: x:4.923132, y:6.656556
[ INFO] [1691889921.904637071]: Turtle pose: x:4.891144, y:6.655711
[ INFO] [1691889921.920525400]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889921.936351946]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889921.952293251]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889921.968227998]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889921.984134339]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.000248528]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.016049234]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.032805745]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.048755014]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.064654370]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.080874716]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.096607718]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.112706638]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.128915941]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.144914936]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.160984702]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.177018643]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.193078747]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.209113678]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.224127436]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.240169768]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.256324080]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.272499755]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.288732790]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.304789579]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.320181587]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.336206526]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.352138685]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.368140737]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.384148108]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.400700501]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.416652277]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.432593842]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.448592295]: Turtle pose: x:4.859155, y:6.654866
[ INFO] [1691889922.464510380]: Turtle pose: x:4.844141, y:6.626607
[ INFO] [1691889922.480414251]: Turtle pose: x:4.829126, y:6.598348
[ INFO] [1691889922.496464895]: Turtle pose: x:4.814111, y:6.570089
[ INFO] [1691889922.512310489]: Turtle pose: x:4.799097, y:6.541830
[ INFO] [1691889922.528349207]: Turtle pose: x:4.784082, y:6.513571
[ INFO] [1691889922.544370980]: Turtle pose: x:4.769068, y:6.485312
[ INFO] [1691889922.560248083]: Turtle pose: x:4.754053, y:6.457054
[ INFO] [1691889922.577109783]: Turtle pose: x:4.739038, y:6.428795
[ INFO] [1691889922.593036954]: Turtle pose: x:4.724024, y:6.400536
[ INFO] [1691889922.608748334]: Turtle pose: x:4.709010, y:6.372277
[ INFO] [1691889922.624707025]: Turtle pose: x:4.693995, y:6.344018
[ INFO] [1691889922.640377773]: Turtle pose: x:4.678980, y:6.315759
[ INFO] [1691889922.656179957]: Turtle pose: x:4.663966, y:6.287500
[ INFO] [1691889922.673095800]: Turtle pose: x:4.648952, y:6.259242
[ INFO] [1691889922.689029385]: Turtle pose: x:4.633937, y:6.230983
[ INFO] [1691889922.704937355]: Turtle pose: x:4.618922, y:6.202724
[ INFO] [1691889922.720833524]: Turtle pose: x:4.603908, y:6.174465
[ INFO] [1691889922.736665756]: Turtle pose: x:4.588893, y:6.146206
[ INFO] [1691889922.752540440]: Turtle pose: x:4.573879, y:6.117947
[ INFO] [1691889922.768437486]: Turtle pose: x:4.558865, y:6.089688
[ INFO] [1691889922.784340470]: Turtle pose: x:4.543850, y:6.061430
[ INFO] [1691889922.800856292]: Turtle pose: x:4.528835, y:6.033170
[ INFO] [1691889922.816584659]: Turtle pose: x:4.513821, y:6.004911
[ INFO] [1691889922.832443907]: Turtle pose: x:4.498806, y:5.976653
[ INFO] [1691889922.848296081]: Turtle pose: x:4.483792, y:5.948394
[ INFO] [1691889922.864194256]: Turtle pose: x:4.468777, y:5.920135
[ INFO] [1691889922.880319566]: Turtle pose: x:4.453763, y:5.891876
[ INFO] [1691889922.896377267]: Turtle pose: x:4.438748, y:5.863617
[ INFO] [1691889922.912208261]: Turtle pose: x:4.423734, y:5.835358
[ INFO] [1691889922.928163217]: Turtle pose: x:4.408719, y:5.807099
[ INFO] [1691889922.944079033]: Turtle pose: x:4.393704, y:5.778841
[ INFO] [1691889922.961042491]: Turtle pose: x:4.378690, y:5.750582
[ INFO] [1691889922.976970418]: Turtle pose: x:4.363676, y:5.722323
[ INFO] [1691889922.992911458]: Turtle pose: x:4.348661, y:5.694064
[ INFO] [1691889923.008767385]: Turtle pose: x:4.333647, y:5.665805
[ INFO] [1691889923.024388033]: Turtle pose: x:4.318632, y:5.637546
[ INFO] [1691889923.040400809]: Turtle pose: x:4.303617, y:5.609287
[ INFO] [1691889923.056351176]: Turtle pose: x:4.288603, y:5.581028
[ INFO] [1691889923.072266925]: Turtle pose: x:4.273589, y:5.552770
[ INFO] [1691889923.088966404]: Turtle pose: x:4.258574, y:5.524511
[ INFO] [1691889923.104873895]: Turtle pose: x:4.243559, y:5.496252
[ INFO] [1691889923.120768099]: Turtle pose: x:4.228545, y:5.467993
[ INFO] [1691889923.136684839]: Turtle pose: x:4.213531, y:5.439734
[ INFO] [1691889923.152652861]: Turtle pose: x:4.198516, y:5.411475
[ INFO] [1691889923.168529918]: Turtle pose: x:4.183501, y:5.383216
[ INFO] [1691889923.184425569]: Turtle pose: x:4.168487, y:5.354958
[ INFO] [1691889923.200321049]: Turtle pose: x:4.153472, y:5.326698
[ INFO] [1691889923.216245066]: Turtle pose: x:4.138458, y:5.298440
[ INFO] [1691889923.232117480]: Turtle pose: x:4.123443, y:5.270181
[ INFO] [1691889923.248347382]: Turtle pose: x:4.108429, y:5.241922
[ INFO] [1691889923.264763495]: Turtle pose: x:4.093414, y:5.213663
[ INFO] [1691889923.280874344]: Turtle pose: x:4.078400, y:5.185404
[ INFO] [1691889923.296933154]: Turtle pose: x:4.063385, y:5.157145
[ INFO] [1691889923.312867114]: Turtle pose: x:4.048371, y:5.128886
[ INFO] [1691889923.328798936]: Turtle pose: x:4.033356, y:5.100627
[ INFO] [1691889923.344686159]: Turtle pose: x:4.018342, y:5.072369
[ INFO] [1691889923.360580762]: Turtle pose: x:4.003327, y:5.044110
[ INFO] [1691889923.376477719]: Turtle pose: x:3.988312, y:5.015851
[ INFO] [1691889923.392365338]: Turtle pose: x:3.973298, y:4.987592
[ INFO] [1691889923.408245284]: Turtle pose: x:3.958283, y:4.959333
[ INFO] [1691889923.424220910]: Turtle pose: x:3.943269, y:4.931074
[ INFO] [1691889923.440230704]: Turtle pose: x:3.928254, y:4.902815
[ INFO] [1691889923.456247677]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.472085474]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.488275063]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.504411872]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.520561024]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.536727905]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.552894362]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.569033430]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.584207344]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.600343488]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.616486007]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.632656715]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.648807247]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.664947728]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.680075728]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.696253947]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.712429119]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.728567929]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.744711745]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.760895074]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.777058333]: Turtle pose: x:3.913240, y:4.874557
[ INFO] [1691889923.792188376]: Turtle pose: x:3.913240, y:4.874557

Python实现

进入脚本文件夹创建Python文件

cd Documents/catkin_ws/src/learning_topic/scripts
vim pose_subscriber.py

内容如下

#!/home/user/anaconda3/envs/py39/bin/python
# -*- coding: utf-8 -*-
# 该例程将订阅/turtle1/pose话题,消息类型turtlesim::Pose

import rospy
from turtlesim.msg import Pose
# 接受到订阅的消息后,会进入消息回调函数
def poseCallback(msg):
    # 将接收的消息打印出来
    rospy.loginfo("Turtle pose: x:%0.6f, y:%0.6f", msg.x, msg.y)

def pose_subscriber():
    # 初始化ROS节点
    rospy.init_node('pose_subscriber', anonymous=True)
    # 创建一个Subscriber,订阅名为/turtle1/pose的topic,注册回调函数poseCallback
    rospy.Subscriber("/turtle1/pose", Pose, poseCallback)
    # 循环等待回调函数
    rospy.spin()

if __name__ == '__main__':

    pose_subscriber()

设置可执行权限

chmod 777 pose_subscriber.py

执行命令 (不同终端窗口)

roscore
rosrun turtlesim turtlesim_node
rosrun turtlesim turtle_teleop_key
rosrun learning_topic pose_subscriber.py

图像传输的发布者与订阅者

进入工作空间

cd Documents/catkin_ws/src

创建一个新的功能包

catkin_create_pkg learning_transfer_img sensor_msgs cv_bridge roscpp std_msgs image_transport

进入到该功能包的 src 目录

cd learning_transfer_img/src

创建发布者的 C++ 文件

vim img_publisher.cpp

内容如下

#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>

using namespace ros;
using namespace cv;
using namespace std;
using namespace sensor_msgs;
     
int main(int argc, char** argv){
    //ROS节点初始化
    init(argc, argv, "image_publisher");
    //创建节点句柄
    NodeHandle n;
    //创建一个图像传输发布者,发布名为/camera/image的topic
    image_transport::ImageTransport it(n);
    image_transport::Publisher pub = it.advertise("/camera/image", 1);
    VideoCapture cap(0);
    if(!cap.isOpened()) return 1;
    Mat frame;
    //初始化一个sensor_msgs::ImagePtr类型的消息
    ImagePtr msg;
    Rate loop_rate(1);
    while (ok()) {
        cap >> frame;
        if(!frame.empty()) {
            //将opencv的Mat转化成sensor_msgs::ImagePtr类型的消息
            msg = cv_bridge::CvImage(std_msgs::Header(), "bgr8", frame).toImageMsg();
            //发布消息
            pub.publish(msg);
        }
        //按照循环频率延时
        loop_rate.sleep();
    }
    return 0;
}

创建订阅者的 C++ 文件

vim img_subscriber.cpp

内容如下

#include <ros/ros.h>
#include <image_transport/image_transport.h>
#include <opencv2/highgui/highgui.hpp>
#include <cv_bridge/cv_bridge.h>

using namespace ros;
using namespace cv;
using namespace std;
using namespace sensor_msgs;
 
void imageCallback(const ImageConstPtr& msg) {
    try {
        //将sensor_msgs::ImagePtr类型的消息转化成opencv的Mat并显示出来
        imshow("view", cv_bridge::toCvShare(msg, "bgr8")->image);
        waitKey(1);
    } catch (cv_bridge::Exception& e) {
        ROS_ERROR("Could not convert from '%s' to 'bgr8'.", msg->encoding.c_str());
    }
}
 
int main(int argc, char **argv) {
    //ROS节点初始化
    init(argc, argv, "img_subscriber");
    //创建节点句柄
    NodeHandle n;
    namedWindow("view");
    startWindowThread();
    //创建一个image_transport::Subscriber,订阅名为/camera/image的topic,注册回调函数imageCallback
    image_transport::ImageTransport it(n);
    image_transport::Subscriber sub = it.subscribe("/camera/image", 1, imageCallback);
    //循环等待回调函数
    spin();
    destroyWindow("view");
    return 0;
}

回到 learning_transfer_img 文件夹,修改 CMakelists.txt。

cd ..
vim CMakelists.txt

添加内容如下

find_package(OpenCV)
include_directories(${OpenCV_INCLUDE_DIRS})

add_executable(img_publisher src/img_publisher.cpp)
target_link_libraries(img_publisher ${catkin_LIBRARIES})
target_link_libraries(img_publisher ${OpenCV_LIBS})

add_executable(img_subscriber src/img_subscriber.cpp)
target_link_libraries(img_subscriber ${catkin_LIBRARIES})
target_link_libraries(img_subscriber ${OpenCV_LIBS})

回到 catkin_ws 文件夹,开始编译

cd ../..
catkin_make

执行该程序,在不同的终端窗口执行

roscore
rosrun learning_transfer_img img_subscriber
rosrun learning_transfer_img img_publisher

话题消息的定义与使用

C++实现

进入包文件夹

cd Documents/catkin_ws/src/learning_topic

新建消息文件夹

mkdir msg
cd msg

创建我们需要的消息文件

vim Person.msg

内容如下

string name
uint8 sex
uint8 age

uint8 unknown = 0
uint8 male    = 1
uint8 femal   = 2

这里我们创建的是一个关于个人的信息。它是语言无关的,ROS会根据定义来编译成C++或Python的程序。

设置编译规则

退回learning_topic文件夹,修改package.xml,添加动态生成程序的功能包依赖

cd ..
vim package.xml

添加的内容如下

<build_depend>message_generation</build_depend>
<exec_depend>message_runtime</exec_depend>

其中<build_depend>为编译依赖,<exec_depend>为执行依赖。

修改CMakeLists.txt

vim CMakeLists.txt

内容如下

find_package(catkin REQUIRED COMPONENTS
  geometry_msgs
  roscpp
  rospy
  std_msgs
  turtlesim
  message_generation
)

add_message_files(FILES Person.msg)
generate_messages(DEPENDENCIES std_msgs)

catkin_package(
#  INCLUDE_DIRS include
#  LIBRARIES learning_topic
   CATKIN_DEPENDS geometry_msgs roscpp rospy std_msgs turtlesim message_runtime
#  DEPENDS system_lib
)

回到 catkin_ws 文件夹,开始编译

cd ../..
catkin_make

此时我们会在Documents/catkin_ws/devel/include/learning_topic目录下看到多了一个Person.h的文件,它是由ROS自动编译生成的,内容如下

// Generated by gencpp from file learning_topic/Person.msg
// DO NOT EDIT!


#ifndef LEARNING_TOPIC_MESSAGE_PERSON_H
#define LEARNING_TOPIC_MESSAGE_PERSON_H


#include <string>
#include <vector>
#include <memory>

#include <ros/types.h>
#include <ros/serialization.h>
#include <ros/builtin_message_traits.h>
#include <ros/message_operations.h>


namespace learning_topic
{
template <class ContainerAllocator>
struct Person_
{
  typedef Person_<ContainerAllocator> Type;

  Person_()
    : name()
    , sex(0)
    , age(0)  {
    }
  Person_(const ContainerAllocator& _alloc)
    : name(_alloc)
    , sex(0)
    , age(0)  {
  (void)_alloc;
    }



   typedef std::basic_string<char, std::char_traits<char>, typename std::allocator_traits<ContainerAllocator>::template rebind_alloc<char>> _name_type;
  _name_type name;

   typedef uint8_t _sex_type;
  _sex_type sex;

   typedef uint8_t _age_type;
  _age_type age;



// reducing the odds to have name collisions with Windows.h 
#if defined(_WIN32) && defined(unknown)
  #undef unknown
#endif
#if defined(_WIN32) && defined(male)
  #undef male
#endif
#if defined(_WIN32) && defined(femal)
  #undef femal
#endif

  enum {
    unknown = 0u,
    male = 1u,
    femal = 2u,
  };


  typedef boost::shared_ptr< ::learning_topic::Person_<ContainerAllocator> > Ptr;
  typedef boost::shared_ptr< ::learning_topic::Person_<ContainerAllocator> const> ConstPtr;

}; // struct Person_

typedef ::learning_topic::Person_<std::allocator<void> > Person;

typedef boost::shared_ptr< ::learning_topic::Person > PersonPtr;
typedef boost::shared_ptr< ::learning_topic::Person const> PersonConstPtr;

// constants requiring out of line definition

   

   

   



template<typename ContainerAllocator>
std::ostream& operator<<(std::ostream& s, const ::learning_topic::Person_<ContainerAllocator> & v)
{
ros::message_operations::Printer< ::learning_topic::Person_<ContainerAllocator> >::stream(s, "", v);
return s;
}


template<typename ContainerAllocator1, typename ContainerAllocator2>
bool operator==(const ::learning_topic::Person_<ContainerAllocator1> & lhs, const ::learning_topic::Person_<ContainerAllocator2> & rhs)
{
  return lhs.name == rhs.name &&
    lhs.sex == rhs.sex &&
    lhs.age == rhs.age;
}

template<typename ContainerAllocator1, typename ContainerAllocator2>
bool operator!=(const ::learning_topic::Person_<ContainerAllocator1> & lhs, const ::learning_topic::Person_<ContainerAllocator2> & rhs)
{
  return !(lhs == rhs);
}


} // namespace learning_topic

namespace ros
{
namespace message_traits
{





template <class ContainerAllocator>
struct IsMessage< ::learning_topic::Person_<ContainerAllocator> >
  : TrueType
  { };

template <class ContainerAllocator>
struct IsMessage< ::learning_topic::Person_<ContainerAllocator> const>
  : TrueType
  { };

template <class ContainerAllocator>
struct IsFixedSize< ::learning_topic::Person_<ContainerAllocator> >
  : FalseType
  { };

template <class ContainerAllocator>
struct IsFixedSize< ::learning_topic::Person_<ContainerAllocator> const>
  : FalseType
  { };

template <class ContainerAllocator>
struct HasHeader< ::learning_t