失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > ROS 2 基本命令总结

ROS 2 基本命令总结

时间:2020-09-30 22:37:09

相关推荐

ROS 2 基本命令总结

ROS 2 基本命令总结

1. 创建工作空间2. 实例测试3. terminal 命令3.1 run3.2 node3.2.1 node list3.2.2 Remapping 3.2.3 node info3.3 topic3.3.1 rqt_graph3.3.2 topic list3.3.3 topic echo3.3.4 topic echo3.3.5 topic pub3.3.6 topic hz 3.4 service3.5 param3.5.1 param list3.5.2 param get3.5.3 param set3.5.4 param dump3.5.5 load param file3.6 action3.6.1 action list 3.7 bag3.7.1 bag record3.7.2 Record multiple topics3.7.3 bag info3.7.4 bag play 4. Package4.1 创建 package4.2 编写一个简单的publisher subscriber程序4.2.1 编写publisher程序4.2.2 添加依赖项4.2.3 CMakeLists.txt4.2.4 编写subscriber程序 5. 创建msg和srv5.1 生成功能包5.1.1 msg文件定义5.1.2 srv文件定义5.1.3 CMakeLists.txt5.1.4 package.xml 5.2 测试接口5.2.1 测试 Num.msg 6. Launch6.1 运行方式 7. colcon8. ament_cmake8.1 基本的项目框架8.2 添加库文件和头文件8.3 添加依赖项

1. 创建工作空间

跟ROS相同,ROS 2也是建议创建一个工作空间,方便管理同一个项目的packages,而且也是将package源文件都放入src文件夹中。

创建工作空间:

mkdir -p ~/colcon_ws/srccd colcon_ws/src

现在我们先关注 colcon 的编译过程,所以 package 源文件就先借用官网的。

git clone /ros2/examples git checkout $ROS_DISTRO# 切换到与本机版本对应的 branch 上# 电脑上要是有两个ROS系统,需要先 source /opt/ros/eloquent/setup.bash

用colcon编译下载的package

cd ..colcon build

编译后,产生了build,install,log三个新文件夹。

如果要单独编译某一个package,可以用如下命令:

colcon build --packages-select PACKAGE_NAME

如果不希望编译某一个package,可以在该package中创建名为 COLCON_IGNORE 的空文件,colcon就会忽略掉该package,不但不编译,连colcon list都不显示,这个package对colcon就是透明的。

完成编译之后,要source一下setup.bash文件,确保系统能够找到当前编译生成的可执行文件和库:

source install/setup.bash

或者将source命令放入.bashrc文件,这样每次打开terminal就可以自动加载路径信息:

echo "source ~/colcon_ws/install/setup.bash" >> ~/.bashrc

我自己的.bashrc文件修改如下(针对电脑双ROS):

这里为老版本,使用起来比较方便,但是在部分程序的build等步骤会杂糅很多不需要的东西:

# version 1.0CUR_PATH=$(pwd | cut -d / -f 4)if [[ "$CUR_PATH" == "colcon_ws" ]];thensource /opt/ros/eloquent/setup.bashsource ~/colcon_ws/install/setup.bashecho 'ROS 2 sourced'elsesource /opt/ros/melodic/setup.bashsource ~/catkin_ws/devel/setup.bashecho 'ROS sourced'fi

因此修正后的代码为:

# version 2.0alias ros1_="source /opt/ros/melodic/setup.bash && source ~/catkin_ws/devel/setup.bash && echo 'ROS sourced' "alias ros2_="source /opt/ros/eloquent/setup.bash && source ~/colcon_ws/install/setup.bash && echo 'ROS 2 sourced'"

2. 实例测试

首先启动一个publisher

ros2 run examples_rclcpp_minimal_publisher publisher_member_function

再启动一个subscriber

ros2 run examples_rclcpp_minimal_subscriber subscriber_member_function

终端显示如下:

