ROS2编译Python节点来发布和订阅的实践《2》

通过熟悉:ROS2对比ROS1的一些变化与优势(全新安装ROS2以及编译错误处理)《1》
我们大概了解到了ROS2的重新设计带来的巨大优势,最核心的就是去掉了roscore,这样就避免了因为节点管理器崩溃而使整个系统都崩溃的场景出现,通过DDS的传输协议进行数据的通信,真正做到了去中心化的设计。
虽然很多命令跟ROS1比较都有了新的变化,但很容易接受并熟悉它,也处理了在编译C++与Python的过程中常遇到的一些问题。这节主要来看下,通过Python来定义发布节点与订阅节点。

1、创建工作空间

1.1、ROS2环境

同样,我们新建一个工作空间和创建一个名叫my_pub_sub的package包,然后在这个包下面来写我们的节点。当然如果说环境包含了ROS1的情况,我们先设置ROS2的环境

source /opt/ros/dashing/setup.bash

当然最好的方法还是前面介绍的自行来选择环境:gedit ~/.bashrc

echo "ROS melodic (1) or ROS2 dashing (2)?"
read edition
if [ "$edition" -eq "1" ];then
  source /opt/ros/melodic/setup.bash
  echo using ros melodic
else
  source /opt/ros/dashing/setup.bash
  echo using ros2 dashing
fi

1.2、创建包

创建包之前,首先建立工作空间 

mkdir -p ~/my_pub_sub_ws/src
cd ~/my_pub_sub_ws/src

对于Python包的创建,生成类型--build-type需指定为ament_python,而编译C++文件指定为ament_cmake
所以这里我们的Python创建包的命令,如下:

ros2 pkg create --build-type ament_python my_pub_sub

如下图,我们将会看到自动生成了my_pub_sub/my_pub_sub的目录,以及一些配置相关文件,诸如:package.xml,setup.cfg,setup.py 

ROS2编译Python节点来发布和订阅的实践《2》_第1张图片

2、测试节点

我们还是使用上节的例子,来测试下新建的环境,这次也是试图没有使用rclpy依赖项,也就是没有指定参数--dependencies rclpy

2.1、PYNode.py

 新建一个Python节点文件

cd ~/my_pub_sub_ws/src/my_pub_sub/my_pub_sub
gedit PYNode.py
#!/usr/bin/env python3

import rclpy
from rclpy.node import Node

def main(args=None):
    rclpy.init(args=args)
    node = Node("ChyiChin_Node")
    node.get_logger().info("Python TEST")
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()

加个可执行权限:chmod +x PYNode.py

当然对文件是否加可执行权限,取决于你的运行方式,这里是运行节点可以不加,有的时候如果是需要直接运行这个Python文件,比如这样的操作:./PYNode.py 就需要加上可执行权限,不然会报权限错误:bash: ./PYNode.py: Permission denied

2.2、修改setup.cfg

cd ~/my_pub_sub_ws/src/my_pub_sub
gedit setup.cfg

将 破折号 修改为 下划线 _,修改为script_dirinstall_scripts
修改之后的内容如下:

script_dir=$base/lib/my_pub_sub
[install]
install_scripts=$base/lib/my_pub_sub

2.3、修改setup.py 

cd ~/my_pub_sub_ws/src/my_pub_sub
gedit setup.py

入口处填写:"ChyiChin_Node=my_pub_sub.PYNode:main"

2.4、编译

修改好了之后,我们就开始编译这个my_pub_sub包

cd ~/my_pub_sub_ws
colcon build --packages-select my_pub_sub
source install/setup.bash

 执行节点:ros2 run my_pub_sub ChyiChin_Node

[INFO] [ChyiChin_Node]: Python TEST

一切OK,没有发现问题,看来只需要指定Python的入口函数的位置,就可以正常的执行Python文件了。所以对于在package.xml里面做修改也是不需要的,因为网上有些是在这个xml文件中做了依赖项的配置,不设置也是可以的,也能正常运行。

cd ~/my_pub_sub_ws/src/my_pub_sub
gedit package.xml
/*
rclpy
std_msgs
*/

代码比较简单,这里的rclpy库,类似ROS1中rospy的用法,主要是对节点的创建与管理,ROS2的底层是使用C语言写的,名字就是rcl库,而这个rclpy就是在这个基础上做的对Python的封装接口,对C++做的封装就是rclcpp,这样的好处就是Python与C++在调用API的时候更加的统一相似,同时对ROS2进行功能更新时,直接更新rcl库,再新增cpp与python的支持即可。

3、发布节点

配置环境和编译没有问题之后,接下来就是常见的发布与订阅操作,我们先来写一个发布的节点。 

cd ~/my_pub_sub_ws/src/my_pub_sub/my_pub_sub
gedit pub_test.py
#!/usr/bin/env python3

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class Publisher(Node):
    def __init__(self,name):
        super().__init__(name)
        self.publisher = self.create_publisher(String,"talker_topic",10)
        timer_period = 5
        self.timer = self.create_timer(timer_period,self.timer_callback)
        self.get_logger().info("hello,I'm %s!" %name)
        self.i = 0

    def timer_callback(self): 
        msg = String()
        msg.data = 'NO.%d,hello!' %(self.i)
        self.publisher.publish(msg)
        self.get_logger().info("Publishing: %s"%msg.data)
        self.i += 1

def main(args=None):
    rclpy.init(args=args)
    node = Publisher("talker") 
    rclpy.spin(node)
    rclpy.shutdown()

