通过熟悉:ROS2对比ROS1的一些变化与优势(全新安装ROS2以及编译错误处理)《1》
我们大概了解到了ROS2的重新设计带来的巨大优势,最核心的就是去掉了,这样就避免了因为节点管理器崩溃而使整个系统都崩溃的场景出现,通过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
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_dir和install_scripts
修改之后的内容如下:
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
/*
<exec_depend>rclpy</exec_depend>
<exec_depend>std_msgs</exec_depend>
*/
代码比较简单,这里的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文件的截图如下所示:
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 node list
/talker
/listener
恩,没有问题,正确显示一个发布节点talker和一个订阅节点listener。
5、混合环境的错误
在这种混合版本的环境,很容易出现下面这样的错误:
如果使用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中出现混合环境:
甚至出现下面这样的错误:
所以最好的设置就是前面说的修改 ~/.bashrc 自行进行环境的选择。