- 2025-03-18
-
发表了主题帖:
《ROS2智能机器人开发与实践》——ROS核心(节点、话题、服务、DDS通信协议等)
# 《ROS2智能机器人开发与实践》——ROS核心(节点、话题、服务、DDS通信协议等)
------------
###### 今天主要是分享一下ROS2的核心包括节点、话题、服务和DDS通信协议的内容
###### 其中会对官方和本书的相关细节进行比对
###### 本文章都以C++为例
## 一、节点
### 1、ROS2中的节点具有以下几个特点
1. 每个节点是一个进程
2. 每个节点都是一个可以独立运行的可执行文件
3. 每个节点可使用不同的编程语言
4. 每个节点可分布式运行在不同的主机中
5. 每个节点需要唯一的名称
### 2、节点编程方法(C++)
#### a.C++程序编写

```cpp
#include "rclcpp/rclcpp.hpp"
/***
创建一个HelloWorld节点, 初始化时输出“hello world”日志
***/
class HelloWorldNode : public rclcpp::Node
{
public:
HelloWorldNode()
: Node("node_helloworld_class") // ROS2节点父类初始化
{
while(rclcpp::ok()) // ROS2系统是否正常运行
{
RCLCPP_INFO(this->get_logger(), "Hello World"); // ROS2日志输出
sleep(1); // 休眠控制循环时间
}
}
};
// ROS2节点主入口main函数
int main(int argc, char * argv[])
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
// 创建ROS2节点对象并进行初始化
rclcpp::spin(std::make_shared());
// 关闭ROS2 C++接口
rclcpp::shutdown();
return 0;
}
```
> ** 可以看的出来ROS2对节点的编程推荐`面向对象编程`的编程方法**
> 同时ROS2的编程难度更大、C++的语法更新、所以使用python也不失为一种方法
#### b.设置编译选项(编译接口)
```
find_package(rclcpp REQUIRED)
add_executable(node_helloworld_class src/node_helloworld_class.cpp)
ament_target_dependencies(node_helloworld_class rclcpp)
install(TARGETS
node_helloworld_class
DESTINATION lib/${PROJECT_NAME})
```
前三行相对于ROS1没什么太大区别,主要是多了最后这个
> 将编译生成的可执行文件拷贝到install安装空间的lib文件夹下
>我们知道ROS2相对于ROS1的工作空间构建方式变了,其中多了install文件夹 所以要注意最后一行
#### c.编译运行
```
#进入工作空间后
colcon build #编译工作空间
ros2 run learning_node_cpp node_helloworld_class # (功能包 可执行文件)
ros2 node list # 查看节点列表
ros2 node info # 查看节点信息
```
## 二、话题

### 1.rqt_graph
```
ros2 run turtlesim turtlesim_node
ros2 run turtlesim turtle_teleop_key
rqt_graph
```
我们将使用 `rqt_graph` 来可视化不断变化的节点和主题,以及它们之间的联系。

