parent
3cc936ba14
commit
cb52c09b01
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1 @@
|
|||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,131 @@
|
|||||||
|
# Air模块配置文件
|
||||||
|
# RoboMaster TT (Tello TLW004) 无人机配置
|
||||||
|
|
||||||
|
# 无人机基本配置
|
||||||
|
drone:
|
||||||
|
type: "tello" # 无人机类型
|
||||||
|
model: "TLW004" # 型号
|
||||||
|
name: "RoboMaster TT" # 显示名称
|
||||||
|
|
||||||
|
# 网络连接配置
|
||||||
|
connection:
|
||||||
|
ip: "192.168.10.1" # 无人机IP地址
|
||||||
|
cmd_port: 8889 # 命令端口
|
||||||
|
state_port: 8890 # 状态端口
|
||||||
|
video_port: 11111 # 视频端口
|
||||||
|
timeout: 5 # 连接超时时间(秒)
|
||||||
|
|
||||||
|
# 视频流配置
|
||||||
|
video:
|
||||||
|
# 支持的视频流格式
|
||||||
|
formats:
|
||||||
|
udp: "udp://{ip}:{port}"
|
||||||
|
rtsp: "rtsp://{ip}:554/live"
|
||||||
|
http: "http://{ip}:8080/video"
|
||||||
|
|
||||||
|
# 默认视频流URL
|
||||||
|
default_stream: "udp://192.168.10.1:11111"
|
||||||
|
|
||||||
|
# 视频参数
|
||||||
|
resolution:
|
||||||
|
width: 960
|
||||||
|
height: 720
|
||||||
|
fps: 30
|
||||||
|
|
||||||
|
# 缓冲设置
|
||||||
|
buffer_size: 10
|
||||||
|
timeout: 10
|
||||||
|
|
||||||
|
# 录制设置
|
||||||
|
recording:
|
||||||
|
enabled: false
|
||||||
|
format: "mp4"
|
||||||
|
quality: "high"
|
||||||
|
|
||||||
|
# 图像分析配置
|
||||||
|
analysis:
|
||||||
|
# 检测阈值
|
||||||
|
confidence_threshold: 0.25
|
||||||
|
part_confidence_threshold: 0.3
|
||||||
|
|
||||||
|
# 模型路径(相对于项目根目录)
|
||||||
|
models:
|
||||||
|
ship_detector: "models/best.pt"
|
||||||
|
part_detector: "models/part_detectors/best.pt"
|
||||||
|
classifier: "models/custom/best.pt"
|
||||||
|
|
||||||
|
# 检测类别
|
||||||
|
ship_classes:
|
||||||
|
- "航空母舰"
|
||||||
|
- "驱逐舰"
|
||||||
|
- "护卫舰"
|
||||||
|
- "潜艇"
|
||||||
|
- "商船"
|
||||||
|
- "油轮"
|
||||||
|
|
||||||
|
# 部件类别
|
||||||
|
part_classes:
|
||||||
|
- "舰桥"
|
||||||
|
- "雷达"
|
||||||
|
- "舰炮"
|
||||||
|
- "导弹发射器"
|
||||||
|
- "直升机甲板"
|
||||||
|
- "烟囱"
|
||||||
|
|
||||||
|
# Web界面配置
|
||||||
|
web:
|
||||||
|
host: "0.0.0.0"
|
||||||
|
port: 5000
|
||||||
|
debug: true
|
||||||
|
|
||||||
|
# 静态文件路径
|
||||||
|
static_folder: "web/static"
|
||||||
|
template_folder: "web/templates"
|
||||||
|
|
||||||
|
# 上传设置
|
||||||
|
upload:
|
||||||
|
max_file_size: "10MB"
|
||||||
|
allowed_extensions: [".jpg", ".jpeg", ".png", ".mp4", ".avi"]
|
||||||
|
save_path: "uploads/drone_captures"
|
||||||
|
|
||||||
|
# 安全设置
|
||||||
|
safety:
|
||||||
|
max_height: 100 # 最大飞行高度(米)
|
||||||
|
max_distance: 500 # 最大飞行距离(米)
|
||||||
|
min_battery: 15 # 最低电量百分比
|
||||||
|
return_home_battery: 30 # 自动返航电量
|
||||||
|
|
||||||
|
# 飞行限制区域
|
||||||
|
no_fly_zones: []
|
||||||
|
|
||||||
|
# 日志配置
|
||||||
|
logging:
|
||||||
|
level: "INFO"
|
||||||
|
format: "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
|
||||||
|
|
||||||
|
# 日志文件
|
||||||
|
files:
|
||||||
|
main: "logs/air_main.log"
|
||||||
|
drone: "logs/drone.log"
|
||||||
|
video: "logs/video.log"
|
||||||
|
analysis: "logs/analysis.log"
|
||||||
|
|
||||||
|
# 性能配置
|
||||||
|
performance:
|
||||||
|
# GPU使用
|
||||||
|
use_gpu: true
|
||||||
|
gpu_memory_fraction: 0.7
|
||||||
|
|
||||||
|
# 多线程设置
|
||||||
|
max_workers: 4
|
||||||
|
|
||||||
|
# 内存限制
|
||||||
|
max_memory_usage: "2GB"
|
||||||
|
|
||||||
|
# 开发调试配置
|
||||||
|
debug:
|
||||||
|
enabled: false
|
||||||
|
save_frames: false
|
||||||
|
frame_save_path: "debug/frames"
|
||||||
|
log_commands: true
|
||||||
|
mock_drone: false # 是否使用模拟无人机
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,13 @@
|
|||||||
|
"""
|
||||||
|
无人机接口子系统(DroneInterface)
|
||||||
|
-------------
|
||||||
|
与无人机建立通信连接
|
||||||
|
接收无人机传回的视频流
|
||||||
|
将视频流转发给图像分析子系统
|
||||||
|
向无人机发送控制命令(如需要)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .drone_manager import DroneManager
|
||||||
|
from .video_receiver import VideoReceiver
|
||||||
|
|
||||||
|
__all__ = ['DroneManager', 'VideoReceiver']
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -0,0 +1,12 @@
|
|||||||
|
"""
|
||||||
|
图像分析子系统(ImageAnalyzer)
|
||||||
|
-------------
|
||||||
|
调用模型进行舰船检测、分类和部件识别
|
||||||
|
处理图像预处理和后处理
|
||||||
|
生成分析结果报告
|
||||||
|
提供API接口供Web应用调用
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .analyzer import ImageAnalyzer
|
||||||
|
|
||||||
|
__all__ = ['ImageAnalyzer']
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,138 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>{% block title %}舰船识别系统{% endblock %} - ShipAI</title>
|
||||||
|
<!-- Bootstrap CSS -->
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<!-- Font Awesome 图标 -->
|
||||||
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" rel="stylesheet">
|
||||||
|
<!-- 自定义CSS -->
|
||||||
|
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
|
||||||
|
{% block extra_css %}{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- 导航栏 -->
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||||
|
<div class="container">
|
||||||
|
<a class="navbar-brand" href="{{ url_for('index') }}">ShipAI - 智能舰船识别系统</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav me-auto">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('index') }}">首页</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('gallery') }}">样本图库</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('drone_control') }}">无人机控制</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="analysisDropdown" role="button" data-bs-toggle="dropdown">
|
||||||
|
分析工具
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<a class="dropdown-item" href="{{ url_for('image_analysis') }}">图像分析</a>
|
||||||
|
<a class="dropdown-item" href="{{ url_for('analytics') }}">分析报告</a>
|
||||||
|
<a class="dropdown-item" href="{{ url_for('data_storage') }}">数据存储</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="modelDropdown" role="button" data-bs-toggle="dropdown">
|
||||||
|
模型管理
|
||||||
|
</a>
|
||||||
|
<div class="dropdown-menu">
|
||||||
|
<a class="dropdown-item" href="{{ url_for('model_settings') }}">模型设置</a>
|
||||||
|
<a class="dropdown-item" href="{{ url_for('annotation_tool') }}">图像标注</a>
|
||||||
|
<a class="dropdown-item" href="{{ url_for('train_model') }}">模型训练</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link {% if request.path == '/ship-database' %}active{% endif %}" href="/ship-database">舰船数据库</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item dropdown">
|
||||||
|
<a class="nav-link dropdown-toggle" href="#" id="modelsDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
部件检测
|
||||||
|
</a>
|
||||||
|
<ul class="dropdown-menu" aria-labelledby="modelsDropdown">
|
||||||
|
<li><a class="dropdown-item" href="{{ url_for('part_detection') }}">部件库管理</a></li>
|
||||||
|
<li><a class="dropdown-item" href="{{ url_for('annotation_tool', type='part') }}">部件标注工具</a></li>
|
||||||
|
<li><a class="dropdown-item" href="{{ url_for('train_part_model') }}">部件模型训练</a></li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item" id="nav-history">
|
||||||
|
<a class="nav-link" href="{{ url_for('detection_history') }}">检测历史</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('about') }}">关于我们</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<!-- 消息提示 -->
|
||||||
|
<div class="container mt-3">
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="alert alert-{{ category if category != 'message' else 'info' }} alert-dismissible fade show">
|
||||||
|
{{ message }}
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 主要内容 -->
|
||||||
|
<main class="py-4">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<!-- 页脚 -->
|
||||||
|
<footer class="bg-dark text-white py-4 mt-5">
|
||||||
|
<div class="container">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<h5>ShipAI - 智能舰船识别系统</h5>
|
||||||
|
<p>基于深度学习的海上舰船自动识别与分析平台</p>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 text-md-end">
|
||||||
|
<p>© {{ current_year }} ShipAI 团队</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- JavaScript -->
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
||||||
|
<script src="{{ url_for('static', filename='js/modal_fix.js') }}"></script>
|
||||||
|
<script>
|
||||||
|
// 初始化所有模态框
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
// 使所有具有data-bs-toggle="modal"属性的元素正确工作
|
||||||
|
var modalTriggers = document.querySelectorAll('[data-bs-toggle="modal"]');
|
||||||
|
modalTriggers.forEach(function(trigger) {
|
||||||
|
trigger.addEventListener('click', function() {
|
||||||
|
var targetId = this.getAttribute('data-bs-target');
|
||||||
|
if (targetId) {
|
||||||
|
var modalElement = document.querySelector(targetId);
|
||||||
|
if (modalElement) {
|
||||||
|
var modal = new bootstrap.Modal(modalElement);
|
||||||
|
modal.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{% block scripts %}{% endblock %}
|
||||||
|
{% block extra_js %}{% endblock %}
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,470 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>🛤️ 无人机轨迹模拟测试</title>
|
||||||
|
<style>
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: 'Microsoft YaHei', sans-serif;
|
||||||
|
background: linear-gradient(135deg, #1e3c72, #2a5298);
|
||||||
|
color: white;
|
||||||
|
min-height: 100vh;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 30px;
|
||||||
|
backdrop-filter: blur(10px);
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header h1 {
|
||||||
|
font-size: 2.5em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header p {
|
||||||
|
font-size: 1.1em;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-panel {
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 15px;
|
||||||
|
padding: 25px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-group h3 {
|
||||||
|
color: #00aaff;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
font-size: 1.2em;
|
||||||
|
border-bottom: 2px solid rgba(0, 170, 255, 0.3);
|
||||||
|
padding-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
padding: 12px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: white;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-circle {
|
||||||
|
background: linear-gradient(135deg, #9C27B0, #7B1FA2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-line {
|
||||||
|
background: linear-gradient(135deg, #FF9800, #F57C00);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-random {
|
||||||
|
background: linear-gradient(135deg, #607D8B, #455A64);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-stop {
|
||||||
|
background: linear-gradient(135deg, #F44336, #D32F2F);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-clear {
|
||||||
|
background: linear-gradient(135deg, #FF5722, #D32F2F);
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group label {
|
||||||
|
min-width: 100px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group input,
|
||||||
|
.input-group select {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
color: white;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group input::placeholder {
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-panel {
|
||||||
|
background: rgba(0, 0, 0, 0.3);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-value {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert {
|
||||||
|
padding: 12px 15px;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert.success {
|
||||||
|
background: rgba(76, 175, 80, 0.2);
|
||||||
|
border-left: 4px solid #4CAF50;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert.error {
|
||||||
|
background: rgba(244, 67, 54, 0.2);
|
||||||
|
border-left: 4px solid #F44336;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert.info {
|
||||||
|
background: rgba(33, 150, 243, 0.2);
|
||||||
|
border-left: 4px solid #2196F3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drone-list {
|
||||||
|
max-height: 200px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: rgba(0, 0, 0, 0.2);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.drone-item {
|
||||||
|
padding: 8px 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-section {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-section a {
|
||||||
|
color: #00aaff;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background: rgba(0, 170, 255, 0.2);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.link-section a:hover {
|
||||||
|
background: rgba(0, 170, 255, 0.4);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1>🛤️ 无人机轨迹模拟测试</h1>
|
||||||
|
<p>测试无人机轨迹记录和可视化功能</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="alertContainer"></div>
|
||||||
|
|
||||||
|
<!-- 快速启动面板 -->
|
||||||
|
<div class="control-panel">
|
||||||
|
<div class="control-group">
|
||||||
|
<h3>🚀 快速启动模拟</h3>
|
||||||
|
<div class="button-grid">
|
||||||
|
<button class="btn btn-circle" onclick="startQuickSimulation('circle')">
|
||||||
|
🔄 圆形轨迹
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-line" onclick="startQuickSimulation('line')">
|
||||||
|
↔️ 直线轨迹
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-random" onclick="startQuickSimulation('random')">
|
||||||
|
🎲 随机轨迹
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-stop" onclick="stopSimulation()">
|
||||||
|
⏹️ 停止模拟
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 高级配置面板 -->
|
||||||
|
<div class="control-panel">
|
||||||
|
<div class="control-group">
|
||||||
|
<h3>⚙️ 高级配置</h3>
|
||||||
|
<div class="input-group">
|
||||||
|
<label>轨迹类型:</label>
|
||||||
|
<select id="simulationType">
|
||||||
|
<option value="circle">圆形轨迹</option>
|
||||||
|
<option value="line">直线往返</option>
|
||||||
|
<option value="random">随机游走</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label>无人机数量:</label>
|
||||||
|
<input type="number" id="droneCount" value="2" min="1" max="5" placeholder="1-5架">
|
||||||
|
</div>
|
||||||
|
<div class="input-group">
|
||||||
|
<label>更新间隔:</label>
|
||||||
|
<input type="number" id="updateSpeed" value="3" min="1" max="10" placeholder="秒">
|
||||||
|
</div>
|
||||||
|
<div class="button-grid">
|
||||||
|
<button class="btn btn-circle" onclick="startCustomSimulation()">
|
||||||
|
🛠️ 启动自定义模拟
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-clear" onclick="clearAllTrajectories()">
|
||||||
|
🗑️ 清除所有轨迹
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 状态显示面板 -->
|
||||||
|
<div class="status-panel">
|
||||||
|
<h3 style="color: #00aaff; margin-bottom: 15px;">📊 模拟状态</h3>
|
||||||
|
<div class="status-item">
|
||||||
|
<span>模拟状态:</span>
|
||||||
|
<span class="status-value" id="simulationStatus">未启动</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-item">
|
||||||
|
<span>轨迹类型:</span>
|
||||||
|
<span class="status-value" id="simulationType">无</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-item">
|
||||||
|
<span>无人机数量:</span>
|
||||||
|
<span class="status-value" id="droneCountStatus">0</span>
|
||||||
|
</div>
|
||||||
|
<div class="status-item">
|
||||||
|
<span>轨迹记录:</span>
|
||||||
|
<span class="status-value" id="trajectoryRecordingStatus">开启</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 无人机列表 -->
|
||||||
|
<div class="control-panel">
|
||||||
|
<div class="control-group">
|
||||||
|
<h3>🚁 模拟无人机列表</h3>
|
||||||
|
<div class="drone-list" id="droneList">
|
||||||
|
<div style="text-align: center; color: #999;">暂无模拟无人机</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 链接到主系统 -->
|
||||||
|
<div class="link-section">
|
||||||
|
<a href="/" target="_blank">🎯 打开主系统查看轨迹</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
// 显示提示信息
|
||||||
|
function showAlert(message, type = 'info') {
|
||||||
|
const alertContainer = document.getElementById('alertContainer');
|
||||||
|
const alert = document.createElement('div');
|
||||||
|
alert.className = `alert ${type}`;
|
||||||
|
alert.textContent = message;
|
||||||
|
|
||||||
|
alertContainer.appendChild(alert);
|
||||||
|
alert.style.display = 'block';
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
alert.style.display = 'none';
|
||||||
|
alertContainer.removeChild(alert);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 快速启动模拟
|
||||||
|
async function startQuickSimulation(type) {
|
||||||
|
await startSimulation(type, 2, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 自定义启动模拟
|
||||||
|
async function startCustomSimulation() {
|
||||||
|
const type = document.getElementById('simulationType').value;
|
||||||
|
const droneCount = parseInt(document.getElementById('droneCount').value) || 2;
|
||||||
|
const speed = parseInt(document.getElementById('updateSpeed').value) || 3;
|
||||||
|
|
||||||
|
await startSimulation(type, droneCount, speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动轨迹模拟
|
||||||
|
async function startSimulation(type, droneCount = 2, speed = 3) {
|
||||||
|
try {
|
||||||
|
showAlert(`🚀 正在启动${droneCount}架无人机的${type}轨迹模拟...`, 'info');
|
||||||
|
|
||||||
|
const response = await fetch('/api/test/start_simulation', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
type: type,
|
||||||
|
drone_count: droneCount,
|
||||||
|
speed: speed
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.status === 'success') {
|
||||||
|
showAlert(`✅ ${result.message}`, 'success');
|
||||||
|
updateStatus();
|
||||||
|
} else {
|
||||||
|
showAlert(`❌ 启动模拟失败: ${result.message}`, 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showAlert(`❌ 启动模拟失败: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止轨迹模拟
|
||||||
|
async function stopSimulation() {
|
||||||
|
try {
|
||||||
|
showAlert('🛑 正在停止轨迹模拟...', 'info');
|
||||||
|
|
||||||
|
const response = await fetch('/api/test/stop_simulation', {
|
||||||
|
method: 'POST'
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
if (result.status === 'success') {
|
||||||
|
showAlert('✅ 轨迹模拟已停止', 'success');
|
||||||
|
updateStatus();
|
||||||
|
} else {
|
||||||
|
showAlert(`❌ 停止模拟失败: ${result.message}`, 'error');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
showAlert(`❌ 停止模拟失败: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清除所有轨迹
|
||||||
|
async function clearAllTrajectories() {
|
||||||
|
try {
|
||||||
|
// 这个功能需要在主页面中执行
|
||||||
|
showAlert('🗑️ 请在主系统页面中点击"清除"按钮来清除轨迹', 'info');
|
||||||
|
} catch (error) {
|
||||||
|
showAlert(`❌ 清除轨迹失败: ${error.message}`, 'error');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新状态显示
|
||||||
|
async function updateStatus() {
|
||||||
|
try {
|
||||||
|
const response = await fetch('/api/test/simulation_status');
|
||||||
|
const result = await response.json();
|
||||||
|
|
||||||
|
if (result.status === 'success') {
|
||||||
|
// 更新状态显示
|
||||||
|
document.getElementById('simulationStatus').textContent =
|
||||||
|
result.simulation_active ? '运行中' : '未启动';
|
||||||
|
document.getElementById('simulationStatus').style.color =
|
||||||
|
result.simulation_active ? '#4CAF50' : '#F44336';
|
||||||
|
|
||||||
|
document.getElementById('simulationType').textContent =
|
||||||
|
result.simulation_type || '无';
|
||||||
|
document.getElementById('droneCountStatus').textContent =
|
||||||
|
result.simulated_drones.length || '0';
|
||||||
|
|
||||||
|
// 更新无人机列表
|
||||||
|
updateDroneList(result.simulated_drones);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取状态失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新无人机列表
|
||||||
|
function updateDroneList(drones) {
|
||||||
|
const droneList = document.getElementById('droneList');
|
||||||
|
|
||||||
|
if (!drones || drones.length === 0) {
|
||||||
|
droneList.innerHTML = '<div style="text-align: center; color: #999;">暂无模拟无人机</div>';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const html = drones.map((drone, index) => `
|
||||||
|
<div class="drone-item">
|
||||||
|
🚁 ${drone.name || `无人机-${index + 1}`}
|
||||||
|
<br>
|
||||||
|
<span style="color: #999;">
|
||||||
|
ID: ${drone.device_id.substring(0, 12)}...
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
`).join('');
|
||||||
|
|
||||||
|
droneList.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 页面加载完成后的初始化
|
||||||
|
window.addEventListener('load', function () {
|
||||||
|
updateStatus();
|
||||||
|
|
||||||
|
// 定期更新状态
|
||||||
|
setInterval(updateStatus, 3000);
|
||||||
|
|
||||||
|
showAlert('🎯 轨迹模拟测试页面已加载', 'success');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
Loading…
Reference in new issue