forked from chiyu1468/AirTrapMine
Upload files to 'src/unitdev01/unitdev01'
This commit is contained in:
@@ -3,11 +3,14 @@ import rclpy
|
|||||||
from rclpy.node import Node
|
from rclpy.node import Node
|
||||||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
|
from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
|
||||||
QWidget, QLabel, QSplitter, QScrollArea,
|
QWidget, QLabel, QSplitter, QScrollArea,
|
||||||
QSizePolicy)
|
QSizePolicy, QApplication)
|
||||||
from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSignal
|
from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSignal, QUrl
|
||||||
from PyQt6.QtGui import QColor
|
from PyQt6.QtGui import QColor
|
||||||
|
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||||
import math
|
import math
|
||||||
import re
|
import re
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
from threading import Lock
|
from threading import Lock
|
||||||
from geometry_msgs.msg import Point, Vector3
|
from geometry_msgs.msg import Point, Vector3
|
||||||
from sensor_msgs.msg import BatteryState, NavSatFix, Imu
|
from sensor_msgs.msg import BatteryState, NavSatFix, Imu
|
||||||
@@ -138,8 +141,8 @@ class DroneMonitor(Node):
|
|||||||
def gps_callback(self, drone_id, msg):
|
def gps_callback(self, drone_id, msg):
|
||||||
self.signals.update_signal.emit('gps', drone_id, {
|
self.signals.update_signal.emit('gps', drone_id, {
|
||||||
'lat': msg.latitude,
|
'lat': msg.latitude,
|
||||||
'lon': msg.longitude,
|
'lon': msg.longitude
|
||||||
'alt': msg.altitude
|
,'alt': msg.altitude
|
||||||
})
|
})
|
||||||
|
|
||||||
def local_vel_callback(self, drone_id, msg):
|
def local_vel_callback(self, drone_id, msg):
|
||||||
@@ -150,7 +153,9 @@ class DroneMonitor(Node):
|
|||||||
})
|
})
|
||||||
|
|
||||||
def altitude_callback(self, drone_id, msg):
|
def altitude_callback(self, drone_id, msg):
|
||||||
self.signals.update_signal.emit('altitude', drone_id, msg.data)
|
self.signals.update_signal.emit('altitude', drone_id, {
|
||||||
|
'altitude': msg.data
|
||||||
|
})
|
||||||
|
|
||||||
def local_pose_callback(self, drone_id, msg):
|
def local_pose_callback(self, drone_id, msg):
|
||||||
self.signals.update_signal.emit('local_pose', drone_id, {
|
self.signals.update_signal.emit('local_pose', drone_id, {
|
||||||
@@ -212,26 +217,43 @@ class ControlStationUI(QMainWindow):
|
|||||||
left_layout.addWidget(scroll_area)
|
left_layout.addWidget(scroll_area)
|
||||||
|
|
||||||
# 右侧地图区域
|
# 右侧地图区域
|
||||||
self.map_widget = QLabel()
|
self.map_view = QWebEngineView()
|
||||||
self.map_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
||||||
self.map_widget.setStyleSheet("""
|
inline_html = '''
|
||||||
QLabel {
|
<!DOCTYPE html>
|
||||||
background-color: #1A1A1A;
|
<html><head>
|
||||||
color: #AAAAAA;
|
<meta charset="utf-8"/>
|
||||||
font-size: 18px;
|
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
|
||||||
border: 2px solid #333;
|
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||||
border-radius: 10px;
|
<style>html,body,#map{height:100%;margin:0}</style>
|
||||||
padding: 20px;
|
</head><body>
|
||||||
|
<div id="map"></div>
|
||||||
|
<script>
|
||||||
|
var map = L.map('map').setView([0,0],2);
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{maxZoom:19}).addTo(map);
|
||||||
|
var markers = {};
|
||||||
|
function updateDrone(lat,lon,id){
|
||||||
|
if(markers[id]) markers[id].setLatLng([lat,lon]);
|
||||||
|
else markers[id]=L.marker([lat,lon]).addTo(map).bindPopup(id);
|
||||||
}
|
}
|
||||||
""")
|
</script>
|
||||||
self.update_map_display("等待GPS數據...")
|
</body></html>
|
||||||
|
'''
|
||||||
|
self.map_view.setHtml(inline_html)
|
||||||
|
self.map_view.loadFinished.connect(self.on_map_loaded)
|
||||||
|
|
||||||
main_splitter.addWidget(left_panel)
|
main_splitter.addWidget(left_panel)
|
||||||
main_splitter.addWidget(self.map_widget)
|
main_splitter.addWidget(self.map_view)
|
||||||
main_splitter.setSizes([400, 1000])
|
main_splitter.setSizes([400, 1000])
|
||||||
|
|
||||||
self.setCentralWidget(main_splitter)
|
self.setCentralWidget(main_splitter)
|
||||||
|
|
||||||
|
def on_map_loaded(self, ok: bool):
|
||||||
|
if ok:
|
||||||
|
self.map_loaded = True
|
||||||
|
else:
|
||||||
|
print("⚠️ 地图页加载失败")
|
||||||
|
|
||||||
def create_drone_panel(self, drone_id):
|
def create_drone_panel(self, drone_id):
|
||||||
panel = QWidget()
|
panel = QWidget()
|
||||||
panel.setObjectName(f"panel_{drone_id}")
|
panel.setObjectName(f"panel_{drone_id}")
|
||||||
@@ -280,11 +302,11 @@ class ControlStationUI(QMainWindow):
|
|||||||
|
|
||||||
# 数据字段(带标签)
|
# 数据字段(带标签)
|
||||||
self.create_data_row(layout, "模式:", f"{drone_id}_mode", "--")
|
self.create_data_row(layout, "模式:", f"{drone_id}_mode", "--")
|
||||||
self.create_data_row(layout, "電壓:", f"{drone_id}_battery", "-- V")
|
self.create_data_row(layout, "電壓:", f"{drone_id}_battery", "--")
|
||||||
self.create_data_row(layout, "GPS:", f"{drone_id}_gps", "等待定位...")
|
self.create_data_row(layout, "GPS:", f"{drone_id}_gps", "--")
|
||||||
self.create_data_row(layout, "Local:", f"{drone_id}_local", "X: -- Y: -- Z: --")
|
self.create_data_row(layout, "高度:", f"{drone_id}_altitude", "--")
|
||||||
self.create_data_row(layout, "速度:", f"{drone_id}_velocity", "VX: -- VY: -- VZ: --")
|
self.create_data_row(layout, "Local:", f"{drone_id}_local", "--")
|
||||||
self.create_data_row(layout, "姿態:", f"{drone_id}_attitude", "Roll: -- Pitch: -- Yaw: --")
|
self.create_data_row(layout, "HUD:", f"{drone_id}_hud", "--")
|
||||||
|
|
||||||
return panel
|
return panel
|
||||||
|
|
||||||
@@ -317,7 +339,7 @@ class ControlStationUI(QMainWindow):
|
|||||||
return
|
return
|
||||||
|
|
||||||
if msg_type == 'mode':
|
if msg_type == 'mode':
|
||||||
self.update_field(panel, drone_id, 'mode', f"模式: {data}",
|
self.update_field(panel, drone_id, 'mode', f"{data}",
|
||||||
'#FF5555' if '返航' in data else '#55FF55')
|
'#FF5555' if '返航' in data else '#55FF55')
|
||||||
|
|
||||||
elif msg_type == 'battery':
|
elif msg_type == 'battery':
|
||||||
@@ -327,18 +349,26 @@ class ControlStationUI(QMainWindow):
|
|||||||
'#FF6464' if voltage < 12 else '#FFFFFF')
|
'#FF6464' if voltage < 12 else '#FFFFFF')
|
||||||
|
|
||||||
elif msg_type == 'gps':
|
elif msg_type == 'gps':
|
||||||
text = (f"緯度: {data['lat']:.6f}\n"
|
text = (f"緯度: {data['lat']:.6f}°\n"
|
||||||
f"經度: {data['lon']:.6f}\n"
|
f"經度: {data['lon']:.6f}°")
|
||||||
f"高度: {data['alt']:.1f} m")
|
|
||||||
self.update_field(panel, drone_id, 'gps', text)
|
self.update_field(panel, drone_id, 'gps', text)
|
||||||
self.update_map_display(f"Drone {drone_id}\n{text}")
|
if self.on_map_loaded:
|
||||||
|
js = f"updateDrone({data['lat']}, {data['lon']:.6f}, {drone_id})"
|
||||||
|
self.map_view.page().runJavaScript(js)
|
||||||
|
|
||||||
|
elif msg_type == 'altitude':
|
||||||
|
text = (f"{data['altitude']:.1f} m")
|
||||||
|
self.update_field(panel, drone_id, 'altitude', text)
|
||||||
|
|
||||||
elif msg_type == 'local_pose':
|
elif msg_type == 'local_pose':
|
||||||
text = (f"X: {data['x']:.1f} m\n"
|
text = (f"({data['x']:.1f}, {data['y']:.1f}, {data['z']:.1f})")
|
||||||
f"Y: {data['y']:.1f} m\n"
|
|
||||||
f"Z: {data['z']:.1f} m")
|
|
||||||
self.update_field(panel, drone_id, 'local', text)
|
self.update_field(panel, drone_id, 'local', text)
|
||||||
|
|
||||||
|
elif msg_type == 'hud':
|
||||||
|
text = (f"地速: {data['groundspeed']:.1f} m/s\n"
|
||||||
|
f"航向: {data['heading']:.1f}°")
|
||||||
|
self.update_field(panel, drone_id, 'hud', text)
|
||||||
|
'''
|
||||||
elif msg_type == 'velocity':
|
elif msg_type == 'velocity':
|
||||||
text = (f"VX: {data['vx']:.1f} m/s\n"
|
text = (f"VX: {data['vx']:.1f} m/s\n"
|
||||||
f"VY: {data['vy']:.1f} m/s\n"
|
f"VY: {data['vy']:.1f} m/s\n"
|
||||||
@@ -350,13 +380,7 @@ class ControlStationUI(QMainWindow):
|
|||||||
f"Pitch: {data['pitch']:.1f}°\n"
|
f"Pitch: {data['pitch']:.1f}°\n"
|
||||||
f"Yaw: {data['yaw']:.1f}°")
|
f"Yaw: {data['yaw']:.1f}°")
|
||||||
self.update_field(panel, drone_id, 'attitude', text)
|
self.update_field(panel, drone_id, 'attitude', text)
|
||||||
|
'''
|
||||||
elif msg_type == 'hud':
|
|
||||||
text = (f"空速: {data['airspeed']:.1f} m/s\n"
|
|
||||||
f"地速: {data['groundspeed']:.1f} m/s\n"
|
|
||||||
f"航向: {data['heading']:.1f}°")
|
|
||||||
self.update_field(panel, drone_id, 'hud', text)
|
|
||||||
|
|
||||||
def update_field(self, panel, drone_id, field, text, color=None):
|
def update_field(self, panel, drone_id, field, text, color=None):
|
||||||
if label := panel.findChild(QLabel, f"{drone_id}_{field}"):
|
if label := panel.findChild(QLabel, f"{drone_id}_{field}"):
|
||||||
label.setText(text)
|
label.setText(text)
|
||||||
@@ -369,10 +393,6 @@ class ControlStationUI(QMainWindow):
|
|||||||
self.info_layout.addWidget(panel)
|
self.info_layout.addWidget(panel)
|
||||||
self.drones[drone_id] = panel
|
self.drones[drone_id] = panel
|
||||||
|
|
||||||
def update_map_display(self, text):
|
|
||||||
self.map_widget.setText(text)
|
|
||||||
self.map_widget.repaint()
|
|
||||||
|
|
||||||
def spin_ros(self):
|
def spin_ros(self):
|
||||||
try:
|
try:
|
||||||
self.executor.spin_once(timeout_sec=0)
|
self.executor.spin_once(timeout_sec=0)
|
||||||
@@ -386,7 +406,7 @@ class ControlStationUI(QMainWindow):
|
|||||||
event.accept()
|
event.accept()
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
app = QApplication([])
|
app = QApplication(sys.argv)
|
||||||
station = ControlStationUI()
|
station = ControlStationUI()
|
||||||
station.show()
|
station.show()
|
||||||
app.exec()
|
app.exec(app.exec())
|
||||||
Reference in New Issue
Block a user