if __name__ == '__main__':
    main()
cd ~/my_pub_sub_ws/src/my_pub_sub
gedit setup.py

入口处填写(这个跟前面的使用逗号进行隔开):"talker=my_pub_sub.pub_test:main"

 编译:

cd ~/my_pub_sub_ws
colcon build --packages-select my_pub_sub
source install/setup.bash

运行发布节点:ros2 run my_pub_sub talker

[INFO] [talker]: hello,I'm talker!
[INFO] [talker]: Publishing: NO.0,hello!
[INFO] [talker]: Publishing: NO.1,hello!
[INFO] [talker]: Publishing: NO.2,hello!
[INFO] [talker]: Publishing: NO.3,hello!
[INFO] [talker]: Publishing: NO.4,hello!
...

 查看下话题:ros2 topic list

/parameter_events
/rosout
/talker_topic

 查看talker_topic话题当前传输的信息:ros2 topic echo /talker_topic

data: NO.9,hello!
---
data: NO.10,hello!
---
data: NO.11,hello!
---
data: NO.12,hello!

代码也比较简单,初始化一个talker节点,里面创建一个类型是String的talker_topic的话题,队列长度为10,另外在上述代码基础上增加了一个定时器,每隔5秒调用一次timer_callback函数,这个函数的作用是发布String类型的字符串。

4、订阅节点

发布节点创建好了,接下来写一个订阅这个发布信息的节点

cd ~/my_pub_sub_ws/src/my_pub_sub/my_pub_sub
gedit sub_test.py

 

#!/usr/bin/env python3

import rclpy
from rclpy.node import Node
from std_msgs.msg import String

class Subscriber(Node):
    def __init__(self,name):
        super().__init__(name)
        self.subscriber = self.create_subscription(String,"talker_topic",self.sub_callback,10)
        self.count = 0

    def sub_callback(self,msg):
        self.get_logger().info("I heard: %s" %msg.data)
        if self.count >= 3 : self.destroy_node()

def main(args=None):
    rclpy.init(args=args)
    node = Subscriber("listener")
    rclpy.spin(node)
    rclpy.shutdown()
 
if __name__ == "__main__":
    main()
cd ~/my_pub_sub_ws/src/my_pub_sub
gedit setup.py

入口处增加,同样使用逗号跟前面的隔开:"listener=my_pub_sub.sub_test:main"
这个setup.py文件的截图如下所示:

ROS2编译Python节点来发布和订阅的实践《2》_第2张图片

cd ~/my_pub_sub_ws
colcon build --packages-select my_pub_sub
source install/setup.bash

运行订阅节点:ros2 run my_pub_sub listener

如果关闭了发布节点,重新运行即可,这样就监听到发布的消息了: 

[INFO] [listener]: I heard: NO.0,hello!
[INFO] [listener]: I heard: NO.1,hello!
[INFO] [listener]: I heard: NO.2,hello!
[INFO] [listener]: I heard: NO.3,hello!
[INFO] [listener]: I heard: NO.4,hello!

发布与订阅,截图如下:

ROS2编译Python节点来发布和订阅的实践《2》_第3张图片

查看节点: ros2 node list

/talker
/listener 

恩,没有问题,正确显示一个发布节点talker和一个订阅节点listener

5、混合环境的错误

在这种混合版本的环境,很容易出现下面这样的错误:

ROS_DISTRO was set to 'dashing' before. Please make sure that the environment does not mix paths from different distributions.
ROS_DISTRO was set to 'melodic' before. Please make sure that the environment does not mix paths from different distributions.

如果使用ROS2版本: 

sudo gedit /opt/ros/melodic/share/ros_environment/catkin_env_hook/1.ros_distro.sh

将其内容注释即可:

if [ -n "$ROS_DISTRO" -a "$ROS_DISTRO" != "melodic" ]; then
  echo "ROS_DISTRO was set to '$ROS_DISTRO' before. Please make sure that the environment does not mix paths from different distributions."
fi
export ROS_DISTRO=melodic

如果使用ROS1版本:

sudo gedit /opt/ros/dashing/share/ros_environment/environment/1.ros_distro.sh

将其内容注释即可:

if [ -n "$ROS_DISTRO" -a "$ROS_DISTRO" != "dashing" ]; then
  echo "ROS_DISTRO was set to '$ROS_DISTRO' before. Please make sure that the environment does not mix paths from different distributions."
fi
export ROS_DISTRO=dashing

测试环境如果经常有ROS1和ROS2的测试的情况,那就混合环境,当然最好的情况还是只有一个版本的环境,避免一些不必要的错误出现。而且是在编译之前做这个操作,不然将会在所在工作空间的install/setup.bash中出现混合环境:

COLCON_CURRENT_PREFIX="/opt/ros/melodic"
_colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash"
COLCON_CURRENT_PREFIX="/opt/ros/dashing"
_colcon_prefix_chain_bash_source_script "$COLCON_CURRENT_PREFIX/local_setup.bash"

甚至出现下面这样的错误: 

AttributeError: type object 'type' has no attribute '_TYPE_SUPPORT' This might be a ROS 1 message type but it should be a ROS 2 message type. Make sure to source your ROS 2 workspace after your ROS 1 workspace.

所以最好的设置就是前面说的修改 ~/.bashrc 自行进行环境的选择。 

你可能感兴趣的:(机器人操作系统(ROS),rclpy.node,setup.cfg,setup.py,colcon,build)