想象你走进一家机器人餐厅:大厅广播“午市套餐上新”,这是 Topic——发出去就完事,没人应答;你按下呼叫铃问“现在几点了?”服务员秒回,这是 Service——一问一答,立等可取;你点了一份炭烤战斧牛排,下单后厨房开火、腌制、翻烤、装盘,全程有人给你报进度,你可以催单也能叫停,这是 Action——为长时间任务而生。ROS2 作为机器人的“操作系统语言”,把后厨里这些沟通方式标准化成了通信机制。本文就用这家餐厅的运营故事,把 Service 与 Action 的接口定义、QoS 配置、回调处理一次讲透。

岗位关联

理解服务与 Action 是掌握 ROS2 分布式协作的基石。学会它,你就能为机器人设计“即问即答”与“长程可控”的协作能力,这也是机器人系统工程师的核心功底。

目录
  1. 可视化演示
  2. 核心概念定位
  3. 接口定义
  4. 服务通信实现
  5. Action机制
  6. QoS配置
  7. 回调处理
  8. 工程实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

对比ROS2 Service同步请求-响应与Action异步目标-反馈-结果通信机制,展示QoS可靠性配置对数据传输的影响。

本节配有交互动效(进入视口后自动加载,约 3KB)。

核心概念定位

餐厅后厨与前厅之间,有些问题急如星火。你按服务铃问“果汁机有空吗?”,服务员抬头就能答——这种“一问一答、立等可取”的沟通,就是 ROS2 中的 Service。而有些任务是马拉松,比如“为一桌二十人准备晚宴”,下单后要备菜、烹饪、上菜,中途还得汇报进度、允许顾客改主意——这种“长期执行、分步反馈、可取消”的协作,就是 ROS2 中的 Action。

技术上看,Service 采用同步请求-应答模型:客户端发出请求后,服务端处理完并返回响应,整个过程短促、双向、有始有终。Action 则在更长的时间尺度上工作,它定义了目标(Goal)、过程反馈(Feedback)和最终结果(Result)三段式消息流,客户端可以在任意时刻取消任务。两者的选择标准很简单:短时、一次性、不需要中间反馈 用 Service;长时、需要进度、可中途取消 用 Action。

所以进店后先想清楚:你只是要“一壶水的答案”,还是要“一整套餐的交付”?选对机制,机器人协作才不会乱套。

Service: Client→Server→ClientAction: Client→Server⇄Client\displaystyle \text{Service: } \text{Client} \to \text{Server} \to \text{Client} \qquad \text{Action: } \text{Client} \to \text{Server} \rightleftarrows \text{Client}
Service 是单箭头往返,Action 是双向持续交互的闭环。
  1. Service 的 Client → Server客户端把一次请求发给服务端,就像顾客按下服务铃。
    Client→Server\text{Client} \to \text{Server}
  2. Service 的 Server → Client服务端处理完立刻把结果返回,一问一答画上句号。
    Server→Client\text{Server} \to \text{Client}
  3. Action 的 Client → Server客户端先下达目标,比如“把牛排做到七分熟”。
    Client→Server\text{Client} \to \text{Server}
  4. Action 的双向箭头服务端在执行中持续回传反馈,客户端可随时下达取消指令,任务结束后再返回最终结果,沟通是往复的。
    Client⇄Server\text{Client} \rightleftarrows \text{Server}

接口定义

餐厅刚开业时菜单上只写菜名,顾客点“随便”后厨就懵了。后来菜单有了标准化编号和规格说明。ROS2 也是同样的道理:通信双方必须先约定“消息长什么样”。srv 文件就像菜单上的“编号+价格”——短、平、快;action 文件则像一本菜品说明书——目标、过程、成品全都写清楚。

srv 接口把一次通信分成两段:请求段(request)和响应段(response),中间用 --- 隔开。action 接口则分成三段:goal(目标)、result(最终结果)和 feedback(过程反馈)。看两个实际的接口定义:

