Compare commits
2 Commits
pipeline-b
...
3eba1f962b
| Author | SHA1 | Date | |
|---|---|---|---|
| 3eba1f962b | |||
| 6f990e645d |
19
.gitignore
vendored
19
.gitignore
vendored
@@ -1,19 +0,0 @@
|
|||||||
# 图片/二进制大文件
|
|
||||||
*.png
|
|
||||||
*.jpg
|
|
||||||
*.jpeg
|
|
||||||
|
|
||||||
# 数据库文件
|
|
||||||
*.sqlite3
|
|
||||||
*.db
|
|
||||||
|
|
||||||
# 日志/缓存
|
|
||||||
logs/
|
|
||||||
*.log
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
*.pid
|
|
||||||
|
|
||||||
# 测试临时文件
|
|
||||||
tools/api_test*.log
|
|
||||||
tools/test_validate/validation_*/
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 174 KiB After Width: | Height: | Size: 98 KiB |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,8 +1,8 @@
|
|||||||
你是一个严格的任务分类器。只输出一个JSON对象,不要输出解释或多余文本。
|
你是一个严格的任务分类器。只输出一个JSON对象,不要输出解释或多余文本。
|
||||||
根据用户指令与下述可用节点定义,判断其为“简单”或“复杂”。
|
根据用户指令与下述可用节点定义,判断其为“简单”或“复杂”。
|
||||||
|
|
||||||
- 简单:单一原子动作即可完成(例如“起飞”“飞机自检”“移动到某地(已给定坐标)”“对着某点环绕XY圈(如‘对着学生宿舍环绕三十两圈’)”等),且无需行为树与安全并行监控。
|
- 简单:单一原子动作即可完成(例如"起飞""飞机自检""移动到某地(已给定坐标)""对着某点环绕XY圈(如'对着学生宿舍环绕三十两圈')"等),且无需行为树。
|
||||||
- 复杂:需要多步流程、搜索/检测/跟踪/评估、战损确认、或需要模板化任务结构与安全并行监控。
|
- 复杂:需要多步流程、搜索/检测/跟踪/评估、战损确认、或需要模板化任务结构。
|
||||||
|
|
||||||
输出格式(严格遵守):
|
输出格式(严格遵守):
|
||||||
{"mode":"simple"} 或 {"mode":"complex"}
|
{"mode":"simple"} 或 {"mode":"complex"}
|
||||||
@@ -14,11 +14,11 @@
|
|||||||
{"name": "takeoff"}, {"name": "land"}, {"name": "fly_to_waypoint"}, {"name": "move_direction"}, {"name": "orbit_around_point"}, {"name": "orbit_around_target"}, {"name": "loiter"},
|
{"name": "takeoff"}, {"name": "land"}, {"name": "fly_to_waypoint"}, {"name": "move_direction"}, {"name": "orbit_around_point"}, {"name": "orbit_around_target"}, {"name": "loiter"},
|
||||||
{"name": "object_detect"}, {"name": "strike_target"}, {"name": "battle_damage_assessment"},
|
{"name": "object_detect"}, {"name": "strike_target"}, {"name": "battle_damage_assessment"},
|
||||||
{"name": "search_pattern"}, {"name": "track_object"}, {"name": "deliver_payload"},
|
{"name": "search_pattern"}, {"name": "track_object"}, {"name": "deliver_payload"},
|
||||||
{"name": "preflight_checks"}, {"name": "emergency_return"}
|
{"name": "preflight_checks"}, {"name": "take_picture"}
|
||||||
],
|
],
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{"name": "battery_above"}, {"name": "at_waypoint"}, {"name": "object_detected"},
|
{"name": "at_waypoint"}, {"name": "object_detected"},
|
||||||
{"name": "target_destroyed"}, {"name": "time_elapsed"}, {"name": "gps_status"}
|
{"name": "target_destroyed"}, {"name": "time_elapsed"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -18,7 +18,11 @@
|
|||||||
{"name": "takeoff", "description": "无人机从当前位置垂直起飞到指定的海拔高度。", "params": {"altitude": "float, 目标海拔高度(米),范围[1, 100],默认为2"}},
|
{"name": "takeoff", "description": "无人机从当前位置垂直起飞到指定的海拔高度。", "params": {"altitude": "float, 目标海拔高度(米),范围[1, 100],默认为2"}},
|
||||||
{"name": "land", "description": "降落无人机。可选择当前位置或返航点降落。", "params": {"mode": "string, 可选值: 'current'(当前位置), 'home'(返航点)"}},
|
{"name": "land", "description": "降落无人机。可选择当前位置或返航点降落。", "params": {"mode": "string, 可选值: 'current'(当前位置), 'home'(返航点)"}},
|
||||||
{"name": "fly_to_waypoint", "description": "导航至一个指定坐标点。使用相对坐标系(x,y,z),单位为米。", "params": {"x": "float", "y": "float", "z": "float", "acceptance_radius": "float, 可选,默认2.0"}},
|
{"name": "fly_to_waypoint", "description": "导航至一个指定坐标点。使用相对坐标系(x,y,z),单位为米。", "params": {"x": "float", "y": "float", "z": "float", "acceptance_radius": "float, 可选,默认2.0"}},
|
||||||
{"name": "move_direction", "description": "按指定方向直线移动。方向可为绝对方位或相对机体朝向。", "params": {"direction": "string: north|south|east|west|forward|backward|left|right", "distance": "float[1,10000], 可选, 不指定则持续移动"}},
|
{"name": "move_direction", "description": "按指定方向直线移动。方向可为绝对方位或相对机体朝向。", "params": {"direction": "string: north|south|east|west|forward|backward|left|right", "distance": "float[1,10000], 可选, 不指定则持续移动", "speed": "float, 可选"}},
|
||||||
|
{"name": "approach_target", "description": "快速趋近目标至固定距离。", "params": {"target_class": "string, 要趋近的目标类别", "description": "string, 可选", "stop_distance": "float, 期望的最终停止距离", "speed": "float, 可选"}},
|
||||||
|
{"name": "rotate", "description": "旋转固定角度。", "params": {"angle": "float, 旋转角度(正数逆时针, 负数顺时针)", "angular_velocity": "rad/s, 旋转角速度"}},
|
||||||
|
{"name": "rotate_search", "description": "原地旋转搜索目标。", "params": {"target_class": "string, 要搜寻的目标类别", "description": "string, 可选", "step_angle": "float, 可选, 每一步旋转的角度", "total_rotation": "float, 可选, 总共旋转搜索的角度"}},
|
||||||
|
{"name": "manual_confirmation", "description": "前端弹窗是否继续执行后续任务。", "params": {}},
|
||||||
{"name": "orbit_around_point", "description": "以给定中心点为中心,等速圆周飞行指定圈数。", "params": {"center_x": "float", "center_y": "float", "center_z": "float", "radius": "float[5,1000]", "laps": "int[1,20]", "clockwise": "boolean, 可选, 默认true", "speed_mps": "float[0.5,15], 可选", "gimbal_lock": "boolean, 可选, 默认true"}},
|
{"name": "orbit_around_point", "description": "以给定中心点为中心,等速圆周飞行指定圈数。", "params": {"center_x": "float", "center_y": "float", "center_z": "float", "radius": "float[5,1000]", "laps": "int[1,20]", "clockwise": "boolean, 可选, 默认true", "speed_mps": "float[0.5,15], 可选", "gimbal_lock": "boolean, 可选, 默认true"}},
|
||||||
{"name": "orbit_around_target", "description": "以目标为中心,等速圆周飞行指定圈数(需已有目标)。", "params": {"target_class": "string, 取值同object_detect列表", "description": "string, 可选", "radius": "float[5,1000]", "laps": "int[1,20]", "clockwise": "boolean, 可选, 默认true", "speed_mps": "float[0.5,15], 可选", "gimbal_lock": "boolean, 可选, 默认true"}},
|
{"name": "orbit_around_target", "description": "以目标为中心,等速圆周飞行指定圈数(需已有目标)。", "params": {"target_class": "string, 取值同object_detect列表", "description": "string, 可选", "radius": "float[5,1000]", "laps": "int[1,20]", "clockwise": "boolean, 可选, 默认true", "speed_mps": "float[0.5,15], 可选", "gimbal_lock": "boolean, 可选, 默认true"}},
|
||||||
{"name": "loiter", "description": "在当前位置上空悬停一段时间或直到条件触发。", "params": {"duration": "float, 可选[1,600]", "until_condition": "string, 可选"}},
|
{"name": "loiter", "description": "在当前位置上空悬停一段时间或直到条件触发。", "params": {"duration": "float, 可选[1,600]", "until_condition": "string, 可选"}},
|
||||||
@@ -29,15 +33,13 @@
|
|||||||
{"name": "track_object", "description": "持续跟踪目标。", "params": {"target_class": "string, 取值同object_detect列表", "description": "string, 可选", "track_time": "float[1,600], 默认30.0", "min_confidence": "float[0.5-1.0], 默认0.7", "safe_distance": "float[2-50], 默认10.0"}},
|
{"name": "track_object", "description": "持续跟踪目标。", "params": {"target_class": "string, 取值同object_detect列表", "description": "string, 可选", "track_time": "float[1,600], 默认30.0", "min_confidence": "float[0.5-1.0], 默认0.7", "safe_distance": "float[2-50], 默认10.0"}},
|
||||||
{"name": "deliver_payload", "description": "投放物资。", "params": {"payload_type": "string", "release_altitude": "float[2,100], 默认5.0"}},
|
{"name": "deliver_payload", "description": "投放物资。", "params": {"payload_type": "string", "release_altitude": "float[2,100], 默认5.0"}},
|
||||||
{"name": "preflight_checks", "description": "飞行前系统自检。", "params": {"check_level": "string: basic|comprehensive"}},
|
{"name": "preflight_checks", "description": "飞行前系统自检。", "params": {"check_level": "string: basic|comprehensive"}},
|
||||||
{"name": "emergency_return", "description": "执行紧急返航程序。", "params": {"reason": "string"}}
|
{"name": "take_picture", "description": "使用机载相机拍摄照片。", "params": {}}
|
||||||
],
|
],
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{"name": "battery_above", "description": "电池电量高于阈值。", "params": {"threshold": "float[0.0,1.0]"}},
|
|
||||||
{"name": "at_waypoint", "description": "在指定坐标容差范围内。", "params": {"x": "float", "y": "float", "z": "float", "tolerance": "float, 可选, 默认3.0"}},
|
{"name": "at_waypoint", "description": "在指定坐标容差范围内。", "params": {"x": "float", "y": "float", "z": "float", "tolerance": "float, 可选, 默认3.0"}},
|
||||||
{"name": "object_detected", "description": "检测到特定目标。", "params": {"target_class": "string", "description": "string, 可选", "count": "int, 可选, 默认1"}},
|
{"name": "object_detected", "description": "检测到特定目标。", "params": {"target_class": "string", "description": "string, 可选", "count": "int, 可选, 默认1"}},
|
||||||
{"name": "target_destroyed", "description": "目标已被摧毁。", "params": {"target_class": "string", "description": "string, 可选", "confidence": "float[0.5-1.0], 默认0.8"}},
|
{"name": "target_destroyed", "description": "目标已被摧毁。", "params": {"target_class": "string", "description": "string, 可选", "confidence": "float[0.5-1.0], 默认0.8"}},
|
||||||
{"name": "time_elapsed", "description": "时间经过。", "params": {"duration": "float[1,2700]"}},
|
{"name": "time_elapsed", "description": "时间经过。", "params": {"duration": "float[1,2700]"}}
|
||||||
{"name": "gps_status", "description": "GPS状态良好。", "params": {"min_satellites": "int[6,15], 默认10"}}
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -58,4 +60,4 @@
|
|||||||
- “环绕X米Y圈” → 若有目标上下文则使用 `orbit_around_target`,否则根据是否给出中心坐标选择 `orbit_around_point`;`radius=X`,`laps=Y`,默认 `clockwise=true`,`gimbal_lock=true`
|
- “环绕X米Y圈” → 若有目标上下文则使用 `orbit_around_target`,否则根据是否给出中心坐标选择 `orbit_around_point`;`radius=X`,`laps=Y`,默认 `clockwise=true`,`gimbal_lock=true`
|
||||||
- “顺时针/逆时针” → `clockwise=true/false`
|
- “顺时针/逆时针” → `clockwise=true/false`
|
||||||
- “等速” → 若未给速度则 `speed_mps` 采用默认值(例如3.0);若口令指明速度,裁剪到[0.5,15]
|
- “等速” → 若未给速度则 `speed_mps` 采用默认值(例如3.0);若口令指明速度,裁剪到[0.5,15]
|
||||||
- “以(x,y,z)为中心”/“当前位置为中心” → 选择 `orbit_around_point` 并填充 `center_x/center_y/center_z`
|
- “以(x,y,z)为中心”/“当前位置为中心” → 选择 `orbit_around_point` 并填充 `center_x/center_y/center_z`
|
||||||
|
|||||||
@@ -9,7 +9,11 @@
|
|||||||
{"name":"takeoff","params":{"altitude":"float[1,100],默认2"}},
|
{"name":"takeoff","params":{"altitude":"float[1,100],默认2"}},
|
||||||
{"name":"land","params":{"mode":"'current'/'home'"}},
|
{"name":"land","params":{"mode":"'current'/'home'"}},
|
||||||
{"name":"fly_to_waypoint","params":{"x":"±10000","y":"±10000","z":"[1,5000]","acceptance_radius":"默认2.0"}},
|
{"name":"fly_to_waypoint","params":{"x":"±10000","y":"±10000","z":"[1,5000]","acceptance_radius":"默认2.0"}},
|
||||||
{"name":"move_direction","params":{"direction":"north/south/east/west/forward/backward/left/right","distance":"[1,10000],缺省持续移动"}},
|
{"name":"move_direction","params":{"direction":"north/south/east/west/forward/backward/left/right","distance":"[1,10000],缺省持续移动","speed":"float,可选"}},
|
||||||
|
{"name":"approach_target","params":{"target_class":"string,要趋近的目标类别","description":"string,可选,目标属性描述","stop_distance":"float,期望的最终停止距离","speed":"float,可选,期望的逼近速度"}},
|
||||||
|
{"name":"rotate","params":{"angle":"float,旋转角度(正数逆时针,负数顺时针)","angular_velocity":"rad/s,旋转角速度"}},
|
||||||
|
{"name":"rotate_search","params":{"target_class":"string,要搜寻的目标类别","description":"string,可选,目标属性描述","step_angle":"float,可选,每一步旋转的角度","total_rotation":"float,可选,总共旋转搜索的角度"}},
|
||||||
|
{"name":"manual_confirmation","params":{}},
|
||||||
{"name":"orbit_around_point","params":{"center_x":"±10000","center_y":"±10000","center_z":"[1,5000]","radius":"[5,1000]","laps":"[1,20]","clockwise":"默认true","speed_mps":"[0.5,15]","gimbal_lock":"默认true"}},
|
{"name":"orbit_around_point","params":{"center_x":"±10000","center_y":"±10000","center_z":"[1,5000]","radius":"[5,1000]","laps":"[1,20]","clockwise":"默认true","speed_mps":"[0.5,15]","gimbal_lock":"默认true"}},
|
||||||
{"name":"orbit_around_target","params":{"target_class":"见object_detect列表","description":"可选,目标属性","radius":"[5,1000]","laps":"[1,20]","clockwise":"默认true","speed_mps":"[0.5,15]","gimbal_lock":"默认true"}},
|
{"name":"orbit_around_target","params":{"target_class":"见object_detect列表","description":"可选,目标属性","radius":"[5,1000]","laps":"[1,20]","clockwise":"默认true","speed_mps":"[0.5,15]","gimbal_lock":"默认true"}},
|
||||||
{"name":"loiter","params":{"duration":"[1,600]秒/until_condition:可选"}},
|
{"name":"loiter","params":{"duration":"[1,600]秒/until_condition:可选"}},
|
||||||
@@ -20,10 +24,10 @@
|
|||||||
{"name":"track_object","params":{"target_class":"同object_detect","description":"可选,目标属性","track_time":"[1,600]秒(必传,不可用'duration')","min_confidence":"[0.5,1.0]默认0.7","safe_distance":"[2,50]默认10"}},
|
{"name":"track_object","params":{"target_class":"同object_detect","description":"可选,目标属性","track_time":"[1,600]秒(必传,不可用'duration')","min_confidence":"[0.5,1.0]默认0.7","safe_distance":"[2,50]默认10"}},
|
||||||
{"name":"deliver_payload","params":{"payload_type":"string","release_altitude":"[2,100]默认5"}},
|
{"name":"deliver_payload","params":{"payload_type":"string","release_altitude":"[2,100]默认5"}},
|
||||||
{"name":"preflight_checks","params":{"check_level":"basic/comprehensive"}},
|
{"name":"preflight_checks","params":{"check_level":"basic/comprehensive"}},
|
||||||
{"name":"emergency_return","params":{"reason":"string"}}
|
{"name":"emergency_return","params":{"reason":"string"}},
|
||||||
|
{"name":"take_photos","params":{"target_class":"同object_detect","description":"可选,目标属性","track_time":"[1,600]秒(必传,不可用'duration')","min_confidence":"[0.5,1.0]默认0.7","safe_distance":"[2,50]默认10"}}
|
||||||
],
|
],
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{"name":"battery_above","params":{"threshold":"[0.0,1.0],必传"}},
|
|
||||||
{"name":"at_waypoint","params":{"x":"±10000","y":"±10000","z":"[1,5000]","tolerance":"默认3.0"}},
|
{"name":"at_waypoint","params":{"x":"±10000","y":"±10000","z":"[1,5000]","tolerance":"默认3.0"}},
|
||||||
{"name":"object_detected","params":{"target_class":"同object_detect(必传)","description":"可选,目标属性","count":"默认1"}},
|
{"name":"object_detected","params":{"target_class":"同object_detect(必传)","description":"可选,目标属性","count":"默认1"}},
|
||||||
{"name":"target_destroyed","params":{"target_class":"同object_detect","description":"可选,目标属性","confidence":"[0.5,1.0]默认0.8"}},
|
{"name":"target_destroyed","params":{"target_class":"同object_detect","description":"可选,目标属性","confidence":"[0.5,1.0]默认0.8"}},
|
||||||
@@ -34,6 +38,9 @@
|
|||||||
{"name":"Sequence","params":{},"children":"子节点数组(按序执行,全成功则成功)"},
|
{"name":"Sequence","params":{},"children":"子节点数组(按序执行,全成功则成功)"},
|
||||||
{"name":"Selector","params":{"memory":"默认true"},"children":"子节点数组(执行到成功为止)"},
|
{"name":"Selector","params":{"memory":"默认true"},"children":"子节点数组(执行到成功为止)"},
|
||||||
{"name":"Parallel","params":{"policy":"all_success"},"children":"子节点数组(同时执行,严禁用'one_success')"}
|
{"name":"Parallel","params":{"policy":"all_success"},"children":"子节点数组(同时执行,严禁用'one_success')"}
|
||||||
|
],
|
||||||
|
"decorators": [
|
||||||
|
{"name":"SuccessIsFailure","params":{},"child":"单一子节点(将子节点的成功结果反转为失败)"}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -42,53 +49,91 @@
|
|||||||
## 二、节点必填字段(后端Schema强制要求,缺一验证失败)
|
## 二、节点必填字段(后端Schema强制要求,缺一验证失败)
|
||||||
每个节点必须包含以下字段,字段名/类型不可自定义:
|
每个节点必须包含以下字段,字段名/类型不可自定义:
|
||||||
1. **`type`**:
|
1. **`type`**:
|
||||||
- 动作节点→`"action"`,条件节点→`"condition"`,控制流节点→`"Sequence"`/`"Selector"`/`"Parallel"`(与`name`字段值完全一致);
|
- 动作节点→`"action"`,条件节点→`"condition"`,控制流节点→`"Sequence"`/`"Selector"`/`"Parallel"`,装饰器节点→`"decorator"`;
|
||||||
2. **`name`**:必须是上述JSON中`actions`/`conditions`/`control_flow`下的`name`值(如“gps_status”不可错写为“gps_check”);
|
2. **`name`**:必须是上述JSON中定义的`name`值;
|
||||||
3. **`params`**:严格匹配上述节点的`params`定义,无自定义参数(如优先级排序不可加“priority”字段,仅用`description`);
|
3. **`params`**:严格匹配上述节点的`params`定义,无自定义参数;
|
||||||
4. **`children`**:仅控制流节点必含(子节点数组),动作/条件节点无此字段。
|
4. **`children`**:仅控制流节点必含(子节点数组);
|
||||||
|
5. **`child`**:仅装饰器节点必含(单一子节点对象,非数组)。
|
||||||
|
|
||||||
|
|
||||||
## 三、行为树固定结构(通用不变,确保安全验证)
|
## 三、标准任务结构模板(单次起降流程)
|
||||||
根节点必须是`Parallel`,`children`含`MainTask`(Sequence)和`SafetyMonitor`(Selector),结构不随任务类型(含优先级排序)修改:
|
大多数任务应遵循“起飞 -> 接近 -> 执行 -> 返航/降落”的单次闭环流程,参考结构如下:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"root": {
|
"root": {
|
||||||
"type": "Parallel",
|
"type": "Sequence",
|
||||||
"name": "MissionWithSafety",
|
"name": "MainTask",
|
||||||
"params": {"policy": "all_success"},
|
|
||||||
"children": [
|
"children": [
|
||||||
{
|
{"type":"action","name":"preflight_checks","params":{"check_level":"comprehensive"}},
|
||||||
"type": "Sequence",
|
{"type":"action","name":"takeoff","params":{"altitude":10.0}},
|
||||||
"name": "MainTask",
|
{"type":"action","name":"fly_to_waypoint","params":{"x":100.0,"y":50.0,"z":10.0}}, // 接近目标区域
|
||||||
"params": {},
|
// --- 核心任务区 (根据指令替换) ---
|
||||||
"children": [
|
{"type":"action","name":"rotate_search","params":{"target_class":"person","description":"目标描述"}},
|
||||||
// 通用主任务步骤(含优先级排序任务示例,需按用户指令替换):
|
{"type":"action","name":"object_detect","params":{"target_class":"person","description":"目标描述"}},
|
||||||
{"type":"action","name":"preflight_checks","params":{"check_level":"comprehensive"}},
|
// -------------------------------
|
||||||
{"type":"action","name":"takeoff","params":{"altitude":10.0}},
|
{"type":"action","name":"land","params":{"mode":"home"}}
|
||||||
{"type":"action","name":"fly_to_waypoint","params":{"x":200.0,"y":150.0,"z":10.0}}, // 搜索区坐标(用户未给时填合理值)
|
]
|
||||||
{"type":"action","name":"search_pattern","params":{"pattern_type":"grid","center_x":200.0,"center_y":150.0,"center_z":10.0,"radius":50.0,"target_class":"balloon","description":"红色"}},
|
}
|
||||||
{"type":"condition","name":"object_detected","params":{"target_class":"balloon","description":"红色"}}, // 确认高优先级目标
|
}
|
||||||
{"type":"action","name":"track_object","params":{"target_class":"balloon","description":"红色","track_time":30.0}},
|
```
|
||||||
{"type":"action","name":"strike_target","params":{"target_class":"balloon","description":"红色"}},
|
|
||||||
{"type":"action","name":"land","params":{"mode":"home"}}
|
## 四、场景示例(请灵活参考)
|
||||||
]
|
|
||||||
},
|
#### 场景 1:线性搜索任务(Sequence + Selector)
|
||||||
|
**指令**:“去研究所正大门,搜索扎辫子女子并拍照。”
|
||||||
|
**结构**:Sequence (按顺序执行)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "Sequence",
|
||||||
|
"name": "MainSearchTask",
|
||||||
|
"children": [
|
||||||
|
{"type":"action","name":"takeoff","params":{"altitude":10.0}},
|
||||||
|
{"type":"action","name":"fly_to_waypoint","params":{"x":100.0,"y":50.0,"z":10.0}},
|
||||||
|
{"type":"action","name":"rotate_search","params":{"target_class":"person","description":"扎辫子女子"}},
|
||||||
{
|
{
|
||||||
"type": "Selector",
|
"type": "Selector",
|
||||||
"name": "SafetyMonitor",
|
"name": "CheckAndPhoto",
|
||||||
"params": {"memory": true},
|
|
||||||
"children": [
|
"children": [
|
||||||
{"type":"condition","name":"battery_above","params":{"threshold":0.3}},
|
|
||||||
{"type":"condition","name":"gps_status","params":{"min_satellites":8}},
|
|
||||||
{
|
{
|
||||||
"type":"Sequence",
|
"type": "Sequence",
|
||||||
"name":"EmergencyHandler",
|
"name": "PhotoIfFound",
|
||||||
"params": {},
|
|
||||||
"children": [
|
"children": [
|
||||||
{"type":"action","name":"emergency_return","params":{"reason":"safety_breach"}},
|
{"type":"condition","name":"object_detected","params":{"target_class":"person","description":"扎辫子女子"}},
|
||||||
{"type":"action","name":"land","params":{"mode":"home"}}
|
{"type":"action","name":"take_photos","params":{"target_class":"person","description":"扎辫子女子","track_time":10.0}}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
{"type":"action","name":"loiter","params":{"duration":5.0}} // 未发现时的备选动作
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{"type":"action","name":"land","params":{"mode":"home"}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 场景 2:带中断逻辑的巡逻(Selector 示例)
|
||||||
|
**指令**:“飞往航点A。如果途中发现可疑人员,则悬停。”
|
||||||
|
**结构**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"root": {
|
||||||
|
"type": "Sequence",
|
||||||
|
"children": [
|
||||||
|
{"type":"action","name":"takeoff","params":{"altitude":10.0}},
|
||||||
|
{
|
||||||
|
"type": "Selector",
|
||||||
|
"name": "FlyOrDetect",
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "Sequence",
|
||||||
|
"name": "InterruptionLogic",
|
||||||
|
"children": [
|
||||||
|
{"type":"action","name":"object_detect","params":{"target_class":"person"}},
|
||||||
|
{"type":"action","name":"loiter","params":{"duration":5.0}}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{"type":"action","name":"fly_to_waypoint","params":{"x":100.0,"y":50.0,"z":10.0}}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -96,22 +141,20 @@
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 五、优先级排序任务通用示例
|
||||||
|
当用户指令中明确提出有多个待考察且具有优先级关系的物体时,节点描述须为优先级关系。
|
||||||
|
| 用户指令场景 | `target_class` | `description` |
|
||||||
|
|-----------------------------|-----------------|-------------------------|
|
||||||
|
| 红气球>蓝气球>绿气球 | `balloon` | `(红>蓝>绿)` |
|
||||||
|
| 军用卡车>民用卡车>面包车 | `truck` | `(军用卡车>民用卡车>面包车)` |
|
||||||
|
|
||||||
## 四、优先级排序任务通用示例
|
## 六、高频错误规避
|
||||||
当用户指令中明确提出有多个待考察且具有优先级关系的物体时,节点描述须为优先级关系。比如当指令为已知有三个气球,危险级关系为红色气球大于蓝色气球大于绿色气球,要求优先跟踪最危险的气球时,节点的描述参考下表情形。
|
1. 优先级排序不可修改`target_class`,仅用`description`填排序规则;
|
||||||
| 用户指令场景 | `target_class` | `description` | 核心节点示例(search_pattern) |
|
2. `track_object`必传`track_time`;
|
||||||
|-----------------------------|-----------------|-------------------------|------------------------------------------------------------------------------------------------|
|
3. `gps_status`的`min_satellites`必须在6-15之间;
|
||||||
| 红气球>蓝气球>绿气球 | `balloon` | `(红>蓝>绿)` | `{"type":"action","name":"search_pattern","params":{"pattern_type":"grid","center_x":200,"center_y":150,"center_z":10,"radius":50,"target_class":"balloon","description":"(红>蓝>绿)"}}` |
|
4. 严禁输出 markdown 代码块标记,直接输出 JSON 纯文本;
|
||||||
| 军用卡车>民用卡车>面包车 | `truck` | `(军用卡车>民用卡车>面包车)` | `{"type":"action","name":"object_detect","params":{"target_class":"truck","description":"(军用卡车>民用卡车>面包车)"}}` |
|
5. 控制流节点的 `type` 必须是 `"Sequence"`, `"Selector"` 或 `"Parallel"`;
|
||||||
|
6. 当用户指令中要求执行动作前增加人工确认时,比如“我确认后拍照”,则必须在拍照动作前增加manual_confirmation节点
|
||||||
|
|
||||||
|
## 七、输出要求
|
||||||
## 五、高频错误规避(确保验证通过)
|
仅输出1个严格符合上述所有规则的JSON对象。
|
||||||
1. 优先级排序不可修改`target_class`:如“民用卡车、面包车与军用卡车中,军用卡车优先”,`target_class`仍为`truck`,仅用`description`填排序规则;
|
|
||||||
2. 在没有明确指出物体之间的优先级关系情况下,`description`字段只描述物体属性本身,严禁与用户指令中不存在的物体进行排序;
|
|
||||||
3. `track_object`必传`track_time`:不可用`duration`替代(如跟踪30秒填`"track_time":30.0`);
|
|
||||||
4. `gps_status`的`min_satellites`必须在6-15之间(如8,不可缺省);
|
|
||||||
5. 无自定义节点:“锁定高优先级目标”需通过`object_detect`+`object_detected`实现,不可用“lock_high_risk_target”。
|
|
||||||
|
|
||||||
|
|
||||||
## 六、输出要求
|
|
||||||
仅输出1个严格符合上述所有规则的JSON对象,**确保:1. 优先级排序逻辑正确填入`description`;2. `target_class`匹配预定义列表;3. 行为树结构不变;4. 后端解析与Schema验证无错误**,无任何冗余内容。
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ def _parse_allowed_nodes_from_prompt(prompt_text: str) -> tuple[Set[str], Set[st
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# 使用更精确的正则表达式匹配节点定义部分
|
# 使用更精确的正则表达式匹配节点定义部分
|
||||||
node_section_pattern = r"#### 2\. 可用节点定义.*?```json\s*({.*?})\s*```"
|
node_section_pattern = r"#### 1\. 可用节点定义.*?```json\s*({.*?})\s*```"
|
||||||
match = re.search(node_section_pattern, prompt_text, re.DOTALL | re.IGNORECASE)
|
match = re.search(node_section_pattern, prompt_text, re.DOTALL | re.IGNORECASE)
|
||||||
|
|
||||||
if not match:
|
if not match:
|
||||||
@@ -144,51 +144,12 @@ def _fallback_parse_nodes(prompt_text: str) -> tuple[Set[str], Set[str]]:
|
|||||||
logging.error("在所有JSON代码块中都没有找到有效的节点定义结构。")
|
logging.error("在所有JSON代码块中都没有找到有效的节点定义结构。")
|
||||||
return set(), set()
|
return set(), set()
|
||||||
|
|
||||||
def _find_nodes_by_name(node: Dict, target_name: str) -> List[Dict]:
|
|
||||||
"""递归查找所有指定名称的节点"""
|
|
||||||
nodes_found = []
|
|
||||||
|
|
||||||
if node.get("name") == target_name:
|
|
||||||
nodes_found.append(node)
|
|
||||||
|
|
||||||
# 递归搜索子节点
|
|
||||||
for child in node.get("children", []):
|
|
||||||
nodes_found.extend(_find_nodes_by_name(child, target_name))
|
|
||||||
|
|
||||||
return nodes_found
|
|
||||||
|
|
||||||
def _validate_safety_monitoring(pytree_instance: dict) -> bool:
|
|
||||||
"""验证行为树是否包含必要的安全监控"""
|
|
||||||
root_node = pytree_instance.get("root", {})
|
|
||||||
|
|
||||||
# 查找所有电池监控节点
|
|
||||||
battery_nodes = _find_nodes_by_name(root_node, "battery_above")
|
|
||||||
|
|
||||||
# 检查是否包含安全监控结构
|
|
||||||
safety_monitors = _find_nodes_by_name(root_node, "SafetyMonitor")
|
|
||||||
|
|
||||||
if not battery_nodes and not safety_monitors:
|
|
||||||
logging.warning("⚠️ 安全警告: 行为树中没有发现电池监控节点或安全监控器")
|
|
||||||
return False
|
|
||||||
|
|
||||||
# 检查电池阈值设置是否合理
|
|
||||||
for battery_node in battery_nodes:
|
|
||||||
threshold = battery_node.get("params", {}).get("threshold")
|
|
||||||
if threshold is not None:
|
|
||||||
if threshold < 0.25:
|
|
||||||
logging.warning(f"⚠️ 安全警告: 电池阈值设置过低 ({threshold}),建议不低于0.25")
|
|
||||||
elif threshold > 0.5:
|
|
||||||
logging.warning(f"⚠️ 安全警告: 电池阈值设置过高 ({threshold}),可能影响任务执行")
|
|
||||||
|
|
||||||
logging.info("✅ 安全监控验证通过")
|
|
||||||
return True
|
|
||||||
|
|
||||||
def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> dict:
|
def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> dict:
|
||||||
"""
|
"""
|
||||||
根据允许的行动和条件节点,动态生成一个JSON Schema。
|
根据允许的行动和条件节点,动态生成一个JSON Schema。
|
||||||
"""
|
"""
|
||||||
# 所有可能的节点类型
|
# 所有可能的节点类型
|
||||||
node_types = ["action", "condition", "Sequence", "Selector", "Parallel"]
|
node_types = ["action", "condition", "Sequence", "Selector", "Parallel", "decorator"]
|
||||||
|
|
||||||
# 目标检测相关的类别枚举
|
# 目标检测相关的类别枚举
|
||||||
target_classes = [
|
target_classes = [
|
||||||
@@ -201,38 +162,44 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
|
|||||||
"sandwich", "orange", "broccoli", "carrot", "hot_dog", "pizza", "donut", "cake", "chair",
|
"sandwich", "orange", "broccoli", "carrot", "hot_dog", "pizza", "donut", "cake", "chair",
|
||||||
"couch", "potted_plant", "bed", "dining_table", "toilet", "tv", "laptop", "mouse", "remote",
|
"couch", "potted_plant", "bed", "dining_table", "toilet", "tv", "laptop", "mouse", "remote",
|
||||||
"keyboard", "cell_phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book",
|
"keyboard", "cell_phone", "microwave", "oven", "toaster", "sink", "refrigerator", "book",
|
||||||
"clock", "vase", "scissors", "teddy_bear", "hair_drier", "toothbrush","balloon"
|
"clock", "vase", "scissors", "teddy_bear", "hair_drier", "toothbrush","balloon","trash","window"
|
||||||
]
|
]
|
||||||
|
|
||||||
# 递归节点定义
|
# 递归节点定义
|
||||||
node_definition = {
|
node_definition = {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {"type": "string", "enum": node_types},
|
# 修改:手动构造不区分大小写的正则,避免使用不支持的 (?i) 标志
|
||||||
|
# 匹配: action, condition, sequence, selector, parallel, decorator (忽略大小写)
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^([Aa][Cc][Tt][Ii][Oo][Nn]|[Cc][Oo][Nn][Dd][Ii][Tt][Ii][Oo][Nn]|[Ss][Ee][Qq][Uu][Ee][Nn][Cc][Ee]|[Ss][Ee][Ll][Ee][Cc][Tt][Oo][Rr]|[Pp][Aa][Rr][Aa][Ll][Ll][Ee][Ll]|[Dd][Ee][Cc][Oo][Rr][Aa][Tt][Oo][Rr])$"
|
||||||
|
},
|
||||||
"name": {"type": "string"},
|
"name": {"type": "string"},
|
||||||
"params": {"type": "object"},
|
"params": {"type": "object"},
|
||||||
"children": {
|
"children": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": {"$ref": "#/definitions/node"}
|
"items": {"$ref": "#/definitions/node"}
|
||||||
}
|
},
|
||||||
|
"child": {"$ref": "#/definitions/node"}
|
||||||
},
|
},
|
||||||
"required": ["type", "name"],
|
"required": ["type", "name"],
|
||||||
"allOf": [
|
"allOf": [
|
||||||
# 动作节点验证
|
# 动作节点验证 (忽略大小写)
|
||||||
{
|
{
|
||||||
"if": {"properties": {"type": {"const": "action"}}},
|
"if": {"properties": {"type": {"pattern": "^[Aa][Cc][Tt][Ii][Oo][Nn]$"}}},
|
||||||
"then": {"properties": {"name": {"enum": sorted(list(allowed_actions))}}}
|
"then": {"properties": {"name": {"enum": sorted(list(allowed_actions))}}}
|
||||||
},
|
},
|
||||||
# 条件节点验证
|
# 条件节点验证 (忽略大小写)
|
||||||
{
|
{
|
||||||
"if": {"properties": {"type": {"const": "condition"}}},
|
"if": {"properties": {"type": {"pattern": "^[Cc][Oo][Nn][Dd][Ii][Tt][Ii][Oo][Nn]$"}}},
|
||||||
"then": {"properties": {"name": {"enum": sorted(list(allowed_conditions))}}}
|
"then": {"properties": {"name": {"enum": sorted(list(allowed_conditions))}}}
|
||||||
},
|
},
|
||||||
# 目标检测动作节点的参数验证
|
# 目标检测动作节点的参数验证 (忽略大小写)
|
||||||
{
|
{
|
||||||
"if": {
|
"if": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {"const": "action"},
|
"type": {"pattern": "^[Aa][Cc][Tt][Ii][Oo][Nn]$"},
|
||||||
"name": {"const": "object_detect"}
|
"name": {"const": "object_detect"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -251,11 +218,11 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
# 目标检测条件节点的参数验证
|
# 目标检测条件节点的参数验证 (忽略大小写)
|
||||||
{
|
{
|
||||||
"if": {
|
"if": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {"const": "condition"},
|
"type": {"pattern": "^[Cc][Oo][Nn][Dd][Ii][Tt][Ii][Oo][Nn]$"},
|
||||||
"name": {"const": "object_detected"}
|
"name": {"const": "object_detected"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -274,11 +241,11 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
# 电池监控节点的参数验证
|
# 电池监控节点的参数验证 (忽略大小写)
|
||||||
{
|
{
|
||||||
"if": {
|
"if": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {"const": "condition"},
|
"type": {"pattern": "^[Cc][Oo][Nn][Dd][Ii][Tt][Ii][Oo][Nn]$"},
|
||||||
"name": {"const": "battery_above"}
|
"name": {"const": "battery_above"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -295,11 +262,11 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
# GPS状态节点的参数验证
|
# GPS状态节点的参数验证 (忽略大小写)
|
||||||
{
|
{
|
||||||
"if": {
|
"if": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"type": {"const": "condition"},
|
"type": {"pattern": "^[Cc][Oo][Nn][Dd][Ii][Tt][Ii][Oo][Nn]$"},
|
||||||
"name": {"const": "gps_status"}
|
"name": {"const": "gps_status"}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -372,10 +339,7 @@ def _validate_pytree_with_schema(pytree_instance: dict, schema: dict) -> bool:
|
|||||||
jsonschema.validate(instance=pytree_instance, schema=schema)
|
jsonschema.validate(instance=pytree_instance, schema=schema)
|
||||||
logging.info("✅ JSON Schema验证成功")
|
logging.info("✅ JSON Schema验证成功")
|
||||||
|
|
||||||
# 额外验证安全监控
|
return True
|
||||||
safety_valid = _validate_safety_monitoring(pytree_instance)
|
|
||||||
|
|
||||||
return True and safety_valid
|
|
||||||
except jsonschema.ValidationError as e:
|
except jsonschema.ValidationError as e:
|
||||||
logging.warning("❌ Pytree验证失败")
|
logging.warning("❌ Pytree验证失败")
|
||||||
logging.warning(f"错误信息: {e.message}")
|
logging.warning(f"错误信息: {e.message}")
|
||||||
@@ -503,6 +467,10 @@ def _add_nodes_and_edges(node: dict, dot, parent_id: str | None = None) -> str:
|
|||||||
shape = 'ellipse'
|
shape = 'ellipse'
|
||||||
style = 'filled'
|
style = 'filled'
|
||||||
fillcolor = '#e1d5e7' # 紫色
|
fillcolor = '#e1d5e7' # 紫色
|
||||||
|
elif node_type == 'decorator':
|
||||||
|
shape = 'doubleoctagon'
|
||||||
|
style = 'filled'
|
||||||
|
fillcolor = '#f8cecc' # 浅红
|
||||||
|
|
||||||
# 特别标记安全相关节点
|
# 特别标记安全相关节点
|
||||||
if node.get('name') in ['battery_above', 'gps_status', 'SafetyMonitor']:
|
if node.get('name') in ['battery_above', 'gps_status', 'SafetyMonitor']:
|
||||||
@@ -515,28 +483,28 @@ def _add_nodes_and_edges(node: dict, dot, parent_id: str | None = None) -> str:
|
|||||||
if parent_id:
|
if parent_id:
|
||||||
dot.edge(parent_id, current_id)
|
dot.edge(parent_id, current_id)
|
||||||
|
|
||||||
# 递归处理子节点
|
# 递归处理子节点 (Sequence, Selector, Parallel 等)
|
||||||
children = node.get("children", [])
|
children = node.get("children", [])
|
||||||
if not children:
|
if children:
|
||||||
return current_id
|
# 记录所有子节点的ID
|
||||||
|
child_ids = []
|
||||||
# 记录所有子节点的ID
|
|
||||||
child_ids = []
|
# 正确的递归连接:每个子节点都连接到当前节点
|
||||||
|
for child in children:
|
||||||
# 正确的递归连接:每个子节点都连接到当前节点
|
child_id = _add_nodes_and_edges(child, dot, current_id)
|
||||||
for child in children:
|
child_ids.append(child_id)
|
||||||
child_id = _add_nodes_and_edges(child, dot, current_id)
|
|
||||||
child_ids.append(child_id)
|
# 子节点同级排列(横向排布,更直观地表现同层)
|
||||||
|
if len(child_ids) > 1:
|
||||||
# 子节点同级排列(横向排布,更直观地表现同层)
|
with dot.subgraph(name=f"rank_{current_id}") as s:
|
||||||
if len(child_ids) > 1:
|
s.attr(rank='same')
|
||||||
with dot.subgraph(name=f"rank_{current_id}") as s:
|
for cid in child_ids:
|
||||||
s.attr(rank='same')
|
s.node(cid)
|
||||||
for cid in child_ids:
|
|
||||||
s.node(cid)
|
# 递归处理单子节点 (Decorator)
|
||||||
|
child = node.get("child")
|
||||||
# 行为树中,所有类型的节点都只是父连子,不需要子节点间的额外连接
|
if child:
|
||||||
# Sequence、Selector、Parallel 的执行逻辑由行为树引擎处理,不需要在可视化中体现
|
_add_nodes_and_edges(child, dot, current_id)
|
||||||
|
|
||||||
return current_id
|
return current_id
|
||||||
|
|
||||||
@@ -588,7 +556,7 @@ class PyTreeGenerator:
|
|||||||
self.complex_llm_client = openai.OpenAI(api_key=self.api_key, base_url=self.complex_base_url)
|
self.complex_llm_client = openai.OpenAI(api_key=self.api_key, base_url=self.complex_base_url)
|
||||||
|
|
||||||
# --- ChromaDB Client Setup ---
|
# --- ChromaDB Client Setup ---
|
||||||
vector_store_path = os.path.abspath(os.path.join(self.base_dir, '..', '..', 'tools', 'vector_store'))
|
vector_store_path = os.path.abspath(os.path.join(self.base_dir, '..', '..', 'tools', 'rag','vector_store'))
|
||||||
self.chroma_client = chromadb.PersistentClient(path=vector_store_path)
|
self.chroma_client = chromadb.PersistentClient(path=vector_store_path)
|
||||||
|
|
||||||
# Explicitly use the remote embedding function for queries
|
# Explicitly use the remote embedding function for queries
|
||||||
@@ -725,7 +693,7 @@ class PyTreeGenerator:
|
|||||||
pytree_str = combined_text if combined_text else (msg_content or "")
|
pytree_str = combined_text if combined_text else (msg_content or "")
|
||||||
raw_full_text_for_logging = pytree_str # 保存完整原文(含 <think>)以便失败时完整打印
|
raw_full_text_for_logging = pytree_str # 保存完整原文(含 <think>)以便失败时完整打印
|
||||||
|
|
||||||
# 提取 <think> 推理链内容(若存在)
|
# 提取 <think> 推理链内容(若有)
|
||||||
reasoning_text = None
|
reasoning_text = None
|
||||||
try:
|
try:
|
||||||
think_match = re.search(r"<think>([\s\S]*?)</think>", pytree_str)
|
think_match = re.search(r"<think>([\s\S]*?)</think>", pytree_str)
|
||||||
@@ -827,49 +795,7 @@ class PyTreeGenerator:
|
|||||||
pytree_dict['final_prompt'] = final_prompt
|
pytree_dict['final_prompt'] = final_prompt
|
||||||
return pytree_dict
|
return pytree_dict
|
||||||
|
|
||||||
# 复杂模式回退:若模型误返回简单结构(root是单个action),则自动包装为含安全监控的行为树
|
# 验证生成的复杂行为树
|
||||||
if mode == "complex" and isinstance(pytree_dict, dict) and 'root' in pytree_dict:
|
|
||||||
root_node = pytree_dict.get('root', {})
|
|
||||||
# 检查是否是简单结构(root是单个action节点,没有children)
|
|
||||||
if (root_node.get('type') == 'action' and
|
|
||||||
('children' not in root_node or not root_node.get('children'))):
|
|
||||||
try:
|
|
||||||
jsonschema.validate(instance=pytree_dict, schema=self.simple_schema)
|
|
||||||
logging.warning("⚠️ 复杂模式生成了简单结构(单个action),触发自动包装为完整行为树的回退逻辑。")
|
|
||||||
action_name = root_node.get('name')
|
|
||||||
action_params = root_node.get('params') if isinstance(root_node.get('params'), dict) else {}
|
|
||||||
|
|
||||||
safety_selector = {
|
|
||||||
"type": "Selector",
|
|
||||||
"name": "SafetyMonitor",
|
|
||||||
"params": {"memory": True},
|
|
||||||
"children": [
|
|
||||||
{"type": "condition", "name": "battery_above", "params": {"threshold": 0.3}},
|
|
||||||
{"type": "condition", "name": "gps_status", "params": {"min_satellites": 8}},
|
|
||||||
{"type": "Sequence", "name": "EmergencyHandler", "children": [
|
|
||||||
{"type": "action", "name": "emergency_return", "params": {"reason": "safety_breach"}},
|
|
||||||
{"type": "action", "name": "land", "params": {"mode": "home"}}
|
|
||||||
]}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
main_children = [{"type": "action", "name": action_name, "params": action_params}]
|
|
||||||
if action_name != "land":
|
|
||||||
main_children.append({"type": "action", "name": "land", "params": {"mode": "home"}})
|
|
||||||
|
|
||||||
root_parallel = {
|
|
||||||
"type": "Parallel",
|
|
||||||
"name": "MissionWithSafety",
|
|
||||||
"params": {"policy": "all_success"},
|
|
||||||
"children": [
|
|
||||||
{"type": "Sequence", "name": "MainTask", "children": main_children},
|
|
||||||
safety_selector
|
|
||||||
]
|
|
||||||
}
|
|
||||||
pytree_dict = {"root": root_parallel}
|
|
||||||
except jsonschema.ValidationError:
|
|
||||||
# 不符合简单结构,按正常复杂验证继续
|
|
||||||
pass
|
|
||||||
if _validate_pytree_with_schema(pytree_dict, self.schema):
|
if _validate_pytree_with_schema(pytree_dict, self.schema):
|
||||||
logging.info("✅ 成功生成并验证了Pytree")
|
logging.info("✅ 成功生成并验证了Pytree")
|
||||||
plan_id = str(uuid.uuid4())
|
plan_id = str(uuid.uuid4())
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user