#### 话题的特点:
- 多对多通信
- 异步通信
- 消息接口
> 话题通信的一个特性,就是`异步通信`所谓异步,只要是指发布者发出数据后,并不知道订阅者什么时候可以收到。所以ROS2的后台会有一个轮询机制,当发现有数据进入消息队列时,就会触发回调函数
### 2.发布话题
```cpp
/***
@作者: 古月居(www.guyuehome.com)
@说明: ROS2话题示例-发布图像话题
***/
#include
#include
#include
#include
#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("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::SharedPtr publisher_; // 发布者指针
};
// ROS2节点主入口main函数
int main(int argc, char * argv[])
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
// 创建ROS2节点对象并进行初始化
rclcpp::spin(std::make_shared());
// 关闭ROS2 C++接口
rclcpp::shutdown();
return 0;
}
```
>都是面向对象编程
### 3.订阅话题
```cpp
/***
@作者: 古月居(www.guyuehome.com)
@说明: ROS2话题示例-订阅“Hello World”话题消息
***/
#include
#include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
#include "std_msgs/msg/string.hpp" // 字符串消息类型
using std::placeholders::_1;
class SubscriberNode : public rclcpp::Node
{
public:
SubscriberNode()
: Node("topic_helloworld_sub") // ROS2节点父类初始化
{
subscription_ = this->create_subscription(
"chatter", 10, std::bind(&SubscriberNode::topic_callback, this, _1)); // 创建订阅者对象(消息类型、话题名、订阅者回调函数、队列长度)
}
private:
void topic_callback(const std_msgs::msg::String & msg) const // 创建回调函数,执行收到话题消息后对数据的处理
{
RCLCPP_INFO(this->get_logger(), "I heard: '%s'", msg.data.c_str()); // 输出日志信息,提示订阅收到的话题消息
}
rclcpp::Subscription::SharedPtr subscription_; // 订阅者指针
};
// ROS2节点主入口main函数
int main(int argc, char * argv[])
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
// 创建ROS2节点对象并进行初始化
rclcpp::spin(std::make_shared());
// 关闭ROS2 C++接口
rclcpp::shutdown();
return 0;
}
```
## 3.服务

> 服务可以实现类似你问我答的`同步通信`效果
### a.服务的特点
##### 服务的特点:
- 同步通信
- 一对多通信(服务端唯一)
- 服务接口
### b.客户端
```cpp
/***
@作者: 古月居(www.guyuehome.com)
@说明: ROS2服务示例-发送两个加数,请求加法器计算
***/
#include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
#include "learning_interface/srv/add_two_ints.hpp" // 自定义的服务接口
#include
#include
#include
using namespace std::chrono_literals;
int main(int argc, char **argv)
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
if (argc != 3) {
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "usage: service_adder_client X Y");
return 1;
}
// 创建ROS2节点对象并进行初始化
std::shared_ptr node = rclcpp::Node::make_shared("service_adder_client");
// 创建服务客户端对象(服务接口类型,服务名)
rclcpp::Client::SharedPtr client =
node->create_client("add_two_ints");
// 创建服务接口数据
auto request = std::make_shared();
request->a = atoll(argv[1]);
request->b = atoll(argv[2]);
// 循环等待服务器端成功启动
while (!client->wait_for_service(1s)) {
if (!rclcpp::ok()) {
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Interrupted while waiting for the service. Exiting.");
return 0;
}
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "service not available, waiting again...");
}
// 异步方式发送服务请求
auto result = client->async_send_request(request);
// 接收服务器端的反馈数据
if (rclcpp::spin_until_future_complete(node, result) ==
rclcpp::FutureReturnCode::SUCCESS)
{
// 将收到的反馈信息打印输出
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Sum: %ld", result.get()->sum);
} else {
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Failed to call service add_two_ints");
}
// 关闭ROS2 C++接口
rclcpp::shutdown();
return 0;
}
```
### c. 服务端
```cpp
/***
@作者: 古月居(www.guyuehome.com)
@说明: ROS2服务示例-提供加法器的服务器处理功能
***/
#include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
#include "learning_interface/srv/add_two_ints.hpp" // 自定义的服务接口
#include
// 创建回调函数,执行收到请求后对数据的处理
void adderServer(const std::shared_ptr request,
std::shared_ptr response)
{
// 完成加法求和计算,将结果放到反馈的数据中
response->sum = request->a + request->b;
// 输出日志信息,提示已经完成加法求和计算
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Incoming request\na: %ld" " b: %ld",
request->a, request->b);
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "sending back response: [%ld]", (long int)response->sum);
}
// ROS2节点主入口main函数
int main(int argc, char **argv)
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
// 创建ROS2节点对象并进行初始化
std::shared_ptr node = rclcpp::Node::make_shared("service_adder_server");
// 创建服务器对象(接口类型、服务名、服务器回调函数)
rclcpp::Service::SharedPtr service =
node->create_service("add_two_ints", &adderServer);
RCLCPP_INFO(rclcpp::get_logger("rclcpp"), "Ready to add two ints.");
// 循环等待ROS2退出
rclcpp::spin(node);
// 关闭ROS2 C++接口
rclcpp::shutdown();
}
```
### d.命令行操作
```
ros2 service list # 查看服务列表
ros2 service type # 查看服务数据类型
ros2 service call # 发送服务请求
```
## 4.通信接口


