highlevel_skills 模块

daystar_api.highlevel_skills 是基于 lowlevel 接口封装的高层任务技能集合。 相比 lowlevel 接口直接对应单个 ROS 服务/动作,highlevel skill 通常组合多个 lowlevel 调用、 内置前置状态检查与降级逻辑,对调用方屏蔽底层状态机复杂性。

模块概览

当前已收录的 highlevel skill:

  • 导航类:go_to_location()(导航到指定点位 / 位姿 / 经过多点)

快速示例

from daystar_api.highlevel_skills import go_to_location

# 1. 单点(自动路网规划)
result = go_to_location("茶水间")
print(f"导航结果: {result.state.cn}")

# 2. 多点路线(直接执行,跳过路网;最后一个即终点)
result = go_to_location(locations=["大厅", "走廊", "会议室"])

# 3. 带回调
def on_complete(ev): print("到达", ev)
result = go_to_location("充电桩", complete_callback=on_complete)

Note

highlevel skill 是纯 Python 实现(位于 daystar_api/highlevel_skills/ 包下)。 本页采用手写 py:function 指令而非 autodoc,与同类无法 autodoc 的页面 (逻辑流控制)风格对齐——daystar_api 顶层包的导入链涉及 ROS workspace 外部依赖(path_planer_api、_lowlevel_skills 的 pybind 绑定等), sphinx 构建环境不保证全部可用。

导航类

go_to_location(location=None, pose=None, locations=None, poses=None, travel_params=None, timeout=60, complete_callback=None, failed_callback=None, progress_callback=None, graph_file=None, start_location=None, auto_graph_planning=False, avoid_obstacles=True, optimize_order=False, ignore_final_yaw=None, reloc_location_name=None, reloc_location_pose=None, reloc_candidate_poses=None)

导航到指定命名点位、坐标位姿,或顺序经过多个点位 / 位姿。在执行导航前自动完成定位检查、 姿态调节、控制模式切换等前置步骤;对单点目标支持基于路网文件的最短路径展开。

底层始终走多点导航 API:单点情形本质上等价于”长度为 1 的多点导航”, 因此不再单独调用 _lowlevel_skills.navigation_to_pose()。

参数互斥规则:location / pose / locations / poses 必须且只能提供其中一个。

