From 9e90b68bf19393e361db273cf929d03b59a5ed9e Mon Sep 17 00:00:00 2001 From: ywb <347742090@qq.com> Date: Tue, 26 May 2026 14:26:43 +0800 Subject: [PATCH] =?UTF-8?q?=E5=9D=90=E6=A0=87=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agv_app/setting.html | 352 +++++++++++++++++++++++++++++---- agv_app/start_all.sh | 1 + agv_app/static/js/setting.js | 17 +- agv_app/stop_all.sh | 4 +- agv_app/style.css | 194 +++++++++++++++++- agv_app/templates/index.html | 6 +- agv_app/templates/setting.html | 6 +- 7 files changed, 535 insertions(+), 45 deletions(-) mode change 100644 => 100755 agv_app/stop_all.sh diff --git a/agv_app/setting.html b/agv_app/setting.html index c500d00..41db5f6 100644 --- a/agv_app/setting.html +++ b/agv_app/setting.html @@ -4,7 +4,7 @@ 设置 - AGV 拍摄系统 - +
@@ -12,7 +12,7 @@ @@ -21,6 +21,8 @@
+ +
@@ -56,26 +58,197 @@
-
- -
- -
-
- -
+
+ +
+ + +
+ +
+
+ +
+
+ +
+
+ + +
+
+

📦 机型配置

+ + +
+ +
+ + +
+

暂无机型配置,请点击上方按钮添加

+
+ + + + + + + + + + + + + + + + + + + + +
ID机型名称描述备注操作
{% raw %}{{ m.id }}{% endraw %}{% raw %}{{ m.name }}{% endraw %}{% raw %}{{ m.description || '—' }}{% endraw %}{% raw %}{{ m.notes || '—' }}{% endraw %} + + +
+ + +
+
+ +
+

🟢 正面姿态

+
+
+ {% raw %}{{ pose.name || '正面姿态' }}{% endraw %} + +
+
+
+ J{% raw %}{{ j }}{% endraw %} + + + + ° +
+
+
+ + +
+
+
+
+ + +
+
+ 当前机械臂角度: + + J{% raw %}{{ currentAngles[0] ? currentAngles[0].toFixed(1) : '—' }}{% endraw %}° + J{% raw %}{{ currentAngles[1] ? currentAngles[1].toFixed(1) : '—' }}{% endraw %}° + J{% raw %}{{ currentAngles[2] ? currentAngles[2].toFixed(1) : '—' }}{% endraw %}° + J{% raw %}{{ currentAngles[3] ? currentAngles[3].toFixed(1) : '—' }}{% endraw %}° + J{% raw %}{{ currentAngles[4] ? currentAngles[4].toFixed(1) : '—' }}{% endraw %}° + J{% raw %}{{ currentAngles[5] ? currentAngles[5].toFixed(1) : '—' }}{% endraw %}° + + (未连接机械臂) +
+
+
+ + +
+

🔴 背面姿态

+
+
+ {% raw %}{{ pose.name || '背面姿态' }}{% endraw %} + +
+
+
+ J{% raw %}{{ j }}{% endraw %} + + + + ° +
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+ + + +
+
+ +
@@ -91,9 +264,10 @@
-
+
- + +
@@ -123,12 +297,16 @@
机器行 {% raw %}{{ ri }}{% endraw %}
- - +
@@ -145,12 +323,16 @@
机器行 {% raw %}{{ missionConfig.rows }}{% endraw %}
- - +
@@ -240,6 +422,39 @@
+ +
+

🔍 二维码位置

+
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+ 📱 二维码值: {% raw %}{{ safeQr('qr_value') }}{% endraw %} + 🏷️ 匹配机型: {% raw %}{{ safeQrModelName() }}{% endraw %} + ⚠️ 未匹配到机型 +
+
+ +
+
+
@@ -290,19 +505,78 @@
当前: ({% raw %}{{ pointEditor.x.toFixed(2) }}{% endraw %}, {% raw %}{{ pointEditor.y.toFixed(2) }}{% endraw %}, {% raw %}{{ pointEditor.yaw.toFixed(2) }}{% endraw %})
-
+
💡 此点位服务于: {% raw %}{{ getPointOwnerLabel(editingPoint.pointRow, editingPoint.col).split('·')[1] || '无' }}{% endraw %}
+
+ + +
+
+

