From b12339ff73dee68ad8a9d5b382d6c79df6f00e7a Mon Sep 17 00:00:00 2001 From: lulijing Date: Thu, 29 Jan 2026 14:27:56 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E6=96=B9=E4=BD=8D=E8=A7=A3?= =?UTF-8?q?=E6=9E=90function=20call?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend_service/src/prompts/scene4_prompt.txt | 18 ++++- backend_service/src/prompts/system_prompt.txt | 18 ++++- .../__pycache__/__init__.cpython-310.pyc | Bin 0 -> 204 bytes .../coordinate_tools.cpython-310.pyc | Bin 0 -> 3191 bytes backend_service/src/tools/coordinate_tools.py | 71 ++++++++++++++++++ 5 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 backend_service/src/tools/__pycache__/__init__.cpython-310.pyc create mode 100644 backend_service/src/tools/__pycache__/coordinate_tools.cpython-310.pyc diff --git a/backend_service/src/prompts/scene4_prompt.txt b/backend_service/src/prompts/scene4_prompt.txt index 87479e8c..fd88d7e8 100644 --- a/backend_service/src/prompts/scene4_prompt.txt +++ b/backend_service/src/prompts/scene4_prompt.txt @@ -299,10 +299,20 @@ **仅当指令涉及前往“具体地点”(如广场、大门)的偏移位置时,才需计算绝对坐标并使用`fly_to_waypoint`:** -当指令包含“具体地点 + 方向 + 距离”的偏移(如“广场西边200米”)时,**必须**先调用工具`calc_offset_enu`计算绝对坐标,再使用`fly_to_waypoint`。工具参数: -- `base`: 参考地点的ENU坐标(含x/y/z) -- `direction`: east/west/north/south/up/down -- `distance`: 偏移距离(米) +当指令包含“具体地点 + 方向 + 距离”的偏移时,按方向类型选择工具: +- **单一方向**(东/西/南/北/上/下,如“广场西边200米”):**必须**先调用工具`calc_offset_enu`计算绝对坐标,再使用`fly_to_waypoint`。工具参数: + - `base`: 参考地点的ENU坐标(含x/y/z) + - `direction`: east/west/north/south/up/down + - `distance`: 偏移距离(米) +- **中文复合方位**(东南/西北/东北/西南/南偏东10度/北偏西15度等):**必须**先调用工具`calc_offset_esu_direction_text`计算绝对坐标,再使用`fly_to_waypoint`。工具参数: + - `base`: 参考地点的ENU坐标(含x/y/z) + - `direction_text`: 中文方位原文(如“东南”“南偏东10度”) + - `distance`: 偏移距离(米) +- **禁止简化**:当出现“东南/西北/东北/西南/南偏东/北偏西”等复合方位时,禁止将其简化为单一方向(如仅“东”或仅“南”)。 + +示例(必须遵守): +- “飞到广场东南方向100米” → 必须调用 `calc_offset_esu_direction_text`,参数 `direction_text` 为 `"东南"`,`distance` 为 `100`,再使用 `fly_to_waypoint`。 +- “飞到广场南偏东10度100米” → 必须调用 `calc_offset_esu_direction_text`,参数 `direction_text` 为 `"南偏东10度"`,`distance` 为 `100`。 当指令只有“方向 + 距离”且**没有具体地点名词**时,**禁止**调用`calc_offset_enu`,必须使用`move_direction`。 当指令描述“附近/边上/区域内”等模糊位置且**无方向+距离**时,视为到该地点本身,不做偏移计算。 diff --git a/backend_service/src/prompts/system_prompt.txt b/backend_service/src/prompts/system_prompt.txt index fe9aebbd..b4869af0 100644 --- a/backend_service/src/prompts/system_prompt.txt +++ b/backend_service/src/prompts/system_prompt.txt @@ -789,10 +789,20 @@ **仅当指令涉及前往“具体地点”(如广场、大门)的偏移位置时,才需计算绝对坐标并使用`fly_to_waypoint`:** -当指令包含“具体地点 + 方向 + 距离”的偏移(如“广场西边200米”)时,**必须**先调用工具`calc_offset_enu`计算绝对坐标,再使用`fly_to_waypoint`。工具参数: -- `base`: 参考地点的ENU坐标(含x/y/z) -- `direction`: east/west/north/south/up/down -- `distance`: 偏移距离(米) +当指令包含“具体地点 + 方向 + 距离”的偏移时,按方向类型选择工具: +- **单一方向**(东/西/南/北/上/下,如“广场西边200米”):**必须**先调用工具`calc_offset_enu`计算绝对坐标,再使用`fly_to_waypoint`。工具参数: + - `base`: 参考地点的ENU坐标(含x/y/z) + - `direction`: east/west/north/south/up/down + - `distance`: 偏移距离(米) +- **中文复合方位**(东南/西北/东北/西南/南偏东10度/北偏西15度等):**必须**先调用工具`calc_offset_esu_direction_text`计算绝对坐标,再使用`fly_to_waypoint`。工具参数: + - `base`: 参考地点的ENU坐标(含x/y/z) + - `direction_text`: 中文方位原文(如“东南”“南偏东10度”) + - `distance`: 偏移距离(米) +- **禁止简化**:当出现“东南/西北/东北/西南/南偏东/北偏西”等复合方位时,禁止将其简化为单一方向(如仅“东”或仅“南”)。 + +示例(必须遵守): +- “飞到广场东南方向100米” → 必须调用 `calc_offset_esu_direction_text`,参数 `direction_text` 为 `"东南"`,`distance` 为 `100`,再使用 `fly_to_waypoint`。 +- “飞到广场南偏东10度100米” → 必须调用 `calc_offset_esu_direction_text`,参数 `direction_text` 为 `"南偏东10度"`,`distance` 为 `100`。 当指令只有“方向 + 距离”且**没有具体地点名词**时,**禁止**调用`calc_offset_enu`,必须使用`move_direction`。 当指令描述“附近/边上/区域内”等模糊位置且**无方向+距离**时,视为到该地点本身,不做偏移计算。 diff --git a/backend_service/src/tools/__pycache__/__init__.cpython-310.pyc b/backend_service/src/tools/__pycache__/__init__.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2f6d5541f643bd17873c265ea8b6ccc7f91e8d32 GIT binary patch literal 204 zcmd1j<>g`k0&(m7Oi3X97{oyaj6jY95EpX*i4=w?h7`tN22G|?At3kZ_N7l-w?A94 z@af#?PkUM)H#GTaGTvg3k59=@j*nl-P{agO1SWp@>1X8Urs`)FCnpx`hvye%2NxtJ zr|Ku?r=;q;6y@io2IM5>n9~9XQ$?+#22R)m1P2z6c;7ym*nT?6zj*wXXa&= b#K-FuRNmsS$<0qG%}KQbxvv;x4+jGPu$wyj literal 0 HcmV?d00001 diff --git a/backend_service/src/tools/__pycache__/coordinate_tools.cpython-310.pyc b/backend_service/src/tools/__pycache__/coordinate_tools.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d438c8212556d2c5fd1ea3403051ccd161de2ba2 GIT binary patch literal 3191 zcmai0-)|g89iQ1>x3_mb+lkx6`MD_}^3pUH5EKzbHBoJ$pyF1w(;_!!wZ0kKi|=-? zv%B%#X|4(}s6wPrUK)uw$QB~dhw{P$p%Lo;z~j6&cA7U{c)&s`-@$2N?4 zha89Y6VQH^oF*=`m&n)28S(<4GSOy{ckT)70(n@Jf+&j9APwUvnejAXOog>nm~&aP zPCfkzhF)yXfa6Pe_B(QH^}Bg`?l2EJ)Ev^=@1Y!WoM#Dx21zQ6H44^@VwSE5Gl?@O zw5%x%60b#_({f|)Bq=-Zs?EIi$Own zM+PI8>T!@Rh$0CYt;sVB9cC0Z36nI4YE+o2hp-q;GZxi!*kAvt0FYPws=UO10^l0K ztBBbtT;Dprg;UH?s;z6uD0p<~;kY?Qj7@zDZ5msst8+ayZ(w#7+D(q{qI+swXDqci zBGx!s$6Z6=g2JZ4w!+riC>k|TT4-Ozs9V6OW5O)Ob<7L5G~#p%?-kbY8cI#BkE82) z*X|a(4mY^XwJ~%AbxYjj#U3tmi#t6$#7n%)he&Bdhp;GT7vW&r8-Jdjxwd`p!=26d zAOH2?qX)m*x&6zXkN&>>;cvHp_M69ldboY})9t_f?u)oYDb{Xkt|BGW)mWS5_j*C zF1Z4?1a1r5**!g&rWQwO5Jqm4H7E;f`6M$R>+C3;fsIRZ42)+B=dGZg(P_qFCY&${ zquy;BLdLq3lKoT@qHSrg2!AOtSpZgN^ME?kM=hA3y6u>AgMh-oDd0H6UwFb4AGWZqD2VadIEa=ca!=2h}S- zczx!|YqS3J>@`st+)GMZ>F(iYOj47nr~k<7HsDpPo`M#RnmWOo2sn!gFnmkv0*{lC zRLeO{j48A*+|@YV)Va2W*%^*gg9DQZ`h_-*y7=8g;4yC?b_v*sfsI;Mm%QFAaGe`4 zA8?eL#}Tj->=LA7j*0C5CY08y#->O?`E0;HBt?ZCz;BS!EF$Gul-ip`GQ{QHEN-#8 z+}d=uBV29(3A274DyFrxC5wOQuQ7KWT)tMxb;jW9XXE%O4A z>--w_3VC4*^YR!1T#;Dr^I86qIswdmn4^A1^TuY_5%{PAyB+W88yRE(l^$rLnYLX? zz-ZaiGMR57%cyealRtRIzoiGFM3oRYs+7^AdfvXnz76f3rb4e5pFq*?6{Cq<@z9}Ez)$2(#XB&1`%<;chMzunNgYyc+olW%>3rdP`%`O!_0+gMS;;~9hlEf z%9;bYilvE6lR~f4h{+5sj7A_ua;4pjQ=u^sE^yRVgtg4#teNBqtiS>i22lbc7lP8& z;=~(b-&Dipio$9#x)ins*-Kckz;e36CS@mErdl7aPdI737BHf4Q61Wsve|=h%szw# zh1K$ta5+*rEjQ=oY~y}2VEKHx^r}<)G|A)@Ca-`Q!v}lhp_!zEe>_;%gQ;J;z$DU&U9xDG|u|bP<&|*VruCB1z(`D(;HQMZH zvC)1Rv>5)NDJ?F57F(djcGr5(>S-}v$H0DIB{06wQ{3V{Rdzs=9d44MQeP*NaVph! z_MrMcdhp54NAH6MKl;Z9U#-FHIT+8Lm*!~=WXHib!%l$pEEQMml#w=InP3kNQ%I9roo$ kOtLzCMQT%W4opSKGZ4YhaQ|1?bsX(@UaS1qu`B2P2iM$8TmS$7 literal 0 HcmV?d00001 diff --git a/backend_service/src/tools/coordinate_tools.py b/backend_service/src/tools/coordinate_tools.py index 41c9aa0a..10056092 100644 --- a/backend_service/src/tools/coordinate_tools.py +++ b/backend_service/src/tools/coordinate_tools.py @@ -1,5 +1,7 @@ from __future__ import annotations +import math +import re from typing import Dict, Tuple @@ -37,3 +39,72 @@ def calc_offset_enu(base: Dict[str, float], direction: str, distance: float) -> offset_z = z + dz * distance return {"x": offset_x, "y": offset_y, "z": offset_z} + + +_DIRECTION_TEXT_DIAGONALS_ESU: Dict[str, Tuple[float, float]] = { + "东南": (1.0, 1.0), + "西南": (-1.0, 1.0), + "东北": (1.0, -1.0), + "西北": (-1.0, -1.0), +} + + +def _parse_direction_text_esu(direction_text: str) -> Tuple[float, float]: + text = (direction_text or "").strip().replace(" ", "") + if not text: + raise ValueError("direction_text must be a non-empty string") + + for prefix in ("向", "正"): + if text.startswith(prefix): + text = text[len(prefix):] + + if text in _DIRECTION_TEXT_DIAGONALS_ESU: + x, y = _DIRECTION_TEXT_DIAGONALS_ESU[text] + length = math.hypot(x, y) + return x / length, y / length + + if text in ("东", "西", "南", "北"): + if text == "东": + return 1.0, 0.0 + if text == "西": + return -1.0, 0.0 + if text == "南": + return 0.0, 1.0 + return 0.0, -1.0 + + match = re.match(r"^(南|北)偏(东|西)(\d+(?:\.\d+)?)度?$", text) + if match: + base_dir, toward_dir, angle_str = match.groups() + angle_deg = float(angle_str) + if angle_deg < 0 or angle_deg > 90: + raise ValueError("angle must be between 0 and 90 degrees") + angle_rad = math.radians(angle_deg) + x_sign = 1.0 if toward_dir == "东" else -1.0 + if base_dir == "南": + return math.sin(angle_rad) * x_sign, math.cos(angle_rad) + return math.sin(angle_rad) * x_sign, -math.cos(angle_rad) + + raise ValueError(f"unsupported direction_text: {direction_text}") + + +def calc_offset_esu_direction_text( + base: Dict[str, float], direction_text: str, distance: float +) -> Dict[str, float]: + """在ESU坐标系下按中文方位偏移固定距离。""" + if distance < 0: + raise ValueError("distance must be non-negative") + if not isinstance(base, dict): + raise ValueError("base must be an object with x/y/z") + + try: + x = float(base["x"]) + y = float(base["y"]) + z = float(base["z"]) + except Exception as exc: + raise ValueError("base must contain numeric x/y/z") from exc + + dx_unit, dy_unit = _parse_direction_text_esu(direction_text) + offset_x = x + dx_unit * distance + offset_y = y + dy_unit * distance + + return {"x": offset_x, "y": offset_y, "z": z}