基于语言目标的空中导航是具身人工智能领域的一项关键挑战,它要求无人机能够根据文本指令,在城市街区等复杂环境中对目标进行定位。
现有的方法通常改编自室内导航,由于视野有限、物体之间存在语义模糊性,以及缺乏结构化的空间推理能力,这些方法难以进行拓展应用。
©️【深蓝具身智能】
为解决以上问题,国防科大联合清华大学提出的 GeoNav 是一种具备地理空间感知能力的多模态智能体,以实现长距离导航。

图1 | GeoNav工作流程示意图
GeoNav分三个阶段运行:地标导航、目标搜索和精确定位,这模仿了人类从粗略到精细的空间策略。
为了支持这种推理过程,它会动态构建两种不同类型的空间记忆:
-
全局但概略的示意性认知地图SCM
它将先前的文本地理知识和具身视觉线索融合成一种自上而下且带有注释的形式,以便能够快速导航至地标区域;
-
局部但精细的分层场景图HSG
该图表示街区、地标和物体之间的层次空间关系,用于对目标进行明确的定位。
在这种结构化表示的基础之上,GeoNav还采用了一种具备空间感知能力的多模态思维链提示机制,使多模态大语言模型(MLLMs)能够在各个阶段进行高效且可解释的决策。
本文将基于上述核心内容配合代码对该研究成果进行详细梳理。
我们开设此账号,想要向各位对【具身智能】感兴趣的人传递前沿权威的知识讯息外,也想和大家一起见证它到底是泡沫还是又一场热浪?
欢迎关注【深蓝具身智能】👇

方法说明
GeoNav包含三个关键模块:示意性认知地图(SCM)、分层场景图(HSG)和多阶段导航策略(MNS),分别模拟智能体的感知、记忆和决策能力,如图所示:在地标导航和目标搜索阶段构建并使用SCM,在搜索目标时构建HSG,最终用于定位目标。
接下来,将依次介绍这三个模块的原理及代码实现逻辑。
示意性认知地图Schematic Cognitive Map(SCM)

图2 | 示意性认知地图构建流程
-
目的
构建整个城市的语义地图,为无人机导航提供全局的环境先验,避免迂回飞行。
-
流程说明
1. Geo and Object Data Collection:
Step1:从OpenGIS数据中检索地标的先验地理信息,并将其投影到地标地图(Landmark Layout)上
Step2:使用GroundDino在无人机俯拍的RGB图像中检测物体,得到物体的Bounding Boxes边界框;
Step3:通过Segment Anything优化步骤2中得到的Bounding Boxes边界框,生成Semantic Mask分割掩码;
2. Spatial Projection:
将Semantic Mask分割掩码的像素转换到世界坐标系下,像素坐标和世界坐标转化过程如下:

3. LandMark and Object Annotations:
得到标有物体信息的语义地图,即示意性认知地图SCM;
class LandmarkNavMap(Map):def plot(self,map_type,query_engine=None,current_pos=None):import numpy as np# 颜色配置(RGBA格式)layer_colors = {'current view area': (0.5, 0, 0.5, 0.3), # 紫色}# 合并地图数据map_layers = np.concatenate([np.expand_dims(self.to_array()[1],0),])# 创建画布fig, ax = plt.subplots(figsize=(10, 10))fig = plt.figure(figsize=(10, 10), facecolor='white')ax = fig.add_subplot(111)# 绘制当前视野区域if map_type != 'landmark':for idx, (layer_name, rgba) in enumerate(layer_colors.items()):self._draw_layer(ax, map_layers[idx], rgba)# 绘制landmark和semantic图层self._merge_color_layers(ax, map_type)# 添加landmark标注if map_type != 'w/o annotation':self._annotate_landmarks(ax)# === 新增轨迹绘制逻辑 ===# 绘制方向箭头if map_type != 'w/o annotation':arrow_length = 15 # 箭头长度(像素)if len(self.trajectory) == 1:pose = self.trajectory[0]col, row = self.to_row_col(pose.xy)[::-1]dx = arrow_length * np.cos(pose.yaw)dy = - arrow_length * np.sin(pose.yaw)ax.arrow(x=col, y=row,dx=dy, dy=dx, # 注意坐标轴方向转换width=3,head_width=8,head_length=10,fc='lime', # 箭头填充色ec='darkgreen', # 箭头边缘色alpha=0.8) # 半透明箭头elif len(self.trajectory) >1:# 转换轨迹坐标trajectory_points = [self.to_row_col(pose.xy)[::-1] # 转换为(col, row)格式for pose in self.trajectory]# 绘制轨迹线x_coords, y_coords = zip(*trajectory_points)ax.plot(x_coords, y_coords, color = 'black', linestyle='-', linewidth=3.5, alpha=0.7) # 白色虚线轨迹prev_pose = trajectory_points[-2]pose = trajectory_points[-1]delta_x = pose[0] - prev_pose[0]delta_y = pose[1] - prev_pose[1]theta = np.arctan2(delta_y, delta_x) # 箭头角度是上一位置到当前位置的角度col, row = pose# 计算箭头方向dx = arrow_length * np.cos(theta)dy = arrow_length * np.sin(theta)ax.arrow(x=col, y=row,dx=dx, dy=dy, # 注意坐标轴方向转换width=3,head_width=8,head_length=10,fc='lime', # 箭头填充色ec='darkgreen', # 箭头边缘色alpha=0.4) # 半透明箭头# 创建图例if map_type != 'landmark':self._annotate_objects(ax, query_engine=query_engine, current_pos=current_pos)legend_elements = self._create_legend_elements()ax.legend(handles=legend_elements,loc='upper center',bbox_to_anchor=(0.5, -0.05),ncol=3)# 生成输出图像plot_img = self._render_output(fig, map_type)plt.close('all')return plot_img
-
代码说明:这部分代码描述了示意性认知地图(SCM)绘制构建逻辑。
分层场景图Hierarchical Scene Graph(HSG)