这里与 ROS 最大的区别是 不需要启动 ROS master 节点,即不需要类似 roscore 的命令。ROS 2 是真正的分布式系统,不需要中心节点,这样系统的鲁棒性更强,不会因为中心节点失效而影响整个系统。

3. terminal 命令

ROS 2根据命令的作用对象划分成多个类别,其中常用的几个类别:

Commands:launchRun a launch filenode Various node related sub-commandsparamVarious param related sub-commandspkg Various package related sub-commandsrun Run a package specific executableservice Various service related sub-commandssrv Various srv related sub-commandstopicVarious topic related sub-commands

在调用时都是采用如下命令格式:

ros2 COMMAND ...

我们可以对比ROS和ROS 2中的几个命令,应该很容易找到其中的规律

运行ROS node

- ROS:rosrun <package_name> <executable_name>

- ROS 2:ros2 run <package_name> <executable_name>

查看当前运行的node

- ROS:rosrun <package_name> <executable_name>

- ROS 2:rosrun <package_name> <executable_name>

3.1 run

命令ros2 run从一个package中情动可执行文件:

ros2 run <package_name> <executable_name>

3.2 node

3.2.1 node list

ros2 node list将会展示你所有运行着的节点名称:

ros2 node list

3.2.2 Remapping

Remapping允许你重新分配默认节点属性,像节点名,topic名,service名等等;

重新分配节点名/turtlesim的方式如下:

ros2 run turtlesim turtlesim_node --ros-args --remap __node:=my_turtle

此时会出现两个节点名:

/turtlesim/my_turtle

3.2.3 node info

node info的方式可以获取节点的更多信息:

ros2 node info <node_name>

3.3 topic

topic不一定是一对一的通信,它也可以是一对多、多对一或者多对多的。

3.3.1 rqt_graph

rqt_graph用来可视化正在改变的node和topic。

要运行rqt_graph,需打开一个新终端并输入命令:

rqt_graph

3.3.2 topic list

在新的终端下运行ros2 topic list命令会返回在系统内所有当前活跃的topic:

ros2 topic list

ros2 topic list -t会返回相同的topic列表,这时候topic会额外显示其类型:

ros2 topic list -t

差异如下:

ros2 topic list

/parameter_events/rosout/turtle1/cmd_vel

ros2 topic list -t

/parameter_events [rcl_interfaces/msg/ParameterEvent]/rosout [rcl_interfaces/msg/Log]/turtle1/cmd_vel [geometry_msgs/msg/Twist]

3.3.3 topic echo

浏览数据在话题上发布:

ros2 topic echo <topic_name>

3.3.4 topic echo

浏览数据在话题上发布:

ros2 topic echo <topic_name>

3.3.5 topic pub

该命令可以用来直接从命令行向一个topic内发布数据:

ros2 topic pub <topic_name> <msg_type> '<args>'

<args>参数是实际数据,需要以 YAML 语法输入,示例如下:

例1:

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}}"

其中 --once 是一个可选参数,其意味着 “发布一个消息然后退出”。

例2:

ros2 topic pub --rate 1 /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}}"

这里 --rate 1 代替了 --once 选项,意思是以一个 1Hz 的稳定流发布命令。

3.3.6 topic hz

使用该命令可以发布数据的速率:

ros2 topic hz /turtle1/pose

输出类似如下:

average rate: 59.354min: 0.005s max: 0.027s std dev: 0.00284s window: 58

3.4 service

service是另一种节点通信方法。service基于call-response模式,与publisher-subscriber模式相对。topic允许节点suscribe数据流并获得持续更新,而service仅在被client指定调用时才会提供数据。

service 的使用方法与 topic 类似。

3.5 param

3.5.1 param list

为了查看参数的节点归属,可以输入如下命令:

ros2 param list

就可以看到每个节点的子命名空间,紧接着的是每个节点的参数:

/teleop_turtle:scale_angularscale_linearuse_sim_time/turtlesim:background_bbackground_gbackground_ruse_sim_time

