herowuxun

  • 2025-03-18
  • 发表了主题帖: 《ROS2智能机器人开发与实践》——ROS核心(节点、话题、服务、DDS通信协议等)

    # 《ROS2智能机器人开发与实践》——ROS核心(节点、话题、服务、DDS通信协议等) ------------ ###### 今天主要是分享一下ROS2的核心包括节点、话题、服务和DDS通信协议的内容 ###### 其中会对官方和本书的相关细节进行比对 ###### 本文章都以C++为例 ## 一、节点 ### 1、ROS2中的节点具有以下几个特点 1. 每个节点是一个进程 2. 每个节点都是一个可以独立运行的可执行文件 3. 每个节点可使用不同的编程语言 4. 每个节点可分布式运行在不同的主机中 5. 每个节点需要唯一的名称 ### 2、节点编程方法(C++) #### a.C++程序编写 ![](https://docs.ros.org/en/jazzy/_images/Nodes-TopicandService.gif) ```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    # 查看节点信息 ``` ## 二、话题 ![](https://docs.ros.org/en/jazzy/_images/Topic-MultiplePublisherandMultipleSubscriber.gif) ### 1.rqt_graph ``` ros2 run turtlesim turtlesim_node ros2 run turtlesim turtle_teleop_key rqt_graph ``` 我们将使用 `rqt_graph` 来可视化不断变化的节点和主题,以及它们之间的联系。 ![](https://docs.ros.org/en/jazzy/_images/rqt_graph.png) #### 话题的特点: - 多对多通信 - 异步通信 - 消息接口 > 话题通信的一个特性,就是`异步通信`所谓异步,只要是指发布者发出数据后,并不知道订阅者什么时候可以收到。所以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.服务 ![](https://docs.ros.org/en/jazzy/_images/Service-MultipleServiceClient.gif) > 服务可以实现类似你问我答的`同步通信`效果 ### 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.通信接口 ![](https://book.guyuehome.com/ROS2/2.%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/image/2.6_%E9%80%9A%E4%BF%A1%E6%8E%A5%E5%8F%A3/image-20220528001533911.png) ![](https://book.guyuehome.com/ROS2/2.%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/image/2.6_%E9%80%9A%E4%BF%A1%E6%8E%A5%E5%8F%A3/image-20220528001633925.png) > 同ROS1一样ROS2同样有的可自定义的通信接口,这些通信接口大大拓展了ROS的应用场景 > 具体的自定义接口的方法可以看官方文档或者 ## 5.动作 ![](https://docs.ros.org/en/jazzy/_images/Action-SingleActionClient.gif) > 动作是一种应用层的通信机制,其底层是基于话题和服务实现的 ### 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 ![](https://book.guyuehome.com/ROS2/2.%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/image/2.10_DDS/image-20220528020829304.jpg) 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智能机器人开发实践》

    个人信息无误,确认可以完成阅读分享计划

最近访客

< 1/1 >

统计信息

已有8人来访过

  • 芯积分:17
  • 好友:--
  • 主题:2
  • 回复:1

留言

你需要登录后才可以留言 登录 | 注册


现在还没有留言