图3 | 构建和查询分层场景图HSG的一个例程
-
说明
如图所示,分层场景图HSG是一个分层次的拓扑图形结构,节点Node有3种类型:城市街区(Block)、地标(LandMark)、物体(Object)。
边Edge表示节点和节点的空间关系,例如:Block和LandMark是包含(Contains)关系,LandMark和Object,Object和Object有方位(On the left of,On the right of,Next to等)关系。
-
目的
分层场景图HSG能提供更加精确的空间信息,保证无人机即使在一堆相似物体中也能精确识别到目标物体。
-
流程说明

|
1. 每个节点必须有唯一的ID 2. 每个节点的必备属性: - 对象类型:取值为[车辆, 道路, 建筑物, 停车场, 绿地等]中的一个 - 边界框:边界框坐标,格式为[xmin, ymin, xmax, ymax] 3. 可选属性: - 颜色:取值为[白色, 黑色, 红色, 灰色, 蓝色, 绿色, 棕色, 银色]中的一个。 |
构建节点的要求
|
1. 仅使用以下具有特定含义的关系标签: (1)拓扑关系: - “contains”(包含):表示一个物体完全在另一个物体内部。 - “adjacent_to”(相邻):表示物体彼此紧邻。 - “near_corner”(靠近角落):表示一个物体靠近另一个物体的角落。 (2)方向关系(从航拍视角的绝对方向): - 主要方向:“north_of”(在……北方)、“south_of”(在……南方)、“east_of”(在……东方)、“west_of”(在……西方)。 - 对角线方向:“northeast_of”(在……东北方)、“northwest_of”(在……西北方)、“southeast_of”(在……东南方)、“southwest_of”(在……西南方)。 2. 绝对方向关系优先于相对术语: - “behind”(在……后面)转换为“north_of”(在……北方)。 - “next to”(在……旁边)转换为“adjacent_to”(相邻)。 - “in”(在……里面)转换为“contains”(包含)。 |
构建节点的要求
class LLMController:def build_scene_graph(self, subgraph, gsm):# 过滤node_data.get("bbox", [])==[]的节点subgraph["nodes"] = [node for node in subgraph["nodes"] if node.get("bbox", [])]id_mapping = {}bboxes = [node_data.get("bbox", []) for node_data in subgraph["nodes"]]# pos, class, confidence, timestampposes = gsm.bbox_to_global_pos(bboxes)for pos, node in zip(poses, subgraph["nodes"]):# 提取节点的所有属性node_attrs = {'obj_class': node['object_type'],'confidence': 1.0,'timestamp': self.timestep}# 添加可选属性if 'color' in node:node_attrs['color'] = node['color']# 添加其他可能的属性for key, value in node.items():if key not in ['id', 'object_type', 'bbox']:node_attrs[key] = value# 创建节点并添加到场景图中global_id = self.scene_graph.add_object_node_with_attrs(pos, node_attrs)if global_id is not None:id_mapping[node['id']] = global_idparent_geo, relation_type = self._find_parent_geo(pos)if parent_geo:# 添加基于空间关系的边self.scene_graph.add_edge(parent_geo.id,global_id,relation_type)# 如果不是 "contains" 关系,还可以添加更详细的空间描述if relation_type != "contains" and relation_type != "adjacent_to":spatial_rel = self._describe_spatial_relation(parent_geo,self.scene_graph.nodes[global_id])self.scene_graph.add_edge(parent_geo.id,global_id,spatial_rel)# 处理边for edge in subgraph.get("edges", []):source = id_mapping.get(edge["source"])target = id_mapping.get(edge["target"])if source is None or target is None:continue# 提取边的属性relationship = edge["relationship"]edge_attrs = {}# 添加其他可能的属性for key, value in edge.items():if key not in ['source', 'target', 'relationship']:edge_attrs[key] = value# 添加边到场景图中self.scene_graph.add_edge(source, target, relationship, **edge_attrs)self.query_engine = QueryEngine(self.scene_graph) # 用
-
代码说明:这部分代码描述了场景图HSG的构建逻辑。
3. 定义基于LLM的目标检索模块
Step1:给定一个指令后,在分层场景图HSG中检索提及的地标节点;
Step2:根据目标与地标节点之间的关系,过滤掉错误边分支;
Step3:得到子图分支后,通过目标与其他物体的关系来定位目标;
Step4:如果查询失败,自动调整查询条件(例如,放宽关系类型),并递归地扩展搜索范围,以确保检索到最相关的结果。
|
将导航命令转换为一系列查询操作。可用的操作如下: - get_geonode_by_name(name_pattern):根据名称查找地理节点。 - get_child_nodes(parent, relation_type):获取与父节点具有指定关系的子节点。 可用的关系类型有:“包含”、“相邻”、“靠近角落”、“在……北方”、“在……南方”、“在……东方”、“在……西方”、“在……东北方”、“在……西北方”、“在……东南方”、“在……西南方”。 - `filter_by_class(obj_class)`:按类别过滤对象节点。类别可以是[“车辆”、“道路”、“建筑物”、“停车场”、“绿地” 等] 中的一个。 - `filter_by_attribute(key, value)`:按属性过滤对象节点。 注意事项: (1) “在……前面” 的描述,在北朝上的地图中,通常对应 “在……北方” 。 (2) “在……后面” 的描述,在北朝上的地图中,通常对应 “在……南方” 。 (3) 对于 “停在道路上”,通常使用道路对象的 “包含” 关系。 (4) 当描述多个对象的相对位置时,需要找到连接这些对象的关系链。 (5) 当多个操作链表示相对关系时,要确保操作链连贯一致。 示例指令:“这是一辆停在戴维路(Davey Road)上的白色汽车。有一辆灰色汽车停在它前面,朝向反方向。” 返回的操作链: [ {"方法": "get_geonode_by_name", "参数": ["戴维路(Davey Road)"]}, {"方法": "get_child_nodes", "关键字参数": {"关系类型": "包含"}}, {"方法": "filter_by_class", "参数": ["车辆"]}, {"方法": "filter_by_attribute", "参数": ["颜色", "白色"]} ] 当前指令:{指令} |
检索目标时用到的提示模板
class LLMController:def query_scene_graph(self, instruction: str, debug=False):"""使用增强版的robust_subgraph_query查询场景图"""from scenegraphnav.prompt.geonav import QUERY_OPERATION_CHAIN_PROMPTimport json# 使用LLM生成操作链prompt = QUERY_OPERATION_CHAIN_PROMPT.format(instruction=instruction)client = OpenAI(api_key= os.environ.get("OPENAI_API_KEY"),base_url= os.environ.get("OPENAI_BASE_URL"),)response = client.chat.completions.create(model="gpt-4-turbo",response_format={"type": "json_object"},messages=[{"role": "system", "content": "You are a professional query planner."},{"role": "user", "content": prompt}])try:operation_chain = json.loads(response.choices[0].message.content)if debug:print(f"生成的操作链: {json.dumps(operation_chain, indent=2, ensure_ascii=False)}")# 使用robust_subgraph_query执行查询results = self.query_engine.robust_subgraph_query(operation_chain, fallback=True, min_results=1, debug=debug)return resultsexcept Exception as e:print(f"查询场景图时发生错误: {str(e)}")return []
-
代码说明:这部分代码描述了对场景图HSG的查询逻辑
4. 增量更新分层场景图HSG

