news 2026/7/20 17:05:38

ros2 基本概念 c++ 解释

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ros2 基本概念 c++ 解释
/* * ROS2 面试核心概念全覆盖示例代码 * 覆盖90%以上ROS2高频面试考点,每个模块附带【面试考点】详细注释 * 包含:节点/回调/执行器/回调组、话题/服务/Action三大通信、DDS与QoS、生命周期节点、参数机制 * 编译环境:ROS2 Humble/Jazzy 全版本通用 */ // ############################## 头文件区(对应核心模块)############################## // ROS2 核心基础头文件,包含节点、日志、基础API #include "rclcpp/rclcpp.hpp" // 标准消息类型:字符串消息,用于话题通信 #include "std_msgs/msg/string.hpp" // 标准服务类型:加法服务,用于服务通信 #include "example_interfaces/srv/add_two_ints.hpp" // 标准动作类型:斐波那契数列,用于Action通信 #include "example_interfaces/action/fibonacci.hpp" // 三种执行器头文件【面试必考:三种执行器区别与适用场景】 #include "rclcpp/executors/single_threaded_executor.hpp" #include "rclcpp/executors/multi_threaded_executor.hpp" #include "rclcpp/executors/static_single_threaded_executor.hpp" // 回调组头文件【面试必考:多线程调度核心,和执行器的配合逻辑】 #include "rclcpp/callback_group.hpp" // 生命周期节点头文件【面试必考:状态机、和普通节点的区别】 #include "rclcpp_lifecycle/lifecycle_node.hpp" #include "rclcpp_lifecycle/lifecycle_publisher.hpp" // Action 相关头文件【面试必考:Action和服务/话题的区别】 #include "rclcpp_action/rclcpp_action.hpp" // QoS 服务质量头文件【面试必考:DDS核心能力,ROS2可靠性保障】 #include "rclcpp/qos.hpp" using std::placeholders::_1; using std::placeholders::_2; using std::placeholders::_3; // ############################## 模块1:综合基础功能节点 ############################## // 【面试考点1:节点的本质】 // 节点是ROS2的最小功能单元,本质是「回调任务+通信接口的容器」 // 节点本身不执行任何代码,所有回调必须由执行器(Executor)调度才能运行 // 一个进程可以创建多个节点,一个执行器可以挂载多个节点 class BasicCoreNode : public rclcpp::Node { public: // 节点构造函数:所有初始化、接口创建都在这里完成 BasicCoreNode(const std::string & node_name) : Node(node_name) { // ===================== 1. 参数机制(面试考点)===================== // 【面试考点:ROS2参数和ROS1的区别】 // ROS1:中心化参数服务器,所有节点共享,存在单点故障 // ROS2:去中心化架构,每个节点自己维护参数,没有全局参数服务器,支持动态修改 // 声明一个参数,参数名"timer_period_ms",默认值500ms this->declare_parameter<int>("timer_period_ms", 500); // 读取参数值 int timer_period = this->get_parameter("timer_period_ms").as_int(); RCLCPP_INFO(this->get_logger(), "【参数初始化】定时器周期:%dms", timer_period); // ===================== 2. 回调组(面试核心考点)===================== // 【面试考点:回调组的作用,和执行器的关系】 // 回调组是执行器调度的最小单元,用来对回调进行分组管理,决定回调是否可以并行执行 // 两种核心回调组: // 1. MutuallyExclusive(互斥组):组内的回调只能串行执行,同一时间只跑一个;节点默认创建的就是互斥组 // 2. Reentrant(可重入组):组内的回调可以并行执行,多线程环境下同时运行 // 注意:即使是多线程执行器,同一个互斥组里的回调也绝对不会并行! // 创建两个独立的互斥回调组:订阅回调和服务回调分开调度,互不阻塞 callback_group_sub_ = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive); callback_group_srv_ = this->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive); // ===================== 3. 话题Topic 通信(面试必考)===================== // 【面试考点:话题通信模式与特点】 // 模式:发布-订阅(Pub/Sub),异步通信 // 特点:一对多(一个发布者可以对应N个订阅者)、无应答确认、低延迟 // 适用场景:高频连续数据流,比如传感器数据、机器人状态、运动指令下发 rclcpp::QoS topic_qos = rclcpp::QoS(10); // QoS队列深度10 // 3.1 创建话题发布者 // 模板参数:消息类型;入参:话题名、QoS策略 publisher_ = this->create_publisher<std_msgs::msg::String>("chatter_topic", topic_qos); // 3.2 创建话题订阅者,绑定到订阅回调组 // 回调函数:收到消息时自动触发,属于最典型的回调函数 subscriber_ = this->create_subscription<std_msgs::msg::String>( "chatter_topic", topic_qos, std::bind(&BasicCoreNode::topic_callback, this, _1), rclcpp::SubscriptionOptions(), callback_group_sub_ // 指定该订阅的回调归属的回调组 ); // ===================== 4. 服务Service 通信(面试必考)===================== // 【面试考点:服务通信模式,和话题的核心区别】 // 模式:请求-响应(Req/Rep),同步通信 // 特点:一对一(一个请求对应一个响应)、有确定返回结果、客户端默认阻塞等待 // 适用场景:低频、有明确结果的短任务,比如触发校准、查询状态、参数设置 add_srv_ = this->create_service<example_interfaces::srv::AddTwoInts>( "add_two_ints", std::bind(&BasicCoreNode::service_callback, this, _1, _2), rmw_qos_profile_services_default, callback_group_srv_ ); // ===================== 5. 定时器Timer ===================== // 定时器回调:按固定周期触发,也是最常用的回调类型 // 常用于周期发布数据、周期检查设备状态 timer_ = this->create_wall_timer( std::chrono::milliseconds(timer_period), std::bind(&BasicCoreNode::timer_callback, this) ); RCLCPP_INFO(this->get_logger(), "基础节点[%s]创建完成,所有回调任务已装入容器,等待执行器调度", node_name.c_str()); } // 服务客户端示例函数:主动发起服务请求 void call_add_service(int a, int b) { // 创建服务客户端 auto client = this->create_client<example_interfaces::srv::AddTwoInts>("add_two_ints"); // 等待服务端上线 while (!client->wait_for_service(std::chrono::seconds(1))) { RCLCPP_INFO(this->get_logger(), "等待服务端上线..."); } // 构造请求体 auto request = std::make_shared<example_interfaces::srv::AddTwoInts::Request>(); request->a = a; request->b = b; // 异步发送请求,绑定响应回调 client->async_send_request(request, std::bind(&BasicCoreNode::service_response_callback, this, _1)); } private: // ===================== 各类回调函数 ===================== // 【面试考点:回调函数的本质】 // 开发者只定义函数、绑定到对应事件,不主动调用;由ROS2执行器在事件触发时自动调用 // 所有回调的执行完全依赖执行器的spin()循环,没有执行器驱动,回调永远不会运行 // 1. 话题订阅回调:收到话题消息时自动触发 void topic_callback(const std_msgs::msg::String::SharedPtr msg) { RCLCPP_INFO(this->get_logger(), "【话题回调触发】收到消息: %s", msg->data.c_str()); } // 2. 服务回调:收到服务请求时自动触发,处理后返回响应 void service_callback( const std::shared_ptr<example_interfaces::srv::AddTwoInts::Request> request, std::shared_ptr<example_interfaces::srv::AddTwoInts::Response> response ) { response->sum = request->a + request->b; RCLCPP_INFO(this->get_logger(), "【服务回调触发】%d + %d = %d", request->a, request->b, response->sum); } // 3. 服务响应回调:客户端收到服务端响应时自动触发 void service_response_callback(rclcpp::Client<example_interfaces::srv::AddTwoInts>::SharedFuture future) { auto response = future.get(); RCLCPP_INFO(this->get_logger(), "【服务响应回调】收到求和结果: %ld", response->sum); } // 4. 定时器回调:到设定周期自动触发 void timer_callback() { auto msg = std_msgs::msg::String(); msg.data = "周期发布的话题消息"; publisher_->publish(msg); RCLCPP_INFO(this->get_logger(), "【定时器回调触发】发布话题消息"); } // ===================== 节点成员(容器里的功能条目)===================== rclcpp::Publisher<std_msgs::msg::String>::SharedPtr publisher_; rclcpp::Subscription<std_msgs::msg::String>::SharedPtr subscriber_; rclcpp::Service<example_interfaces::srv::AddTwoInts>::SharedPtr add_srv_; rclcpp::TimerBase::SharedPtr timer_; rclcpp::CallbackGroup::SharedPtr callback_group_sub_; rclcpp::CallbackGroup::SharedPtr callback_group_srv_; }; // ############################## 模块2:Action 动作通信节点 ############################## // 【面试必考:Action是什么,和服务/话题的核心区别】 // 1. 本质:基于话题+服务实现的高级通信机制,专门针对长时任务设计 // 2. 模式:目标-反馈-结果,异步可抢占 // 3. 核心组成: // - Goal 目标:客户端发送任务目标 // - Feedback 反馈:服务端周期返回任务执行进度 // - Result 结果:任务完成后返回最终结果 // - Cancel 取消:客户端可以中途取消/抢占任务 // 4. 和服务的核心区别: // - 服务:短平快任务,同步等待,没有进度反馈,不能中途取消 // - Action:长时任务,异步执行,有实时进度反馈,支持取消/抢占 // 5. 适用场景:导航移动、机械臂运动、拍照序列等耗时长、需要进度感知的任务 class ActionServerNode : public rclcpp::Node { public: // Action 类型别名,简化代码 using Fibonacci = example_interfaces::action::Fibonacci; using GoalHandleFibonacci = rclcpp_action::ServerGoalHandle<Fibonacci>; ActionServerNode(const std::string & node_name) : Node(node_name) { // 创建Action服务端,绑定三个核心回调 action_server_ = rclcpp_action::create_server<Fibonacci>( this, "fibonacci_action", // Action名称 // 目标接收回调:收到新目标时触发,决定接受还是拒绝 std::bind(&ActionServerNode::handle_goal, this, _1, _2), // 取消请求回调:收到取消指令时触发,决定是否允许取消 std::bind(&ActionServerNode::handle_cancel, this, _1), // 目标接受回调:目标被接受后,执行任务的主逻辑 std::bind(&ActionServerNode::handle_accepted, this, _1) ); RCLCPP_INFO(this->get_logger(), "Action服务端节点[%s]创建完成", node_name.c_str()); } private: // 1. 目标处理回调:收到新目标,返回接受/拒绝 rclcpp_action::GoalResponse handle_goal( const rclcpp_action::GoalUUID & uuid, std::shared_ptr<const Fibonacci::Goal> goal ) { RCLCPP_INFO(this->get_logger(), "【Action回调】收到新目标,阶数:%d", goal->order); (void)uuid; // 阶数大于100就拒绝,否则接受执行 if (goal->order > 100) { return rclcpp_action::GoalResponse::REJECT; } return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE; } // 2. 取消处理回调:收到取消请求,返回接受/拒绝 rclcpp_action::CancelResponse handle_cancel( const std::shared_ptr<GoalHandleFibonacci> goal_handle ) { RCLCPP_INFO(this->get_logger(), "【Action回调】收到取消请求"); (void)goal_handle; return rclcpp_action::CancelResponse::ACCEPT; } // 3. 目标执行回调:目标被接受后,启动任务执行 void handle_accepted(const std::shared_ptr<GoalHandleFibonacci> goal_handle) { // 单独开线程执行任务,避免阻塞执行器的其他回调 std::thread{std::bind(&ActionServerNode::execute, this, _1), goal_handle}.detach(); } // 任务执行主逻辑:生成斐波那契数列,周期反馈进度 void execute(const std::shared_ptr<GoalHandleFibonacci> goal_handle) { RCLCPP_INFO(this->get_logger(), "【Action执行】开始执行任务"); rclcpp::Rate loop_rate(1); // 1秒反馈一次进度 const auto goal = goal_handle->get_goal(); auto feedback = std::make_shared<Fibonacci::Feedback>(); auto result = std::make_shared<Fibonacci::Result>(); int a = 0, b = 1; feedback->sequence.push_back(a); feedback->sequence.push_back(b); // 循环生成数列,直到达到目标阶数 for (int i = 2; i < goal->order; i++) { // 检查任务是否被取消 if (goal_handle->is_canceling()) { result->sequence = feedback->sequence; goal_handle->canceled(result); RCLCPP_INFO(this->get_logger(), "【Action执行】任务被取消"); return; } // 计算下一个数,更新反馈 int next = a + b; a = b; b = next; feedback->sequence.push_back(next); goal_handle->publish_feedback(feedback); RCLCPP_INFO(this->get_logger(), "【Action反馈】当前进度:%d/%d", i+1, goal->order); loop_rate.sleep(); } // 任务正常完成,返回最终结果 if (rclcpp::ok()) { result->sequence = feedback->sequence; goal_handle->succeed(result); RCLCPP_INFO(this->get_logger(), "【Action执行】任务完成"); } } // Action服务端成员 rclcpp_action::Server<Fibonacci>::SharedPtr action_server_; }; // ############################## 模块3:生命周期节点 Lifecycle Node ############################## // 【面试必考:生命周期节点和普通节点的区别,状态机原理】 // 1. 本质:带状态管理的节点,继承自rclcpp_lifecycle::LifecycleNode // 2. 核心:有限状态机,节点的启动、配置、激活、停用、销毁都有明确的状态和对应回调 // 3. 四大核心状态: // - Unconfigured(未配置):节点刚创建的初始状态,仅完成基础构造 // - Inactive(未激活):配置完成,不执行核心业务,不发布话题,待机状态 // - Active(激活):正常工作状态,所有功能、通信接口正常运行 // - Finalized(已销毁):节点资源释放,生命周期结束 // 4. 状态转换回调:on_configure、on_activate、on_deactivate、on_cleanup、on_shutdown // 5. 适用场景:机器人系统有序启动(先初始化传感器,再启动算法,最后执行运动),避免启动顺序异常; // 故障时安全停用,保证系统安全性,工业机器人、量产场景常用 class DemoLifecycleNode : public rclcpp_lifecycle::LifecycleNode { public: DemoLifecycleNode(const std::string & node_name) : LifecycleNode(node_name) { RCLCPP_INFO(this->get_logger(), "【生命周期节点】创建完成,当前状态:Unconfigured 未配置"); } // ===================== 状态转换回调(核心)===================== // 1. 配置回调:从Unconfigured → Inactive 时触发 // 用来做参数加载、硬件初始化、接口创建等一次性配置工作 rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_configure(const rclcpp_lifecycle::State & previous_state) { (void)previous_state; // 创建生命周期发布者:只有节点激活时才会真正发布消息,停用后自动停止发布 lifecycle_pub_ = this->create_publisher<std_msgs::msg::String>("lifecycle_topic", 10); timer_ = this->create_wall_timer( std::chrono::seconds(1), std::bind(&DemoLifecycleNode::timer_publish, this) ); // 定时器默认不启动,激活节点时再启动 timer_->cancel(); RCLCPP_INFO(this->get_logger(), "【生命周期回调】on_configure 配置完成,进入Inactive未激活状态"); return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; } // 2. 激活回调:从Inactive → Active 时触发 // 启动业务逻辑、开始发布订阅、进入正式工作状态 rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_activate(const rclcpp_lifecycle::State & previous_state) { (void)previous_state; timer_->reset(); // 启动定时器 RCLCPP_INFO(this->get_logger(), "【生命周期回调】on_activate 激活完成,进入Active工作状态"); return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; } // 3. 停用回调:从Active → Inactive 时触发 // 暂停业务逻辑、停止发布,回到待机状态 rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_deactivate(const rclcpp_lifecycle::State & previous_state) { (void)previous_state; timer_->cancel(); // 停止定时器 RCLCPP_INFO(this->get_logger(), "【生命周期回调】on_deactivate 停用完成,回到Inactive未激活状态"); return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; } // 4. 清理回调:从Inactive → Unconfigured 时触发 // 释放配置资源,回到初始未配置状态 rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_cleanup(const rclcpp_lifecycle::State & previous_state) { (void)previous_state; lifecycle_pub_.reset(); timer_.reset(); RCLCPP_INFO(this->get_logger(), "【生命周期回调】on_cleanup 清理完成,回到Unconfigured未配置状态"); return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; } // 5. 关闭回调:任意状态 → Finalized 时触发 // 节点销毁前的最终资源释放 rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn on_shutdown(const rclcpp_lifecycle::State & previous_state) { (void)previous_state; RCLCPP_INFO(this->get_logger(), "【生命周期回调】on_shutdown 节点关闭,进入Finalized销毁状态"); return rclcpp_lifecycle::node_interfaces::LifecycleNodeInterface::CallbackReturn::SUCCESS; } private: void timer_publish() { auto msg = std_msgs::msg::String(); msg.data = "生命周期节点激活中发布的消息"; lifecycle_pub_->publish(msg); } rclcpp_lifecycle::LifecyclePublisher<std_msgs::msg::String>::SharedPtr lifecycle_pub_; rclcpp::TimerBase::SharedPtr timer_; }; // ############################## 模块4:执行器完整用法 + DDS/QoS核心说明 ############################## // 【面试必考:DDS是什么,ROS2和DDS的关系】 // 1. DDS(Data Distribution Service,数据分发服务):ROS2的底层通信中间件 // 2. ROS1 底层是自研的TCPROS/UDPROS,依赖中心化的Master节点,存在单点故障风险 // 3. ROS2 底层直接采用工业级DDS标准,去中心化架构,没有Master,所有节点对等通信 // 4. DDS核心优势:高实时性、高可靠性、原生支持QoS服务质量、符合工业自动化标准、支持大规模分布式系统 // 5. Domain ID 域ID:ROS2通过域ID隔离通信网络,同一个域ID的节点才能互相发现和通信,默认值为0 // 不同机器上的ROS2节点,只要域ID相同、在同一局域网内,就能自动发现并通信 // 【面试必考:QoS服务质量】 // QoS是DDS提供的核心能力,用来配置通信的可靠性、延迟、持久性等策略 // 常用QoS策略: // 1. Reliability 可靠性: // - BEST_EFFORT 尽力而为:发了就不管,丢包不重传,延迟最低,适合高频传感器数据 // - RELIABLE 可靠:保证消息一定送达,丢包自动重传,适合指令、状态等不能丢失的数据 // 2. Durability 持久性: // - VOLATILE 临时:只保留当前消息,新订阅者收不到历史消息 // - TRANSIENT_LOCAL 瞬态本地:发布者保留历史消息,新订阅者连上就能收到最新的历史消息,适合地图、配置参数 // 3. Depth 队列深度:缓存的消息数量,队列满了就丢弃最旧的消息 // 4. Deadline 截止时间:消息必须在指定时间内送达,否则视为失效 // ============== 示例1:单线程执行器 ============== // 特点:单线程串行执行所有回调,所有回调按顺序排队,不会并行 // 适用:简单功能、怕数据竞争、对时序要求严格的场景,稳定性最高 int run_single_thread_executor(int argc, char ** argv) { rclcpp::init(argc, argv); // 创建两个不同功能的节点 auto basic_node = std::make_shared<BasicCoreNode>("basic_node_1"); auto action_node = std::make_shared<ActionServerNode>("action_server_node"); // 创建单线程执行器 rclcpp::executors::SingleThreadedExecutor executor; // 挂载多个节点 → 1个执行器可以管理多个节点 executor.add_node(basic_node); executor.add_node(action_node); // 【面试考点:spin()的本质】 // spin()是阻塞死循环,执行器在循环里不断做3件事: // 1. 遍历所有挂载的节点,检查所有回调组里的回调任务 // 2. 判断哪个回调满足触发条件(有消息到达、定时器到点、有服务请求) // 3. 按调度规则执行对应回调 // 单线程执行器:一次只执行一个回调,执行完成才取下一个 RCLCPP_INFO(rclcpp::get_logger("executor"), "单线程执行器启动,进入spin阻塞循环"); executor.spin(); rclcpp::shutdown(); return 0; } // ============== 示例2:多线程执行器 ============== // 特点:开启多个线程,不同回调组的回调可以并行执行 // 注意:同一个互斥回调组里的回调,即使多线程也只能串行!只有不同回调组的回调才能并行 // 适用:多任务高并发、多传感器数据处理、避免长回调阻塞其他任务 int run_multi_thread_executor(int argc, char ** argv) { rclcpp::init(argc, argv); auto vision_node = std::make_shared<BasicCoreNode>("vision_node"); auto motion_node = std::make_shared<BasicCoreNode>("motion_node"); // 创建多线程执行器,指定4个工作线程 rclcpp::executors::MultiThreadedExecutor executor( rclcpp::ExecutorOptions(), 4 ); executor.add_node(vision_node); executor.add_node(motion_node); // 多线程执行器:多个线程同时从就绪队列里取回调执行 // 不同回调组的回调可以同时运行,大幅提升并发性能 RCLCPP_INFO(rclcpp::get_logger("executor"), "多线程执行器启动,4线程并行调度"); executor.spin(); rclcpp::shutdown(); return 0; } // ============== 示例3:静态单线程执行器 ============== // 特点:启动前必须挂载完所有节点,运行中不能动态增删节点 // 优势:省去动态节点管理的额外开销,执行效率最高,延迟最低 // 适用:架构固定的量产程序、嵌入式资源受限场景 int run_static_single_executor(int argc, char ** argv) { rclcpp::init(argc, argv); auto fixed_node1 = std::make_shared<BasicCoreNode>("fixed_node_1"); auto fixed_lifecycle_node = std::make_shared<DemoLifecycleNode>("fixed_lifecycle_node"); rclcpp::executors::StaticSingleThreadedExecutor executor; // spin启动前必须把所有节点添加完成 executor.add_node(fixed_node1); executor.add_node(fixed_lifecycle_node->get_node_base_interface()); // 生命周期节点特殊挂载方式 RCLCPP_INFO(rclcpp::get_logger("executor"), "静态单线程执行器启动,运行中不可新增节点"); executor.spin(); // spin运行后禁止调用add_node,静态执行器不支持运行时动态修改节点 rclcpp::shutdown(); return 0; } // ############################## 主函数 ############################## // 编译运行时,选择要执行的示例即可 int main(int argc, char ** argv) { // 默认运行单线程执行器示例,可自行切换其他示例 return run_single_thread_executor(argc, argv); } /* ############################## ROS2面试高频考点汇总(对应以上代码)############################## 1. 基础核心 - 节点:功能容器,存放回调和通信接口,本身不执行代码 - 回调函数:事件触发自动执行,完全依赖执行器调度 - 执行器:回调调度器,三种类型(单线程/多线程/静态单线程),spin为阻塞调度循环 - 回调组:调度最小单元,互斥组串行、可重入组并行;多线程执行器的并行前提是回调分属不同组 2. 三大通信机制对比(必问) | 类型 | 通信模式 | 同步/异步 | 数量关系 | 核心特点 | 适用场景 | |--------|----------------|-----------|----------|------------------------|------------------------------| | 话题 | 发布-订阅 | 异步 | 一对多 | 无反馈、低延迟、高频 | 传感器数据、状态发布、指令流 | | 服务 | 请求-响应 | 同步 | 一对一 | 有确定结果、会阻塞 | 短任务、参数设置、状态查询 | | Action | 目标-反馈-结果 | 异步 | 一对一 | 有进度、可取消、长耗时 | 导航、机械臂运动、长时任务 | 3. DDS与QoS - ROS2底层基于DDS,去中心化无Master;ROS1有中心化Master,存在单点故障 - Domain ID隔离网络,同域节点自动发现通信 - QoS核心策略:可靠性、持久性、队列深度,按需适配不同通信场景 4. 生命周期节点 - 带有限状态机的节点,四大核心状态:未配置/未激活/激活/销毁 - 状态转换有明确回调,支持系统有序启动、安全停用 - 适合工业场景、启动顺序要求严格的量产系统 5. 参数机制 - ROS2参数是节点级的,去中心化,每个节点独立维护 - ROS1是中心化参数服务器,存在单点故障风险 */
find_package(ament_cmake REQUIRED) find_package(rclcpp REQUIRED) find_package(std_msgs REQUIRED) find_package(example_interfaces REQUIRED) find_package(rclcpp_action REQUIRED) find_package(rclcpp_lifecycle REQUIRED) add_executable(ros2_interview_demo src/ros2_interview_demo.cpp) ament_target_dependencies(ros2_interview_demo rclcpp std_msgs example_interfaces rclcpp_action rclcpp_lifecycle )
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/20 17:02:55

