零蚀
这应该也会很困扰一些没有过ros开发,又想直接ros2开始的人吧
开启第一个ros2程序
-
从环境搭建开始
-
如果没有经历过ros的开发,在macos上开发ros2你可能会很凌乱,这些都是啥,到底该怎么开发,用什么ide,怎么做才能让开发变得简洁。当有ros开发经验后,你会发现其实我们要做的内容和之前大致一样(官网上啥都没说,只是说你打开你喜欢的编辑文本,现在能用文本编辑c++和python代码)。
-
如果没有ros开发和安装经验的话,可以先去看一下我的关于ros安装和第一个程序
~/ros2/install/setup.bash ~/ros2/ros2-osx/setup.bash
- 然后我们创建我们包,在src的路径下,这里和ros就大不同了,ros感觉比较杂揉,它里面是cpp和python在一起开发(我之前用的是clion),项目里有个顶层CmakeLists来管理项目,c++可能需要用到catkin cmake来重新构建项目,但是如果在里面编辑python代码,完全是不必要的,所以这就很乱,而且,python和c++可以编辑相同的功能,就会显得没必要在一起混合开发,用某一个语言不就可以了?,当然ros2网上还阐述了很多优点(对标ros)。然后ros2 就被拆开了,python做python项目,c++做c++项目,这里的build-type就是限定做的项目类型,但不能将两个项目类型放在一起。
. install/local_setup.bash
是将当前的运行环境设置为ros的专属环境。
cd ~/ws/src #cpp ros2 pkg create --build-type ament_cmake <package_name> ros2 pkg create --build-type ament_cmake --node-name my_node my_package #python ros2 pkg create --build-type ament_python <package_name> ros2 pkg create --build-type ament_python --node-name my_node my_package # 构建 cd ~/ws colcon build colcon build --packages-select my_package # 将软件包添加到当前路径的环境中 . install/local_setup.bash
-
然后就是如何编辑代码,如果你是没有开发过ros,大概率是双击clion或者pycharm,但是打开你会发现,clion不能解析CmakeLists文件,pycharm也不能
import rclpy
(这个等同于ros中的rospy,ros的python库)。项目在构建的时候就找不到ros2的运行库,哪怕你将python地址改成ros2安装的python@3.8也不行。 -
回想ros不难发现,ros开发(基于ubuntu的linux)它是将项目在ros的运行环境下打开的,比如
sh clion.sh
,它是在ros的运行环境中打开了软件的shell运行脚本,所以ros2必然也是这样,在寻找中我发现了如下两个执行文件,在合理的猜测下解决了编译器的问题
# cpp 和ros一样是打开CmakeLists文件 /Applications/Clion.app/Contents/MacOS/clion # python /Applications/PyCharm.app/Contents/MacOS/pycharm
- 然后我们先看一下clion,运行后是这样当我们打开了clion之后open项目,选择我们cpp项目的顶层CMakelists之后,我们可以看到Cmake (Debug)构建完成,表示项目已经构建成功。反之如果出现报错,则是不是ros环境中打开,导致找不到调用的库。
- 然后我们看一下python,python开发就简单许多了,因为完全用不到顶层CMakeLists所以我们不需要以项目的形式打开某个文件,直接用PyCharm正常打开项目就行,如果导入rclpy没有报错,说明打开成功,反之没有在环境中打开项目。
- 项目创建就到这,这些都是官网甚至外网都没解决的问题,其实也就是举一反三的事,并没什么,但没必要在这方面浪费时间,最后来看一下运行项目,作为一个有折腾过ros经验的人,能清楚的知道,我们不能在IDE图标上点击运行,因为这样是错的,很多东西不会在环境中生效,所以老规矩,命令行(以python项目为例):
-
小知识点
-
launch
- launch的功能是为了一次性开启多个node,因为我们可能编辑了很多node,为了方便同时开启,ros是加入了launch这个启动项,ros中launch 是在项目下的 launch文件夹中进行编写,而ros2项目进行了拆分,所以launch是在项目之外建立的。这里我是在ros_ws目录下构建的。然后重点是我运行官网的案例报错了,经过认真比对错误日志,我发现,官网的写法在我这是有问题的,如果想运行多个node的正确写法应该如下:
rom launch import LaunchDescription from launch_ros.actions import Node def generate_launch_description(): return LaunchDescription([ Node( package='turtlesim', node_executable='turtlesim_node', name='sim1' ), Node( package='turtlesim', node_executable='turtlesim_node', name='sim2' ) ])
ros2 launch turtlesim_minic_test_launch.py
- 为了更好了解节点之间的关系,我们可以使用rqt_graph
- 查看ros2的日志消息使用rqt_console
ros2 run rqt_console rqt_console
- 展示所有的topic
ros2 topic list
,如果想知道topic所携带message的类型可使用ros2 topic list -t
ros2 topic pub --once /turtle1/cmd_vel geometry_msgs/msg/Twist "{linear: {x: 2.0, y: 0.0, z: 0.0}, angular: {x: 0.0, y: 0.0, z: 1.8}}"
ROS2 开发
-
topic开发(Python)
- topic就是一个消息池,功能和UDP类似。代码如下:
# publish import rclpy from rclpy.node import Node from std_msgs.msg import String import time def main(args=None): rclpy.init(args=args) # 构建一个node node = Node("my_demo_publish") # 构建一个publish publish = node.create_publisher(String, 'my_topic', 100) for i in range(0, 9): content = String() content.data = "消息的index:"+str(i) publish.publish(content) node.get_logger().info('Publishing: "%s"' % content.data) time.sleep(1) rclpy.spin(node) if __name__ == '__main__': main() # Subscribe import rclpy from rclpy.node import Node from std_msgs.msg import String node = None # 监听的回调函数 def call_bask(msg): node.get_logger().info('Publishing: "%s"' % msg.data) def main(args=None): rclpy.init(args=args) # 构建一个node global node node = Node("my_demo_subscribe") # 构建一个subscribe subscribe = node.create_subscription(String, 'my_topic', call_bask, 100) rclpy.spin(node) if __name__ == '__main__': main()
- 文件名分别对应publish–>py_my_demo,subscribe–>py_my_demo1,然后我们将其构建到我们的build里面去。首先我们要在setup.py中添加我们node,
entry_points={ 'console_scripts': [ 'py_my_demo = py_demo.py_my_demo:main', 'py_my_demo1 = py_demo.py_my_demo1:main' ], },
- 然后构建项目
colcon build --packages-select py_demo ros2 run py_demo py_my_demo1 ros2 run py_demo py_my_demo
- 输出内容👌
# publish [INFO] [my_demo_publish]: Publishing: "消息的index:0" [INFO] [my_demo_publish]: Publishing: "消息的index:1" [INFO] [my_demo_publish]: Publishing: "消息的index:2" [INFO] [my_demo_publish]: Publishing: "消息的index:3" [INFO] [my_demo_publish]: Publishing: "消息的index:4" [INFO] [my_demo_publish]: Publishing: "消息的index:5" [INFO] [my_demo_publish]: Publishing: "消息的index:6" [INFO] [my_demo_publish]: Publishing: "消息的index:7" [INFO] [my_demo_publish]: Publishing: "消息的index:8" # subscribe [INFO] [my_demo_subscribe]: Publishing: "消息的index:0" [INFO] [my_demo_subscribe]: Publishing: "消息的index:1" [INFO] [my_demo_subscribe]: Publishing: "消息的index:2" [INFO] [my_demo_subscribe]: Publishing: "消息的index:3" [INFO] [my_demo_subscribe]: Publishing: "消息的index:4" [INFO] [my_demo_subscribe]: Publishing: "消息的index:5" [INFO] [my_demo_subscribe]: Publishing: "消息的index:6" [INFO] [my_demo_subscribe]: Publishing: "消息的index:7" [INFO] [my_demo_subscribe]: Publishing: "消息的index:8"
-
topic开发(Cpp)
- 在开发c++的功能时候需要先在CMakeLists中导入对应的库
- 然后我们看一下cpp的代码实现。官网上都是以类的形式,我觉得没必要,我这都简化了。
// publish #include <cstdio> #include <chrono> #include <memory> #include <iostream> #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" using namespace std; int main(int argc, char **argv) { rclcpp::init(argc,argv); //创建一个publish auto node = rclcpp::Node::make_shared("cpp_demo_publisher"); //构建推送 auto publisher=node->create_publisher<std_msgs::msg::String>("my_topic",10); auto message = std_msgs::msg::String(); //1s内执行的速度 rclcpp::WallRate rate(1); for (int i = 0; i < 10; ++i) { message.data="这index值是:"+std::to_string(i); RCLCPP_INFO(node->get_logger(),"cpp publisher:%s",message.data.c_str()); publisher->publish(message); rclcpp::spin_some(node); rate.sleep(); } rclcpp::shutdown(); return 0; } // subscribe #include <memory> #include "rclcpp/rclcpp.hpp" #include "std_msgs/msg/string.hpp" using std::placeholders::_1; int main(int argc, char * argv[]){ rclcpp::init(argc, argv); auto node = rclcpp::Node::make_shared("cpp_demo_subscribe"); rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscriber = node->create_subscription<std_msgs::msg::String>("my_topic",10,[node](std_msgs::msg::String::SharedPtr msg){ RCLCPP_INFO(node->get_logger(), "I heard: '%s'", msg->data.c_str()); }); rclcpp::spin(node); rclcpp::shutdown(); return 0; }
- 后面的功能开发不再提交,后面笔记会再次从 摄像头 生成点云图开始,主要内容是ROS2+kinect2驱动开发。
🔗 前言
🔗 Robot ROS 操作系统列表
🔗 NO.1 Ros 安装&介绍
🔗 NO.2 Ros 第一个程序(cpp & py)
🔗 NO.3 Ros Topic 通讯
🔗 NO.4 Ros 消息补充&小乌龟
🔗 NO.5 Ros Turtle &日志开发
🔗 NO.6 Ros Service 简单通讯
🔗 NO.7 Ros Service 复杂通讯
🔗 NO.8 Ros PID算法案例
🔗 NO.9 PID原理&曲线计算原理
🔗 NO.10 Action 通讯 上
🔗 NO.11 Action 通讯 下
🔗 NO.12 Param & Launch & TF 案例
🔗 NO.13 ROS 2 Foxy安装(Mac OS)