📷 二维码配置

+

配置机械臂姿态(6个关节角度),通过机械臂摄像头识别二维码并匹配机型。

+ +
+
+ +
+
+
+ + +
+
+

暂无二维码配置,请点击上方按钮添加

+
+ + + + + + + + + + + + + + + + + + + + + + + + +
名称J1J2J3J4J5J6二维码值匹配机型操作
+ + + + {% raw %}{{ q.qr_value || '—' }}{% endraw %}{% raw %}{{ getQrModelName(q.model_id) }}{% endraw %} + + + + +
+
+
+
@@ -312,7 +586,7 @@
- +

关节角度控制

@@ -325,7 +599,6 @@
-
@@ -349,8 +622,19 @@
🔋 电压: {% raw %}{{ agvBattery !== null ? agvBattery + 'V' : '—' }}{% endraw %} - 📍 位置: X={% raw %}{{ agvPosition[0] ? agvPosition[0].toFixed(2) : '?' }}{% endraw %} Y={% raw %}{{ agvPosition[1] ? agvPosition[1].toFixed(2) : '?' }}{% endraw %} + 📍 位置: X={% raw %}{{ agvPosition[0] !== undefined ? agvPosition[0].toFixed(2) : '?' }}{% endraw %} Y={% raw %}{{ agvPosition[1] !== undefined ? agvPosition[1].toFixed(2) : '?' }}{% endraw %} yaw={% raw %}{{ agvPosition[2] !== undefined ? (agvPosition[2] * 180 / Math.PI).toFixed(1) : '?' }}{% endraw }}° + + {% raw %}{{ initPoseMsg }}{% endraw %} +
+ +
@@ -392,7 +676,7 @@
- - + + diff --git a/agv_app/start_all.sh b/agv_app/start_all.sh index 1267cdc..f073e0a 100755 --- a/agv_app/start_all.sh +++ b/agv_app/start_all.sh @@ -247,6 +247,7 @@ echo " ✅ 精度参数已设置" # ---------- 7. 启动 Flask ---------- echo "[7/8] 启动 Flask API..." +export ROS_DOMAIN_ID=1 cd "$AGV_APP_DIR" nohup python3 app.py > /tmp/agv_flask.log 2>&1 & FLASK_PID=$! diff --git a/agv_app/static/js/setting.js b/agv_app/static/js/setting.js index 6e5886d..9829fcd 100644 --- a/agv_app/static/js/setting.js +++ b/agv_app/static/js/setting.js @@ -162,20 +162,30 @@ createApp({ if (res.ok) { const data = await res.json() this.nav2Available = data.nav2_available - if (data.current_pos) { - this.navCurrentPos = data.current_pos + if (data.current_position) { + this.navCurrentPos = data.current_position } } } catch (e) {} }, async onMapClick(e) { - if (!this.mapMeta || !this.agvConnected) return + if (!this.mapMeta) { + this.mapMsg = '❌ 地图未加载' + setTimeout(() => { this.mapMsg = '' }, 3000) + return + } + if (!this.agvConnected) { + this.mapMsg = '❌ AGV 未连接,无法导航' + setTimeout(() => { this.mapMsg = '' }, 3000) + return + } const rect = e.target.getBoundingClientRect() const px = (e.clientX - rect.left) / rect.width const py = (e.clientY - rect.top) / rect.height const { resolution, origin } = this.mapMeta const wx = origin[0] + px * resolution * this.mapMeta.width const wy = origin[1] + (1 - py) * resolution * this.mapMeta.height + if (!confirm(`是否导航到该坐标?\nX: ${wx.toFixed(3)}\nY: ${wy.toFixed(3)}`)) return try { const res = await fetch(API + '/api/navigate/to', { method: 'POST', @@ -325,6 +335,7 @@ createApp({ } catch (e) { alert('保存失败: ' + e.message) } }, async navigateToPoint() { + if (!confirm(`确认导航到该点位?\nX: ${this.pointEditor.x} Y: ${this.pointEditor.y} Yaw: ${this.pointEditor.yaw}`)) return try { const res = await fetch(API + '/api/navigate/to', { method: 'POST', diff --git a/agv_app/stop_all.sh b/agv_app/stop_all.sh old mode 100644 new mode 100755 index 612dd33..fd822ed --- a/agv_app/stop_all.sh +++ b/agv_app/stop_all.sh @@ -21,6 +21,7 @@ pkill -f "agv_pro_node" 2>/dev/null || true pkill -f "lslidar_driver_node" 2>/dev/null || true pkill -f "component_container" 2>/dev/null || true pkill -f "fix_scan_timestamp" 2>/dev/null || true +pkill -f "clock_publisher" 2>/dev/null || true pkill -f "robot_state_publisher" 2>/dev/null || true pkill -f "start_all.sh" 2>/dev/null || true sleep 2 @@ -48,6 +49,7 @@ fi # 清理 scan_fixer 锁文件 rm -f /tmp/scan_fixer.lock +rm -f /tmp/clock_publisher.lock echo " ✅ FastRTPS 清理完成" # ---------- 4. 【关键】重置 ros2 daemon ---------- @@ -61,7 +63,7 @@ echo " ✅ ros2 daemon 已重置" # ---------- 5. 验证清理结果 ---------- echo "[5/5] 验证清理结果..." -PROC_COUNT=$(ps aux | grep -E 'agv_pro_node|lslidar_driver_node|component_container|fix_scan_timestamp|app.py|ros2-daemon' | grep -v grep | wc -l || echo 0) +PROC_COUNT=$(ps aux | grep -E 'agv_pro_node|lslidar_driver_node|component_container|fix_scan_timestamp|clock_publisher|app.py|ros2-daemon' | grep -v grep | wc -l || echo 0) FASTRTPS_LEFT=$(ls /dev/shm/fastrtps_* 2>/dev/null | wc -l || echo 0) echo " 残留进程数: $PROC_COUNT" diff --git a/agv_app/style.css b/agv_app/style.css index 3ee0cd7..ad3eda8 100644 --- a/agv_app/style.css +++ b/agv_app/style.css @@ -707,7 +707,7 @@ a:hover { text-decoration: underline; } .map-container { position: relative; } .map-overlay { position: absolute; top: 0; left: 0; right: 0; bottom: 0; - pointer-events: none; + pointer-events: none; z-index: 10; } .map-dot { position: absolute; @@ -720,3 +720,195 @@ a:hover { text-decoration: underline; } border: 2px solid #fff; box-shadow: 0 0 6px rgba(243,156,18,0.9); } +/* AGV 实时位置点 */ +.agv-dot { + width: 14px !important; + height: 14px !important; + background: #ff5722 !important; + border: 2px solid #fff !important; + border-radius: 50% !important; + box-shadow: 0 0 8px #ff5722, 0 0 16px #ff572288 !important; + animation: agv-pulse 1.5s ease-in-out infinite !important; + z-index: 10 !important; +} +@keyframes agv-pulse { + 0%, 100% { transform: scale(1); opacity: 1; } + 50% { transform: scale(1.3); opacity: 0.7; } +} + +/* 二维码位置点 */ +.qr-dot { + width: 12px !important; + height: 12px !important; + background: #ff9800 !important; + border: 2px solid #fff !important; + border-radius: 3px !important; + box-shadow: 0 0 8px #ff9800, 0 0 14px #ff980088 !important; + animation: qr-pulse 2s ease-in-out infinite !important; + z-index: 10 !important; +} +@keyframes qr-pulse { + 0%, 100% { transform: scale(1); opacity: 1; } + 50% { transform: scale(1.2); opacity: 0.6; } +} + +/* 机器行:有机/无机器 切换 */ +.machine-toggle { + display: flex; + align-items: center; + gap: 6px; + cursor: pointer; + user-select: none; +} +.machine-toggle input[type="checkbox"] { + accent-color: #4caf50; + width: 16px; + height: 16px; + cursor: pointer; +} +.machine-status.on { + color: #4caf50; + font-size: 12px; +} +.machine-status.off { + color: #666; + font-size: 12px; +} + +/* ========== 实时日志 ========== */ +.log-box { + background: #0a0a0a; + color: #00ff88; + font-family: 'Courier New', 'Menlo', monospace; + font-size: 13px; + line-height: 1.6; + max-height: 320px; + overflow-y: auto; + padding: 12px 16px; + border-radius: 6px; + margin-top: 8px; + border: 1px solid #1a1a1a; +} +.log-line { + padding: 2px 0; + border-bottom: 1px solid #111; + word-break: break-all; +} +.log-empty { + color: #555; + font-style: italic; + padding: 12px 0; +} + +/* ========== 弹窗 ========== */ +.modal-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.75); + display: flex; + align-items: center; + justify-content: center; + z-index: 9999; +} +.modal { + background: #1a1a2e; + padding: 28px 32px; + border-radius: 12px; + min-width: 400px; + max-width: 90%; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5); +} +.modal h3 { + margin: 0 0 12px 0; + color: #e0e0e0; +} +.modal p { + color: #aaa; + margin: 0 0 16px 0; +} +.modal input[type="text"] { + width: 100%; + padding: 10px 12px; + background: #0a0a0a; + border: 1px solid #333; + border-radius: 6px; + color: #e0e0e0; + font-size: 15px; + outline: none; + box-sizing: border-box; +} +.modal input[type="text"]:focus { + border-color: #409eff; +} +.modal-actions { + display: flex; + gap: 12px; + margin-top: 20px; +} +.modal-actions .btn { + flex: 1; +} + +/* ========== 任务清单 ========== */ +.task-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); + gap: 10px; + margin-top: 10px; +} +.task-cell { + background: #0a0a0a; + border: 1px solid #1a1a1a; + border-radius: 8px; + padding: 12px; + text-align: center; + transition: all 0.3s; +} +.task-cell.task-active { + border-color: #409eff; + background: #0d1b2a; +} +.task-cell.task-completed { + border-color: #4caf50; + opacity: 0.7; +} +.task-cell.task-active .task-step-text { + color: #409eff; + font-weight: bold; +} +.task-pos { + font-size: 16px; + font-weight: bold; + color: #e0e0e0; + margin-bottom: 6px; +} +.task-status-icon { + font-size: 20px; + margin-bottom: 4px; +} +.task-step-text { + font-size: 12px; + color: #888; + margin-bottom: 4px; +} +.task-info { + font-size: 11px; + color: #666; +} +.task-qr { + font-family: monospace; + color: #aaa; +} +.task-photos { + color: #888; +} +.pulse-icon { + animation: taskPulse 1s infinite; +} +@keyframes taskPulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.3; } +} diff --git a/agv_app/templates/index.html b/agv_app/templates/index.html index c32b73b..2f57ee6 100644 --- a/agv_app/templates/index.html +++ b/agv_app/templates/index.html @@ -100,9 +100,9 @@
机械臂摄像头
- -
机械臂摄像头异常
-
未连接
+ +
机械臂摄像头异常
+
未连接
diff --git a/agv_app/templates/setting.html b/agv_app/templates/setting.html index bed3942..5ec3a8c 100644 --- a/agv_app/templates/setting.html +++ b/agv_app/templates/setting.html @@ -4,7 +4,7 @@ 设置 - AGV 拍摄系统 - +
@@ -581,7 +581,7 @@
- - + +