规格写清楚,系统才不会出现“我要牛排你却端来沙拉”的乌龙。定义接口文件,就是在为机器人的协作立“菜单规范”。

srv:{r1,r2,…}→{s1,s2,…};action:{G}→{F1,F2,…,Fn}→{R}\displaystyle \text{srv}: \{r_1, r_2, \ldots\} \to \{s_1, s_2, \ldots\}; \qquad \text{action}: \{G\} \to \{F_1, F_2, \ldots, F_n\} \to \{R\}
srv 是“一组输入换一组输出”,action 是“一个目标换来一串进度和一个结局”。
  1. srv 的请求字段花括号里的 $r_i$ 是调用方传入的参数,比如加法的两个加数。
    {r1,r2,…}\{r_1, r_2, \ldots\}
  2. srv 的响应字段$s_j$ 是服务端算好后回传的数据,比如它们的和。
    {s1,s2,…}\{s_1, s_2, \ldots\}
  3. action 的目标 G启动一项长期任务所需的指令文字,比如目的地坐标。
    {G}\{G\}
  4. 反馈序列 $F_i$执行过程中向客户端通报的进度快照,比如“已走完 30%”。
    {F1,F2,…,Fn}\{F_1, F_2, \ldots, F_n\}
  5. 结果 R任务收尾时给出的最终交付物,比如“到达终点,耗时 3 分钟”。
    {R}\{R\}
text5 行
# 加法器服务:int64 a / int64 b 作为请求,int64 sum 作为响应int64 aint64 b---int64 sum
text6 行
# 倒计时动作:target 是目标,finished 是最终结果,progress 是过程反馈int64 target---bool finished---float32 progress

服务通信实现

顾客按服务铃,服务员走到桌边听清需求,转身处理,再回来答复。如果顾客不急,可以回座位等水送过来——这时他说“水好了叫我一声”,这就是异步。ROS2 的 Service 通信同样有这两种姿态。

实现上,Service 由服务端(Server)和客户端(Client)组成。服务端用 create_service 创建并注册回调,客户端用 create_client 建立连接。调用的关键区别在于:同步调用会阻塞当前线程直到拿到响应,适合主流程必须先拿到数据再往下走的场景;异步调用立即返回一个 future 凭证,等响应到达后由 executor 触发回调,主线程不阻塞。

使用异步时,客户端可以一边等服务端计算,一边继续发布话题或处理其他回调。但要注意:服务端只有在其节点被 spin 时才会处理请求,因此两端都必须有正在转动的 executor,否则就会“服务员在休息,没人接你的铃”。

聪明的服务员会灵活选择“立等可取”与“稍后通知”。写代码时多想想:这一问,主线程能不能等?

Service: send(req)→RPCrecv(rsp);sync: blockingasync: non-blocking\displaystyle \text{Service: } \text{send}(\text{req}) \xrightarrow{\text{RPC}} \text{recv}(\text{rsp}); \qquad \text{sync: blocking} \quad \text{async: non-blocking}
同步是死等,异步是“先拿到回执,完成后叫我”。
  1. send(req) 与 recv(rsp)一次远程过程调用的两端,客户端发请求,服务端收请求。
    send(req)→RPCrecv(rsp)\text{send}(\text{req}) \xrightarrow{\text{RPC}} \text{recv}(\text{rsp})
  2. RPC 箭头请求穿越 ROS2 中间件到达服务端的过程,相当于信号从铃铛传到服务员耳朵。
    →RPC\xrightarrow{\text{RPC}}
  3. blocking(同步)调用后线程挂起等待结果,像顾客站在前台直到水壶递到手。
    blocking\text{blocking}
  4. non-blocking(异步)调用立即返回一个 future,后续回调伺机执行,像顾客回桌等水。
    non-blocking\text{non-blocking}

Action机制

