任务开关
This commit is contained in:
@@ -1247,6 +1247,15 @@ def api_mission_start():
|
|||||||
"""开始执行任务(V3: M×N Grid 蛇形路径)"""
|
"""开始执行任务(V3: M×N Grid 蛇形路径)"""
|
||||||
data = request.json or {}
|
data = request.json or {}
|
||||||
single_step = bool(data.get("single_step", False))
|
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"):
|
if hasattr(MissionExecutorV3, "_instance"):
|
||||||
@@ -1282,6 +1291,7 @@ def api_mission_start():
|
|||||||
qr_configs=gs.qr_config,
|
qr_configs=gs.qr_config,
|
||||||
models=models_list,
|
models=models_list,
|
||||||
single_step=single_step,
|
single_step=single_step,
|
||||||
|
options=options,
|
||||||
)
|
)
|
||||||
gs.mission_report = report
|
gs.mission_report = report
|
||||||
executor.disconnect_all()
|
executor.disconnect_all()
|
||||||
|
|||||||
@@ -920,3 +920,71 @@ a:hover { text-decoration: underline; }
|
|||||||
gap: 16px;
|
gap: 16px;
|
||||||
margin-top: 12px;
|
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: '',
|
errorMsg: '',
|
||||||
waitingStep: false,
|
waitingStep: false,
|
||||||
stepLabel: '',
|
stepLabel: '',
|
||||||
|
// 任务步骤控制开关
|
||||||
|
armInitEnabled: true,
|
||||||
|
agvMoveEnabled: true,
|
||||||
|
qrScanEnabled: true,
|
||||||
|
frontPhotoEnabled: true,
|
||||||
|
backPhotoEnabled: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
@@ -114,7 +120,13 @@ createApp({
|
|||||||
await fetch(API + '/api/mission/start', {
|
await fetch(API + '/api/mission/start', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
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'
|
this.missionState = 'running'
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -48,6 +48,54 @@
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</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">
|
<section class="card" v-if="tasks.length > 0">
|
||||||
<h2>📋 任务清单 ([[ tasks.length ]] 台机器)</h2>
|
<h2>📋 任务清单 ([[ tasks.length ]] 台机器)</h2>
|
||||||
|
|||||||
@@ -124,10 +124,22 @@ class MissionExecutorV3:
|
|||||||
qr_configs: list,
|
qr_configs: list,
|
||||||
models: list,
|
models: list,
|
||||||
single_step: bool = False,
|
single_step: bool = False,
|
||||||
|
options: dict = None,
|
||||||
) -> dict:
|
) -> dict:
|
||||||
"""
|
"""
|
||||||
执行完整拍摄任务。
|
执行完整拍摄任务。
|
||||||
|
|
||||||
|
Args:
|
||||||
|
options: 任务步骤控制开关
|
||||||
|
- arm_init: 是否执行机械臂位置初始化
|
||||||
|
- agv_move: 是否执行AGV移动
|
||||||
|
- qr_scan: 是否执行二维码识别
|
||||||
|
- front_photo: 是否执行正面拍照
|
||||||
|
- back_photo: 是否执行背面拍照
|
||||||
|
"""
|
||||||
|
"""
|
||||||
|
执行完整拍摄任务。
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
mission_config: {rows, cols, grid, positions}
|
mission_config: {rows, cols, grid, positions}
|
||||||
machines: [{id, row, col, front: {coords}, back: {coords}}]
|
machines: [{id, row, col, front: {coords}, back: {coords}}]
|
||||||
@@ -187,6 +199,17 @@ class MissionExecutorV3:
|
|||||||
"photos_back": 0,
|
"photos_back": 0,
|
||||||
} for (r, c) in path]
|
} 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 移动前恢复)
|
# 机械臂初始姿态(AGV 移动前恢复)
|
||||||
arm_initial_pose = mission_config.get("arm_initial_pose", [0.0] * 6)
|
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)
|
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
|
rl, cl = r + 1, c + 1
|
||||||
|
|
||||||
# 恢复机械臂初始姿态
|
# 恢复机械臂初始姿态
|
||||||
if has_arm_pose:
|
if opt_arm_init and has_arm_pose:
|
||||||
self._log(" 🦾 恢复机械臂初始姿态")
|
self._log(" 🦾 恢复机械臂初始姿态")
|
||||||
self.arm_client.set_angles(arm_initial_pose, speed=500)
|
self.arm_client.set_angles(arm_initial_pose, speed=500)
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
elif not opt_arm_init:
|
||||||
|
self._log(" ⏭️ 跳过机械臂位置初始化")
|
||||||
|
|
||||||
# 更新任务状态 → 正面开始
|
# 更新任务状态 → 正面开始
|
||||||
task = self._get_task(r, c)
|
task = self._get_task(r, c)
|
||||||
@@ -226,6 +251,7 @@ class MissionExecutorV3:
|
|||||||
self._step(f"机器 {rl}-{cl} 正面")
|
self._step(f"机器 {rl}-{cl} 正面")
|
||||||
|
|
||||||
# 导航到正面点位
|
# 导航到正面点位
|
||||||
|
if opt_agv_move:
|
||||||
front_pt = self._find_point(positions, r, c, "front")
|
front_pt = self._find_point(positions, r, c, "front")
|
||||||
if front_pt and self._has_coords(front_pt):
|
if front_pt and self._has_coords(front_pt):
|
||||||
if not self._navigate(front_pt, "正面"):
|
if not self._navigate(front_pt, "正面"):
|
||||||
@@ -235,11 +261,17 @@ class MissionExecutorV3:
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self._log(f"⚠️ 无正面点位坐标")
|
self._log(f"⚠️ 无正面点位坐标")
|
||||||
|
else:
|
||||||
|
self._log(" ⏭️ 跳过AGV移动(正面)")
|
||||||
|
|
||||||
# 扫描二维码
|
# 扫描二维码
|
||||||
|
qr_value = None
|
||||||
|
if opt_qr_scan:
|
||||||
qr_value = self._scan_qr_with_poses(qr_configs)
|
qr_value = self._scan_qr_with_poses(qr_configs)
|
||||||
if self._stop.is_set():
|
if self._stop.is_set():
|
||||||
break
|
break
|
||||||
|
else:
|
||||||
|
self._log(" ⏭️ 跳过二维码识别")
|
||||||
|
|
||||||
# 查机型 + 更新任务步骤
|
# 查机型 + 更新任务步骤
|
||||||
model_name = self._lookup_model(qr_value)
|
model_name = self._lookup_model(qr_value)
|
||||||
@@ -250,11 +282,14 @@ class MissionExecutorV3:
|
|||||||
task["step"] = "正面拍照"
|
task["step"] = "正面拍照"
|
||||||
|
|
||||||
# 正面拍照
|
# 正面拍照
|
||||||
|
if opt_front_photo:
|
||||||
model = self._find_model(models, model_name)
|
model = self._find_model(models, model_name)
|
||||||
if model:
|
if model:
|
||||||
self._shoot(model, "front", rl, cl, qr_value or "unknown")
|
self._shoot(model, "front", rl, cl, qr_value or "unknown")
|
||||||
else:
|
else:
|
||||||
self._log(f" ⚠️ 未找到机型 {model_name}")
|
self._log(f" ⚠️ 未找到机型 {model_name}")
|
||||||
|
else:
|
||||||
|
self._log(" ⏭️ 跳过正面拍照")
|
||||||
|
|
||||||
self._progress(machine_idx, 1)
|
self._progress(machine_idx, 1)
|
||||||
|
|
||||||
@@ -265,6 +300,7 @@ class MissionExecutorV3:
|
|||||||
self._step(f"机器 {rl}-{cl} 背面")
|
self._step(f"机器 {rl}-{cl} 背面")
|
||||||
|
|
||||||
# 导航到背面点位
|
# 导航到背面点位
|
||||||
|
if opt_agv_move:
|
||||||
back_pt = self._find_point(positions, r + 1, c, "back")
|
back_pt = self._find_point(positions, r + 1, c, "back")
|
||||||
if back_pt and self._has_coords(back_pt):
|
if back_pt and self._has_coords(back_pt):
|
||||||
if not self._navigate(back_pt, "背面"):
|
if not self._navigate(back_pt, "背面"):
|
||||||
@@ -274,10 +310,15 @@ class MissionExecutorV3:
|
|||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
self._log(f"⚠️ 无背面点位坐标")
|
self._log(f"⚠️ 无背面点位坐标")
|
||||||
|
else:
|
||||||
|
self._log(" ⏭️ 跳过AGV移动(背面)")
|
||||||
|
|
||||||
# 背面拍照
|
# 背面拍照
|
||||||
|
if opt_back_photo:
|
||||||
if model:
|
if model:
|
||||||
self._shoot(model, "back", rl, cl, qr_value or "unknown")
|
self._shoot(model, "back", rl, cl, qr_value or "unknown")
|
||||||
|
else:
|
||||||
|
self._log(" ⏭️ 跳过背面拍照")
|
||||||
|
|
||||||
# 标记任务完成
|
# 标记任务完成
|
||||||
if task:
|
if task:
|
||||||
@@ -300,10 +341,12 @@ class MissionExecutorV3:
|
|||||||
machine_idx += 1
|
machine_idx += 1
|
||||||
|
|
||||||
# 3. 回到出发点
|
# 3. 回到出发点
|
||||||
if not self._stop.is_set():
|
if not self._stop.is_set() and opt_agv_move:
|
||||||
self._step("返回出发点")
|
self._step("返回出发点")
|
||||||
self._log("→ 返回 (0, 0)")
|
self._log("→ 返回 (0, 0)")
|
||||||
self._nav2_go_to_point(0, 0, 0, timeout_sec=60)
|
self._nav2_go_to_point(0, 0, 0, timeout_sec=60)
|
||||||
|
elif not self._stop.is_set():
|
||||||
|
self._log("⏭️ 跳过返回出发点")
|
||||||
|
|
||||||
elapsed = time.time() - start_time
|
elapsed = time.time() - start_time
|
||||||
return self._finish(elapsed)
|
return self._finish(elapsed)
|
||||||
|
|||||||
Reference in New Issue
Block a user