ROS topic

def

ROS Topics are the mechanism used to pass messages between nodes. When a node wants to make information available to other nodes at run-time, it publishes messages to a topic.

Topic 使用的是PUSH模式,采用的是观察者模式,订阅者通过订阅TOPIC获取更新的messages。

publishers 和subsribers 都可以是多个。

ros下载python3 ros publisher python_ros下载python3


实践1

创建单个publisher

如何创建topic,如何创建多个publishers 和多个subsribers
python 创建publisher,python 的好处是只需要修改package.xml文件,不需要修改其他文件

#! /usr/bin/env python
import rospy
from std_msgs.msg import Int32
def talker():
    pub = rospy.Publisher('counter', Int32, queue_size=10)
    rospy.init_node('publisher', anonymous=True)
    rate = rospy.Rate(2) # 10hz
    cnt = 0
    while not rospy.is_shutdown():
        hello_str = "hello world %s" % cnt
        rospy.loginfo(hello_str)
        pub.publish(cnt)
        cnt += 1
        rate.sleep()
if __name__ == '__main__':
    try:
        talker()
    except rospy.ROSInterruptException:
        pass

使用cpp 版本,需要修改cmakelist.txt文件

#include "ros/ros.h"
#include "std_msgs/Int32.h"

int main(int argc, char **argv)
{
  ros::init(argc, argv, "talker");
  ros::NodeHandle n;
  ros::Publisher chatter_pub = n.advertise<std_msgs::Int32>("chatter", 1000);
  ros::Rate loop_rate(2);

  int count = 0;
  while (ros::ok())
  {
    std_msgs::Int32 msg;
    msg.data = count;
    chatter_pub.publish(msg);
    count++;
    //ros::spinOnce();
    loop_rate.sleep();
  }
  return 0;
}

修改cmakelist.txt

add_executable(publisher src/topic_publisher.cpp)
target_link_libraries(publisher ${catkin_LIBRARIES})
add_dependencies(publisher beginner_tutorials_generate_messages_cpp)

增加订阅者

创建单个订阅者

#!/usr/bin/env python
import rospy
from std_msgs.msg import Int32
def callback(data):
    rospy.loginfo(rospy.get_caller_id() + "I heard %d", data.data)
def listener():
    rospy.init_node('topic_subscriber', anonymous=True)
    rospy.Subscriber("counter", Int32, callback)
    rospy.spin()
if __name__ == '__main__':
    listener()
#include "ros/ros.h"
#include "std_msgs/Int32.h"

void chatterCallback(const std_msgs::Int32::ConstPtr& msg)
{
  ROS_INFO("I heard: [%d]", msg->data);
  
}

int main(int argc, char **argv)
{
  ros::init(argc, argv, "listener");
  ros::NodeHandle n;
  ros::Subscriber sub = n.subscribe("counter", 1000, chatterCallback);
  ros::spin();
  return 0;
}

创建多个订阅者

因为ROS是通过名称来索引topic,只要定义好topic名称即可。

同样,publisher只要是向同一个名称的topic上发送即可。

ros下载python3 ros publisher python_#include_02

创建自定义的message

step1:创建msg文件夹
step2:在msg中定义msg数据类型(所有的设定使用文本形式,如json类型)
step3:在package.xml(属性:依赖关系)添加编译和运行时的约束

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

step4:修改xml文件

#找到编译运行所需要的依赖文件(ros 会把相关的package汇集在一起)
find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs message_generation)
#添加msg数据类型的定义
add_message_files(
FILES
Num.msg
)
#生成msg
generate_messages(DEPENDENCIES
std_msgs) #如果使用了其他消息类型中的数据类型,需要在这里添加相应的依赖
#在cmake上声明运行时所需要的依赖
catkin_package(
CATKIN_DEPENDS message_runtime …
)
#使用cmake编译生成新的msg
catkin_make install # 安装刚刚生成的msg,这一步很重要,不然容易找不到msg
== 注意添加的顺序,先添加msg文件然后再生成,最后添加运行时的依赖。==
step5:验证是否添加成功

rosmsg show Num
rosmsg show beginner_tutorials/Num

step6:如何调用

python 方式实现

在src/beginner_tutorials/ 下创建talker.py

import rospy
from beginner_tutorials.msg import Num

def talker():
    pub = rospy.Publisher('counter', Num, queue_size=10)
    rospy.init_node('publisher', anonymous=True)
    rate = rospy.Rate(2) # 10hz
    cnt = 1000
    while not rospy.is_shutdown():
        hello_str = "hello world %s" % cnt
        rospy.loginfo(hello_str)
        pub.publish(cnt)
        cnt += 100
        rate.sleep()