点牛排时,顾客不想站在明火旁干等。厨房发一个取餐牌(Goal 已接收),之后每完成一步就广播进度(腌制完成、翻面、装盘),最后上桌(Result)。顾客也可以中途大喊“不要了”(Cancel)。这正是 Action 的完整生命周期。

实现上,Action 通信由 Action Server 与 Action Client 组成。服务端接收 goal、调度执行体、周期性发布 feedback、最终发布 result,并响应取消请求;客户端发送 goal、注册 feedback 回调、接收 result、可随时发起 cancel。底层虽然由多组 Topic 与 Service 组合实现,但对用户暴露的是统一的三段式 API。

与“手动写 Service + 轮询”相比,Action 的价值在于把进度和取消都标准化了:你不需要自己造轮子去维护“任务状态机”,框架替你完成了大部分调度和错误处理。

Action 就是给机器人“点菜”的完整闭环:长期任务永远有进度条,也永远有紧急刹车。

Action=⟨G,{Fi}i=1n,R,cancel⟩\displaystyle \text{Action} = \langle G, \{F_i\}_{i=1}^{n}, R, \text{cancel} \rangle
Action 是一个四元组:目标、一串反馈、最终结果,外加取消信号。
  1. G(Goal)目标消息,标明任务内容和参数,比如“去充电桩,速度 0.5 m/s”。
    GG
  2. \{F_i\} 反馈流每完成一步就推送一条进度消息,客户端可以实时更新 UI。
    {Fi}i=1n\{F_i\}_{i=1}^{n}
  3. R(Result)最终结果,包含成功与否与最终数据,比如“已到达,耗时 2 分 30 秒”。
    RR
  4. cancel 取消信号客户端可随时下达取消命令,服务端收到后中止执行并返回取消状态。
    cancel\text{cancel}

QoS配置

有的菜必须趁热送到,比如滚烫的砂锅汤;有的菜可以慢慢送,比如水果拼盘。餐厅对不同菜品选择不同配送方式。ROS2 通信也一样,用 QoS(Quality of Service,服务质量)策略决定消息“配送”的保障等级。

QoS 是一组策略参数的集合,核心包括:Reliability(可靠性)、History(历史策略)、Depth(队列深度) 和 Durability(持久性)。对 Service 与 Action 这种需要双向确认的通信,RELIABLE(可靠投递)是默认且常见的选择——请求不能丢,丢了要重发。而传感器流常用 BEST_EFFORT(尽力而为),丢几帧无所谓。

配置 QoS 时最关键的一点是兼容性:通信双方的 QoS 参数必须兼容才能建立连接。比如服务端用 RELIABLE,客户端用 BEST_EFFORT,就可能出现握手失败或收不到消息的情况。这就像顾客说“必须闪送”,外卖平台却说“只能平邮”——这单根本接不了。

给 Service 与 Action 配上合适的 QoS,就像在菜单上注明“此菜需趁热食用”——配送方式定对了,沟通才不凉。

QoS=⟨Reliability,History,Depth,Durability⟩\displaystyle \text{QoS} = \langle \text{Reliability}, \text{History}, \text{Depth}, \text{Durability} \rangle
QoS 是四元组:可靠性、历史策略、队列深度、持久性,任何一端不匹配都会出问题。
  1. Reliability可靠投递(RELIABLE)还是尽力而为(BEST_EFFORT),对应闪送和平邮。
    Reliability\text{Reliability}
  2. HistoryKEEP_LAST 只保留最近若干条,KEEP_ALL 全部保留,像冰柜里只留最新水果盘还是全留着。
    History\text{History}
  3. Depth队列能塞多少条消息,深度越小越容易丢旧数据。
    Depth\text{Depth}
  4. Durability是否让后来加入的订阅者也能拿到旧值,像“新来的服务员也能看到昨天交接班记录”。
    Durability\text{Durability}

回调处理

餐厅忙时,一个服务员要同时处理 A 桌加菜、B 桌结账、C 桌投诉。他必须有个工作节奏:先记下 A 桌需求,转身给 B 桌递账单,再安抚 C 桌。ROS2 的节点也是如此——多个事件同时在催你,需要执行器(Executor)统一调度回调函数。