需要注意的是,每个节点都有参数use_sim_time,它并不是独一无二的。

3.5.2 param get

该命令用于获取当前参数的值:

ros2 param get <node_name> <parameter_name>

例如探究上述/turtlesim内的参数background_g:

ros2 param get /turtlesim background_g

其参数值为:

Integer value is: 86

现在就可以知道background_g保留一个整数值。

3.5.3 param set

为了在运行时间内改变参数值,使用命令:

ros2 param set <node_name> <parameter_name> <value>

例如:

ros2 param set /turtlesim background_r 150

3.5.4 param dump

可以将一个节点的所有参数值“dump”进一个文件以储存,使得之后能使用该命令:

ros2 param dump <node_name>

例如,如果想储存节点/turtlesim当前的配置参数,可以输入命令:

ros2 param dump /turtlesim

终端将会返回消息:

Saving to: ./turtlesim.yaml

你将发现在该工作空间目录有一个新文件被写入了。打开该文件,会显示以下内容:

turtlesim:ros__parameters:background_b: 255background_g: 86background_r: 150use_sim_time: false

该参数表需要手动加载。

3.5.5 load param file

为了使用在上一小节中保存的参数值,输入:

ros2 run <package_name> <executable_name> --ros-args --params-file <file_name>

与之前运行可执行文件方式一样,就是多了两个flags: ‘–ros-args’ 和 ‘–params-file’,之后再接上想要加载的文件。

紧接着上一节的示例,想要读取该参数,需输入命令:

ros2 run turtlesim turtlesim_node --ros-args --params-file ./turtlesim.yaml

3.6 action

ROS中常用的通信机制是话题(Topic)和服务(Service),但是在很多场景下,这两种通信机制往往满足不了所有需求。比如用话题发布运动目标,由于话题是单向通信,则需要另外订阅一个话题来获得机器人运动过程中的状态反馈。如果用服务发布运动目标,虽然可获得一次反馈信息,但是对于控制来说数据太少,而且当反馈迟迟没有收到时,就只能傻傻等待,做不了其他事情。

action是一种类似于 Service 的问答通信机制,不同之处在于 action 带有连续反馈,可以不断反馈任务进度,也可以在任务过程中中止运行(actions are preemptable)。使用 action 发布机器人的运动目标,机器人在收到这个 action 后就开始运动,在运动过程中不断反馈当前的运动状态;过程中也可以随时取消运动,让机器人停止;当机器人完成运动目标后,action 返回任务完成的消息。

3.6.1 action list

为了识别在ROS中所有的 action,运行此命令:

ros2 action list

3.7 bag

3.7.1 bag record

记录发布到一个 topic 的消息可使用命令语句:

ros2 bag record <topic_name>

3.7.2 Record multiple topics

也可以同时记录多个话题:

ros2 bag record -o subset /turtle1/cmd_vel /turtle1/pose

选项-o允许给 bag 文件定义一个独一无二的名称。紧接着的字符串,subset就是文件名。

3.7.3 bag info

该命令用来查看记录好的 bag:

ros2 bag info <bag_file_name>

3.7.4 bag play

该命令用于播放记录好的 bag:

ros2 bag play <bag_file_name>

注意,与 ROS 不同的是,该命令并不会显示播放的时长以及总时长。

4. Package

4.1 创建 package

在 ROS 2 内,创建一个新的功能包的命令语法为:

ros2 pkg create --build-type ament_cmake <package_name>

例如使用语句:

ros2 pkg create --build-type ament_cmake --node-name my_node my_package

上述语句多使用了一个可选参数 --node-name,它产生了一个叫my_node的 Hello World 类型可执行文件(在src中创建了名为my_node.cpp的文件)。

在 build 的时候要是只想搭建功能包 my_package,可以运行:

colcon build --packages-select my_package

一般来说,使用以下命令会更方便:

ros2 pkg create --build-type ament_cmake <pkg-name> --dependencies [deps]# ros2 pkg create --build-type ament_cmake cpp_parameters --dependencies rclcpp

--dependencies后接所需要的依赖项,这样依赖项会被自动添加进package.xmlCMakeLists.txt内。

4.2 编写一个简单的publisher subscriber程序

4.2.1 编写publisher程序

使用示例文件 publisher_member_function.cpp

#include <chrono> // C++11日期和时间库#include <functional>#include <memory>#include <string>#include "rclcpp/rclcpp.hpp" // 包含了 ROS2 系统的大多部分(rclcpp:ROS Client Library for C++)#include "std_msgs/msg/string.hpp" // 这里跟 ROS1 区别不大using namespace std::chrono_literals;/* This example creates a subclass of Node and uses std::bind() to register a* member function as a callback from the timer. */// 通过从 rclcpp::Node 继承创建节点类。代码中的每一个this都指代这个nodeclass MinimalPublisher : public rclcpp::Node{public:MinimalPublisher(): Node("minimal_publisher"), count_(0) //节点被命名为了 minimal_publisher,初始化count_为0{publisher_ = this->create_publisher<std_msgs::msg::String>("topic", 10);timer_ = this->create_wall_timer( // 初始化timer_500ms, std::bind(&MinimalPublisher::timer_callback, this)); // 一秒执行两次timer_callback}private:void timer_callback(){auto message = std_msgs::msg::String();message.data = "Hello, world! " + std::to_string(count_++);RCLCPP_INFO(this->get_logger(), "Publishing: '%s'", message.data.c_str());// 确保每一个被发布的消息都被打印到控制台publisher_->publish(message);}rclcpp::TimerBase::SharedPtr timer_;rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_;size_t count_;};int main(int argc, char * argv[]){rclcpp::init(argc, argv); // 初始化 ROS2rclcpp::spin(std::make_shared<MinimalPublisher>()); // 开始从节点处理数据rclcpp::shutdown();return 0;}

4.2.2 添加依赖项

使用文本编译器打开package.xml文件,复制以下命令:

<depend>rclcpp</depend><depend>std_msgs</depend>

这声明了功能包在它的代码执行过程中需要rclppstd_msgs

4.2.3 CMakeLists.txt

打开文件 CMakeLists.txt,添加依赖项:

find_package(ament_cmake REQUIRED)find_package(rclcpp REQUIRED)find_package(std_msgs REQUIRED)

之后,添加可执行文件并命名为talker这样就可以使用节点运行 ros2 run:

add_executable(talker src/publisher_member_function.cpp)ament_target_dependencies(talker rclcpp std_msgs)

ament_target_dependencies的用法为:

ament_target_dependencies(<executable-name> [dependencies])

该 CMake 宏用来创建可执行节点并且与依赖项关联起来。

最后,添加 install(TARGETS…) 部分,这样 ros2 run 可以找到该可执行文件:

install(TARGETStalkerDESTINATION lib/${PROJECT_NAME})

为了 install 启动文件和节点,可以使用install()宏放置在文件最后,但在宏ament_package()之后, 示例如下:

# Install launch filesinstall(DIRECTORY launchDESTINATION share/${PROJECT_NAME})# Install nodesinstall(TARGETS [node-names]DESTINATION lib/${PROJECT_NAME})

在精简了 CMakeLists.txt 后,它的样子如下:

cmake_minimum_required(VERSION 3.5)project(cpp_pubsub)# Default to C++14if(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_package(ament_cmake REQUIRED)find_package(rclcpp REQUIRED)find_package(std_msgs REQUIRED)add_executable(talker src/publisher_member_function.cpp)ament_target_dependencies(talker rclcpp std_msgs)install(TARGETStalkerDESTINATION lib/${PROJECT_NAME})ament_package()

4.2.4 编写subscriber程序

使用示例文件 subscriber_member_function.cpp

