赞
踩
参考链接;
古月居的视频+网站
【古月居】古月·ROS2入门21讲 | 带你认识一个全新的机器人操作系统_哔哩哔哩_bilibili
鱼香肉丝的帖子
【ROS2机器人入门到实战】_ros2机器人编程实战 pdf-CSDN博客
网上直接搜的新建项目的起手式都比较雷同,一般是要从克隆一个小乌龟的例程开始,可这在正常的任务中是没啥用的。
这篇笔记内容是正常情况下应该怎么开始一个ros2的项目,用来给自己做备忘的。防止自己以后把这些完全忘了,所以写的可能有点罗里吧嗦。文中特地创建了和古月居不同的路径,以帮助以后的自己对比两者发现本质。
如果刚好能帮到您,麻烦给个赞。
创建工作空间分三步:创建路径、编译工作空间、设置环境变量
首先,打开终端,用普通的命令创建一个文件夹作为工作空间的路径,且文件夹下右一个src子文件夹。
(后续的功能包啥的都是放到src子文件夹里的,所以必须有这个src)
(因为这里只是普通的新建文件夹,所以不使用命令行,直接到目标位置右键新建文件夹也行。)
工作空间的名称可以随便取,一般为了方便辨认,会在命名时加上“ws”字样
具体操作:在终端里敲以下代码:
mkdir -p ~/Code/ROS2/MyRobot_ws/src
这行代码里,-p表示自动创建目标目录的上层目录,即使该上层目录已经存在也不会报错。我将该工作空间创建到了我的~/Code/ROS2/目录下了,这个可以根据实际情况进行修改。
古月居的教程里,创建好路径后要去克隆一个小乌龟的例程,并且要自动安装小乌龟需要的依赖,本笔记只追求最简单的创建流程,所以不需要这样,直接进入下一步。
编译的操作是在刚才新建好的工作空间目录下执行:
- cd ~/Code/ROS2/MyRobot_ws/
- colcon build
执行后,MyRobot_ws文件夹内出现了一些新的文件夹:build, install, log
这里的colcon不用管是啥意思,和ros1里的catkin一样有历史渊源,感兴趣的同学自己去搜一下。
不光是创建工作空间时需要编译,以后创建功能包后,修改代码后都要重新编译
在古月居教程中,首先执行了安装命令,如图:
我感觉应该是不需要的,编译时如果有报错再安装吧。
编译完成后,还需要设置环境变量才能让系统知道这个工作空间内有哪些功能包和可执行文件,因此还需要设置环境变量。这一步的作用是初始化工作区
有两种操作,第一种操作使环境变量只在当前终端内生效,第二种操作使环境变量在所有终端生效。两种操作并不冲突,使用第二种后,也可以使用第一种手动刷新环境变量。
第一种:先进入工作空间路劲下,然后执行source
- cd ~/Code/ROS2/MyRobot_ws/
- source install/local_setup.sh
第二种:
echo " source ~/Code/ROS2/MyRobot_ws/install/local_setup.sh" >> ~/.bashrc
设置环境变量的操作在每次编译之后都要执行,编译后只要在所有终端上执行第一种设置环境变量的命令即可。若用第二种方法设置了环境变量,则打开新的终端时自动生效。
功能包都在工作空间下的src文件夹内新建,区别于工作空间,不能用普通的右键新建文件夹或者mkdir命令直接新建,需要使用ros2 pkg create 命令新建。
按照古月居的教程的内容,一个功能包里最好只有一种编程语言编写的内容,可是神奇的chatgpt说可以有多种不同的语言,需要修改CMakeLists.txt的内容。
这里将建立编译的常规方法和同时存在两种语言的进阶方法都试一下。实际操作中建议还是怎么简单怎么来,不同语言分不同的功能包算了。
在c++写的功能包里有两个比较重要的文件
创建计划使用C++编写的功能包:
- cd ~/Code/ROS2/MyRobot_ws/src
- ros2 pkg create rbt_ctrl_c --build-type ament_cmake --dependencies rclcpp std_msgs
package.xml
文件是ROS2项目的功能包管理文件。 CMakeLists.txt
是ROS2项目的编译配置文件
创建计划使用python编写的功能包:
- cd ~/Code/ROS2/MyRobot_ws/src
- ros2 pkg create rbt_ui_py --build-type ament_cmake --dependencies rclpy std_msgs
其中,依赖项可以先不写,可以在创建完成后在package.xml 中修改。
上面的依赖项中,std_msgs基本都能用上,所以写上去没啥坏处。
在创建好的功能包中,我们可以继续完成代码的编写,之后需要编译和配置环境变量,才能正常运行:
- cd ~/Code/ROS2/MyRobot_ws/
- colcon build # 编译工作空间所有功能包
- source install/local_setup.bash #配置环境初始化
占位---------暂时用不到,以后有空写。
c++版的功能包,将代码文件新建到功能包文件夹下的src文件夹内。
Python版的功能包,将代码文件新建到功能包文件夹下的与功能包同名的文件夹下。
举例:上一节创建的两个功能包的代码分别放在:
~/Code/ROS2/MyRobot_ws/src/rbt_ctrl_cpp/src/
~/Code/ROS2/MyRobot_ws/src/rbt_ui_py/rbt_ui_py/
我使用vscode写c++, 用pycharm写python。配置方法如下:
如果只想通过VS Code进行代码编写,不在里面运行,则只需要在常规的VSCode的C++配置的基础上再配置一下文件告诉软件应该到哪里找ROS2的库。具体步骤如下:
0. 按正常步骤在VSCode里打开src文件夹(或者功能包所在文件夹,或者代码所在文件夹,无所谓打开哪个,只要包含你要运行的代码应该就可以。保险起见,打开src文件夹吧),然后新建c++文件。 如果此时你的.vscode路径下没有出现settings.json,就去百度怎么搞出来这个。
1. 修改.vscode下的配置文件。目的是让软件能够找到将ROS2的库路径。
找到的两篇帖子一个改了c_cpp_properties.json,一个改了settings.json。我试了一下,我是两个文件都要改,莫名其妙的。将下面这个路径分别添加到两个文件里:
"/opt/ros/foxy/include/**"
位置是c_cpp_properties.json里的“includePath”后面的方括号里;settings.json文件里的“C_CPP_Runner.includePaths”后面的方括号里。如果方括号里已经有了其他路径,则用逗号隔开。
这样就可以在VS Code里看到代码提示了。
同样不追求在pycharm里直接调试,只要它能在写代码时给我提示就行。
用pycharm打开功能包所在文件夹,或者直接在功能包里创建项目 ,选一个或建一个常用的Python解释器,
然后在设置里讲ROS2的Python库的路径放到项目的内容根里。就行了。
我的这个库路径是:/opt/ros/foxy/lib/python3.8/site-packages ,把它添加到设置-项目-项目结构-添加内容根 里,即可。
我自己的代码还没写,这里先用古月居的例程代码做例子:至少能保证代码编写没问题,先确保流程没问题
(尴尬的是,古月居的c++订阅例程好像有点问题,我跑不起来)
文件路径:Code/ROS2/MyRobot_ws/src/rbt_ctrl_cpp/src/topic_helloworld_pub.cpp
- /***
- @作者: 古月居(www.guyuehome.com)
- @说明: ROS2话题示例-发布图像话题
- ***/
-
- #include <chrono>
- #include <functional>
- #include <memory>
- #include <string>
-
- #include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
- #include "std_msgs/msg/string.hpp" // 字符串消息类型
-
- using namespace std::chrono_literals;
-
-
- class PublisherNode : public rclcpp::Node
- {
- public:
- PublisherNode()
- : Node("topic_helloworld_pub") // ROS2节点父类初始化
- {
- // 创建发布者对象(消息类型、话题名、队列长度)
- publisher_ = this->create_publisher<std_msgs::msg::String>("chatter", 10);
- // 创建一个定时器,定时执行回调函数
- timer_ = this->create_wall_timer(
- 500ms, std::bind(&PublisherNode::timer_callback, this));
- }
-
- private:
- // 创建定时器周期执行的回调函数
- void timer_callback()
- {
- // 创建一个String类型的消息对象
- auto msg = std_msgs::msg::String();
- // 填充消息对象中的消息数据
- msg.data = "Hello World";
- // 发布话题消息
- RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", msg.data.c_str());
- // 输出日志信息,提示已经完成话题发布
- publisher_->publish(msg);
- }
-
- rclcpp::TimerBase::SharedPtr timer_; // 定时器指针
- rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_; // 发布者指针
- };
-
- // ROS2节点主入口main函数
- int main(int argc, char * argv[])
- {
- // ROS2 C++接口初始化
- rclcpp::init(argc, argv);
-
- // 创建ROS2节点对象并进行初始化
- rclcpp::spin(std::make_shared<PublisherNode>());
-
- // 关闭ROS2 C++接口
- rclcpp::shutdown();
-
- return 0;
- }
通过add_executable() 确定 可执行文件的名字:topic_helloworld_pub,用来生成可执行文件的c++文件:src/topic_helloworld_pub.cpp;
通过ament_target_dependencies() 说明该可执行文件的依赖,第一个位置放可执行文件的名字,后面放依赖的名称,这里是:topic_helloworld_pub rclcpp std_msgs,空格隔开。注意,这里似乎只需要写与ROS2相关的依赖,其他已安装的C++第三方库不需要在这里写。
- # find dependencies
- find_package(ament_cmake REQUIRED)
- find_package(rclcpp REQUIRED)
- find_package(std_msgs REQUIRED)
-
- add_executable(topic_helloworld_pub src/topic_helloworld_pub.cpp)
- ament_target_dependencies(topic_helloworld_pub rclcpp std_msgs)
-
- install(TARGETS
- topic_helloworld_pub
- DESTINATION lib/${PROJECT_NAME}
- )
如果功能包里有多个包含节点的C++文件,则 在install上方添加add_executable()和ment_target_dependencies(),在install内部换行添加节点名称。例:
- # find dependencies
- find_package(ament_cmake REQUIRED)
- find_package(rclcpp REQUIRED)
- find_package(std_msgs REQUIRED)
-
- add_executable(topic_helloworld_pub src/topic_helloworld_pub.cpp)
- ament_target_dependencies(topic_helloworld_pub rclcpp std_msgs)
- add_executable(topic_helloworld_sub src/topic_helloworld_sub.cpp)
- ament_target_dependencies(topic_helloworld_sub rclcpp std_msgs)
-
- install(TARGETS
- topic_helloworld_pub
- topic_helloworld_sub
- DESTINATION lib/${PROJECT_NAME}
- )
文件中其他内容为自动生成的,完整文件内容为:
- cmake_minimum_required(VERSION 3.5)
- project(rbt_ctrl_cpp)
-
- # Default to C99
- if(NOT CMAKE_C_STANDARD)
- set(CMAKE_C_STANDARD 99)
- endif()
-
- # Default to C++14
- if(NOT CMAKE_CXX_STANDARD)
- set(CMAKE_CXX_STANDARD 14)
- endif()
-
- if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
- add_compile_options(-Wall -Wextra -Wpedantic)
- endif()
-
- # find dependencies
- find_package(ament_cmake REQUIRED)
- find_package(rclcpp REQUIRED)
- find_package(std_msgs REQUIRED)
-
- add_executable(topic_helloworld_pub src/topic_helloworld_pub.cpp)
- ament_target_dependencies(topic_helloworld_pub rclcpp std_msgs)
- add_executable(topic_helloworld_sub src/topic_helloworld_sub.cpp)
- ament_target_dependencies(topic_helloworld_sub rclcpp std_msgs)
-
- install(TARGETS
- topic_helloworld_pub
- topic_helloworld_sub
- DESTINATION lib/${PROJECT_NAME}
- )
-
-
- if(BUILD_TESTING)
- find_package(ament_lint_auto REQUIRED)
- # the following line skips the linter which checks for copyrights
- # uncomment the line when a copyright and license is not present in all source files
- #set(ament_cmake_copyright_FOUND TRUE)
- # the following line skips cpplint (only works in a git repo)
- # uncomment the line when this package is not in a git repo
- #set(ament_cmake_cpplint_FOUND TRUE)
- ament_lint_auto_find_test_dependencies()
- endif()
-
- ament_package()
在该文件内添加depend标签并将相关依赖写进去:
- <depend>rclcpp</depend>
- <depend>std_msgs</depend>
文件里的其他内容是创建功能包并编译之后自动生成的,完整内容为:
- <?xml version="1.0"?>
- <?xml-model href="http://download.ros.org/schema/package_format3.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
- <package format="3">
- <name>rbt_ctrl_cpp</name>
- <version>0.0.0</version>
- <description>TODO: Package description</description>
- <maintainer email="zf@todo.todo">zf</maintainer>
- <license>TODO: License declaration</license>
-
- <buildtool_depend>ament_cmake</buildtool_depend>
-
- <depend>rclcpp</depend>
- <depend>std_msgs</depend>
-
- <test_depend>ament_lint_auto</test_depend>
- <test_depend>ament_lint_common</test_depend>
-
- <export>
- <build_type>ament_cmake</build_type>
- </export>
- </package>
编译:到工作空间的路径下执行build。不知道正不正常,我这里不能按tab自动补全名称
- cd Code/ROS2/MyRobot_ws
- colcon build --packages-select rbt_ctrl_cpp
运行:编译后在当前终端或打开新终端,先source, 后run
- source install/setup.bash
- ros2 run rbt_ctrl_cpp topic_helloworld_pub
注:修改代码后必须重新build, source, 才能生效。
同样使用古月居的例程代码:
文件路径:Code/ROS2/MyRobot_ws/src/rbt_ui_py/src/topic_helloworld_sub.py
- #!/usr/bin/env python3
- # -*- coding: utf-8 -*-
-
- """
- @作者: 古月居(www.guyuehome.com)
- @说明: ROS2话题示例-订阅“Hello World”话题消息
- """
-
- import rclpy # ROS2 Python接口库
- from rclpy.node import Node # ROS2 节点类
- from std_msgs.msg import String # ROS2标准定义的String消息
-
- """
- 创建一个订阅者节点
- """
- class SubscriberNode(Node):
-
- def __init__(self, name):
- super().__init__(name) # ROS2节点父类初始化
- self.sub = self.create_subscription(\
- String, "chatter", self.listener_callback, 10) # 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
-
- def listener_callback(self, msg): # 创建回调函数,执行收到话题消息后对数据的处理
- self.get_logger().info('I heard: "%s"' % msg.data) # 输出日志信息,提示订阅收到的话题消息
-
- def main(args=None): # ROS2节点主入口main函数
- rclpy.init(args=args) # ROS2 Python接口初始化
- node = SubscriberNode("topic_helloworld_sub") # 创建ROS2节点对象并进行初始化
- rclpy.spin(node) # 循环等待ROS2退出
- node.destroy_node() # 销毁节点对象
- rclpy.shutdown() # 关闭ROS2 Python接口
作用是告诉ROS2节点的入口:
- entry_points={
- 'console_scripts': ["topic_helloworld_sub = rbt_ui_py.topic_helloworld_sub:main"
- ],
当存在多个要编译的节点时,在[]内用逗号隔开。
鱼香肉丝的教程里这一步没有编译,大概是忘了
编译:
- cd Code/ROS2/MyRobot_ws
- colcon build --packages-select rbt_ui_py
运行:同样是在当前终端或新建终端都行,要先source
- source install/setup.bash
- ros2 run rbt_ui_py topic_helloworld_sub
这一步不是必须的,如果你要传递的消息类型比较简单,直接包含在标准库里,可以不用单独创建消息文件。这一步创建的消息文件可以类比成在c++中创建了一个类,只是这个类可以在ros2中的不同节点之间传递。
通常,消息文件被创建到相关的功能包内,(实测还是单独定义一个功能包放消息文件比较省事)
首先单独创建一个用来放消息文件的功能包,在功能包内新建一个msg文件夹,在msg文件夹内新建 .msg文件,在 .msg文件内定义消息结构,然后在功能包的package.xml内添加消息依赖,在cmakelist.txt文件内添加编译指令。然后到工作空间目录中用colcon编译。若使用Python写功能包,则在setup.py里添加编译指令。
若消息文件同时被好多功能包共用,也可以考虑为消息文件单独创建一个功能包。
注意:msg文件的命名必须以大写字母开头,且名字里只能有大写、小写、数字,以.msg作为后缀。 msg内部变量的名称必须以小写字母开头,变量名中只能有小写字母或数字下划线。
例子
UI和后台控制主程序之间通讯的消息,定义在主程序的功能包内。单独建一个消息功能包,并新建文件夹msg:
- cd Code/ROS2/MyRobot_ws/src
- ros2 pkg create --build-type ament_cmake rbt_msgs
- cd rbt_msgs
- mkdir msg
然后在文件夹msg内新建.msg文件(可以不新建msg文件夹,只要.msg文件在功能包内且编译文件里写对.msg文件的路径就行。但是这样做更有条理)。我在msg里新建的.msg文件名字是RbtState.msg。文件内容为:
- # rbt_state.msg
-
- # current state of each axis. get the value from series communication
- float32[6] axis_pos
- float32[6] axis_vel
- float32[6] axis_acc
-
- # current state of the robot platform. values are calculated by forward kinemics
- float32[6] platform_pos
- float32[6] platform_vel
- float32[6] platform_acc
-
- # current value of force sensors
- float32[6] force_sens
这里每个变量都是长度为6的数组所以都有“[6]”。如上文所言,注意文件和变量的命名规则。
写好.msg文件后,在CMakeLists.txt里添加以下内容:
- find_package(rosidl_default_generators REQUIRED)
-
- rosidl_generate_interfaces(
- ${PROJECT_NAME}
- "msg/RbtState.msg"
- )
在package.xml里添加:
- <buildtool_depend>rosidl_default_generators</buildtool_depend>
- <exec_depend>rosidl_default_runtime</exec_depend>
- <member_of_group>rosidl_interface_packages</member_of_group>
然后回到工作空间目录进行编译即可:
- cd ~/Code/ROS2/MyRobot_ws
- colcon build --symlink-install --packages-select rbt_msgs
若出现以下警告:
[6.241s] WARNING:colcon.colcon_core.package_selection:Some selected packages are already built in one or more underlay workspaces: 'rbt_msgs' is in: /home/zf/Code/ROS2/MyRobot_ws/install/rbt_msgs If a package in a merged underlay workspace is overridden and it installs headers, then all packages in the overlay must sort their include directories by workspace order. Failure to do so may result in build failures or undefined behavior at run time. If the overridden package is used by another package in any underlay, then the overriding package in the overlay must be API and ABI compatible or undefined behavior at run time may occur. If you understand the risks and want to override a package anyways, add the following to the command line: --allow-overriding rbt_msgs那么,在编译时添加它提到的语句,完整命令是:
colcon build --symlink-install --packages-select rbt_msgs --allow-overriding rbt_msgs
编译完成后可以在以下路径里分别看到该消息类型的.hpp和.py文件 :
- /Code/ROS2/MyRobot_ws/install/rbt_msgs/include/rbt_msgs/msg
- /Code/ROS2/MyRobot_ws/install/rbt_msgs/lib/python3.8/site-packages/rbt_msgs/msg
我在编译时因为各种命名错误报了好多次错误,还是要好好读文档呀。。。
同时我也尝试了在Python编译的功能包里添加消息类型,需要在setup.py文件里添加消息文件的路径,结果编译后找不到.hpp和.py文件。赶时间,就不钻研Python的消息怎么编译了,大不了单独建一个c++编译的功能包专门用来编译消息文件。
例:在rbt_ctrl_cpp/src/robot_ctrl_main.cpp内调用该消息类型
在vscode里写代码时,可以将/Code/ROS2/MyRobot_ws/install/rbt_msgs/include/放到c_cpp_properties.json文件夹里,以便写代码时获得自动补全:
- "includePath": [
- "${workspaceFolder}/**",
- "/opt/ros/foxy/include",
- "/home/zf/Code/ROS2/MyRobot_ws/install/rbt_msgs/include"
- ],
注意修改这里的引用路径并不会影响编译,因为我们的编译规则其实是靠CMakeLists.txt完成的。这里写这些只是为了方便写代码。
文件为robot_ctrl_main.cpp
#include "rbt_msgs/msg/rbt_state.hpp"
使用:
- // 下面两种声明方法看具体使用场景选一种就行了
-
- rbt_msgs::msg::RbtState rbt_sta; // 声明变量
-
- auto rbt_para2 = std::shared_ptr<rbt_msgs::msg::RbtState>();//创建共享指针
CMakeLists.txt:
- find_package(rbt_msgs REQUIRED)
-
- add_executable(rbt_ctrl_main src/robot_ctrl_main.cpp)
- ament_target_dependencies(rbt_ctrl_main rclcpp std_msgs rbt_msgs)
package.xml:
<depend>rbt_msgs</depend>
后续可能根据经验进行增删修改。
加油!
2024.03.13 修改了新建消息类型的相关内容
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。