Parameters:
  • location (str | None) – 单个目标点位名称,须已在系统中注册(可通过 _lowlevel_skills.get_available_location() 查询)。

  • pose (Pose | None) – 单个目标坐标位姿,通常配合 _lowlevel_skills.get_current_pose() 使用以实现”返回原位”。

  • locations (list[str] | None) – 多个已注册点位的顺序列表,最后一个即终点。 想要”先经过 A、B 再到 C”,直接写 locations=["A", "B", "C"]。 auto_graph_planning=True 时按此顺序逐段路网展开,False 时原样串行执行。

  • poses (list[Pose] | None) – 多个位姿的顺序列表,最后一个即终点。 auto_graph_planning=True 时按此顺序逐段路网展开,False 时原样串行执行。

  • travel_params (MsgTravelParams | None) –

    导航行驶参数。仅作用于序列最后一个点 (即真正的终点);底层 action server 设计如此。为 None 时使用默认值。

    Warning

    单点导航(``location`` / ``pose`` 单目标,且未展开路网)走的是长度为 1 的 多点导航,在导航栈眼中即单点,行为完全由 travel_params.path_following_mode 决定:1=直线循线(不显式设置时 SDK 兜底按此下发)、2=关闭循线即自由绕障。想让机器人绕开障碍物到达 单个目标点,必须显式构造 travel_params 并设 ``path_following_mode=2``, 否则它会直线循线过去。注意本参数与 avoid_obstacles/ auto_graph_planning 不是一回事——后两者影响的是高层路网选路, 前者决定导航栈在两点之间怎么走。详见 单点 path_following_mode 兜底。

    示例(单点绕障):

    from daystar_api.lowlevel_skills import MsgTravelParams
    
    tp = MsgTravelParams()
    tp.path_following_mode = 2   # PATH_MODE_OFF:关闭循线,自由绕障
    go_to_location(location="kitchen", travel_params=tp)
    

  • timeout (int) – 整个导航过程的超时时间(秒),默认 300。上限 5 分钟。

  • complete_callback (callable | None) – 导航成功完成时的回调, 接收 _lowlevel_skills.NavCompleteEvent。

  • failed_callback (callable | None) – 导航失败时的回调, 接收 _lowlevel_skills.NavFailedEvent。

  • progress_callback (callable | None) – 导航进度回调, 接收 _lowlevel_skills.NavProgressEvent。

  • graph_file (str | None) – 路网图文件名或路径。为 None 时自动发现 当前地图的路网(手动路网 = graph/<map>/default.yaml;无手动路网时按避障 数据源自动生成 auto_generated.yaml)。仅 auto_graph_planning=True 时生效。

  • start_location (str | None) – 起始路网节点名称。为 None 时根据当前位置 自动匹配(优先避障可达最近节点)。仅 auto_graph_planning=True 时生效。

  • auto_graph_planning (bool) – 是否启用自动路网规划,默认 False(单点直达、 多点原样串行,不走路网)。True 时:手动路网优先;无手动路网则按当前地图的 避障数据源(ground.pcd 可行域点云优先,回退 map.png)自动生成路网—— 建边判据三级:直线可通 → 管内小幅擦挡可绕 → 可行域栅格 A* 可达(仅 pcd 数据源启用;A* 边携带绕行拐点,执行时自动注入为中间位姿)。被真墙隔开的点对 保持断连,规划不出路时抛错让调用方接手。daystar_agent 经 go-to-location skill 调用时注入 True。

  • avoid_obstacles (bool) – 是否开启绕障导航,默认 True(路网绕障规划 + via 序列一次性下发)。False 时忽略路网规划,逐点调 _lowlevel_skills.navigation_to_location() / _lowlevel_skills.navigation_to_pose() 单点自主导航,任一点失败即停。

  • optimize_order (bool) – 多点目标(locations / poses)且 auto_graph_planning=True 时,是否用 ATSP(PyLKH)重排访问顺序求最短总路程, 默认 False 保持用户顺序。语义:末点固定为终点,仅重排中间目标。 PyLKH 不可用 / 目标不足 3 个 / 任一目标解析不到路网节点时自动回落保序执行 (不报错)。单点目标忽略本参数。

  • reloc_location_name (str | None) – 定位异常自愈用的重定位初值——已注册点位名。

  • reloc_location_pose (Pose | None) – 定位异常自愈用的重定位初值——手动位姿 (map 坐标系)。

  • reloc_candidate_poses (list[Pose] | None) – 定位异常自愈用的重定位初值—— 多候选位姿列表(走 autoselect 自动选优)。三个 reloc_* 参数优先级: reloc_candidate_poses > reloc_location_name > reloc_location_pose。 导航 0731 起自动重定位不再对外:定位异常且三者均未提供时报 NAV_RELOCALIZE_FAILED(不再隐式自动重定位),需先完成定位初始化。

Returns:

底层多点导航结果。state.code == 0 表示成功;state.cn 包含中文描述。

Return type:

_lowlevel_skills.NavigationViaPosesResponse

Raises:
  • GoToLocationError – 前置检查失败或参数不合法时抛出 (如多个目标参数同时提供、目标点位未注册等)。

  • RuntimeError – 在导航回调内部递归调用本函数(或任何 block=True 的底层 nav API)时由 C++ 层立即抛出,防止 ROS 动作客户端死锁。详见下方”回调死锁防御”。

执行路径:

avoid_obstacles=False 时不走下表(逐点单点自主导航)。 auto_graph_planning=False(默认)时单点/多点一律不走路网、按目标原样下发。 下表为 auto_graph_planning=True 时的行为:

入参

行为

单 location + 目标在路网中

路网最短路径展开为完整节点序列(含起点),一次性调用 navigation_via_locations; 路径含 A* 拐点边或路网合成节点时整条转 Pose 改走 navigation_via_poses