ROS2 节点处理事件靠回调函数:收到 Service 请求会调用服务回调,收到 Action 反馈会调用反馈回调。SingleThreadedExecutor 单线程挨个执行回调,像只有一位服务员跑堂;MultiThreadedExecutor 用线程池并行处理,像多人分工接待。选哪种取决于回调是否耗时:如果某个回调里有等待硬件 IO 的阻塞操作,单线程执行器会被卡住,其他回调全部排队。

Action 的 feedback 回调触发频率通常很高,必须写得轻量——不要在反馈回调里做重计算或磁盘写入。一句话:回调要短小精悍,就像服务员不会站在一桌旁边聊十分钟。

管理好回调,就是管理好接待节奏。多线程不是万能药——一个不讲武德的阻塞回调照样会让线程池的线程饿死。

Tcycle=∑i=1nTcbi (single);Tcycle≈max⁡iTcbi (multi)\displaystyle T_{\text{cycle}} = \sum_{i=1}^{n} T_{\text{cb}_i} \ (\text{single}); \qquad T_{\text{cycle}} \approx \max_i T_{\text{cb}_i} \ (\text{multi})
单线程执行器所有回调排队求和,多线程执行器的瓶颈只在于最慢那个回调。
  1. $T_{\text{cb}_i}$ 每个回调耗时每一个小任务的处理时间,比如递账单要 5 秒。
    TcbiT_{\text{cb}_i}
  2. 单线程求和所有回调排队相加,一个卡住全店停摆。
    ∑i=1nTcbi\sum_{i=1}^{n} T_{\text{cb}_i}
  3. 多线程取最大回调并行执行,总周期由最慢的那个决定。
    max⁡iTcbi\max_i T_{\text{cb}_i}
  4. 设计原则把重活丢到独立线程池,回调里只做轻量登记。

工程实践

成熟的餐厅会记账:排班表、进货单、顾客反馈。机器人开发也离不开这些幕后功夫——调试工具、生命周期、性能监控就是 ROS2 的“运营台账”。

开发时最常用的是命令行工具:ros2 service list 查看当前有哪些服务,ros2 service call 手动调用服务测试;ros2 action list 查看 Action,ros2 action send_goal 手动下发目标。这些命令能快速确认“服务员有没有在岗、接不接单”。此外要留意生命周期管理:Lifecycle Node 提供了 unconfigured→inactive→active→finalized 的状态机,适合有启动顺序要求的复杂机器人系统;性能监控则关注消息频率、回调耗时和队列深度,排查“哪一环拖慢了整条链路”。

常见故障排查口诀:服务端没 spin,请求无人理;QoS 不兼容,握手直接断;回调里死等,全节点卡死。理解了整条链路的时延构成,问题定位就不难。

台账越清晰,餐厅越不容易翻车。把这条时延公式刻在脑子里,排查性能问题就有章可循。

te2e=tserialize+ttransport+tqueue+tprocess+tdeserialize\displaystyle t_{\text{e2e}} = t_{\text{serialize}} + t_{\text{transport}} + t_{\text{queue}} + t_{\text{process}} + t_{\text{deserialize}}
端到端时延 = 序列化 + 传输 + 排队 + 处理 + 反序列化,每一环都可能成为瓶颈。
  1. 序列化与反序列化消息在发送前打包、接收后解包,像把食材装进保鲜盒再打开。
    tserialize+tdeserializet_{\text{serialize}} + t_{\text{deserialize}}
  2. 传输时延消息在网络中飞行的时间,像外卖骑手在路上跑。
    ttransportt_{\text{transport}}
  3. 排队时延消息到达后等待执行器调度的时间,像高峰期等位。
    tqueuet_{\text{queue}}
  4. 处理时延服务端真正执行业务逻辑的耗时,像后厨炒菜。
    tprocesst_{\text{process}}

