feat: 增加云台功能控制
This commit is contained in:
67
app.py
67
app.py
@@ -11,7 +11,8 @@ logger = logging.getLogger(__name__)
|
|||||||
# Flask 应用初始化
|
# Flask 应用初始化
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config['SECRET_KEY'] = 'h2Ms4pw9GzvwiFHyNPhH' # 请更换为安全的密钥
|
app.config['SECRET_KEY'] = 'h2Ms4pw9GzvwiFHyNPhH' # 请更换为安全的密钥
|
||||||
socketio = SocketIO(app, cors_allowed_origins="*")
|
# socketio = SocketIO(app, cors_allowed_origins="*")
|
||||||
|
socketio = SocketIO(app)
|
||||||
|
|
||||||
# 全局变量存储摇杆数据
|
# 全局变量存储摇杆数据
|
||||||
joystick_data = {}
|
joystick_data = {}
|
||||||
@@ -38,7 +39,7 @@ def handle_joystick_data(data):
|
|||||||
|
|
||||||
# print(f"force: {force} x: {x}, y: {y}")
|
# print(f"force: {force} x: {x}, y: {y}")
|
||||||
ptz.set_pitch_speed(int(y * 100))
|
ptz.set_pitch_speed(int(y * 100))
|
||||||
ptz.set_yaw_speed(int(x * 100))
|
ptz.set_yaw_speed(int(x * -100))
|
||||||
|
|
||||||
# direction = data.get('direction', '')
|
# direction = data.get('direction', '')
|
||||||
# logger.info(f"收到摇杆数据:{direction}")
|
# logger.info(f"收到摇杆数据:{direction}")
|
||||||
@@ -85,27 +86,49 @@ def handle_ping():
|
|||||||
"""处理心跳检测"""
|
"""处理心跳检测"""
|
||||||
emit('pong')
|
emit('pong')
|
||||||
|
|
||||||
@app.route('/get_joystick_data')
|
@socketio.on('gimbal_center')
|
||||||
def get_joystick_data():
|
def handle_gimbal_center():
|
||||||
"""API 端点:获取当前摇杆数据"""
|
"""处理云台中心"""
|
||||||
from flask import jsonify
|
ptz.center()
|
||||||
return jsonify(joystick_data)
|
logger.info(f"云台回中")
|
||||||
|
|
||||||
@app.route('/control/<action>')
|
@socketio.on('gimbal_pip')
|
||||||
def robot_control(action):
|
def handle_gimbal_pip(data):
|
||||||
"""
|
"""处理云台PIP"""
|
||||||
示例控制端点
|
mode = int(data)
|
||||||
可用于直接控制机器人动作
|
if mode >= 0 and mode <= 3:
|
||||||
"""
|
ptz.set_pip_mode(mode)
|
||||||
from flask import jsonify
|
logger.info(f"云台PIP {mode}")
|
||||||
valid_actions = ['forward', 'backward', 'left', 'right', 'stop']
|
|
||||||
|
|
||||||
if action in valid_actions:
|
@socketio.on('gimbal_ir_mode')
|
||||||
logger.info(f"执行机器人控制:{action}")
|
def handle_gimbal_ir_mode(data):
|
||||||
# 在这里添加实际的机器人控制逻辑
|
"""处理云台IR"""
|
||||||
return jsonify({'status': 'success', 'action': action})
|
mode = int(data)
|
||||||
else:
|
if mode >= 0 and mode <= 9:
|
||||||
return jsonify({'status': 'error', 'message': '无效的动作'}), 400
|
ptz.set_ir_mode(mode)
|
||||||
|
logger.info(f"云台IR {mode}")
|
||||||
|
|
||||||
|
# @app.route('/get_joystick_data')
|
||||||
|
# def get_joystick_data():
|
||||||
|
# """API 端点:获取当前摇杆数据"""
|
||||||
|
# from flask import jsonify
|
||||||
|
# return jsonify(joystick_data)
|
||||||
|
|
||||||
|
# @app.route('/control/<action>')
|
||||||
|
# def robot_control(action):
|
||||||
|
# """
|
||||||
|
# 示例控制端点
|
||||||
|
# 可用于直接控制机器人动作
|
||||||
|
# """
|
||||||
|
# from flask import jsonify
|
||||||
|
# valid_actions = ['forward', 'backward', 'left', 'right', 'stop']
|
||||||
|
|
||||||
|
# if action in valid_actions:
|
||||||
|
# logger.info(f"执行机器人控制:{action}")
|
||||||
|
# # 在这里添加实际的机器人控制逻辑
|
||||||
|
# return jsonify({'status': 'success', 'action': action})
|
||||||
|
# else:
|
||||||
|
# return jsonify({'status': 'error', 'message': '无效的动作'}), 400
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
socketio.run(app, host='0.0.0.0', port=5000, debug=True)
|
socketio.run(app, host='10.21.31.250', port=5000, debug=True)
|
||||||
24
gimbal.py
24
gimbal.py
@@ -185,20 +185,20 @@ if __name__ == "__main__":
|
|||||||
ptz.center()
|
ptz.center()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
ptz.set_pitch_speed(99)
|
# ptz.set_pitch_speed(99)
|
||||||
time.sleep(1)
|
# time.sleep(1)
|
||||||
ptz.set_pitch_speed(0)
|
# ptz.set_pitch_speed(0)
|
||||||
time.sleep(1)
|
# time.sleep(1)
|
||||||
|
|
||||||
ptz.set_yaw_speed(-99)
|
# ptz.set_yaw_speed(-99)
|
||||||
time.sleep(1)
|
# time.sleep(1)
|
||||||
ptz.set_yaw_speed(0)
|
# ptz.set_yaw_speed(0)
|
||||||
time.sleep(1)
|
# time.sleep(1)
|
||||||
|
|
||||||
ptz.set_roll_speed(-99)
|
# ptz.set_roll_speed(-99)
|
||||||
time.sleep(1)
|
# time.sleep(1)
|
||||||
ptz.set_roll_speed(0)
|
# ptz.set_roll_speed(0)
|
||||||
time.sleep(1)
|
# time.sleep(1)
|
||||||
|
|
||||||
print("All commands sent.")
|
print("All commands sent.")
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
background-color: #f0f0f0;
|
background-color: #f0f0f0;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(3, 1fr);
|
||||||
@@ -21,6 +23,7 @@
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
height: calc(100vh - 40px);
|
height: calc(100vh - 40px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.camera-frame {
|
.camera-frame {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -30,6 +33,7 @@
|
|||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.camera-frame.main-view {
|
.camera-frame.main-view {
|
||||||
grid-column: 1 / span 2;
|
grid-column: 1 / span 2;
|
||||||
grid-row: 1 / span 2;
|
grid-row: 1 / span 2;
|
||||||
@@ -37,26 +41,32 @@
|
|||||||
border: 3px solid #4a90e2;
|
border: 3px solid #4a90e2;
|
||||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.camera-frame.main-view .camera-title {
|
.camera-frame.main-view .camera-title {
|
||||||
background-color: #2c5aa0;
|
background-color: #2c5aa0;
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.camera-frame.front-view {
|
.camera-frame.front-view {
|
||||||
grid-column: 3 / span 1;
|
grid-column: 3 / span 1;
|
||||||
grid-row: 1 / span 1;
|
grid-row: 1 / span 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.camera-frame.back-view {
|
.camera-frame.back-view {
|
||||||
grid-column: 3 / span 1;
|
grid-column: 3 / span 1;
|
||||||
grid-row: 2 / span 1;
|
grid-row: 2 / span 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.camera-frame.left-view {
|
.camera-frame.left-view {
|
||||||
grid-column: 1 / span 1;
|
grid-column: 1 / span 1;
|
||||||
grid-row: 3 / span 1;
|
grid-row: 3 / span 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.camera-frame.right-view {
|
.camera-frame.right-view {
|
||||||
grid-column: 2 / span 1;
|
grid-column: 2 / span 1;
|
||||||
grid-row: 3 / span 1;
|
grid-row: 3 / span 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.joystick-container {
|
.joystick-container {
|
||||||
grid-column: 3 / span 1;
|
grid-column: 3 / span 1;
|
||||||
grid-row: 3 / span 1;
|
grid-row: 3 / span 1;
|
||||||
@@ -67,6 +77,7 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.camera-title {
|
.camera-title {
|
||||||
background-color: #4a90e2;
|
background-color: #4a90e2;
|
||||||
color: white;
|
color: white;
|
||||||
@@ -76,14 +87,17 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.camera-title:hover {
|
.camera-title:hover {
|
||||||
background-color: #3a7bc8;
|
background-color: #3a7bc8;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-indicator {
|
.status-indicator {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 5px;
|
top: 5px;
|
||||||
@@ -93,20 +107,25 @@
|
|||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual-joystick {
|
virtual-joystick {
|
||||||
--radius: 100px;
|
--radius: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual-joystick::part(joystick) {
|
virtual-joystick::part(joystick) {
|
||||||
background-color: rgba(255, 255, 255, 0.2);
|
background-color: rgba(255, 255, 255, 0.2);
|
||||||
border: 2px solid rgba(74, 144, 226, 0.5);
|
border: 2px solid rgba(74, 144, 226, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual-joystick::part(joystick):before {
|
virtual-joystick::part(joystick):before {
|
||||||
background-color: rgba(74, 144, 226, 0.2);
|
background-color: rgba(74, 144, 226, 0.2);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual-joystick::part(joystick):after {
|
virtual-joystick::part(joystick):after {
|
||||||
background-color: #4a90e2;
|
background-color: #4a90e2;
|
||||||
border: 1px solid #2c5aa0;
|
border: 1px solid #2c5aa0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#connection-status {
|
#connection-status {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
@@ -116,16 +135,42 @@
|
|||||||
color: white;
|
color: white;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.connected {
|
.connected {
|
||||||
background-color: #28a745;
|
background-color: #28a745;
|
||||||
}
|
}
|
||||||
|
|
||||||
.disconnected {
|
.disconnected {
|
||||||
background-color: #dc3545;
|
background-color: #dc3545;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="connection-status" class="disconnected">未连接</div>
|
<div id="connection-status" class="disconnected">未连接</div>
|
||||||
|
<div class="gimbal">
|
||||||
|
<button id="gimbal_center">云台回中</button>
|
||||||
|
<!-- <button id="gimbal_pip">画中画</button> -->
|
||||||
|
<!-- 下拉选框 -->
|
||||||
|
<select id="gimbal_pip">
|
||||||
|
<option value="0">RGB主</option>
|
||||||
|
<option value="1">仅RGB</option>
|
||||||
|
<option value="2">仅IR</option>
|
||||||
|
<option value="3">IR主</option>
|
||||||
|
</select>
|
||||||
|
<select id="ir_mode">
|
||||||
|
<option value="0">0</option>
|
||||||
|
<option value="1">1</option>
|
||||||
|
<option value="2">2</option>
|
||||||
|
<option value="3">3</option>
|
||||||
|
<option value="4">4</option>
|
||||||
|
<option value="5">5</option>
|
||||||
|
<option value="6">6</option>
|
||||||
|
<option value="7">7</option>
|
||||||
|
<option value="8">8</option>
|
||||||
|
<option value="9">9</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="camera-frame main-view" data-camera="cam_gimbal">
|
<div class="camera-frame main-view" data-camera="cam_gimbal">
|
||||||
@@ -181,13 +226,36 @@
|
|||||||
connectionStatus.className = 'disconnected';
|
connectionStatus.className = 'disconnected';
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('connection_response', function(data) {
|
// socket.on('connection_response', function (data) {
|
||||||
console.log('服务器响应:', data);
|
// console.log('服务器响应:', data);
|
||||||
|
// });
|
||||||
|
|
||||||
|
// socket.on('data_received', function (data) {
|
||||||
|
// console.log('数据确认:', data);
|
||||||
|
// });
|
||||||
|
|
||||||
|
const button_gimbal_center = document.getElementById('gimbal_center');
|
||||||
|
// const button_gimbal_pip = document.getElementById('gimbal_pip');
|
||||||
|
const select_gimbal_pip = document.getElementById('gimbal_pip');
|
||||||
|
const select_ir_mode = document.getElementById('ir_mode');
|
||||||
|
|
||||||
|
button_gimbal_center.addEventListener('click', function () {
|
||||||
|
socket.emit('gimbal_center');
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('data_received', function(data) {
|
// button_gimbal_pip.addEventListener('click', function () {
|
||||||
console.log('数据确认:', data);
|
// socket.emit('gimbal_pip');
|
||||||
});
|
// });
|
||||||
|
|
||||||
|
select_gimbal_pip.addEventListener('change', function () {
|
||||||
|
const selectedValue = select_gimbal_pip.value;
|
||||||
|
socket.emit('gimbal_pip', selectedValue);
|
||||||
|
})
|
||||||
|
|
||||||
|
select_ir_mode.addEventListener('change', function () {
|
||||||
|
const selectedValue = select_ir_mode.value;
|
||||||
|
socket.emit('gimbal_ir_mode', selectedValue);
|
||||||
|
})
|
||||||
|
|
||||||
// 获取摇杆元素
|
// 获取摇杆元素
|
||||||
const joystick = document.querySelector('virtual-joystick');
|
const joystick = document.querySelector('virtual-joystick');
|
||||||
@@ -233,4 +301,5 @@
|
|||||||
}, 30000); // 每 30 秒发送一次心跳
|
}, 30000); // 每 30 秒发送一次心跳
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user