> 同ROS1一样ROS2同样有的可自定义的通信接口,这些通信接口大大拓展了ROS的应用场景
> 具体的自定义接口的方法可以看官方文档或者
## 5.动作

> 动作是一种应用层的通信机制,其底层是基于话题和服务实现的
### a. 服务端
```cpp
/***
@作者: 古月居(www.guyuehome.com)
@说明: ROS2动作示例-负责执行圆周运动动作的服务端
***/
#include
#include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
#include "rclcpp_action/rclcpp_action.hpp" // ROS2 动作类
#include "learning_interface/action/move_circle.hpp" // 自定义的圆周运动接口
using namespace std;
class MoveCircleActionServer : public rclcpp::Node
{
public:
// 定义一个自定义的动作接口类,便于后续使用
using CustomAction = learning_interface::action::MoveCircle;
// 定义一个处理动作请求、取消、执行的服务器端
using GoalHandle = rclcpp_action::ServerGoalHandle;
explicit MoveCircleActionServer(const rclcpp::NodeOptions & action_server_options = rclcpp::NodeOptions())
: Node("action_move_server", action_server_options) // ROS2节点父类初始化
{
using namespace std::placeholders;
this->action_server_ = rclcpp_action::create_server( // 创建动作服务器(接口类型、动作名、回调函数)
this->get_node_base_interface(),
this->get_node_clock_interface(),
this->get_node_logging_interface(),
this->get_node_waitables_interface(),
"move_circle",
std::bind(&MoveCircleActionServer::handle_goal, this, _1, _2),
std::bind(&MoveCircleActionServer::handle_cancel, this, _1),
std::bind(&MoveCircleActionServer::handle_accepted, this, _1));
}
private:
rclcpp_action::Server::SharedPtr action_server_; // 动作服务器
// 响应动作目标的请求
rclcpp_action::GoalResponse handle_goal(
const rclcpp_action::GoalUUID & uuid,
std::shared_ptr goal_request)
{
RCLCPP_INFO(this->get_logger(), "Server: Received goal request: %d", goal_request->enable);
(void)uuid;
// 如请求为enable则接受运动请求,否则就拒绝
if (goal_request->enable)
{
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
}
else
{
return rclcpp_action::GoalResponse::REJECT;
}
}
// 响应动作取消的请求
rclcpp_action::CancelResponse handle_cancel(
const std::shared_ptr goal_handle_canceled_)
{
RCLCPP_INFO(this->get_logger(), "Server: Received request to cancel action");
(void) goal_handle_canceled_;
return rclcpp_action::CancelResponse::ACCEPT;
}
// 处理动作接受后具体执行的过程
void handle_accepted(const std::shared_ptr goal_handle_accepted_)
{
using namespace std::placeholders;
// 在线程中执行动作过程
std::thread{std::bind(&MoveCircleActionServer::execute, this, _1), goal_handle_accepted_}.detach();
}
void execute(const std::shared_ptr goal_handle_)
{
const auto requested_goal = goal_handle_->get_goal(); // 动作目标
auto feedback = std::make_shared(); // 动作反馈
auto result = std::make_shared(); // 动作结果
RCLCPP_INFO(this->get_logger(), "Server: Executing goal");
rclcpp::Rate loop_rate(1);
// 动作执行的过程
for (int i = 0; (i < 361) && rclcpp::ok(); i=i+30)
{
// 检查是否取消动作
if (goal_handle_->is_canceling())
{
result->finish = false;
goal_handle_->canceled(result);
RCLCPP_INFO(this->get_logger(), "Server: Goal canceled");
return;
}
// 更新反馈状态
feedback->state = i;
// 发布反馈状态
goal_handle_->publish_feedback(feedback);
RCLCPP_INFO(this->get_logger(), "Server: Publish feedback");
loop_rate.sleep();
}
// 动作执行完成
if (rclcpp::ok())
{
result->finish = true;
goal_handle_->succeed(result);
RCLCPP_INFO(this->get_logger(), "Server: Goal succeeded");
}
}
};
// ROS2节点主入口main函数
int main(int argc, char * argv[])
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
// 创建ROS2节点对象并进行初始化
rclcpp::spin(std::make_shared());
// 关闭ROS2 C++接口
rclcpp::shutdown();
return 0;
}
```
### b. 客户端
```cpp
/***
@作者: 古月居(www.guyuehome.com)
@说明: ROS2动作示例-请求执行圆周运动动作的客户端
***/
#include
#include "rclcpp/rclcpp.hpp" // ROS2 C++接口库
#include "rclcpp_action/rclcpp_action.hpp" // ROS2 动作类
#include "learning_interface/action/move_circle.hpp" // 自定义的圆周运动接口
using namespace std;
class MoveCircleActionClient : public rclcpp::Node
{
public:
// 定义一个自定义的动作接口类,便于后续使用
using CustomAction = learning_interface::action::MoveCircle;
// 定义一个处理动作请求、取消、执行的客户端类
using GoalHandle = rclcpp_action::ClientGoalHandle;
explicit MoveCircleActionClient(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
: Node("action_move_client", node_options) // ROS2节点父类初始化
{
this->client_ptr_ = rclcpp_action::create_client( // 创建动作客户端(接口类型、动作名)
this->get_node_base_interface(),
this->get_node_graph_interface(),
this->get_node_logging_interface(),
this->get_node_waitables_interface(),
"move_circle");
}
// 创建一个发送动作目标的函数
void send_goal(bool enable)
{
// 检查动作服务器是否可以使用
if (!this->client_ptr_->wait_for_action_server(std::chrono::seconds(10)))
{
RCLCPP_ERROR(this->get_logger(), "Client: Action server not available after waiting");
rclcpp::shutdown();
return;
}
// 绑定动作请求、取消、执行的回调函数
auto send_goal_options = rclcpp_action::Client::SendGoalOptions();
using namespace std::placeholders;
send_goal_options.goal_response_callback =
std::bind(&MoveCircleActionClient::goal_response_callback, this, _1);
send_goal_options.feedback_callback =
std::bind(&MoveCircleActionClient::feedback_callback, this, _1, _2);
send_goal_options.result_callback =
std::bind(&MoveCircleActionClient::result_callback, this, _1);
// 创建一个动作目标的消息
auto goal_msg = CustomAction::Goal();
goal_msg.enable = enable;
// 异步方式发送动作的目标
RCLCPP_INFO(this->get_logger(), "Client: Sending goal");
this->client_ptr_->async_send_goal(goal_msg, send_goal_options);
}
private:
rclcpp_action::Client::SharedPtr client_ptr_;
// 创建一个服务器收到目标之后反馈时的回调函数
void goal_response_callback(GoalHandle::SharedPtr goal_message)
{
if (!goal_message)
{
RCLCPP_ERROR(this->get_logger(), "Client: Goal was rejected by server");
rclcpp::shutdown(); // Shut down client node
}
else
{
RCLCPP_INFO(this->get_logger(), "Client: Goal accepted by server, waiting for result");
}
}
// 创建处理周期反馈消息的回调函数
void feedback_callback(
GoalHandle::SharedPtr,
const std::shared_ptr feedback_message)
{
std::stringstream ss;
ss get_logger(), "%s", ss.str().c_str());
}
// 创建一个收到最终结果的回调函数
void result_callback(const GoalHandle::WrappedResult & result_message)
{
switch (result_message.code)
{
case rclcpp_action::ResultCode::SUCCEEDED:
break;
case rclcpp_action::ResultCode::ABORTED:
RCLCPP_ERROR(this->get_logger(), "Client: Goal was aborted");
rclcpp::shutdown(); // 关闭客户端节点
return;
case rclcpp_action::ResultCode::CANCELED:
RCLCPP_ERROR(this->get_logger(), "Client: Goal was canceled");
rclcpp::shutdown(); // 关闭客户端节点
return;
default:
RCLCPP_ERROR(this->get_logger(), "Client: Unknown result code");
rclcpp::shutdown(); // 关闭客户端节点
return;
}
RCLCPP_INFO(this->get_logger(), "Client: Result received: %s", (result_message.result->finish ? "true" : "false"));
rclcpp::shutdown(); // 关闭客户端节点
}
};
// ROS2节点主入口main函数
int main(int argc, char * argv[])
{
// ROS2 C++接口初始化
rclcpp::init(argc, argv);
// 创建一个客户端指针
auto action_client = std::make_shared();
// 发送动作目标
action_client->send_goal(true);
// 创建ROS2节点对象并进行初始化
rclcpp::spin(action_client);
// 关闭ROS2 C++接口
rclcpp::shutdown();
return 0;
}
```
### c. 命令行操作
```
$ ros2 action list # 查看服务列表
$ ros2 action info # 查看服务数据类型
$ ros2 action send_goal # 发送服务请求
```
## 6.参数
> 参数是ROS2模块化封装的重要功能,可以利用全局参数可视化显示以及动态调整
> 默认情况下,某一个参数被修改后,使用它的节点并不会立刻被同步,ROS2提供了动态配置的机制,需要在程序中进行设置,通过回调函数的方法,动态调整参数
关于参数,具体可以看[https://docs.ros.org/en/jazzy/Concepts/Basic/About-Parameters.html#parameters](https://docs.ros.org/en/jazzy/Concepts/Basic/About-Parameters.html#parameters)
## 7.DDS

DS的全称是Data Distribution Service,也就是数据分发服务,2004年由对象管理组织OMG发布和维护,是一套专门为实时系统设计的数据分发/订阅标准,最早应用于美国海军, 解决舰船复杂网络环境中大量软件升级的兼容性问题,现在已经成为强制标准。
DDS强调以数据为中心,可以提供丰富的服务质量策略,以保障数据进行实时、高效、灵活地分发,可满足各种分布式实时通信应用需求。
### 在命令行中配置DDS
```
$ ros2 topic pub /chatter std_msgs/msg/Int32 "data: 42" --qos-reliability best_effort
$ ros2 topic echo /chatter --qos-reliability reliable
$ ros2 topic echo /chatter --qos-reliability best_effort
$ ros2 topic info /chatter --verbose #查看节点的Qos策略
```
## 8.分布式通信
> 分布式通信是解决使用一台计算机算力不足的情况
> 有几个实用的例子,也是我自己在使用的:
> 一台电脑运行执行器节点 另一台电脑读取数据并使用QT可视化数据
> 一台电脑运行节点 另一台使用rviz等等
- 2025-02-10
-
发表了主题帖:
《ROS2智能机器人开发实践》——阅读测评
# 《ROS2智能机器人开发实践》阅读心得
各位,新年伊始,给各位拜个晚年了!
非常荣幸能够参加这次阅读活动,自从收到书之后,由于过年,年后的拜年,走亲戚,以及与好友聚会等原因,让我很少拥有自己的时间,加之毕业设计的开题报告,以及一些其他专业书籍的阅读,让我这段时间有点忙忙的感觉哈哈哈哈。
但是想着既然参加了这个活动就一定要坚持下去,一定要开个好开头。接下来的一段时间我会持续跟进我的阅读进度,和大家分享我的理解,对比书的优劣,和大家一起进步!我的毕业设计就是做的移动机器人,不过可惜的是,我用的是ROS1,虽然ROS1和ROS2相比变化很大,但是我相信未来是ROS2的,我也会在有ROS1的基础上,持续跟进ROS2的学习。如果大家可以持续跟帖,我会将我的学习方法,遇到的问题分享给大家,菜鸟可以跟着我学会从官方文档入手,辅助这本书学习ROS2,也希望大佬可以对我批评指正!还请轻点喷,感谢感谢!
# 一、学习内容
这次的学习内容有点少,而且章节不整,希望大家可以多多包涵!谢谢大家喔,后续更新不会这个样子了!
## 1.第一章: ROS:智能机器人的灵魂
关于为什么ROS2和ROS1架构上的区别,我就不说了,感兴趣的可以看一下B站小虎哥的视频:
https://www.bilibili.com/video/BV15D4y167Hv?spm_id_from=333.788.player.switch&vd_source=d338071225639d42527e58e702fb919d
ROS2相较于ROS1,实时性得到了很大的提高,节点间的耦合大大减少,跨平台能力大大增强,我看到过他们用Esp32系列的板子,装了MicroROS系统,但是这个系统跟ROS2的写法还是有一定区别的。
ROS2相对于ROS1的具体差别表现在以下方面:
引用书中说法
>1.重新设计了系统架构。ROS2的节点拜托了ROSMaster的集中控制,实现了真正的分布式通信,借助于全新的通信框架DDS、Zenoh,为所有节点的通讯提供了可靠的保障。
2.重新设计了软件API。ROS1原本的API已经无法满足需求了,ROS2结合C++最新标准和python3语言特性,设计了更加通用的API,当然,也提供了移植教程
3.优化升级了编译系统。ROS1中的编译问题很多,相信大家在rosbuild和catkin的问题上有很多吧,没少骂过这个系统哈哈哈。ROS2进行了优化,优化后的编译系统叫做ament和colcon,它们提供了更加稳定、高效的编译和链接过程,减少了出错的可能。
ROS2的安装大家参考小鱼一键安装: `wget http://fishros.com/install -O fishros && . fishros`
我安装的是jazzy(Ubuntu24.04)版本,多么优雅的一个版本!
虚拟机大家可以下载vmware然后跟着以下的链接进行下载
https://blog.csdn.net/2301_80224556/article/details/142675808
**一些基本概念我就不赘述了**
## 接下来开始我们的操作,开始编译我们的工作空间吧!
>我推荐不管学ROS1还是ROS2,都要从官方文档入门,网上的视频再好,也是别人笑话过的东西,当然我们刚入手的时候可以通过它们进行大概的了解!
下面给出jazzy版本的文档链接
https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Colcon-Tutorial.html
>值得一提的是 ROS2命令行的操作机制与linux相同,不过所有操作都集成在ROS2的总命令中,
如`ros2 run turtlesim turtlrsim_node`,"ros2"后边第一个参数表示不同的操作目的,如node表示对节点的操作,topic表示对话题的操作,后边还可以添加一系列参数
#### 考虑到这并不是一个操作手册,所以我会尽量将重点放在书外的思考和学习中,剩下的包括运行节点程序,查看节点信息,话题信息,数据的录制和播放等就不再过多说明,大家在一些网站上都可以找到。
## 第二章:ROS2核心原理:构建机器人的基石
### 1.编译工作空间
>由于使用colcon的编译方式,ROS2工作空间编译后会出现四个文件夹分别是:build、log、install、src
相对于ROS1来说,少了devvel文件夹多了install和log,前两者功能类似,都是存放编译生成的可执行文件等
##### 官方文档:
>准备
```shell
mkdir -p ~/ros2_ws/src
cd ~/ros2_ws/src
git clone https://github.com/ros/ros_tutorials.git -b jazzy
```
###### 接下来是安装依赖库
>官方文档:
```shell
rosdep install -i --from-path src --rosdistro jazzy -y
```
本书:
```shell
rosdep install --from-paths src --ignore-src -r -y
```
总结最简单:
```shell
rosdep install -i --from-paths src -y
```
`--from-path ` :指定一个路径, `rosdep` 会从该路径下的文件夹中查找 ROS 包。
`src ` :通常是指 ROS 工作空间的 src 目录,该目录包含所有的 ROS 包。
`--rosdistro `:指定 ROS 发行版(如 jazzy 、 foxy 、 galactic 等), rosdep 会根据指定的发行版来解析依赖项。
`-i `:忽略已安装的依赖项,只安装尚未安装的依赖项,这意味着 rosdep 不会解析 src 目录中的包的依赖项,而是只解析其他路径中的包的依赖项。和`--ignore-src`一样。
`-y ` :自动确认安装,无需手动输入 y 。
###### 下载ROS2编译器colcon:
>本书:
```shell
sudo apt install python3-colcon-ros
```
官方:
```shell
sudo apt install python3-colcon-common-extensions
```
`python3-colcon-ros是python3-colcon-common-extensions的一个分支,专门针对ros的扩展包
###### 编译工作空间:
>本书:
```shell
cd ~/ros2_ws/
colcon build
```
官方:
```shell
colcon build --symlink-install
```
` --symlink-install` 选项会使用符号链接(symlink)将安装目录中的文件指向源代码目录中的文件。
这意味着,当你修改源代码目录中的文件时,安装目录中的文件也会自动更新,无需重新编译。•适用于开
发阶段,可以快速测试代码的修改。
• 当你修改源代码时,安装目录中的文件会自动更新,因为它们是通过符号链接指向源代码的。
• 这意味着你不需要重新编译整个项目来测试代码的修改,只需重新编译修改的部分即可。
###### 添加环境变量:
```shell
#一次性
source ~/dev_ws/install/setup.bash
#永久,加入~/.bashrc中
echo "source ~/dev_ws/install/setup.bash" >> ~/.bashrc
//注意不是devel而是install
```
### 2.创建一个功能包
>注意什么样的文件夹才是功能包呢,一定是包含cmakelist.txt以及package.xml的才是!
###### 创建:
>本书
```shell
cd ros2_ws/src
ros2 pkg create --build-type ament_cmake learning_pkg_cpp //C++
ros2 pkg create --build-type ament_cmake learning_pkg_cpp //python
```
官方
```shell
ros2 pkg create --build-type ament_cmake --license Apache-2.0
//--license Apache-2.0是开源声明
ros2 pkg create --build-type ament_python --license Apache-2.0
```
***可以知道相比于ROS1通用结构的功能包类型,ROS2对功能包的类型进行了明确的划分,有助于复杂系统的编译和链接***
其他的一些cmakelist.txt以及package.xml的文件内容,感兴趣大家可以自己去官方文档查询:
https://docs.ros.org/en/jazzy/Tutorials/Beginner-Client-Libraries/Creating-Your-First-ROS2-Package.html
# 二、阅读感受
>我还是坚持,ros最好的教程是官方的wiki和document。在我读的ROS1的书和这本书中我都没有看到对ROS1官方文档的解读。我理解这是因为书的受众是初学者,但是如果可以有对ros官方文档的提炼和解读,可以更好的培养我们独立自主的学习能力。这只是一点点小的意见,个人看法,不喜勿喷哇,谢谢!
整本书开头也是循序渐进,对于初学者很受用。
这次是因为过年,开题等原因,读的确实少,后面会继续加油,认真读完这本书,并做一个较好的测评!
注:免责声明:以上都是个人看法和个人见解,欢迎大佬们批评指正,喷的轻一点!
- 2025-01-20
-
回复了主题帖:
读书活动入围名单:《ROS2智能机器人开发实践》
个人信息无误,确认可以完成阅读分享计划