def rgb_entropy(rgb_img):hsv_img = color.rgb2hsv(rgb_img)entropy = 0for i in range(3):channer = (hsv_img[..., i]*255).astype(np.uint8)hist = cv2.calcHist([channer], [0], None, [256], [0, 256])hist = hist / hist.sum() + 1e-10entropy += -np.sum(hist * np.log2(hist))return entropydef structural_similarity(current, previous):"""计算结构相似性指标"""from skimage.metrics import structural_similarity as ssimreturn ssim(current, previous, multichannel=True, win_size=3)class ExplorationAnalyzer:def __init__(self, threshold=0.08):self.buffer = []self.threshold = thresholdself.history_entropy = []self.grid_size = 20 # 将地图划分为20x20网格def _grid_analysis(self, rgb_map):"""网格级信息变化检测"""height, width = rgb_map.shape[:2]cell_h = height // self.grid_sizecell_w = width // self.grid_sizegrid_entropy = np.zeros((self.grid_size, self.grid_size))for i in range(self.grid_size):for j in range(self.grid_size):cell = rgb_map[i*cell_h:(i+1)*cell_h, j*cell_w:(j+1)*cell_w]grid_entropy[i,j] = rgb_entropy(cell)return grid_entropy.std() # 返回网格熵值的标准差def should_continue(self, current_map, previous_map):# 信息熵差异current_e = rgb_entropy(current_map)previous_e = rgb_entropy(previous_map)delta_e = abs(current_e - previous_e)print(f"Delta Entropy: {delta_e}")# # 结构相似性# ssim_score = structural_similarity(current_map, previous_map)# 空间分布变化spatial_variation = self._grid_analysis(current_map - previous_map)# 综合决策(可调节权重)decision_score = delta_e #+ 0.3*(1-ssim_score) + 0.2*spatial_variationself.history_entropy.append(decision_score)return decision_score > self.threshold
-
代码说明:这部分代码描述了相似性度量的代码逻辑,通过和上一帧进行比较,进而判断是否完成目标搜索任务。
多阶段导航策略(MNS)
-
说明
整个导航过程分为3个阶段:导航、搜索和定位:
导航和搜索阶段采用VLA范式,结合示意性认知地图SCM得到actions;
定位阶段查询分层场景图HSG,确定最终目标,然后控制无人机到达目标位置。
-
流程说明
针对阶段的切换,设计了一种静态任务规划期,其工作原理:
(1)导航阶段