如何用开源AI助手Meetily在4分钟内完成专业会议纪要

如何用开源AI助手Meetily在4分钟内完成专业会议纪要 【免费下载链接】meetily Privacy first, AI meeting assistant with 4x faster Parakeet/Whisper live transcription, speaker diarization, and Ollama summarization built on Rust. 100% local processing. no cloud re…

作者头像 李华
网站建设 2026/7/20 17:02:22

国产Linux操作系统技术解析与信创发展

1. 国产操作系统的技术本质与现状国产操作系统确实大多基于Linux内核进行二次开发&#xff0c;这种现象被外界称为"套壳"。但这里的"套壳"并非简单的复制粘贴&#xff0c;而是包含大量本土化改造和自主创新。以深度科技的Deepin为例&#xff0c;其底层虽采…

作者头像 李华
网站建设 2026/7/20 17:00:08

gpt-tokenizer特殊令牌处理:自定义允许与禁止令牌集教程

gpt-tokenizer特殊令牌处理&#xff1a;自定义允许与禁止令牌集教程 【免费下载链接】gpt-tokenizer The fastest JavaScript BPE Tokenizer Encoder Decoder for OpenAIs GPT models (gpt-5, gpt-o*, gpt-4o, etc.). Port of OpenAIs tiktoken with additional features. 项…

作者头像 李华
网站建设 2026/7/20 16:54:27

LangChain文本向量与检索器实践

让大模型真正懂你的数据&#xff1a;LangChain 文本向量、检索器与 RAG 实践 大语言模型擅长理解和生成语言&#xff0c;却不会自动知道企业内部的制度、项目文档或昨天刚更新的数据。要让模型回答这些问题&#xff0c;关键不是继续堆叠提示词&#xff0c;而是建立一条稳定的数…

作者头像 李华