AI 应用场景

AGV 物流机器人的长程配送

阶段:任务调度与执行

解决的问题:传统运动控制用 Topic 广播目标点,既拿不到进度也没法取消;改用 Action 后,配送机器人接收 Goal,沿途不断反馈位置与电量,调度系统随时可取消改派,任务执行透明可控。

具体案例:某电商仓库中,多台 AGV 接收“将料箱从 A 区送至 B 区”的 Action 目标。调度中枢看到每台车的实时反馈,当一台 AGV 发生拥堵时,系统下发取消指令并重新分配任务,整体吞吐量提升约 30%。

巡检机器人的状态自检

阶段:状态查询与健康诊断

解决的问题:传统方式需人工 SSH 连接机器人或轮询话题,信息不即时且浪费带宽;用 Service 封装“查询电量/温度/自检结果”接口,中控大厅一键呼叫即问即答,像按服务铃一样简单。

具体案例:物业巡检机器人每 10 分钟被中控调用一次“电量查询”服务,若返回电量低于 20%,中控立即下发“回充站导航”的 Action 目标,实现全自动补能闭环。

机械臂协作装配

阶段:多环节协同控制

解决的问题:装配任务既有“查状态”的短请求,也有“执行轨迹”的长任务。单独用 Service 扛不住长时间执行,单独用 Action 又不够轻快。组合使用:先 Service 查询夹爪开合状态,再 Action 下达搬运装配目标,两者配合恰到好处。

具体案例:3C 产线上的协作机械臂接到“抓取螺丝并拧入孔位”的 Action 任务,每完成一个动作就反馈进度;在切换工件前,调度系统用 Service 快速确认吸盘真空度是否达标,保障节拍稳定。

Python 演示

python53 行
import timeimport threading  class RoboRestaurant:    """机器人餐厅: 演示 Service 与 Action 两种通信模式"""     def __init__(self):        self._cancel_events = {}        self._serial = 0     # ---- Service: 一问一答 ----    def query_water(self, table):        """按服务铃: 请求一次, 立即返回响应"""        return {"table": table, "status": "water served", "cost_ms": 50}     # ---- Action: 从下单到上桌 ----    def place_order(self, meal, steps=3):        """点大餐: 逐阶段执行, 支持反馈与取消"""        self._serial += 1        goal_id = self._serial        cancel_event = threading.Event()        self._cancel_events[goal_id] = cancel_event         def _cook():            for i in range(1, steps + 1):                if cancel_event.is_set():                    print("[{}] cancelled: {}".format(goal_id, meal))                    return "cancelled"                time.sleep(0.2)                print("[{}] feedback {}/{}: cooking {}".format(                    goal_id, i, steps, meal))            print("[{}] result: {} served".format(goal_id, meal))            del self._cancel_events[goal_id]         threading.Thread(target=_cook, daemon=True).start()        return goal_id     def cancel_order(self, goal_id):        event = self._cancel_events.get(goal_id)        if event:            event.set()            return True        return False  if __name__ == "__main__":    restaurant = RoboRestaurant()    print(restaurant.query_water(3))      # Service 同步秒回    gid = restaurant.place_order("steak", 3)  # Action 长期执行    time.sleep(0.5)    restaurant.cancel_order(gid)          # 中途取消    time.sleep(0.5)
目的:用纯 Python 模拟 ROS2 中 Service 与 Action 的两种编程模型,演示同步请求-响应和带反馈、可取消的长时间任务。
  1. def query_water(self, table):Service 处理函数:一次请求立即算出并返回结果,对应服务端回调。
  2. return {"table": table, "status": ...}构造响应体,相当于 srv 文件的 response 段。
  3. cancel_event = threading.Event()为这次任务配备一个“取消信号旗”,对应 Action 的取消机制。
  4. if cancel_event.is_set():执行循环中检查取消标志,收到信号就终止任务。
  5. time.sleep(0.2)模拟耗时的烹饪步骤,现实中对应电机转动或路径规划。
  6. print("[{}] feedback {}/{}...")每阶段输出进度反馈,对应 action 的 feedback 消息。
  7. threading.Thread(target=_cook, daemon=True).start()后台线程执行长任务,主线程不被阻塞。
  8. restaurant.cancel_order(gid)客户端发起取消请求,对应 Action 的 cancel goal。