(2)搜索阶段

(3)定位阶段

任务规划器中,采用了CoT的策略来设计各阶段的提示指令:

图4 | GeoNav中MLLM的多模态推理
(a)地标导航提示(Prompting for landmark navigation)
-
子目标描述(Subgoal Description):导航至圣约翰学院图书馆。
-
推理依据(Rationale):告知当前不在任何地标处,圣约翰学院图书馆在东边94.1米 ,并要求答案给出思考和行动两部分,逐步给出位置和方向推理过程。
-
答案(Answer):包含思考过程和行动结果两部分
这是典型的思维链(Chain of Thought, CoT)应用,通过逐步推理明确行动方向 。
(b)目标搜索提示(Prompting for target search)
-
子目标描述(Subgoal Description):在图书馆附近区域搜索一栋建筑。
-
推理依据(Rationale):提示当前在城市地标附近,应依据新颖性和目标吸引力搜索区域,并要求答案给出思考和行动两部分,基于地图逐步推理并确定移动方向。
-
答案(Answer):包含思考过程和行动结果两部分。
利用CoT引导模型基于环境信息完成行动规划。
(c)目标定位提示(Prompting the MLLM for target localization)
-
子目标描述(Subgoal Description):定位具体的目标建筑。
-
推理依据(Rationale):将导航指令转换为一系列查询操作,列出可用操作,要求返回操作链。
-
答案(Answer):包含操作链步骤
通过CoT将复杂定位任务拆解为有序步骤,实现目标定位。
def check_subgoals(self, task):if task['strategy'] == 'Navigate':self.switch_time = self.controller.timestepif self.controller.timestep > 6: #防止陷入NotFoundself.strategy_distances['Navigate'] = self.controller.pose.xy.dist_to(self.episode.target_position.xy)return True# 导航阶段,判断是否抵达lamdmark,是就进入下一阶段return all(lm.position.xy.dist_to(self.controller.pose.xy) < 40.0 for lm in self.landmark_nav_map.landmark_map.landmarks)elif task['strategy'] == 'Search':if self.controller.timestep > (self.switch_time + 6): #防止陷入search aroundself.strategy_distances['Search'] = self.controller.pose.xy.dist_to(self.episode.target_position.xy)return True# evaluate whether the information has changedsem_map = self.landmark_nav_map.get_semantic_map()if not hasattr(self, 'exploration_analyzer'):self.exploration_analyzer = ExplorationAnalyzer(threshold=1e-8)# 与上一帧比较if self.exploration_analyzer.should_continue(sem_map, self.previous_sem_map):self.previous_sem_map = sem_map.copy()return Falseelif task['strategy'] == 'Locate':# 利用知识推断目标名称,飞到对应位置# 假如没找到目标,还要继续return Truereturn Falsedef run(self):# Step 5: check the subgoalsif self.check_subgoals(current_task):self.subtask_status = "completed"self.current_task_index += 1# 检查是否所有任务完成if self.current_task_index >= len(self.plan['sub_goals']):print("All sub-tasks completed!")self.strategy_distances['Locate'] = self.controller.pose.xy.dist_to(self.episode.target_position.xy)if self.controller.reached_target(self.controller.pose, self.target):print("Target reached.")Success = Truepos_log.append(self.controller.pose)self.results["steps"].append({"time_step": self.controller.timestep,"pose": (self.controller.pose.x, self.controller.pose.y, self.controller.pose.z, self.controller.pose.yaw),"distance_to_target": self.controller.pose.xy.dist_to(self.target.xy),"plan": current_task,"observation_suggestion":self.observation,"action_suggestion": self.action,})break # 提前退出循环
-
代码说明:这部分代码描述了导航策略切换的逻辑。