#include <memory>#include "rclcpp/rclcpp.hpp"#include "std_msgs/msg/string.hpp"using std::placeholders::_1;class MinimalSubscriber : public rclcpp::Node{public:MinimalSubscriber(): Node("minimal_subscriber"){subscription_ = this->create_subscription<std_msgs::msg::String>("topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));}private:void topic_callback(const std_msgs::msg::String::SharedPtr msg) const{RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg->data.c_str());}rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscription_;};int main(int argc, char * argv[]){rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<MinimalSubscriber>());rclcpp::shutdown();return 0;}

在这里程序与 Publisher 程序基本相同,除了 spin 的含义。对于 publisher 节点,spin意味着启动计时器,但对于 subscriber 来说,它只是意味着准备在消息到来时接收消息。

5. 创建msg和srv

5.1 生成功能包

msg 和 srv 文件内构造与 ROS1 相同。

5.1.1 msg文件定义

创建一个称作Num.msg的新文件,里面写入代码:

int64 num

该自定义消息,传输一个名为num的64位整数。

5.1.2 srv文件定义

创建一个称作AddThreeInts.srv的新文件,里面写入代码:

int64 aint64 bint64 c---int64 sum

该自定义消息,它 request 三个名为 a、b 和 c 的整数,并用一个名为sum的整数 respond。

5.1.3 CMakeLists.txt

要将您定义的接口转换为特定于语言的代码(如c++和Python),以便在这些语言中使用它们,请向CMakeLists.txt添加以下代码行:

find_package(rosidl_default_generators REQUIRED)rosidl_generate_interfaces(${PROJECT_NAME}"msg/Num.msg""srv/AddThreeInts.srv")

5.1.4 package.xml

由于接口依赖rosidl_default_generators来生成语言指定代码,需要声明一个依赖性:

<build_depend>rosidl_default_generators</build_depend><exec_depend>rosidl_default_runtime</exec_depend><member_of_group>rosidl_interface_packages</member_of_group>

5.2 测试接口

5.2.1 测试 Num.msg

与之前的程序相比,微调了一些地方:

Publisher:

#include <chrono>#include <memory>#include "rclcpp/rclcpp.hpp"#include "tutorial_interfaces/msg/num.hpp"// CHANGEusing namespace std::chrono_literals;class MinimalPublisher : public rclcpp::Node{public:MinimalPublisher(): Node("minimal_publisher"), count_(0){publisher_ = this->create_publisher<tutorial_interfaces::msg::Num>("topic", 10); // CHANGEtimer_ = this->create_wall_timer(500ms, std::bind(&MinimalPublisher::timer_callback, this));}private:void timer_callback(){auto message = tutorial_interfaces::msg::Num(); // CHANGEmessage.num = this->count_++;// CHANGERCLCPP_INFO(this->get_logger(), "Publishing: '%d'", message.num); // CHANGEpublisher_->publish(message);}rclcpp::TimerBase::SharedPtr timer_;rclcpp::Publisher<tutorial_interfaces::msg::Num>::SharedPtr publisher_; // CHANGEsize_t count_;};int main(int argc, char * argv[]){rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<MinimalPublisher>());rclcpp::shutdown();return 0;}

Subscriber:

#include <memory>#include "rclcpp/rclcpp.hpp"#include "tutorial_interfaces/msg/num.hpp"// CHANGEusing std::placeholders::_1;class MinimalSubscriber : public rclcpp::Node{public:MinimalSubscriber(): Node("minimal_subscriber"){subscription_ = this->create_subscription<tutorial_interfaces::msg::Num>(// CHANGE"topic", 10, std::bind(&MinimalSubscriber::topic_callback, this, _1));}private:void topic_callback(const tutorial_interfaces::msg::Num::SharedPtr msg) const // CHANGE{RCLCPP_INFO(this->get_logger(), "I heard: '%d'", msg->num); // CHANGE}rclcpp::Subscription<tutorial_interfaces::msg::Num>::SharedPtr subscription_; // CHANGE};int main(int argc, char * argv[]){rclcpp::init(argc, argv);rclcpp::spin(std::make_shared<MinimalSubscriber>());rclcpp::shutdown();return 0;}