常见误区

误区“我的任务要跑 5 分钟,但我不想学 Action,直接用 Service 不就行了?我把请求发过去,服务端慢慢算,最后返回结果。”

正解Service 是一问一答的短时模型,客户端同步等待时线程会被阻塞,期间无法收到任何进度反馈,也不能中途取消。如果服务端处理超过数秒,用户体验极差,系统还容易触发超时。长时间、需要反馈、可能取消的任务应该交给 Action,正如点牛排不该按服务铃解决。

为何常见:初学者刚接触 Service 时觉得“能返回结果就行”,忽略了长任务需要的反馈和取消能力,误把 Service 当成通用 RPC。

误区“Action 底层不就是两个 Topic 加一个 Service 吗?我自己写回调也行,没必要用 Action 框架。”

正解Action 虽然在底层封装了 goal/cancel/result 等若干 Topic 与 Service,但它提供了统一的三段式 API、完整的状态机、取消语义和内置的反馈/结果处理。手动拼装极易遗漏边界情况,比如“任务执行到一半取消时,反馈线程怎么退出”。用框架是走成熟路线,不是偷懒。

为何常见:深入源码后看到底层实现,容易产生“不过如此”的错觉,低估了应用层状态管理的复杂度。

误区“我在代码里给 Service 配了 QoS,RELIABLE 和 KEEP_LAST,应该就没问题了吧?”

正解QoS 是通信双方协商的结果,不是单方面的事。如果服务端用 RELIABLE、客户端用 BEST_EFFORT,二者不匹配可能导致连接建立失败或消息静默丢失。配置 QoS 时必须同时检查发布端和订阅端(或服务端与客户端)的策略是否兼容,就像外卖员和顾客必须约定同一个配送方式。

为何常见:QoS 参数较多且文档分散,初学者容易只配置一端,忽视“双方需兼容”这一核心约束。

误区“我的回调里要等电机转到位,写个 while 循环不断读传感器不就行了?反正回调会执行完。”

正解在默认的单线程执行器里,一个回调阻塞,整个节点的所有订阅、定时器和回调都会停摆,就像服务员站在一桌前打持久战,其他桌全晾着。回调必须短小精悍,耗时操作应放入独立线程池或改用多线程执行器,并配合线程安全机制。

为何常见:写普通 Python 脚本时,while 循环很常见;但节点模型是事件驱动的,阻塞回调等于堵死事件循环,这个转变初学者往往意识不到。

误区“服务端收到 goal 之后,execute 函数应该立刻跑起来,任务就在执行了吧?”

正解Action 服务端收到 goal 后,需要先经过 accept 确认,之后执行器才会在 spin 过程中调度 execute 回调。若执行器没在转,goal 只是躺在队列里。此外,accept 和 execute 是解耦的,可以在 accept 阶段做权限校验,拒绝不合理的目标。

为何常见:把 Action 的 goal 机制想成了普通 Service 的“请求即处理”,忽略了 action 服务端的确认环节和调度时序。

面试题

Q1. 以下哪个场景最适合使用 Service 通信?

单选题 · easy

  • A.导航至指定目标点并返回
  • B.查询电池当前电量数值← 正确答案
  • C.持续广播激光雷达数据
  • D.周期上报超声波距离值

答案:B。Service 适合短时请求-响应,查询电量是一问一答;导航要长时间执行且需进度,适合 Action;激光雷达和超声波数据是持续流,适合 Topic。

Q2. Action 消息模型中,用于报告任务执行进度的部分是?

