Compare commits
3 Commits
c4f851d387
...
pipeline
| Author | SHA1 | Date | |
|---|---|---|---|
| 8945e922d5 | |||
| c08cdfb339 | |||
| 43a0636913 |
@@ -6,38 +6,80 @@
|
||||
|
||||
接下来,任务流程应该是:起飞→飞往搜索区域→搜索目标→检测到目标后跟踪→打击。同时必须包含安全监控。
|
||||
|
||||
根据用户提供的参考知识,可能需要将搜索区域设置为某个中心点。比如,用户提到的“跷跷板”在(x:15, y:-8.5, z:1.2),但z坐标需要调整到至少1米,这里已经是1.2,没问题。或者可能选择其他地点作为搜索中心。但用户没有明确说明,可能需要假设搜索区域是这些地点附近,或者使用一个综合的中心点。
|
||||
根据用户提供的参考知识,可能需要将搜索区域设置为某个中心点。比如,用户提到的“跷跷板”在(x:15, y:-8.5, z:1.2),但z坐标需要调整到至少1米,所以可能设置为z=2。或者选择其他地点作为搜索中心。但用户没有明确说明,可能需要假设搜索区域是这些地点的附近,或者使用其中一个作为中心。
|
||||
|
||||
不过用户可能希望无人机先飞往某个特定的搜索区。比如,参考知识中的“学生宿舍”可能是一个可能的区域,但需要确认。或者用户可能希望无人机在某个中心点周围进行搜索。例如,使用search_pattern的中心点可能选在某个已知地点,比如“跷跷板”附近,或者综合多个点。
|
||||
另外,用户提到要搜索并锁定危险性最高的气球,所以需要使用search_pattern或者object_detect。但因为是未知区域,可能更适合使用search_pattern,或者先飞到某个区域再进行检测。
|
||||
|
||||
但用户没有明确指定搜索区域,所以可能需要使用search_pattern的中心点为某个已知地点,比如“跷跷板”的坐标,或者选择一个合理的中心点。例如,假设搜索区域是“跷跷板”所在的位置,那么中心坐标为(15, -8.5, 1.2)。或者可能需要将搜索区域设置为多个地点的组合,但用户没有说明,所以可能需要选择一个中心点。
|
||||
根据任务范式,可能需要先飞到某个坐标点,然后进行搜索。例如,使用fly_to_waypoint飞到某个中心点,然后执行search_pattern。或者直接使用search_pattern覆盖多个区域。
|
||||
|
||||
接下来,考虑使用search_pattern来搜索,因为目标位置未知。参数中需要指定pattern_type,比如spiral或grid。假设选择spiral模式,覆盖更大的区域。中心点可能选在某个已知地点,比如“跷跷板”的坐标,或者综合多个点。但用户没有明确,所以可能需要选择一个合理的中心点,比如“跷跷板”的坐标。
|
||||
不过参考知识中的三个地点可能作为搜索区域的中心,可能需要将搜索区域设置为这些点的附近。但用户没有明确说明,可能需要选择一个中心点,比如“学生宿舍”的坐标,或者综合考虑。
|
||||
|
||||
然后,检测到目标后,需要跟踪30秒,再打击。所以流程是:起飞→飞往搜索区→搜索→检测→跟踪→打击→降落。
|
||||
另外,用户提到要锁定危险性最高的气球,所以需要检测到红色气球。因此,在search_pattern中,target_class应为balloon,并且description为“红色气球危险性高于蓝色气球高于绿色气球”,或者在object_detect中设置description参数为“红>蓝>绿”。
|
||||
|
||||
安全监控必须包含在Parallel节点中,监控电池和GPS,并在紧急情况下返航。
|
||||
但根据搜索模式,search_pattern的参数需要target_class和description。所以可能需要设置target_class为balloon,description为“红>蓝>绿”。
|
||||
|
||||
现在,构建行为树的结构:
|
||||
接下来,流程应该是:
|
||||
|
||||
根节点是Parallel,包含主任务和安全监控。主任务是Sequence,包含起飞、飞往搜索区、搜索、检测、跟踪、打击、降落。安全监控是Selector,包含电池和GPS条件,以及紧急处理Sequence。
|
||||
1. 起飞到一定高度。
|
||||
2. 飞往搜索区域的中心点(比如学生宿舍的坐标,调整z到2)。
|
||||
3. 执行search_pattern,搜索红色气球。
|
||||
4. 一旦检测到目标,进行跟踪。
|
||||
5. 跟踪30秒后打击。
|
||||
|
||||
在搜索部分,使用search_pattern,参数包括pattern_type为spiral,中心点选在某个已知地点,比如“跷跷板”的坐标(15, -8.5, 1.2),半径可能设为50米,覆盖较大区域。目标类别是balloon,描述为“红色气球危险性高于蓝色气球高于绿色气球”,但参数中description需要描述目标属性,比如“红>蓝>绿”。
|
||||
同时,安全监控必须包含电池和GPS检查,以及紧急处理。
|
||||
|
||||
检测到目标后,使用track_object,参数target_class为balloon,description为“红>蓝>绿”,track_time为30秒。然后strike_target,最后评估战损。
|
||||
现在需要构建行为树结构。根节点是Parallel,包含主任务和安全监控。主任务是Sequence,包含起飞、飞往搜索点、搜索、跟踪、打击、降落。
|
||||
|
||||
需要确保所有参数符合约束,比如z坐标在1-5000米之间,半径在5-1000米之间,速度等参数在合理范围。
|
||||
在搜索部分,可能需要使用search_pattern,或者先飞到某个点再进行object_detect。但根据用户指令,可能更适合使用search_pattern,因为需要在未知区域搜索。
|
||||
|
||||
现在,将各个步骤的参数填入JSON中。例如,起飞到2米,飞往搜索区的中心点可能选在“跷跷板”的坐标(15, -8.5, 1.2),但z需要调整到至少1米,这里已经是1.2。所以fly_to_waypoint的参数x=15,y=-8.5,z=1.2,接受半径2米。
|
||||
所以,主任务步骤:
|
||||
|
||||
搜索使用search_pattern,pattern_type为spiral,中心点为(15, -8.5, 1.2),半径设为50米,覆盖区域。目标类别是balloon,description为“红>蓝>绿”。
|
||||
- preflight_checks
|
||||
- takeoff
|
||||
- fly_to_waypoint到某个中心点(比如学生宿舍的坐标,调整z为2)
|
||||
- search_pattern,参数为balloon,description为“红>蓝>绿”,pattern_type可能是spiral或grid,假设用spiral,中心点为学生宿舍的坐标,半径适当,比如50米。
|
||||
- 然后,当检测到目标后,进行track_object,跟踪30秒。
|
||||
- 然后strike_target,打击。
|
||||
- 最后降落。
|
||||
|
||||
检测到目标后,使用track_object,参数target_class为balloon,description为“红>蓝>绿”,track_time为30秒,min_confidence设为0.7,safe_distance设为15米。
|
||||
但需要确保在search_pattern之后,检测到目标,然后进入跟踪。或者可能需要使用Selector来选择直接检测或搜索。
|
||||
|
||||
然后strike_target,参数target_class为balloon,description为“红>蓝>绿”,count为1。
|
||||
但根据用户指令,可能需要先飞到某个点,再执行search_pattern。或者直接使用search_pattern覆盖多个区域。
|
||||
|
||||
最后,降落。
|
||||
参考知识中的三个地点可能作为搜索区域的中心,但用户没有明确说明,可能需要选择其中一个作为搜索中心。比如学生宿舍的坐标(x:5, y:3, z:2),调整z为2。
|
||||
|
||||
安全监控部分,电池阈值设为0.35,GPS的min_satellites设为8,紧急处理Sequence包括emergency_return和land。
|
||||
所以,fly_to_waypoint的参数是x=5, y=3, z=2, acceptance_radius=2.0。
|
||||
|
||||
现在,将所有步骤的参数填入JSON中,确保符合所有规则。
|
||||
然后执行search_pattern,参数为balloon,description为“红>蓝>绿”,pattern_type为spiral,中心点为学生宿舍的坐标,半径比如50米。
|
||||
|
||||
之后,当检测到目标后,进行track_object,参数为balloon,description为“红>蓝>绿”,track_time=30.0,min_confidence=0.7,safe_distance=15.0。
|
||||
|
||||
然后strike_target,参数为balloon,description为“红>蓝>绿”,count=1。
|
||||
|
||||
最后降落。
|
||||
|
||||
同时,安全监控需要包含电池和GPS检查,以及紧急处理。
|
||||
|
||||
现在需要构建JSON结构,确保所有参数符合约束,并且流程正确。
|
||||
|
||||
可能的搜索中心是学生宿舍的坐标,调整z为2。然后search_pattern的参数为balloon,description为“红>蓝>绿”,pattern_type为spiral,radius为50米,center_x=5, center_y=3, center_z=2。
|
||||
|
||||
然后,当检测到目标后,进行track_object,跟踪30秒,然后打击。
|
||||
|
||||
在行为树中,主任务Sequence的步骤包括:
|
||||
|
||||
- preflight_checks
|
||||
- takeoff (altitude=2.0)
|
||||
- fly_to_waypoint到学生宿舍的坐标,调整z为2
|
||||
- search_pattern,参数为balloon,description为“红>蓝>绿”,pattern_type为spiral,radius=50,center_x=5, center_y=3, center_z=2
|
||||
- 然后,当检测到目标后,进行track_object
|
||||
- strike_target
|
||||
- land
|
||||
|
||||
同时,安全监控的Selector包含电池和GPS条件,以及紧急处理。
|
||||
|
||||
现在需要检查所有参数是否符合约束,例如z=2,符合≥1。
|
||||
|
||||
其他参数如radius=50,符合[5,1000]。
|
||||
|
||||
所以,生成的JSON结构应该符合这些要求,并且流程正确。
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 233 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对象,不要输出解释或多余文本。
|
||||
根据用户指令与下述可用节点定义,判断其为“简单”或“复杂”。
|
||||
|
||||
- 简单:单一原子动作即可完成(例如“起飞”“飞机自检”“移动到某地(已给定坐标)”“对着某点环绕XY圈(如‘对着学生宿舍环绕三十两圈’)”等),且无需行为树与安全并行监控。
|
||||
- 复杂:需要多步流程、搜索/检测/跟踪/评估、战损确认、或需要模板化任务结构与安全并行监控。
|
||||
- 简单:单一原子动作即可完成(例如"起飞""飞机自检""移动到某地(已给定坐标)""对着某点环绕XY圈(如'对着学生宿舍环绕三十两圈')"等),且无需行为树。
|
||||
- 复杂:需要多步流程、搜索/检测/跟踪/评估、战损确认、或需要模板化任务结构。
|
||||
|
||||
输出格式(严格遵守):
|
||||
{"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": "object_detect"}, {"name": "strike_target"}, {"name": "battle_damage_assessment"},
|
||||
{"name": "search_pattern"}, {"name": "track_object"}, {"name": "deliver_payload"},
|
||||
{"name": "preflight_checks"}, {"name": "emergency_return"}
|
||||
{"name": "preflight_checks"}, {"name": "take_picture"}
|
||||
],
|
||||
"conditions": [
|
||||
{"name": "battery_above"}, {"name": "at_waypoint"}, {"name": "object_detected"},
|
||||
{"name": "target_destroyed"}, {"name": "time_elapsed"}, {"name": "gps_status"}
|
||||
{"name": "at_waypoint"}, {"name": "object_detected"},
|
||||
{"name": "target_destroyed"}, {"name": "time_elapsed"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
- 只输出一个JSON对象,不要任何解释或多余文本。
|
||||
- JSON结构:
|
||||
{"root":{"type":"action","name":"<action_name>","params":{...}}}
|
||||
- <action_name> 与参数定义、取值范围,必须与“复杂模式”提示词(system_prompt.txt)中的定义完全一致。
|
||||
- 简单模式下root节点必须是action类型节点,不能是控制流节点。
|
||||
- root节点必须是action类型节点,不能是控制流节点。
|
||||
|
||||
示例:
|
||||
- “起飞到10米” → {"root":{"type":"action","name":"takeoff","params":{"altitude":10.0}}}
|
||||
@@ -30,15 +29,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": "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": "emergency_return", "description": "执行紧急返航程序。", "params": {"reason": "string"}}
|
||||
{"name": "take_picture", "description": "使用机载相机拍摄照片。", "params": {}}
|
||||
],
|
||||
"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": "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": "time_elapsed", "description": "时间经过。", "params": {"duration": "float[1,2700]"}},
|
||||
{"name": "gps_status", "description": "GPS状态良好。", "params": {"min_satellites": "int[6,15], 默认10"}}
|
||||
{"name": "time_elapsed", "description": "时间经过。", "params": {"duration": "float[1,2700]"}}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
@@ -20,15 +20,13 @@
|
||||
{"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":"preflight_checks","params":{"check_level":"basic/comprehensive"}},
|
||||
{"name":"emergency_return","params":{"reason":"string"}}
|
||||
{"name":"take_picture","params":{}}
|
||||
],
|
||||
"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":"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":"time_elapsed","params":{"duration":"[1,2700]秒"}},
|
||||
{"name":"gps_status","params":{"min_satellites":"int[6,15],必传(如8)"}}
|
||||
{"name":"time_elapsed","params":{"duration":"[1,2700]秒"}}
|
||||
],
|
||||
"control_flow": [
|
||||
{"name":"Sequence","params":{},"children":"子节点数组(按序执行,全成功则成功)"},
|
||||
@@ -43,18 +41,18 @@
|
||||
每个节点必须包含以下字段,字段名/类型不可自定义:
|
||||
1. **`type`**:
|
||||
- 动作节点→`"action"`,条件节点→`"condition"`,控制流节点→`"Sequence"`/`"Selector"`/`"Parallel"`(与`name`字段值完全一致);
|
||||
2. **`name`**:必须是上述JSON中`actions`/`conditions`/`control_flow`下的`name`值(如“gps_status”不可错写为“gps_check”);
|
||||
2. **`name`**:必须是上述JSON中`actions`/`conditions`/`control_flow`下的`name`值;
|
||||
3. **`params`**:严格匹配上述节点的`params`定义,无自定义参数(如优先级排序不可加“priority”字段,仅用`description`);
|
||||
4. **`children`**:仅控制流节点必含(子节点数组),动作/条件节点无此字段。
|
||||
|
||||
|
||||
## 三、行为树固定结构(通用不变,确保安全验证)
|
||||
根节点必须是`Parallel`,`children`含`MainTask`(Sequence)和`SafetyMonitor`(Selector),结构不随任务类型(含优先级排序)修改:
|
||||
## 三、行为树固定结构(通用不变)
|
||||
根节点必须是`Parallel`,`children`含`MainTask`(Sequence),结构不随任务类型(含优先级排序)修改:
|
||||
```json
|
||||
{
|
||||
"root": {
|
||||
"type": "Parallel",
|
||||
"name": "MissionWithSafety",
|
||||
"name": "Mission",
|
||||
"params": {"policy": "all_success"},
|
||||
"children": [
|
||||
{
|
||||
@@ -72,24 +70,6 @@
|
||||
{"type":"action","name":"strike_target","params":{"target_class":"balloon","description":"红色"}},
|
||||
{"type":"action","name":"land","params":{"mode":"home"}}
|
||||
]
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"params": {},
|
||||
"children": [
|
||||
{"type":"action","name":"emergency_return","params":{"reason":"safety_breach"}},
|
||||
{"type":"action","name":"land","params":{"mode":"home"}}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -106,11 +86,10 @@
|
||||
|
||||
|
||||
## 五、高频错误规避(确保验证通过)
|
||||
1. 优先级排序不可修改`target_class`:如“民用卡车、面包车与军用卡车中,军用卡车优先”,`target_class`仍为`truck`,仅用`description`填排序规则;
|
||||
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”。
|
||||
4. 无自定义节点:"锁定高优先级目标"需通过`object_detect`+`object_detected`实现,不可用"lock_high_risk_target"。
|
||||
|
||||
|
||||
## 六、输出要求
|
||||
|
||||
@@ -157,32 +157,6 @@ def _find_nodes_by_name(node: Dict, target_name: str) -> List[Dict]:
|
||||
|
||||
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:
|
||||
"""
|
||||
根据允许的行动和条件节点,动态生成一个JSON Schema。
|
||||
@@ -273,48 +247,6 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
# 电池监控节点的参数验证
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"type": {"const": "condition"},
|
||||
"name": {"const": "battery_above"}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"params": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"threshold": {"type": "number", "minimum": 0.0, "maximum": 1.0}
|
||||
},
|
||||
"required": ["threshold"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
# GPS状态节点的参数验证
|
||||
{
|
||||
"if": {
|
||||
"properties": {
|
||||
"type": {"const": "condition"},
|
||||
"name": {"const": "gps_status"}
|
||||
}
|
||||
},
|
||||
"then": {
|
||||
"properties": {
|
||||
"params": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"min_satellites": {"type": "integer", "minimum": 6, "maximum": 15}
|
||||
},
|
||||
"required": ["min_satellites"],
|
||||
"additionalProperties": False
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -337,7 +269,9 @@ def _generate_pytree_schema(allowed_actions: set, allowed_conditions: set) -> di
|
||||
|
||||
def _generate_simple_mode_schema(allowed_actions: set) -> dict:
|
||||
"""
|
||||
生成简单模式JSON Schema:{"mode":"simple","action":{...}}
|
||||
生成简单模式JSON Schema:{"root":{"type":"action","name":"...","params":{...}}}
|
||||
简单模式与复杂模式使用相同的格式(root字段),但要求root必须是action类型且没有children。
|
||||
严格按照提示词要求:root节点必须是action类型节点,不能是控制流节点(即不能有children)。
|
||||
仅校验动作名称在允许集合内,以及基本结构完整性;参数按对象形状放宽,由上游提示词与运行时再约束。
|
||||
"""
|
||||
schema = {
|
||||
@@ -345,19 +279,20 @@ def _generate_simple_mode_schema(allowed_actions: set) -> dict:
|
||||
"title": "SimpleMode",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"mode": {"type": "string", "const": "simple"},
|
||||
"action": {
|
||||
"root": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string", "enum": sorted(list(allowed_actions))},
|
||||
"params": {"type": "object"}
|
||||
"type": {"type": "string", "const": "action"}, # 必须是action类型,不能是控制流节点(Sequence/Selector/Parallel)
|
||||
"name": {"type": "string", "enum": sorted(list(allowed_actions))}, # 动作名称必须在允许列表中
|
||||
"params": {"type": "object"} # params是对象,具体参数由提示词和运行时约束
|
||||
},
|
||||
"required": ["name"],
|
||||
"additionalProperties": True
|
||||
"required": ["type", "name"], # type和name是必需的,params可选
|
||||
"additionalProperties": True # 允许root节点有其他属性(如额外的元数据)
|
||||
# 注意:children字段的检查在验证后手动进行,因为JSON Schema的not/allOf在检查不存在字段时可能有问题
|
||||
}
|
||||
},
|
||||
"required": ["mode", "action"],
|
||||
"additionalProperties": False
|
||||
"required": ["root"], # 顶层必须有root字段
|
||||
"additionalProperties": False # 顶层只能有root字段,不能有其他字段(如mode等)
|
||||
}
|
||||
return schema
|
||||
|
||||
@@ -368,11 +303,7 @@ def _validate_pytree_with_schema(pytree_instance: dict, schema: dict) -> bool:
|
||||
try:
|
||||
jsonschema.validate(instance=pytree_instance, schema=schema)
|
||||
logging.info("✅ JSON Schema验证成功")
|
||||
|
||||
# 额外验证安全监控
|
||||
safety_valid = _validate_safety_monitoring(pytree_instance)
|
||||
|
||||
return True and safety_valid
|
||||
return True
|
||||
except jsonschema.ValidationError as e:
|
||||
logging.warning("❌ Pytree验证失败")
|
||||
logging.warning(f"错误信息: {e.message}")
|
||||
@@ -382,10 +313,6 @@ def _validate_pytree_with_schema(pytree_instance: dict, schema: dict) -> bool:
|
||||
# 提供更具体的错误信息
|
||||
if "object_detect" in str(e.message) or "object_detected" in str(e.message):
|
||||
logging.warning("💡 提示: 请确保目标类别是预定义列表中的有效值")
|
||||
elif "battery_above" in str(e.message):
|
||||
logging.warning("💡 提示: 电池阈值必须在0.0到1.0之间")
|
||||
elif "gps_status" in str(e.message):
|
||||
logging.warning("💡 提示: 最小卫星数量必须在6到15之间")
|
||||
|
||||
return False
|
||||
except Exception as e:
|
||||
@@ -407,10 +334,10 @@ def _visualize_pytree(node: Dict, file_path: str):
|
||||
|
||||
# 选择合适的中文字体,避免中文乱码
|
||||
def _pick_zh_font():
|
||||
sys = platform.system()
|
||||
if sys == "Windows":
|
||||
system = platform.system()
|
||||
if system == "Windows":
|
||||
return "Microsoft YaHei"
|
||||
elif sys == "Darwin":
|
||||
elif system == "Darwin":
|
||||
return "PingFang SC"
|
||||
else:
|
||||
return "Noto Sans CJK SC"
|
||||
@@ -440,7 +367,19 @@ def _visualize_pytree(node: Dict, file_path: str):
|
||||
except Exception as e:
|
||||
logging.error("❌ 生成可视化图形失败")
|
||||
logging.error("请确保您的系统已经正确安装了Graphviz图形库。")
|
||||
logging.error("安装方法:")
|
||||
logging.error(" Ubuntu/Debian: sudo apt-get install graphviz")
|
||||
logging.error(" CentOS/RHEL: sudo yum install graphviz")
|
||||
logging.error(" macOS: brew install graphviz")
|
||||
logging.error(f"错误详情: {e}")
|
||||
# 清理可能残留的源文件
|
||||
try:
|
||||
gv_file = f"{render_path}.gv"
|
||||
if os.path.exists(gv_file):
|
||||
os.remove(gv_file)
|
||||
logging.info(f"已清理残留的源文件: {gv_file}")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _add_nodes_and_edges(node: dict, dot, parent_id: str | None = None) -> str:
|
||||
"""递归辅助函数,用于添加节点和边。"""
|
||||
@@ -501,11 +440,6 @@ def _add_nodes_and_edges(node: dict, dot, parent_id: str | None = None) -> str:
|
||||
style = 'filled'
|
||||
fillcolor = '#e1d5e7' # 紫色
|
||||
|
||||
# 特别标记安全相关节点
|
||||
if node.get('name') in ['battery_above', 'gps_status', 'SafetyMonitor']:
|
||||
border_color = '#ff0000' # 红色边框突出显示安全节点
|
||||
style = 'filled,bold' # 加粗
|
||||
|
||||
dot.node(current_id, label=node_label, shape=shape, style=style, fillcolor=fillcolor, color=border_color)
|
||||
|
||||
# 连接父节点
|
||||
@@ -777,6 +711,13 @@ class PyTreeGenerator:
|
||||
if mode == "simple":
|
||||
try:
|
||||
jsonschema.validate(instance=pytree_dict, schema=self.simple_schema)
|
||||
# 手动检查:简单模式的root节点不能有children(或children必须是空数组)
|
||||
root_node = pytree_dict.get('root', {})
|
||||
if 'children' in root_node:
|
||||
children = root_node.get('children', [])
|
||||
if isinstance(children, list) and len(children) > 0:
|
||||
logging.warning(f"❌ 简单模式验证失败: root节点不能有children,但发现 {len(children)} 个子节点")
|
||||
continue
|
||||
logging.info("✅ 简单模式JSON Schema验证成功")
|
||||
except jsonschema.ValidationError as e:
|
||||
logging.warning(f"❌ 简单模式验证失败: {e.message}")
|
||||
@@ -784,16 +725,13 @@ class PyTreeGenerator:
|
||||
# 附加元信息并生成简单可视化(单动作)
|
||||
plan_id = str(uuid.uuid4())
|
||||
pytree_dict['plan_id'] = plan_id
|
||||
# 简单模式可视化:构造一个简化节点图
|
||||
# 简单模式可视化:使用root节点(已经是action类型)
|
||||
try:
|
||||
vis_filename = "py_tree.png"
|
||||
vis_path = os.path.join(self.vis_dir, vis_filename)
|
||||
simple_node = {
|
||||
"type": "action",
|
||||
"name": pytree_dict.get('action', {}).get('name', 'action'),
|
||||
"params": pytree_dict.get('action', {}).get('params', {})
|
||||
}
|
||||
_visualize_pytree(simple_node, os.path.splitext(vis_path)[0])
|
||||
# 简单模式的root节点就是action节点,直接使用
|
||||
root_node = pytree_dict.get('root', {})
|
||||
_visualize_pytree(root_node, os.path.splitext(vis_path)[0])
|
||||
pytree_dict['visualization_url'] = f"/static/{vis_filename}"
|
||||
except Exception as e:
|
||||
logging.warning(f"简单模式可视化失败: {e}")
|
||||
@@ -820,46 +758,34 @@ class PyTreeGenerator:
|
||||
pytree_dict['final_prompt'] = final_prompt
|
||||
return pytree_dict
|
||||
|
||||
# 复杂模式回退:若模型误返回简单结构,则自动包装为含安全监控的行为树
|
||||
if mode == "complex" and isinstance(pytree_dict, dict) and 'root' not in pytree_dict:
|
||||
try:
|
||||
jsonschema.validate(instance=pytree_dict, schema=self.simple_schema)
|
||||
logging.warning("⚠️ 复杂模式生成了简单结构,触发自动包装为完整行为树的回退逻辑。")
|
||||
simple_action_obj = pytree_dict.get('action') or {}
|
||||
action_name = simple_action_obj.get('name')
|
||||
action_params = simple_action_obj.get('params') if isinstance(simple_action_obj.get('params'), dict) else {}
|
||||
# 复杂模式回退:若模型误返回简单结构(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"}})
|
||||
|
||||
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
|
||||
root_parallel = {
|
||||
"type": "Parallel",
|
||||
"name": "Mission",
|
||||
"params": {"policy": "all_success"},
|
||||
"children": [
|
||||
{"type": "Sequence", "name": "MainTask", "children": main_children}
|
||||
]
|
||||
}
|
||||
pytree_dict = {"root": root_parallel}
|
||||
except jsonschema.ValidationError:
|
||||
# 不符合简单结构,按正常复杂验证继续
|
||||
pass
|
||||
if _validate_pytree_with_schema(pytree_dict, self.schema):
|
||||
logging.info("✅ 成功生成并验证了Pytree")
|
||||
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