CMakeLists.txt:

find_package(ament_cmake REQUIRED)find_package(rclcpp REQUIRED)find_package(tutorial_interfaces REQUIRED)# CHANGEadd_executable(talker src/publisher_member_function.cpp)ament_target_dependencies(talker rclcpp tutorial_interfaces) # CHANGEadd_executable(listener src/subscriber_member_function.cpp)ament_target_dependencies(listener rclcpp tutorial_interfaces)# CHANGEinstall(TARGETStalkerlistenerDESTINATION lib/${PROJECT_NAME})ament_package()

package.xml:

<depend>tutorial_interfaces</depend>

6. Launch

使用 ROS2 启动命令运行一个单独的启动文件将会立即启动整个系统——所有节点及其配置。这在程序的运行中是非常方便的。需要注意的是,ROS2 与 ROS1 在 Launch 文件上有很大的不同。ROS1 使用的是.launch文件,ROS2 使用的是.py文件。

示例文件 turtlesim_mimic_launch.py(版本:Eloquent)

// 引入python的launch模块from launch import LaunchDescriptionfrom launch_ros.actions import Nodedef generate_launch_description():return LaunchDescription([Node(package='turtlesim',node_namespace='turtlesim1',node_executable='turtlesim_node',node_name='sim'),Node(package='turtlesim',node_namespace='turtlesim2',node_executable='turtlesim_node',node_name='sim'),Node(package='turtlesim',node_executable='mimic',node_name='mimic',remappings=[('/input/pose', '/turtlesim1/turtle1/pose'),('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),])])

这里程序创建了三个节点,所有的都来自于 turtlesim 功能包。系统的目标是打开两个 turtlesim 窗口,其中一只乌龟模仿另一只乌龟的移动。

其中,如下语句为launch文件描述的开始:

def generate_launch_description():return LaunchDescription([])

下述的两个节点:

