任务开关
This commit is contained in:
@@ -1247,6 +1247,15 @@ def api_mission_start():
|
||||
"""开始执行任务(V3: M×N Grid 蛇形路径)"""
|
||||
data = request.json or {}
|
||||
single_step = bool(data.get("single_step", False))
|
||||
# 任务步骤控制开关
|
||||
options = {
|
||||
"arm_init": bool(data.get("arm_init", True)),
|
||||
"agv_move": bool(data.get("agv_move", True)),
|
||||
"qr_scan": bool(data.get("qr_scan", True)),
|
||||
"front_photo": bool(data.get("front_photo", True)),
|
||||
"back_photo": bool(data.get("back_photo", True)),
|
||||
}
|
||||
print(f"[Mission] options: {options}")
|
||||
|
||||
# 清除可能存在的旧实例,确保可以启动
|
||||
if hasattr(MissionExecutorV3, "_instance"):
|
||||
@@ -1282,6 +1291,7 @@ def api_mission_start():
|
||||
qr_configs=gs.qr_config,
|
||||
models=models_list,
|
||||
single_step=single_step,
|
||||
options=options,
|
||||
)
|
||||
gs.mission_report = report
|
||||
executor.disconnect_all()
|
||||
|
||||
@@ -920,3 +920,71 @@ a:hover { text-decoration: underline; }
|
||||
gap: 16px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
/* ========== 任务步骤控制开关 ========== */
|
||||
.toggle-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
.toggle-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 10px 14px;
|
||||
background: #0a0a0a;
|
||||
border: 1px solid #1a1a1a;
|
||||
border-radius: 8px;
|
||||
transition: border-color 0.2s;
|
||||
}
|
||||
.toggle-item:hover {
|
||||
border-color: #333;
|
||||
}
|
||||
.toggle-switch {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 44px;
|
||||
height: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.toggle-switch input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.toggle-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0; left: 0; right: 0; bottom: 0;
|
||||
background-color: #333;
|
||||
border-radius: 24px;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.toggle-slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 3px;
|
||||
bottom: 3px;
|
||||
background-color: #888;
|
||||
border-radius: 50%;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.toggle-switch input:checked + .toggle-slider {
|
||||
background-color: #2e7d32;
|
||||
}
|
||||
.toggle-switch input:checked + .toggle-slider:before {
|
||||
transform: translateX(20px);
|
||||
background-color: #4caf50;
|
||||
}
|
||||
.toggle-label {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #e0e0e0;
|
||||
min-width: 140px;
|
||||
}
|
||||
.toggle-hint {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@ createApp({
|
||||
errorMsg: '',
|
||||
waitingStep: false,
|
||||
stepLabel: '',
|
||||
// 任务步骤控制开关
|
||||
armInitEnabled: true,
|
||||
agvMoveEnabled: true,
|
||||
qrScanEnabled: true,
|
||||
frontPhotoEnabled: true,
|
||||
backPhotoEnabled: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -114,7 +120,13 @@ createApp({
|
||||
await fetch(API + '/api/mission/start', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({})
|
||||
body: JSON.stringify({
|
||||
arm_init: this.armInitEnabled,
|
||||
agv_move: this.agvMoveEnabled,
|
||||
qr_scan: this.qrScanEnabled,
|
||||
front_photo: this.frontPhotoEnabled,
|
||||
back_photo: this.backPhotoEnabled,
|
||||
})
|
||||
})
|
||||
this.missionState = 'running'
|
||||
},
|
||||
|
||||
@@ -48,6 +48,54 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 任务步骤控制开关 -->
|
||||
<section class="card">
|
||||
<h2>🎛️ 任务步骤控制</h2>
|
||||
<p class="hint" style="margin-bottom:12px">关闭的步骤将在本次任务中跳过</p>
|
||||
<div class="toggle-grid">
|
||||
<div class="toggle-item">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" v-model="armInitEnabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label">🦾 机械臂位置初始化</span>
|
||||
<span class="toggle-hint">移动到每个点位前恢复初始姿态</span>
|
||||
</div>
|
||||
<div class="toggle-item">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" v-model="agvMoveEnabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label">🚗 AGV移动</span>
|
||||
<span class="toggle-hint">按之字形路线移动到各点位</span>
|
||||
</div>
|
||||
<div class="toggle-item">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" v-model="qrScanEnabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label">📷 识别二维码</span>
|
||||
<span class="toggle-hint">调整机械臂姿态扫描二维码</span>
|
||||
</div>
|
||||
<div class="toggle-item">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" v-model="frontPhotoEnabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label">📸 拍正面照</span>
|
||||
<span class="toggle-hint">按机型正面姿态拍照</span>
|
||||
</div>
|
||||
<div class="toggle-item">
|
||||
<label class="toggle-switch">
|
||||
<input type="checkbox" v-model="backPhotoEnabled">
|
||||
<span class="toggle-slider"></span>
|
||||
</label>
|
||||
<span class="toggle-label">📸 拍背面照</span>
|
||||
<span class="toggle-hint">按机型背面姿态拍照</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 任务清单 -->
|
||||
<section class="card" v-if="tasks.length > 0">
|
||||
<h2>📋 任务清单 ([[ tasks.length ]] 台机器)</h2>
|
||||
|
||||
@@ -124,10 +124,22 @@ class MissionExecutorV3:
|
||||
qr_configs: list,
|
||||
models: list,
|
||||
single_step: bool = False,
|
||||
options: dict = None,
|
||||
) -> dict:
|
||||
"""
|
||||
执行完整拍摄任务。
|
||||
|
||||
Args:
|
||||
options: 任务步骤控制开关
|
||||
- arm_init: 是否执行机械臂位置初始化
|
||||
- agv_move: 是否执行AGV移动
|
||||
- qr_scan: 是否执行二维码识别
|
||||
- front_photo: 是否执行正面拍照
|
||||
- back_photo: 是否执行背面拍照
|
||||
"""
|
||||
"""
|
||||
执行完整拍摄任务。
|
||||
|
||||
Args:
|
||||
mission_config: {rows, cols, grid, positions}
|
||||
machines: [{id, row, col, front: {coords}, back: {coords}}]
|
||||
@@ -187,6 +199,17 @@ class MissionExecutorV3:
|
||||
"photos_back": 0,
|
||||
} for (r, c) in path]
|
||||
|
||||
# 任务步骤控制开关
|
||||
if options is None:
|
||||
options = {}
|
||||
opt_arm_init = options.get("arm_init", True)
|
||||
opt_agv_move = options.get("agv_move", True)
|
||||
opt_qr_scan = options.get("qr_scan", True)
|
||||
opt_front_photo = options.get("front_photo", True)
|
||||
opt_back_photo = options.get("back_photo", True)
|
||||
self._log(f"⚙️ 任务步骤: arm_init={opt_arm_init}, agv_move={opt_agv_move}, "
|
||||
f"qr_scan={opt_qr_scan}, front={opt_front_photo}, back={opt_back_photo}")
|
||||
|
||||
# 机械臂初始姿态(AGV 移动前恢复)
|
||||
arm_initial_pose = mission_config.get("arm_initial_pose", [0.0] * 6)
|
||||
has_arm_pose = self.arm_client and any(abs(a) > 0.01 for a in arm_initial_pose)
|
||||
@@ -203,10 +226,12 @@ class MissionExecutorV3:
|
||||
rl, cl = r + 1, c + 1
|
||||
|
||||
# 恢复机械臂初始姿态
|
||||
if has_arm_pose:
|
||||
if opt_arm_init and has_arm_pose:
|
||||
self._log(" 🦾 恢复机械臂初始姿态")
|
||||
self.arm_client.set_angles(arm_initial_pose, speed=500)
|
||||
time.sleep(2)
|
||||
elif not opt_arm_init:
|
||||
self._log(" ⏭️ 跳过机械臂位置初始化")
|
||||
|
||||
# 更新任务状态 → 正面开始
|
||||
task = self._get_task(r, c)
|
||||
@@ -226,20 +251,27 @@ class MissionExecutorV3:
|
||||
self._step(f"机器 {rl}-{cl} 正面")
|
||||
|
||||
# 导航到正面点位
|
||||
front_pt = self._find_point(positions, r, c, "front")
|
||||
if front_pt and self._has_coords(front_pt):
|
||||
if not self._navigate(front_pt, "正面"):
|
||||
self._log(f"⚠️ 导航失败,尝试继续")
|
||||
choice = self._wait_error(f"机器 {rl}-{cl} 正面导航失败")
|
||||
if choice == "abort":
|
||||
break
|
||||
if opt_agv_move:
|
||||
front_pt = self._find_point(positions, r, c, "front")
|
||||
if front_pt and self._has_coords(front_pt):
|
||||
if not self._navigate(front_pt, "正面"):
|
||||
self._log(f"⚠️ 导航失败,尝试继续")
|
||||
choice = self._wait_error(f"机器 {rl}-{cl} 正面导航失败")
|
||||
if choice == "abort":
|
||||
break
|
||||
else:
|
||||
self._log(f"⚠️ 无正面点位坐标")
|
||||
else:
|
||||
self._log(f"⚠️ 无正面点位坐标")
|
||||
self._log(" ⏭️ 跳过AGV移动(正面)")
|
||||
|
||||
# 扫描二维码
|
||||
qr_value = self._scan_qr_with_poses(qr_configs)
|
||||
if self._stop.is_set():
|
||||
break
|
||||
qr_value = None
|
||||
if opt_qr_scan:
|
||||
qr_value = self._scan_qr_with_poses(qr_configs)
|
||||
if self._stop.is_set():
|
||||
break
|
||||
else:
|
||||
self._log(" ⏭️ 跳过二维码识别")
|
||||
|
||||
# 查机型 + 更新任务步骤
|
||||
model_name = self._lookup_model(qr_value)
|
||||
@@ -250,11 +282,14 @@ class MissionExecutorV3:
|
||||
task["step"] = "正面拍照"
|
||||
|
||||
# 正面拍照
|
||||
model = self._find_model(models, model_name)
|
||||
if model:
|
||||
self._shoot(model, "front", rl, cl, qr_value or "unknown")
|
||||
if opt_front_photo:
|
||||
model = self._find_model(models, model_name)
|
||||
if model:
|
||||
self._shoot(model, "front", rl, cl, qr_value or "unknown")
|
||||
else:
|
||||
self._log(f" ⚠️ 未找到机型 {model_name}")
|
||||
else:
|
||||
self._log(f" ⚠️ 未找到机型 {model_name}")
|
||||
self._log(" ⏭️ 跳过正面拍照")
|
||||
|
||||
self._progress(machine_idx, 1)
|
||||
|
||||
@@ -265,19 +300,25 @@ class MissionExecutorV3:
|
||||
self._step(f"机器 {rl}-{cl} 背面")
|
||||
|
||||
# 导航到背面点位
|
||||
back_pt = self._find_point(positions, r + 1, c, "back")
|
||||
if back_pt and self._has_coords(back_pt):
|
||||
if not self._navigate(back_pt, "背面"):
|
||||
self._log(f"⚠️ 导航失败,尝试继续")
|
||||
choice = self._wait_error(f"机器 {rl}-{cl} 背面导航失败")
|
||||
if choice == "abort":
|
||||
break
|
||||
if opt_agv_move:
|
||||
back_pt = self._find_point(positions, r + 1, c, "back")
|
||||
if back_pt and self._has_coords(back_pt):
|
||||
if not self._navigate(back_pt, "背面"):
|
||||
self._log(f"⚠️ 导航失败,尝试继续")
|
||||
choice = self._wait_error(f"机器 {rl}-{cl} 背面导航失败")
|
||||
if choice == "abort":
|
||||
break
|
||||
else:
|
||||
self._log(f"⚠️ 无背面点位坐标")
|
||||
else:
|
||||
self._log(f"⚠️ 无背面点位坐标")
|
||||
self._log(" ⏭️ 跳过AGV移动(背面)")
|
||||
|
||||
# 背面拍照
|
||||
if model:
|
||||
self._shoot(model, "back", rl, cl, qr_value or "unknown")
|
||||
if opt_back_photo:
|
||||
if model:
|
||||
self._shoot(model, "back", rl, cl, qr_value or "unknown")
|
||||
else:
|
||||
self._log(" ⏭️ 跳过背面拍照")
|
||||
|
||||
# 标记任务完成
|
||||
if task:
|
||||
@@ -300,10 +341,12 @@ class MissionExecutorV3:
|
||||
machine_idx += 1
|
||||
|
||||
# 3. 回到出发点
|
||||
if not self._stop.is_set():
|
||||
if not self._stop.is_set() and opt_agv_move:
|
||||
self._step("返回出发点")
|
||||
self._log("→ 返回 (0, 0)")
|
||||
self._nav2_go_to_point(0, 0, 0, timeout_sec=60)
|
||||
elif not self._stop.is_set():
|
||||
self._log("⏭️ 跳过返回出发点")
|
||||
|
||||
elapsed = time.time() - start_time
|
||||
return self._finish(elapsed)
|
||||
|
||||
Reference in New Issue
Block a user