Update mission flow and inspection log scrolling
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## 项目结构与模块组织
|
||||
|
||||
本仓库是 AGV 智能巡检系统,后端、前端、ROS2 启动链路分目录维护。`agv_app/` 是 Flask 后端,包含 API、模板、静态资源与 `data/*.json` 持久化配置;`agv_app/utils/` 放置 AGV、Nav2、机械臂、二维码与任务执行逻辑。`arm_server/` 是机械臂端 TCP/摄像头服务及 systemd 配置。`scan_fixer/` 提供生产链路中的 ROS2 时间戳修正工具。`public-frontend/` 是 Next.js 平板端,源码在 `src/app`、`src/components`、`src/services`、`src/store`、`src/types`。`scripts/` 与 `Makefile` 负责本地开发、生产启动和停止流程,`docs/` 存放技术文档。
|
||||
|
||||
## 构建、测试与开发命令
|
||||
|
||||
- `make install`:执行 `uv sync` 并安装前端 npm 依赖。
|
||||
- `make dev`:显示本地开发启动说明。
|
||||
- `make dev-backend`:以 Mock 硬件模式启动 Flask 后端。
|
||||
- `make dev-frontend`:启动 Next.js 开发服务器。
|
||||
- `make prod`:在 AGV 上启动 ROS2、Nav2、scan fixer 与 Flask 完整生产链路。
|
||||
- `cd public-frontend && npm run build`:构建前端。
|
||||
- `cd public-frontend && npm run lint && npm run typecheck`:执行前端 lint 与 TypeScript 检查。
|
||||
|
||||
## 编码风格与命名约定
|
||||
|
||||
Python 目标版本为 3.10,依赖由根目录 `pyproject.toml` 和 `uv.lock` 管理。保持模块职责清晰,配置集中放在 `agv_app/config.py`,复用硬件抽象时优先放入 `agv_app/utils/`。前端使用 TypeScript、React、Next.js App Router 与 Ant Design;组件使用 PascalCase,例如 `CameraFrame.tsx`,服务和工具使用 camelCase,例如 `apiClient.ts`。新增代码应自解释命名,避免魔术值,必要注释只说明业务背景或非常规取舍。
|
||||
|
||||
## 测试指南
|
||||
|
||||
当前仓库未配置独立单元测试框架。提交前至少运行相关静态检查:前端改动运行 `npm run lint` 和 `npm run typecheck`,后端改动用 `make dev-backend` 做 Flask 启动与关键接口冒烟验证。涉及硬件、ROS2 或生产脚本的改动,应说明是否已在真实 AGV 或 Mock 模式验证。
|
||||
|
||||
## 提交与 Pull Request 规范
|
||||
|
||||
历史提交主要使用英文祈使句式,例如 `Improve camera status and production startup`、`Refactor ROS startup scripts`、`Fix shell compatibility issues in prod-backend.sh`。继续使用简短英文提交信息,避免无意义占位。PR 应包含变更摘要、验证命令、硬件/环境影响、相关 issue;前端界面变化需附截图或录屏,生产启动链路变化需列出影响的脚本和服务。
|
||||
|
||||
## 安全与配置提示
|
||||
|
||||
不要提交新的密钥、令牌、设备私有地址或本地日志。环境示例放在 `.env.example`,运行时覆盖优先使用环境变量,例如 `BACKEND_URL`、`MOCK_HARDWARE`、`AGV_PROJECT_DIR`。涉及 AGV、机械臂或 ROS2 生产配置时,先核对 `scripts/README.md` 中的默认路径与日志位置。
|
||||
+81
-4
@@ -46,6 +46,15 @@ logging.basicConfig(
|
||||
)
|
||||
logger = logging.getLogger("agv_app")
|
||||
|
||||
ORIGIN_X = 0.0
|
||||
ORIGIN_Y = 0.0
|
||||
ORIGIN_YAW = 0.0
|
||||
ORIGIN_MATCH_TOLERANCE = 1e-6
|
||||
ARM_RETURN_SPEED = 500
|
||||
ARM_INITIAL_POSE_TIMEOUT = 15.0
|
||||
ARM_INITIAL_POSE_TOLERANCE = 2.0
|
||||
ARM_INITIAL_POSE_UNSET_TOLERANCE = 0.01
|
||||
|
||||
app = Flask(__name__, template_folder="templates", static_folder="static", static_url_path="/static")
|
||||
app.config["SECRET_KEY"] = SERVER_CONFIG["secret_key"]
|
||||
CORS(app)
|
||||
@@ -88,6 +97,60 @@ gs = GlobalState()
|
||||
|
||||
|
||||
# ========== 辅助函数 ==========
|
||||
def _is_origin_goal(x: float, y: float, yaw: float = None) -> bool:
|
||||
yaw_is_origin = yaw is None or abs(yaw - ORIGIN_YAW) <= ORIGIN_MATCH_TOLERANCE
|
||||
return (
|
||||
abs(x - ORIGIN_X) <= ORIGIN_MATCH_TOLERANCE
|
||||
and abs(y - ORIGIN_Y) <= ORIGIN_MATCH_TOLERANCE
|
||||
and yaw_is_origin
|
||||
)
|
||||
|
||||
|
||||
def _normalize_arm_pose(raw_pose):
|
||||
if not isinstance(raw_pose, list) or len(raw_pose) != 6:
|
||||
return None
|
||||
try:
|
||||
return [float(angle) for angle in raw_pose]
|
||||
except (TypeError, ValueError):
|
||||
return None
|
||||
|
||||
|
||||
def _wait_arm_initial_pose(target_angles, timeout=ARM_INITIAL_POSE_TIMEOUT, tolerance=ARM_INITIAL_POSE_TOLERANCE):
|
||||
deadline = time.time() + timeout
|
||||
while time.time() < deadline:
|
||||
try:
|
||||
ok, current_angles = gs.arm_client.get_angles()
|
||||
if ok and current_angles and len(current_angles) >= 6:
|
||||
current_angles = [float(angle) for angle in current_angles[:6]]
|
||||
if all(abs(current_angles[index] - target_angles[index]) <= tolerance for index in range(6)):
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.warning(f"等待机械臂初始姿态失败: {e}")
|
||||
time.sleep(0.5)
|
||||
return False
|
||||
|
||||
|
||||
def _restore_arm_initial_pose_for_origin():
|
||||
arm_initial_pose = _normalize_arm_pose(gs.mission_config.get("arm_initial_pose"))
|
||||
if not arm_initial_pose:
|
||||
return {"ok": True, "skipped": True, "message": "未配置有效机械臂初始姿态,跳过复原"}
|
||||
if not any(abs(angle) > ARM_INITIAL_POSE_UNSET_TOLERANCE for angle in arm_initial_pose):
|
||||
return {"ok": True, "skipped": True, "message": "未配置机械臂初始姿态,跳过复原"}
|
||||
if not gs.arm_client:
|
||||
return {"ok": False, "error": "机械臂未连接,无法先复原再回原点"}
|
||||
|
||||
try:
|
||||
ok = gs.arm_client.set_angles(arm_initial_pose, ARM_RETURN_SPEED)
|
||||
if not ok:
|
||||
return {"ok": False, "error": "机械臂初始姿态指令发送失败,已取消回原点"}
|
||||
if not _wait_arm_initial_pose(arm_initial_pose):
|
||||
return {"ok": False, "error": "机械臂未在规定时间内复原,已取消回原点"}
|
||||
return {"ok": True, "skipped": False, "message": "机械臂已复原到初始姿态"}
|
||||
except Exception as e:
|
||||
logger.error(f"回原点前复原机械臂失败: {e}")
|
||||
return {"ok": False, "error": f"回原点前复原机械臂失败: {e}"}
|
||||
|
||||
|
||||
def get_data_path(name):
|
||||
return os.path.join(DATA_DIR, name)
|
||||
|
||||
@@ -464,24 +527,38 @@ def api_navigate_to():
|
||||
if not gs.map_config or "map_yaml" not in gs.map_config:
|
||||
return jsonify({"ok": False, "error": "地图未加载,请先在设置中加载地图"}), 400
|
||||
|
||||
data = request.json
|
||||
data = request.json or {}
|
||||
goal_x = data.get("x")
|
||||
goal_y = data.get("y")
|
||||
goal_yaw = data.get("yaw") # 姿态参数,可选
|
||||
if goal_x is None or goal_y is None:
|
||||
return jsonify({"ok": False, "error": "缺少目标坐标 x, y"}), 400
|
||||
try:
|
||||
goal_x = float(goal_x)
|
||||
goal_y = float(goal_y)
|
||||
yaw_arg = float(goal_yaw) if goal_yaw is not None else None
|
||||
except (TypeError, ValueError):
|
||||
return jsonify({"ok": False, "error": "目标坐标格式错误"}), 400
|
||||
|
||||
if not gs.agv_controller or not gs.agv_controller.is_connected():
|
||||
return jsonify({"ok": False, "error": "AGV 未连接,请先连接 AGV"}), 400
|
||||
|
||||
try:
|
||||
restore_message = ""
|
||||
restore_arm = bool(data.get("restore_arm", True))
|
||||
if restore_arm and _is_origin_goal(goal_x, goal_y, yaw_arg):
|
||||
restore_result = _restore_arm_initial_pose_for_origin()
|
||||
if not restore_result["ok"]:
|
||||
return jsonify({"ok": False, "error": restore_result["error"]}), 400
|
||||
restore_message = restore_result.get("message", "")
|
||||
|
||||
if gs.navigator is None:
|
||||
gs.navigator = Nav2Navigator()
|
||||
# navigate_to_pose(x, y, yaw=None, timeout_sec=120, blocking=False)
|
||||
yaw_arg = float(goal_yaw) if goal_yaw is not None else None
|
||||
ok = gs.navigator.navigate_to_pose(float(goal_x), float(goal_y), yaw_arg, blocking=False)
|
||||
ok = gs.navigator.navigate_to_pose(goal_x, goal_y, yaw_arg, blocking=False)
|
||||
if ok:
|
||||
return jsonify({"ok": True, "message": "导航已启动"})
|
||||
message = f"{restore_message},导航已启动" if restore_message else "导航已启动"
|
||||
return jsonify({"ok": True, "message": message})
|
||||
else:
|
||||
return jsonify({"ok": False, "error": "导航启动失败,可能是Nav2未运行或AGV未连接"}), 400
|
||||
except Exception as e:
|
||||
|
||||
+212
-146
@@ -1,25 +1,4 @@
|
||||
[
|
||||
{
|
||||
"id": "m_2_0",
|
||||
"row": 2,
|
||||
"col": 0,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_2_2",
|
||||
"row": 2,
|
||||
@@ -167,27 +146,6 @@
|
||||
"poses": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_0_4",
|
||||
"row": 0,
|
||||
"col": 4,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_0_5",
|
||||
"row": 0,
|
||||
@@ -251,27 +209,6 @@
|
||||
"poses": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_2_1",
|
||||
"row": 2,
|
||||
"col": 1,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_2_4",
|
||||
"row": 2,
|
||||
@@ -293,27 +230,6 @@
|
||||
"poses": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_2_5",
|
||||
"row": 2,
|
||||
"col": 5,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_0_0",
|
||||
"row": 0,
|
||||
@@ -356,36 +272,6 @@
|
||||
"poses": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_0_1",
|
||||
"row": 0,
|
||||
"col": 1,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"qr": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"qr_value": "",
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_0_2",
|
||||
"row": 0,
|
||||
@@ -416,36 +302,6 @@
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_0_3",
|
||||
"row": 0,
|
||||
"col": 3,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"qr": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"qr_value": "",
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_1_1",
|
||||
"row": 1,
|
||||
@@ -506,6 +362,126 @@
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_1_4",
|
||||
"row": 1,
|
||||
"col": 4,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"qr": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"qr_value": "",
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_0_6",
|
||||
"row": 0,
|
||||
"col": 6,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"qr": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"qr_value": "",
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_1_6",
|
||||
"row": 1,
|
||||
"col": 6,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"qr": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"qr_value": "",
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_0_3",
|
||||
"row": 0,
|
||||
"col": 3,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"qr": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"qr_value": "",
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_1_3",
|
||||
"row": 1,
|
||||
@@ -537,8 +513,98 @@
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_1_4",
|
||||
"row": 1,
|
||||
"id": "m_2_0",
|
||||
"row": 2,
|
||||
"col": 0,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"qr": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"qr_value": "",
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_2_1",
|
||||
"row": 2,
|
||||
"col": 1,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"qr": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"qr_value": "",
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_0_1",
|
||||
"row": 0,
|
||||
"col": 1,
|
||||
"front": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"back": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
"qr": {
|
||||
"coords": [
|
||||
0,
|
||||
0,
|
||||
0
|
||||
],
|
||||
"qr_value": "",
|
||||
"model_id": ""
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "m_0_4",
|
||||
"row": 0,
|
||||
"col": 4,
|
||||
"front": {
|
||||
"coords": [
|
||||
|
||||
+360
-140
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"rows": 2,
|
||||
"cols": 5,
|
||||
"rows": 3,
|
||||
"cols": 7,
|
||||
"grid": [],
|
||||
"positions": [
|
||||
{
|
||||
@@ -8,9 +8,9 @@
|
||||
"col": 0,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
0.5391402360519819,
|
||||
-1.3221212932804989,
|
||||
-0.04968159116162075
|
||||
0.726789462066099,
|
||||
0.38329959318460166,
|
||||
0.023383889548769105
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
@@ -19,9 +19,9 @@
|
||||
"col": 1,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
1.1801154538454173,
|
||||
-1.3641834306281595,
|
||||
-0.0384636372066124
|
||||
1.5997282224959652,
|
||||
0.3969445083875386,
|
||||
0.021611522117307245
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
@@ -30,9 +30,9 @@
|
||||
"col": 1,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
1.3273254588744863,
|
||||
-3.5287940020200854,
|
||||
-3.11993523836094
|
||||
1.5365709266996668,
|
||||
-1.5897806027940793,
|
||||
-3.138003565212702
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
@@ -41,108 +41,9 @@
|
||||
"col": 0,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
0.6499623251724095,
|
||||
-3.634895898964233,
|
||||
-3.06371982741706
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 0,
|
||||
"col": 2,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
1.9780660285152205,
|
||||
-1.4118225222055494,
|
||||
-0.03933461738640764
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 0,
|
||||
"col": 3,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
2.783104887196572,
|
||||
-1.4531680360293173,
|
||||
-0.005407493209801511
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 0,
|
||||
"col": 4,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
3.4135017183966694,
|
||||
-1.463517938299615,
|
||||
-0.0022379727318056074
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 4,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
3.595502564320599,
|
||||
-3.5861571623928663,
|
||||
3.105599537556842
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 4,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
3.595502564320599,
|
||||
-3.5861571623928663,
|
||||
3.105599537556842
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 3,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
2.8436692518324937,
|
||||
-3.5087893361886504,
|
||||
-3.0640151322957476
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 3,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
2.8436692518324937,
|
||||
-3.5087893361886504,
|
||||
-3.0640151322957476
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 2,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
2.0238357078548397,
|
||||
-3.519588818855445,
|
||||
-3.0949553553741684
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 2,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
2.0238357078548397,
|
||||
-3.519588818855445,
|
||||
-3.0949553553741684
|
||||
0.7250933954217442,
|
||||
-1.585626974855502,
|
||||
3.1407193246894285
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
@@ -151,9 +52,9 @@
|
||||
"col": 1,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
1.3273254588744863,
|
||||
-3.5287940020200854,
|
||||
-3.11993523836094
|
||||
1.5365709266996668,
|
||||
-1.5897806027940793,
|
||||
-3.138003565212702
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
@@ -162,9 +63,9 @@
|
||||
"col": 0,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
0.6499623251724095,
|
||||
-3.634895898964233,
|
||||
-3.06371982741706
|
||||
0.7250933954217442,
|
||||
-1.585626974855502,
|
||||
3.1407193246894285
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
@@ -173,9 +74,9 @@
|
||||
"col": 0,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
0.39025594509020667,
|
||||
-5.593393651151741,
|
||||
-0.09001000079607593
|
||||
0.695891857743624,
|
||||
-3.6093540626907155,
|
||||
-0.01891669546737457
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
@@ -184,9 +85,196 @@
|
||||
"col": 1,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
0.9989880876134289,
|
||||
-5.633047903271201,
|
||||
-0.08518177305398167
|
||||
1.7054640840200619,
|
||||
-3.6172190671922944,
|
||||
0.04050928996758301
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 0,
|
||||
"col": 2,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
2.569630979384912,
|
||||
0.39887714413011477,
|
||||
0.013956327040945097
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 0,
|
||||
"col": 3,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
3.5131599402572933,
|
||||
0.3616416504697895,
|
||||
0.0177534721641621
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 0,
|
||||
"col": 4,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
4.385626456624971,
|
||||
0.37358124345384475,
|
||||
0.014464186351950986
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 0,
|
||||
"col": 5,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
5.286651848873122,
|
||||
0.36270375923170595,
|
||||
-0.02478120105661721
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 0,
|
||||
"col": 6,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
6.301663107708812,
|
||||
0.35009837193686855,
|
||||
0.05028809910702322
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 6,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
6.302211902696101,
|
||||
-1.6741865723108142,
|
||||
-3.141294889035836
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 6,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
6.302211902696101,
|
||||
-1.6741865723108142,
|
||||
-3.141294889035836
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 5,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
5.385423949389957,
|
||||
-1.6569851054137088,
|
||||
3.114584306608913
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 5,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
5.385423949389957,
|
||||
-1.6569851054137088,
|
||||
3.114584306608913
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 4,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
4.520056315813734,
|
||||
-1.6370730446353792,
|
||||
3.12134578550089
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 4,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
4.520056315813734,
|
||||
-1.6370730446353792,
|
||||
3.12134578550089
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 3,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
3.600184234659078,
|
||||
-1.6299160740114962,
|
||||
-3.127182700330921
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 3,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
3.600184234659078,
|
||||
-1.6299160740114962,
|
||||
-3.127182700330921
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 2,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
2.5627723519921295,
|
||||
-1.606403204776104,
|
||||
3.1392697865149666
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 1,
|
||||
"col": 2,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
2.5627723519921295,
|
||||
-1.606403204776104,
|
||||
3.1392697865149666
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 2,
|
||||
"col": 0,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
0.695891857743624,
|
||||
-3.6093540626907155,
|
||||
-0.01891669546737457
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 2,
|
||||
"col": 1,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
1.7054640840200619,
|
||||
-3.6172190671922944,
|
||||
0.04050928996758301
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
@@ -195,9 +283,20 @@
|
||||
"col": 2,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
1.7493440267548326,
|
||||
-5.7036971258959746,
|
||||
-0.10505541857885684
|
||||
2.49138966620064,
|
||||
-3.6258193640543435,
|
||||
0.011734020269766346
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 2,
|
||||
"col": 2,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
2.49138966620064,
|
||||
-3.6258193640543435,
|
||||
0.011734020269766346
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
@@ -206,9 +305,20 @@
|
||||
"col": 3,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
2.407336669431407,
|
||||
-5.76958512207572,
|
||||
-0.10251877401062247
|
||||
3.384299605055195,
|
||||
-3.633059580331103,
|
||||
0.00825489611393585
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 2,
|
||||
"col": 3,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
3.384299605055195,
|
||||
-3.633059580331103,
|
||||
0.00825489611393585
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
@@ -217,19 +327,129 @@
|
||||
"col": 4,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
3.132062476985721,
|
||||
-5.850166571217611,
|
||||
-0.09127007182804729
|
||||
4.499614777619532,
|
||||
-3.6568143703418405,
|
||||
0.006128126167414207
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 2,
|
||||
"col": 4,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
4.499614777619532,
|
||||
-3.6568143703418405,
|
||||
0.006128126167414207
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 2,
|
||||
"col": 5,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
5.358194832629194,
|
||||
-3.6555884207351923,
|
||||
0.00220732555627522
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 2,
|
||||
"col": 5,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
5.358194832629194,
|
||||
-3.6555884207351923,
|
||||
0.00220732555627522
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 2,
|
||||
"col": 6,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
6.186931240051006,
|
||||
-3.660069561737178,
|
||||
-0.004740651362042846
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 2,
|
||||
"col": 6,
|
||||
"side": "front",
|
||||
"coords": [
|
||||
6.186931240051006,
|
||||
-3.660069561737178,
|
||||
-0.004740651362042846
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 3,
|
||||
"col": 4,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
4.437106056578727,
|
||||
-5.675385129487837,
|
||||
3.113166940596223
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 3,
|
||||
"col": 3,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
3.4577008440661285,
|
||||
-5.703184532839961,
|
||||
-3.109685273113602
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 3,
|
||||
"col": 2,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
2.3341924779097645,
|
||||
-5.623728684341702,
|
||||
-3.095440697434253
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 3,
|
||||
"col": 1,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
1.6126711501886855,
|
||||
-5.64506932868408,
|
||||
-3.0994397969930265
|
||||
],
|
||||
"poses": []
|
||||
},
|
||||
{
|
||||
"row": 3,
|
||||
"col": 0,
|
||||
"side": "back",
|
||||
"coords": [
|
||||
0.6321940488248773,
|
||||
-5.633649464598426,
|
||||
3.129000825841382
|
||||
],
|
||||
"poses": []
|
||||
}
|
||||
],
|
||||
"arm_initial_pose": [
|
||||
-90.333323,
|
||||
-90.079952,
|
||||
0.160037,
|
||||
-90.571318,
|
||||
0.093654,
|
||||
22.232898
|
||||
-89.999795,
|
||||
-89.999946,
|
||||
-0.000131,
|
||||
-90.00001,
|
||||
4.1e-05,
|
||||
0.000116
|
||||
]
|
||||
}
|
||||
@@ -9,12 +9,12 @@
|
||||
"name": "front_1",
|
||||
"photo_type": "front",
|
||||
"arm_angles": [
|
||||
-93.586541,
|
||||
-184.343305,
|
||||
50.583239,
|
||||
-38.326674,
|
||||
-85.153333,
|
||||
20.399989
|
||||
-81.796775,
|
||||
-85.406752,
|
||||
-5.803223,
|
||||
-109.799747,
|
||||
91.66639,
|
||||
2.446712
|
||||
],
|
||||
"speed": 500,
|
||||
"description": ""
|
||||
@@ -24,12 +24,12 @@
|
||||
"name": "back_1",
|
||||
"photo_type": "back",
|
||||
"arm_angles": [
|
||||
15.860045,
|
||||
-161.133416,
|
||||
137.999016,
|
||||
-161.996719,
|
||||
168.000989,
|
||||
15.653445
|
||||
-81.796823,
|
||||
-85.406717,
|
||||
-5.803284,
|
||||
-109.799953,
|
||||
91.666459,
|
||||
2.44676
|
||||
],
|
||||
"speed": 500,
|
||||
"description": ""
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
"id": "qr_1779278140334",
|
||||
"name": "二维码1",
|
||||
"joint_angles": [
|
||||
-89.796645,
|
||||
-2.013175,
|
||||
-87.176721,
|
||||
-82.49663,
|
||||
-93.323403,
|
||||
20.399941
|
||||
89.999979,
|
||||
-0.000118,
|
||||
-120.09949,
|
||||
-30,
|
||||
-105,
|
||||
0
|
||||
],
|
||||
"qr_value": "BG042110276",
|
||||
"model_id": ""
|
||||
|
||||
@@ -381,8 +381,9 @@ createApp({
|
||||
} catch (e) { alert('导航失败: ' + e.message) }
|
||||
},
|
||||
async goToOrigin() {
|
||||
if (!confirm('确认导航到原点 (0, 0, 0)?')) return
|
||||
if (!confirm('确认先复原机械臂,再导航到原点 (0, 0, 0)?')) return
|
||||
try {
|
||||
this.mapMsg = '正在复原机械臂并发送原点导航...'
|
||||
const res = await fetch(API + '/api/navigate/to', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
@@ -390,7 +391,7 @@ createApp({
|
||||
})
|
||||
const data = await res.json()
|
||||
if (data.ok) {
|
||||
this.mapMsg = '✅ 已发送导航到原点'
|
||||
this.mapMsg = '✅ ' + (data.message || '已发送导航到原点')
|
||||
} else {
|
||||
this.mapMsg = '❌ ' + (data.error || '导航失败')
|
||||
}
|
||||
|
||||
@@ -414,6 +414,14 @@ class MissionExecutorV3:
|
||||
model_name = self._lookup_model(qr_value)
|
||||
self._log(f" 🏷️ 机型: {model_name}")
|
||||
|
||||
if qr_value and opt_arm_init and has_arm_pose and opt_agv_move and not self._stop.is_set():
|
||||
self._log(" 🦾 扫码完成,恢复机械臂初始姿态")
|
||||
try:
|
||||
self.arm_client.set_angles(arm_initial_pose, speed=self.arm_speed)
|
||||
self._wait_arm_ready(arm_initial_pose)
|
||||
except Exception as e:
|
||||
self._log(f" ⚠️ 机械臂复位失败: {e}")
|
||||
|
||||
if opt_front_photo and not self._stop.is_set():
|
||||
model = self._find_model(models, model_name)
|
||||
if model:
|
||||
@@ -642,7 +650,7 @@ class MissionExecutorV3:
|
||||
self._log("↪️ 调用设置页同款接口返回原点")
|
||||
resp = requests.post(
|
||||
"http://127.0.0.1:5000/api/navigate/to",
|
||||
json={"x": 0, "y": 0, "yaw": 0},
|
||||
json={"x": 0, "y": 0, "yaw": 0, "restore_arm": False},
|
||||
timeout=8,
|
||||
)
|
||||
data = resp.json() if resp.content else {}
|
||||
@@ -676,7 +684,7 @@ class MissionExecutorV3:
|
||||
return False
|
||||
def _scan_qr_with_poses(self, qr_configs: list, machine_row: int = 0) -> Optional[str]:
|
||||
"""用二维码配置中的姿态依次尝试,逐一调整姿态+等2秒+扫码,全部失败才弹框
|
||||
|
||||
|
||||
machine_row: 机器所在行,奇数行(1,3,5)时关节角度需取反
|
||||
"""
|
||||
if not qr_configs:
|
||||
|
||||
@@ -39,6 +39,7 @@ import type { ActivityItem, CameraInfo, CustomsDeclaration, InspectionIssue, Ins
|
||||
|
||||
const { Text } = Typography;
|
||||
const { TextArea } = Input;
|
||||
const LOG_AUTO_SCROLL_THRESHOLD = 48;
|
||||
|
||||
interface ProgressItem extends InspectionItem {
|
||||
currentInspected: number;
|
||||
@@ -78,7 +79,8 @@ function InspectionContent() {
|
||||
const [operationLoading, setOperationLoading] = useState(false);
|
||||
const [messageApi, contextHolder] = message.useMessage();
|
||||
const { token } = theme.useToken();
|
||||
const logsEndRef = useRef<HTMLDivElement>(null);
|
||||
const logsContainerRef = useRef<HTMLDivElement>(null);
|
||||
const [isUserNearBottom, setIsUserNearBottom] = useState(true);
|
||||
|
||||
const refreshRuntime = async () => {
|
||||
const [missionState, currentInspection, missionLogs, nextIssues] = await Promise.all([
|
||||
@@ -231,8 +233,19 @@ function InspectionContent() {
|
||||
}, [setInspection]);
|
||||
|
||||
useEffect(() => {
|
||||
logsEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
||||
}, [logs]);
|
||||
const container = logsContainerRef.current;
|
||||
if (container && isUserNearBottom) {
|
||||
container.scrollTop = container.scrollHeight;
|
||||
}
|
||||
}, [logs, isUserNearBottom]);
|
||||
|
||||
const handleLogsScroll = () => {
|
||||
const container = logsContainerRef.current;
|
||||
if (!container) return;
|
||||
|
||||
const distanceToBottom = container.scrollHeight - container.scrollTop - container.clientHeight;
|
||||
setIsUserNearBottom(distanceToBottom <= LOG_AUTO_SCROLL_THRESHOLD);
|
||||
};
|
||||
|
||||
const calculateTotalProgress = () => {
|
||||
if (!progressData.length) return 0;
|
||||
@@ -524,7 +537,7 @@ function InspectionContent() {
|
||||
style={{ flex: 3, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}
|
||||
styles={{ body: { padding: 12, flex: 1, display: 'flex', flexDirection: 'column', minHeight: 0, background: token.colorFillQuaternary } }}
|
||||
>
|
||||
<div style={{ flex: 1, overflowY: 'auto', paddingRight: 8 }}>
|
||||
<div ref={logsContainerRef} onScroll={handleLogsScroll} style={{ flex: 1, overflowY: 'auto', paddingRight: 8 }}>
|
||||
{logs.length > 0 ? (
|
||||
<Timeline
|
||||
items={logs.map((item) => ({
|
||||
@@ -540,7 +553,6 @@ function InspectionContent() {
|
||||
) : (
|
||||
<Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description="暂无日志" style={{ margin: '20px 0' }} />
|
||||
)}
|
||||
<div ref={logsEndRef} />
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
Reference in New Issue
Block a user