实验
-
评估指标:
(1)NE是导航误差,智能体最终位置与目标真实位置的欧氏距离;
(2)SR是导航成功率,智能体导航到的最终位置在目标位置的20米范围内,视为导航成功,计算导航成功的百分比;
(3)OSR 指智能体导航过程中是否出现距离目标位置小于阈值20米的情况,计算出现这种情况占总导航次数的百分比;
(4)SPL是成功率乘以到目标位置的最短路径长度与实际路径长度的比率。

图5 | 与当前SOTAs相比的整体空中导航性能
-
说明:
如上表所示,将GeoNav与当前最先进的方法在CityNav数据集上进行比较,可以发现,GeoNav远超其他方法。
(1)在简单难度任务中,GeoNav的SR指标比第二名的MGP高出12.53%,OSR指标高出49.47%;
(2)在中等难度任务中,GeoNav的SR指标比第二名的MGP高出6.25%,OSR指标高出20.83%;
(3)在困难难度任务中,GeoNav的SR指标比第二名的MGP高出 8.34%,OSR指标高出12.5%。
(4)在SPL指标上,GeoNav也表现出色,说明GeoNav很好的平衡了导航成功率和路径规划效率。

图6 | 在CityNav验证中不同组件的消减情况
说明:
如上表所示,是GeoNav在CityNav验证集上进行不同组件组合的消融实验。
(1)仅使用SCM组件时,导航误差精度NE最大,且成功率都很低;
(2)使用SCM+HSG的组合,导航误差精度减少18.8米,SR、OSR和SPL分别提高2.28%、27.78%和3.03%;
(3)使用SCM+MNS的组合,效果和SCM+HSG的组合相当,但在SR和SPL上明显落后于完整的组合;
(4)使用HSG+MNS的组合,相较于完整的组合,SR大幅下降了17.64%。
综上所述,各模块组件对提升整体性能都是至关重要的。

结论
GeoNav框架在语言目标空中导航任务中取得一定成果,但导航成功率和效率仍有提升空间。未来可优化多模态思维链提示调度框架,联合构建和推理两种空间表示以提高模型性能。
论文题目:GeoNav: Empowering MLLMs with Explicit Geospatial Reasoning Abilities for Language-Goal Aerial Navigation
论文作者:Haotian Xu, Yue Hu, Chen Gao, Zhengqiu Zhu, Yong Zhao, Yong Li, Quanjun Yin
论文链接:https://arxiv.org/pdf/2504.09587
2045

被折叠的 条评论
为什么被折叠?