Node(package='turtlesim',node_namespace='turtlesim1',node_executable='turtlesim_node', // 可执行程序的名称node_name='sim'),Node(package='turtlesim',node_namespace='turtlesim2',node_executable='turtlesim_node',node_name='sim'),

注意,这两个节点之间的唯一区别是它们的 namespace 值。独一无二的 namespace 允许系统在没有 node name 或 topic name 冲突的情况下启动两个模拟器。

在这个系统中,两只海龟都接收到关于同一主题的命令,并发布它们关于同一主题的姿态。如果没有独一无二的 namespace,就没有办法区分属于某个turtle的消息和属于另一个turtle的消息。

最后的一个节点也来自于 turtlesim 功能包,但是有不同的可执行文件:

Node(package='turtlesim',node_executable='mimic',node_name='mimic',remappings=[('/input/pose', '/turtlesim1/turtle1/pose'),('/output/cmd_vel', '/turtlesim2/turtle1/cmd_vel'),])

mimic/input/posetopic 被重映射为了/turtlesim1/turtle1/pose/output/cmd_vel话题被重映射为了/turtlesim2/turtle1/cmd_vel。这意味着 mimic 会订阅/turtlesim1/sim的位姿 topic 并重新发布它给/turtlesim2/sim的速度命令 topic 来订阅。

这意味着mimic将订阅/turtlesim1/sim的pose主题,并为/turtlesim2/sim的velocity命令主题重新发布要订阅的主题。

综上,大致的节点间通信如下:

6.1 运行方式

Lauch 文件有两种运行方法:

在当前路径下打开:

ros2 launch <launch_file_name> # ros2 launch turtlesim_mimic_launch.py

提供功能包名称打开:

ros2 launch <package_name> <launch_file_name>

7. colcon

默认情况下,它将创建以下目录作为src目录的对等目录:

build:用来存储中间文件。对于每一个功能包,将创建一个子文件夹,如 CMake 被调用的子文件夹。install:每个功能包被配置到的位置。在默认情况下,每一个功能包都会被配置到一个独立的子目录中。log:包含关于每次 colcon 调用的各种日志信息。

安装目录是每个包将被安装到的位置。默认情况下,每个包将安装到单独的子目录中。

日志目录包含关于每次colcon调用的各种日志信息。

需要注意的是,与 ROS1 使用到的 catkin 不同,colcon 没有 devel 目录。

8. ament_cmake

在命令行上使用ros2 pkg create可以生成一个基本的 CMake 框架,然后将基本构建信息收集到两个文件中:package.xmlCMakeLists.txt

package.xml必须包含所有依赖项和一些元数据,以便让 colcon 为这个功能包找到正确的构建顺序,在CI中安装所需的依赖项,并为bloom发行版提供信息。

CMakeLists.txt包含构建+功能包的可执行文件和库 的命令,这将是本届主要的关注点。

8.1 基本的项目框架

一个 ament 功能包的CMakeLists.txt的基本框架包含:

cmake_minimum_required(VERSION 3.5)project(my_project)ament_package()

参数project表示功能包名称,必须要与在package.xml内的功能包名称相同。

项目设置由ament_package()完成,每个功能包它必须也只能被调用一次。ament_package()配置package.xml,注册带 ament 索引的功能包,并为 CMake 搭建配置文件,以便其他使用find_package的功能包可以找到它。因为ament_package()CMakeLists.txt收集了很多信息,它需要被CMakeLists.txt最后调用。尽管可以在调用install函数复制文件和目录之后调用ament_package(),但是将ament_package()恒作为最后的调用更简便。

ament_package()可以被提供的额外参数:

CONFIG_EXTRASCONFIG_EXTRAS_POST

8.2 添加库文件和头文件

有两个主要的目标需要搭建:由add_library搭建的库文件以及由add_executable搭建的可执行文件。

由于C/ c++中头文件和实现文件的分离,所以并不总是需要将这两个文件作为参数添加到add_library/add_executable中。

以下是建议的最佳做法:

如果您正在构建一个库,请将所有 clinet 应该会使用到的头文件放在一边,因此必须安装到 include 文件夹的子目录中,而所有其他文件(不应该导出的.c/.cpp和头文件)都放在src文件夹中。只有 cpp 文件在add_libraryadd_executable调用中被显示引用。允许通过以下方式查找头文件:

target_include_directories(my_targetPUBLIC$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>$<INSTALL_INTERFACE:include>)

这个命令在 build 期间内和被 installed 时添加了所有在文件夹${CMAKE_CURRENT_SOURCE_DIR}/include内的文件到公共接口。

原则上,如果${CMAKE_CURRENT_SOURCE_DIR}${CMAKE_INSTALL_DIR}同时调用了include和顶层文件夹,那么使用生成器表达式在这里是没有必要的,但它是非常常见的。

8.3 添加依赖项

有两种方式来连接功能包和新的依赖项:

第一种为推荐的方式,使用 ament 宏ament_target_dependencies。举个栗子,假设想连接 my_target 和线性几何库 Eigen3。

find_package(Eigen3 REQUIRED)ament_target_dependencies(my_target Eigen3)

这包含了必须的头文件和库文件,并且它们的依赖项会被项目正确找到。它也确保了所有依赖项的 include 目录顺序正确,在使用 overlay 工作空间的时候。

第二种方式是使用target_link_libraries

find_package(Eigen3 REQUIRED)target_link_libraries(my_target Eigen3::Eigen)

这也将包括必要的头文件、库和它们的依赖关系,但是与ament_target_dependencies相比,在使用 overlay 工作区时,它可能不会正确地对依赖关系排序。

如果觉得《ROS 2 基本命令总结》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。