单 location 无路网(或目标不在路网中)

退化为单点列表 [location] 调 navigation_via_locations

单 pose + 有可用路网

路网节点转 Pose 拼前缀(含 A* 边拐点与出入口接入拐点)+ 最终 pose, 一次性调用 navigation_via_poses

单 pose 无路网

退化为单点列表 [pose] 调 navigation_via_poses

locations 显式列表

按用户顺序逐段路网展开(缺节点的段直走)调 navigation_via_locations; optimize_order=True 时先 ATSP 重排中间点(末点固定)

poses 显式列表

按用户顺序逐段路网展开(每段终点落到真实 pose)调 navigation_via_poses; optimize_order=True 同上

Note

导航路径诊断出图:每次调用会把最终下发序列落盘 graph/<map>/path/nav_path_latest.yaml,并在后台线程渲染 nav_path_<时间戳>.png(可行域底图 + 实际路径折线,保留最近 10 张; 取不到地图名回落 user_logs/nav_path/)。不占导航关键路径, env DAYSTAR_NAV_PATH_RENDER=0 可关闭。

Note

路网展开后的列表**包含起点节点**(机器人当前位置最近的路网节点),原样下发。 是否 skip 已到达的 waypoint 由底层 navigation 控制器自行判定, Python 高层不做猜测式剔除。这保证了”路网展开”与”显式 locations 列表” 在底层眼中完全等价。

回调安装机制:

每次调用通过底层 C++ 绑定的 NavCallbackGuard RAII 临时安装 callback, 调用结束自动还原调用前的全局回调(per-call scoped)。不会污染其它模块注册的全局回调。

回调死锁防御:

底层 navigation_client_ 绑定在 send_goal_cb_group_(MutuallyExclusive), 同组同时只能执行一个回调。若在 complete_callback / failed_callback / progress_callback 内部又调用 block=True 的 nav API,会等同组下一次回调被派发, 但该组已被当前回调占用——必然死锁。

C++ 层加了 thread_local 标志 + RAII:进入用户 callback 前置位, 阻塞 nav 入口(RequestNavigationAct(block=true) / WaitForNavigation / RequestCancelNav)检测到立刻抛 std::runtime_error,pybind11 自动转 Python RuntimeError。

回调中可安全调用:block=False 的异步派发,get_current_pose / get_robot_state 等其它回调组的服务,纯 Python 计算 / 置标志位, threading.Thread 起独立线程后在其中调阻塞 nav(独立 OS 线程不触发防御)。

回调中禁止调用:go_to_location(block 隐含 True) / navigation_*(block=True) / wait_for_navigation / cancel_navigation。

from daystar_api.highlevel_skills import go_to_location
from daystar_api.lowlevel_skills import get_current_pose, Pose

# 1. 单点(自动路网规划,无路网则直接导航)
result = go_to_location("茶水间")
if result.state.code != 0:
    raise RuntimeError(f"导航失败: {result.state.cn}")

# 2. 多点名字路线(直接执行,不规划。"先去 A 再去 B 最后到 C" 就走这里)
result = go_to_location(locations=["大厅", "走廊", "会议室"])

# 3. 多点位姿
result = go_to_location(poses=[pose_a, pose_b, pose_c])

# 4. 返回原位(先记录出发位置,执行任务后回去)
pose_resp = get_current_pose()
origin_pose = pose_resp.response.location_pose
# ... 执行中间任务 ...
result = go_to_location(pose=origin_pose)
print(f"返回原位: {result.state.cn}")

# 5. 带回调(callback 内禁止调阻塞 nav API)
def on_complete(ev):
    print("到达终点", ev)
def on_failed(ev):
    print("失败", ev)
result = go_to_location(
    "充电桩",
    complete_callback=on_complete,
    failed_callback=on_failed,
)

# 6. 回调中发起下一段导航的推荐做法:主线程串行
result = go_to_location("A")
if result.state.code == 0:
    result = go_to_location("B")
exception GoToLocationError

go_to_location 前置检查失败或参数不合法时抛出。继承自 RuntimeError。

相关数据类型