From 9d8d7444b47491ad413f87c6f86e0280000a58a4 Mon Sep 17 00:00:00 2001 From: Cx330 <1487537121@qq.com> Date: Sun, 12 Apr 2026 14:44:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E7=89=88=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/CSS/control.css | 134 ++++++++++++++ main/CSS/record.css | 10 + main/CSS/style.css | 79 ++++++++ main/Javascript/control.js | 145 +++++++++++++++ main/Javascript/record.js | 112 ++++++++++++ main/Javascript/script.js | 38 ++++ main/__pycache__/motor.cpython-313.pyc | Bin 0 -> 4033 bytes main/camera.py | 244 +++++++++++++++++++------ main/index.html | 42 +++++ main/motor.py | 239 ++++++++++++++---------- main/server.py | 51 ++++++ 11 files changed, 941 insertions(+), 153 deletions(-) create mode 100644 main/CSS/control.css create mode 100644 main/CSS/record.css create mode 100644 main/CSS/style.css create mode 100644 main/Javascript/control.js create mode 100644 main/Javascript/record.js create mode 100644 main/Javascript/script.js create mode 100644 main/__pycache__/motor.cpython-313.pyc create mode 100644 main/index.html create mode 100644 main/server.py diff --git a/main/CSS/control.css b/main/CSS/control.css new file mode 100644 index 0000000..936b41d --- /dev/null +++ b/main/CSS/control.css @@ -0,0 +1,134 @@ +/* 移动控制相关样式 */ +.container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 80vh; + padding: 20px; +} + +.title { + font-size: 24px; + font-weight: bold; + color: #333; + margin-bottom: 40px; + text-align: center; +} + +/* 手柄风格布局 */ +.controller { + display: flex; + flex-direction: column; + align-items: center; + gap: 30px; + width: 100%; + max-width: 350px; +} + +/* 移动控制按钮区域 */ +.movement-controls { + display: flex; + flex-direction: column; + align-items: center; + gap: 15px; + width: 100%; +} + +.movement-row { + display: flex; + gap: 15px; + justify-content: center; +} + +/* 录制回放按钮区域 */ +.record-controls { + display: flex; + gap: 15px; + justify-content: center; + width: 100%; + margin-top: 20px; +} + +/* 按钮样式 */ +.btn { + width: 120px; + height: 50px; + border: none; + border-radius: 25px; + font-size: 16px; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; + position: relative; + overflow: hidden; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); +} + +.btn::before { + content: ''; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: linear-gradient(135deg, rgba(255,255,255,0.2), rgba(255,255,255,0)); + z-index: 1; +} + +.btn span { + position: relative; + z-index: 2; +} + +.btn-forward { + background-color: #4CAF50; + color: white; +} + +.btn-backward { + background-color: #f44336; + color: white; +} + +.btn:hover { + transform: translateY(-3px); + box-shadow: 0 6px 12px rgba(0, 0, 0, 0.15); +} + +.btn:active { + transform: translateY(0); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +/* 状态显示 */ +.status { + margin-top: 30px; + padding: 15px; + background-color: #f5f5f5; + border-radius: 10px; + min-height: 50px; + width: 100%; + max-width: 350px; + text-align: center; + box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1); +} + +.status p { + color: #666; + margin: 0; + font-size: 16px; +} + +/* 响应式设计 */ +@media (max-width: 400px) { + .btn { + width: 100px; + height: 45px; + font-size: 14px; + } + + .title { + font-size: 20px; + } +} \ No newline at end of file diff --git a/main/CSS/record.css b/main/CSS/record.css new file mode 100644 index 0000000..2b8e710 --- /dev/null +++ b/main/CSS/record.css @@ -0,0 +1,10 @@ +/* 录制回放相关样式 */ +.btn-record { + background-color: #ff9800; + color: white; +} + +.btn-playback { + background-color: #2196f3; + color: white; +} \ No newline at end of file diff --git a/main/CSS/style.css b/main/CSS/style.css new file mode 100644 index 0000000..1e43f28 --- /dev/null +++ b/main/CSS/style.css @@ -0,0 +1,79 @@ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: Arial, sans-serif; + background-color: #f0f0f0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 100vh; +} + +.container { + background-color: white; + border-radius: 10px; + padding: 30px; + box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); + text-align: center; + width: 90%; + max-width: 400px; +} + +h1 { + color: #333; + margin-bottom: 30px; +} + +.controls { + display: flex; + justify-content: space-around; + margin-bottom: 30px; +} + +.btn { + width: 120px; + height: 50px; + border: none; + border-radius: 5px; + font-size: 16px; + font-weight: bold; + cursor: pointer; + transition: all 0.3s ease; +} + +.btn-forward { + background-color: #4CAF50; + color: white; +} + +.btn-backward { + background-color: #f44336; + color: white; +} + +.btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); +} + +.btn:active { + transform: translateY(0); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); +} + +.status { + margin-top: 20px; + padding: 10px; + background-color: #f5f5f5; + border-radius: 5px; + min-height: 40px; +} + +.status p { + color: #666; +} \ No newline at end of file diff --git a/main/Javascript/control.js b/main/Javascript/control.js new file mode 100644 index 0000000..f307d4c --- /dev/null +++ b/main/Javascript/control.js @@ -0,0 +1,145 @@ +// 获取按钮和状态元素 +const forwardBtn = document.getElementById('forwardBtn'); +const backwardBtn = document.getElementById('backwardBtn'); +const status = document.getElementById('status'); + +// 实际控制函数 +function controlMotor(direction) { + // 根据方向设置状态消息 + if (direction === 'stop') { + status.innerHTML = `
正在停止...
`; + } else { + status.innerHTML = `正在${direction === 'forward' ? '前进' : '后退'}...
`; + } + + // 记录操作(如果正在录制) + if (window.isRecording) { + const timestamp = Date.now() - window.recordingStartTime; + window.recordedActions.push({ direction, timestamp }); + console.log('Recorded action:', { direction, timestamp }); + } else { + console.log('Not recording, action:', direction); + } + + // 发送AJAX请求到后端服务器 + fetch('/control', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ direction: direction }) + }) + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + status.innerHTML = `${data.message}
`; + } else { + status.innerHTML = `错误: ${data.message}
`; + } + }) + .catch(error => { + status.innerHTML = `通信错误: ${error.message}
`; + }); +} + +// 按钮按下和松开事件 + +// 前进按钮 - 支持鼠标和触摸事件 +forwardBtn.addEventListener('mousedown', () => { + // 发送前进请求 + controlMotor('forward'); +}); + +forwardBtn.addEventListener('mouseup', () => { + // 停止电机 + stopMotor(); +}); + +forwardBtn.addEventListener('mouseleave', () => { + // 鼠标离开按钮时也停止 + stopMotor(); +}); + +// 前进按钮 - 触摸事件 +forwardBtn.addEventListener('touchstart', (e) => { + e.preventDefault(); // 防止默认行为 + // 发送前进请求 + controlMotor('forward'); +}); + +forwardBtn.addEventListener('touchend', (e) => { + e.preventDefault(); // 防止默认行为 + // 停止电机 + stopMotor(); +}); + +forwardBtn.addEventListener('touchcancel', (e) => { + e.preventDefault(); // 防止默认行为 + // 触摸被取消时停止电机 + stopMotor(); +}); + +// 后退按钮 - 支持鼠标和触摸事件 +backwardBtn.addEventListener('mousedown', () => { + // 发送后退请求 + controlMotor('backward'); +}); + +backwardBtn.addEventListener('mouseup', () => { + // 停止电机 + stopMotor(); +}); + +backwardBtn.addEventListener('mouseleave', () => { + // 鼠标离开按钮时也停止 + stopMotor(); +}); + +// 后退按钮 - 触摸事件 +backwardBtn.addEventListener('touchstart', (e) => { + e.preventDefault(); // 防止默认行为 + // 发送后退请求 + controlMotor('backward'); +}); + +backwardBtn.addEventListener('touchend', (e) => { + e.preventDefault(); // 防止默认行为 + // 停止电机 + stopMotor(); +}); + +backwardBtn.addEventListener('touchcancel', (e) => { + e.preventDefault(); // 防止默认行为 + // 触摸被取消时停止电机 + stopMotor(); +}); + +// 停止电机函数 +function stopMotor() { + // 记录操作(如果正在录制) + if (window.isRecording) { + const timestamp = Date.now() - window.recordingStartTime; + window.recordedActions.push({ direction: 'stop', timestamp }); + console.log('Recorded action:', { direction: 'stop', timestamp }); + } + + // 发送停止请求到服务器 + fetch('/control', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ direction: 'stop' }) + }) + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + // 停止成功,不显示消息 + } else { + status.innerHTML = `错误: ${data.message}
`; + } + }) + .catch(error => { + status.innerHTML = `通信错误: ${error.message}
`; + }); +} \ No newline at end of file diff --git a/main/Javascript/record.js b/main/Javascript/record.js new file mode 100644 index 0000000..171aa7e --- /dev/null +++ b/main/Javascript/record.js @@ -0,0 +1,112 @@ +// 确保在DOM加载完成后执行 +window.addEventListener('DOMContentLoaded', function() { + console.log('DOMContentLoaded - Record.js executing'); + + // 录制相关全局变量 + window.isRecording = false; + window.recordedActions = []; + window.recordingStartTime = 0; + + // 获取录制和回放按钮 + const recordBtn = document.getElementById('recordBtn'); + const playbackBtn = document.getElementById('playbackBtn'); + const status = document.getElementById('status'); + + // 调试信息 + console.log('Record.js loaded'); + console.log('recordBtn:', recordBtn); + console.log('playbackBtn:', playbackBtn); + console.log('status:', status); + + // 录制按钮点击事件 - 支持鼠标和触摸事件 + function toggleRecording() { + console.log('toggleRecording called, current isRecording:', window.isRecording); + if (window.isRecording) { + // 停止录制 + window.isRecording = false; + recordBtn.textContent = '开始录制'; + status.innerHTML = `录制完成,共记录 ${window.recordedActions.length} 个操作
`; + console.log('Recording stopped, actions:', window.recordedActions); + } else { + // 开始录制 + window.isRecording = true; + window.recordedActions = []; + window.recordingStartTime = Date.now(); + recordBtn.textContent = '停止录制'; + status.innerHTML = `开始录制...
`; + console.log('Recording started'); + } + } + + console.log('Adding click event listener to recordBtn'); + recordBtn.addEventListener('click', toggleRecording); + + // 录制按钮 - 触摸事件 + console.log('Adding touchstart event listener to recordBtn'); + recordBtn.addEventListener('touchstart', (e) => { + console.log('Touchstart event on recordBtn'); + e.preventDefault(); // 防止默认行为 + toggleRecording(); + }); + + // 回放按钮点击事件 - 支持鼠标和触摸事件 + function startPlayback() { + console.log('startPlayback called, recordedActions:', window.recordedActions); + if (window.recordedActions.length === 0) { + status.innerHTML = `没有可回放的操作
`; + console.log('No actions to playback'); + return; + } + + // 保存当前录制状态并禁用录制 + const originalRecordingState = window.isRecording; + window.isRecording = false; + + status.innerHTML = `开始回放...
`; + console.log('Starting playback'); + + // 按时间顺序回放操作 + let lastTimestamp = 0; + const totalDuration = window.recordedActions[window.recordedActions.length - 1].timestamp; + + // 确保至少有一个操作 + if (window.recordedActions.length > 0) { + // 执行第一个操作 + console.log('Executing first action immediately:', window.recordedActions[0]); + controlMotor(window.recordedActions[0].direction); + + // 执行剩余的操作 + for (let i = 1; i < window.recordedActions.length; i++) { + const action = window.recordedActions[i]; + const delay = action.timestamp - window.recordedActions[i-1].timestamp; + console.log('Scheduling action:', action, 'at delay:', delay); + setTimeout(() => { + // 调用控制函数执行操作 + console.log('Executing action:', action); + controlMotor(action.direction); + }, delay); + } + } + + // 回放完成后,确保发送停止请求 + setTimeout(() => { + console.log('Playback completed, sending stop command'); + controlMotor('stop'); + status.innerHTML = `回放完成
`; + console.log('Playback completed'); + // 恢复原始录制状态 + window.isRecording = originalRecordingState; + }, totalDuration + 1000); + } + + console.log('Adding click event listener to playbackBtn'); + playbackBtn.addEventListener('click', startPlayback); + + // 回放按钮 - 触摸事件 + console.log('Adding touchstart event listener to playbackBtn'); + playbackBtn.addEventListener('touchstart', (e) => { + console.log('Touchstart event on playbackBtn'); + e.preventDefault(); // 防止默认行为 + startPlayback(); + }); +}); \ No newline at end of file diff --git a/main/Javascript/script.js b/main/Javascript/script.js new file mode 100644 index 0000000..502ec25 --- /dev/null +++ b/main/Javascript/script.js @@ -0,0 +1,38 @@ +// 获取按钮和状态元素 +const forwardBtn = document.getElementById('forwardBtn'); +const backwardBtn = document.getElementById('backwardBtn'); +const status = document.getElementById('status'); + +// 实际控制函数 +function controlMotor(direction) { + status.innerHTML = `正在${direction === 'forward' ? '前进' : '后退'}...
`; + + // 发送AJAX请求到后端服务器 + fetch('/control', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ direction: direction }) + }) + .then(response => response.json()) + .then(data => { + if (data.status === 'success') { + status.innerHTML = `${data.message}
`; + } else { + status.innerHTML = `错误: ${data.message}
`; + } + }) + .catch(error => { + status.innerHTML = `通信错误: ${error.message}
`; + }); +} + +// 按钮点击事件 +forwardBtn.addEventListener('click', () => { + controlMotor('forward'); +}); + +backwardBtn.addEventListener('click', () => { + controlMotor('backward'); +}); \ No newline at end of file diff --git a/main/__pycache__/motor.cpython-313.pyc b/main/__pycache__/motor.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..564929a45ff85d2932dd9182d35b9dabcf73fb2e GIT binary patch literal 4033 zcmc&$-E&h#6yJOA-sI-1=@+Ffq8$V^NGqQTh%!h~K5UV0就绪
+