单选题 · easy

  • A.目标消息 Goal
  • B.反馈消息 Feedback← 正确答案
  • C.结果消息 Result
  • D.取消请求 Cancel

答案:B。Action 三段式中,Goal 是目标,Result 是最终结果,Feedback 是执行过程中的进度报告。Cancel 不属于三段式消息,而是独立的取消机制。

Q3. srv 文件中用 --- 分隔符分割的是什么?

单选题 · mid

  • A.请求段与响应段← 正确答案
  • B.消息头与消息负载
  • C.注释与有效字段
  • D.消息版本与类型名

答案:A。srv 接口文件把 request 字段写在 --- 上方,response 字段写在下方,这个分隔符是服务接口的语法标志。

Q4. ROS2 中服务端用 RELIABLE、客户端用 BEST_EFFORT 配置 QoS,可能发生什么?

单选题 · hard

  • A.数据全部丢失且无法恢复
  • B.连接建立失败或消息丢失← 正确答案
  • C.消息发送频率自动提升
  • D.客户端自动升级为可靠模式

答案:B。QoS 必须双方兼容。RELIABLE 与 BEST_EFFORT 在可靠性维度不匹配,会导致握手失败或收发异常。系统不会自动降级或升级,必须人工对齐。

Q5. Action 的 message 模型中包含哪三部分?

多选题 · mid

  • A.目标消息 Goal← 正确答案
  • B.反馈消息 Feedback← 正确答案
  • C.结果消息 Result← 正确答案
  • D.心跳消息 Heartbeat

答案:ABC。Action 接口由 Goal、Feedback、Result 三段消息组成,分别对应任务目标、过程进度和最终结果。Heartbeat 是通用的连接保活机制,不属于 Action 消息模型。

Q6. 关于 Executor 执行器,下列说法正确的是?

多选题 · mid

  • A.单线程执行器串行处理回调← 正确答案
  • B.多线程执行器可并行处理回调← 正确答案
  • C.执行器负责调度等待中的回调← 正确答案
  • D.每个回调必须单独占一个线程

答案:ABC。SingleThreadedExecutor 逐个执行回调,MultiThreadedExecutor 用线程池并行执行,Executor 的职责就是调度就绪回调。D 错误,多线程执行器共享线程池,并非每个回调独占线程。

Q7. 客户端想中途停止一个正在执行的 Action 任务,应该怎么做?

单选题 · easy

  • A.发送取消目标请求← 正确答案
  • B.重启全部网络节点
  • C.删除 Action 源文件
  • D.关闭机器人电源

答案:A。Action 内置了取消语义,客户端发送 cancel goal 请求,服务端收到后中止执行并返回取消状态。重启节点、删文件、断电都属于粗暴且错误的方式。

Q8. 下列哪些命令行工具用于 Action 调试?

多选题 · mid

  • A.ros2 action list← 正确答案
  • B.ros2 action send_goal← 正确答案
  • C.ros2 pkg executables
  • D.ros2 daemon stop

答案:AB。ros2 action list 查看当前 action,ros2 action send_goal 手动下发目标,是调试 action 的常用命令。ros2 pkg executables 查看可执行文件,ros2 daemon stop 控制后台守护进程,均与 action 调试无关。

Q9. 下列哪种场景更适合用 Service 而非 Action?

单选题 · hard

  • A.需要持续反馈执行进度
  • B.需要同步获取明确响应← 正确答案
  • C.需要中途取消任务
  • D.需要周期广播传感器流

答案:B。Service 的核心特征是同步请求-响应,适合“问一句、答一句”的短交互。需要反馈或取消应选 Action,周期广播应选 Topic。

Q10. ROS2 中 Action 接口文件的扩展名是什么?

单选题 · easy

  • A..srv
  • B..msg
  • C..action← 正确答案
  • D..yaml

答案:C。Action 接口文件使用 .action 后缀,Service 用 .srv,普通消息用 .msg,配置文件常用 .yaml。三者分别是不同通信机制的接口定义格式。