diff --git a/drone_controller/mission_manager.py b/drone_controller/mission_manager.py index 081a103..04acecc 100644 --- a/drone_controller/mission_manager.py +++ b/drone_controller/mission_manager.py @@ -2,9 +2,17 @@ mission_manager.py — 基于 ZeroMQ RouterProxy 的航点巡航状态机 【协议变更 v2 — 2026-07】 - 航点存储格式使用经纬度 (lat, lon, alt)。发送 POSITION SETPOINT - 时在本模块内转换为相对当前位置的 LOCAL_OFFSET_NED 偏移 - (x=北向, y=东向, z=向下)。 + 航点存储格式使用经纬度 (lat, lon, alt)。导航/返航/悬停均发送 + VELOCITY SETPOINT:把目标经纬度换算成相对当前位置的 NED 方向, + 再按 max_speed 限速成速度命令(差分量,与参考原点无关)。 + +【坐标系说明 — 2026-08 修复】 + 此前 POSITION LOCAL_OFFSET_NED 路径用 GPS 经纬度反算相对偏移, + 交给 PX4 在自身 LOCAL_NED 帧解析。GPS 与本地估计一旦不一致 + (跳变/漂移),目标跟着跳、飞控满速追(实测 HOLD_FINAL 7.77m/s)。 + 故 HOLD_TASK / HOLD_FINAL 悬停改为速度闭环,从根上消除 + GPS↔本地帧混用。POSITION setpoint 仅保留给 TAKEOFF 预热/降级爬升 + (零水平偏移,且已加单帧钳制)。 【核心改动】 1. self.drone → self.proxy(注入的 RouterProxy 实例) @@ -670,12 +678,14 @@ class MissionManager: self.tracking_completed = False return - # ---- 保持当前位置悬停(相对当前位置 LOCAL_OFFSET_NED) ---- + # ---- 保持当前位置悬停(速度闭环,坐标系一致) ---- + # 说明:悬停不用 POSITION LOCAL_OFFSET_NED。位置模式把"由 GPS 经纬度 + # 反算的相对偏移"交给 PX4 在自身 LOCAL_NED 帧里解析,GPS 与本地估计 + # 一旦不一致(跳变/漂移),目标跟着跳、飞控满速追(曾实测 7.77m/s)。 + # 速度模式是差分量,与参考原点无关,命令值即速度上界,天然安全。 wp = self.waypoints[self.current_wp_index] - self._send_relative_position_setpoint( - state, - wp["lat"], wp["lon"], wp.get("alt", self.target_altitude), - yaw=wp.get("yaw", 0), + self._send_velocity_to_target( + state, wp["lat"], wp["lon"], wp.get("alt", self.target_altitude), ) self.current_target = { "lat": wp["lat"], "lon": wp["lon"], @@ -857,7 +867,8 @@ class MissionManager: """ [HOLD_FINAL] 末端悬停,保持最终航点位置 - POSITION SETPOINT 发送 LOCAL_OFFSET_NED 相对偏移。 + 使用速度闭环(_send_velocity_to_target)而非 POSITION LOCAL_OFFSET_NED, + 避免 GPS 偏移与 PX4 本地坐标系不一致导致飞控满速追目标(曾实测 7.77m/s)。 """ if self.waypoints: final_wp = self.waypoints[-1] @@ -865,11 +876,10 @@ class MissionManager: final_wp = {"lat": 0.0, "lon": 0.0, "alt": self.target_altitude, "yaw": 0} - self._send_relative_position_setpoint( + self._send_velocity_to_target( state, final_wp["lat"], final_wp["lon"], final_wp.get("alt", self.target_altitude), - yaw=final_wp.get("yaw", 0), ) self.current_target = { "lat": final_wp["lat"], "lon": final_wp["lon"], @@ -886,8 +896,23 @@ class MissionManager: # ================================================================ def _send_relative_position_setpoint(self, state, target_lat, target_lon, - target_alt, yaw=0.0): - """按相对当前位置的 LOCAL_OFFSET_NED 发送 POSITION SETPOINT。""" + target_alt, yaw=0.0, max_speed=None): + """ + 按相对当前位置的 LOCAL_OFFSET_NED 发送 POSITION SETPOINT。 + + 统一限速:位置设定点按单帧最大推进距离 max_step 钳制, + 即大偏移被拆成多帧逐步逼近,从源头避免飞控满速冲撞。 + (此前 HOLD_FINAL 阶段曾因偏移异常出现 7.77m/s 失控) + + max_step = max(max_speed * 0.5, 0.3)。正常悬停(偏移 < 0.3m) + 不受影响;异常大偏移被钳制的同时打印 WARNING 便于诊断。 + + Args: + max_speed: 限速(m/s),默认 self.max_speed + """ + if max_speed is None: + max_speed = self.max_speed + drone = state.get("drone", {}) if state else {} current_lat = drone.get("lat", 0.0) current_lon = drone.get("lon", 0.0) @@ -898,6 +923,19 @@ class MissionManager: current_lat, current_lon, current_alt, ) + # ---- 统一限速:单帧最大目标偏移(约半个限速秒距离) ---- + max_step = max(max_speed * 0.5, 0.3) + dist_h = math.sqrt(x * x + y * y) + if dist_h > max_step * 1.5: + logger.warning( + "[限速] POSITION 偏移过大 %.1fm,钳制为 %.1fm(max_speed=%.1f)", + dist_h, max_step, max_speed) + if dist_h > max_step: + x = x / dist_h * max_step + y = y / dist_h * max_step + # 垂直方向同样钳制(z>0 表示目标在下) + z = max(min(z, max_step), -max_step) + return self.proxy.send_setpoint( x=x, y=y, z=z, yaw=yaw, frame="LOCAL_OFFSET_NED", diff --git a/run_mission.py b/run_mission.py index 4ed03e1..a353f48 100644 --- a/run_mission.py +++ b/run_mission.py @@ -13,7 +13,7 @@ run_mission.py — 基于 ZMQ Router 协议的主任务入口 ▼ ▼ ┌─ MissionManager ┌─ UAVControlLoop ──┐ │ 状态机 + 航点巡航 │ 20Hz VELOCITY │ - │ POSITION SETPOINT │ 闭环追踪 │ + │ VELOCITY SETPOINT │ 闭环追踪 │ │ (非追踪期间) │ (VISUAL_TRACKING) │ └───────────────────┴────────────────────┘ ┌─ 感知模块 ─────────┐ @@ -23,7 +23,8 @@ run_mission.py — 基于 ZMQ Router 协议的主任务入口 【控制权协调规则】 同一时刻只有一个人持有「SETPOINT 发令权」: - - VISUAL_TRACKING 之外 → MissionManager 发 POSITION SETPOINT + - VISUAL_TRACKING 之外 → MissionManager 发 VELOCITY SETPOINT(航点/ + 悬停均用速度闭环,与坐标系无关;仅 TAKEOFF 预热用零偏移 POSITION) - VISUAL_TRACKING 之中 → UAVControlLoop 线程发 VELOCITY SETPOINT - 切换由主循环检测 MissionManager.state 变化时触发 @@ -479,7 +480,7 @@ class MissionOrchestrator: ┌──────────────┬─────────────────────┬──────────────────────┐ │ 状态机状态 │ MissionManager │ UAVControlLoop │ ├──────────────┼─────────────────────┼──────────────────────┤ - │ 非 TRACKING │ proxy → POSITION │ 控制线程已停止 │ + │ 非 TRACKING │ proxy → VELOCITY │ 控制线程已停止 │ │ │ SETPOINT │ │ ├──────────────┼─────────────────────┼──────────────────────┤ │ TRACKING │ 不发 SETPOINT │ 20Hz VELOCITY 线程 │