在bin/Num_publisher 创建Num_publisher

#! /usr/bin/env python
if __name__ == '__main__':
    try:
        talker()
    except rospy.ROSInterruptException:
        pass

python 代码在调用的时候很容易出现找不到module的情况,所以需要合理的管理python的文件
按照ros推荐的风格进行程序规划:
资料来源于:http://wiki.ros.org/rospy_tutorials/Tutorials/Makefile
http://wiki.ros.org/PyStyleGuide Step6.1: 管理好目录

1.no msgs/srvs
packagename
 |- src/
  |- packagename.py
 |- scripts/
  |- non-exported python files
2.with msg/srvs
packagename
  |- src/
   |- packagename/
    |- init.py
    |- talker.py
 |- scripts/
   |- non-exported python files
 |- bin/Num_publisher
 |- setup.py

Step6.2:添加__init__.py文件

Files named init.py are used to mark directories on disk as a Python package
通俗的讲就是作为python 文件夹的标示,没有什么其他作用,所以一般也不添加代码

Step6.3:添加setup.py

## ! DO NOT MANUALLY INVOKE THIS setup.py, USE CATKIN INSTEAD

from distutils.core import setup
from catkin_pkg.python_setup import generate_distutils_setup

# fetch values from package.xml
setup_args = generate_distutils_setup(
    packages=['tutorial_package'],
    package_dir={'': 'src'},
)

setup(**setup_args)

Step6.4 修改ros python makefile

#package.xml
<buildtool_depend>catkin</buildtool_depend> #限定编译的工具 如果使用ros_build 应该怎么做?
#cmakeList.txt
catkin_python_setup()
还可以指定安装位置,这个我们后续再进行更新

因为我们使用的是python所以在cmakeList.txt中并不需要修改太多内容

cpp 方式实现

#include "ros/ros.h"
#include "beginner_tutorials/Num.h"

int main(int argc, char **argv)
{
  ros::init(argc, argv, "talker");
  ros::NodeHandle n;
  ros::Publisher chatter_pub = n.advertise<beginner_tutorials::Num>("counter", 1000);
  ros::Rate loop_rate(2);

  int count = 0;
  while (ros::ok())
  {
    beginner_tutorials::Num msg;
    msg.num = count;
    chatter_pub.publish(msg);
    count++;
    //ros::spinOnce();
    loop_rate.sleep();
  }
  return 0;
}

根据代码关键在于根据Num.msg 生成相应的Num.h
资料来源于:
在cmakelist.txt中添加libraries

catkin_package(
#   INCLUDE_DIRS include
    LIBRARIES beginner_tutorials    #因为我们的代码msg和运行代码在一个package中,这个可以注释
    CATKIN_DEPENDS roscpp message_runtime
)

include_directories(
    include    #因为要添加Num.h,这个必须要添加
    ${catkin_INCLUDE_DIRS}
)

其他库调用我们创建的msg

cd catkin_ws/src
catkin_create_pkg test roscpp rospy std_msgs

修改库依赖

#package.xml
  <buildtool_depend>catkin</buildtool_depend>
  <build_depend>roscpp</build_depend>
  <build_depend>rospy</build_depend>
  <build_depend>std_msgs</build_depend>
  <build_depend>beginner_tutorials</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>

修改cmakeList.txt中的依赖选项

find_package(catkin REQUIRED COMPONENTS
  roscpp
  rospy
  std_msgs
  beginner_tutorials   #只要能find 对应的package 就没有什么问题
)

代码部分cpp和python的完全一样

总结

package.xml文件应该怎么写

编译和运行需要的外部库文件需要在package.xml中声明;
如果需要生成message则需要message_generation 这个外部库
如果需要调用外部的message 库函数,调用<build_depend>beginner_tutorials</build_depend>
如果要使用setup.py管理python文件,说明使用的是catkin <buildtool_depend>catkin</buildtool_depend>

cmakelist.txt文件应该怎么写

如果只是生成节点,采用find_package 找到所需的依赖即可
如果需要生成message,1.add_msg;2.generate_msg;3.catkin_package 声明库所需要运行时的工具message_runtime
(ROS独有的找外部包依赖的方法)
如果需要写python的makefile文件,需要使用catkin_python_setup()
如果需要一些头文件,使用include_directories,或者是catkin_package中的LIBRARIES

python 代码如何管理

采用__init__.py 及同一层级的代码是函数,运行代码放置在bin/, 环境代码放置在package/