Upload files to 'src/unitdev01/unitdev01'
This commit is contained in:
@@ -3,11 +3,14 @@ import rclpy
|
||||
from rclpy.node import Node
|
||||
from PyQt6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout, QHBoxLayout,
|
||||
QWidget, QLabel, QSplitter, QScrollArea,
|
||||
QSizePolicy)
|
||||
from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSignal
|
||||
QSizePolicy, QApplication)
|
||||
from PyQt6.QtCore import Qt, QTimer, QObject, pyqtSignal, QUrl
|
||||
from PyQt6.QtGui import QColor
|
||||
from PyQt6.QtWebEngineWidgets import QWebEngineView
|
||||
import math
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
from threading import Lock
|
||||
from geometry_msgs.msg import Point, Vector3
|
||||
from sensor_msgs.msg import BatteryState, NavSatFix, Imu
|
||||
@@ -138,8 +141,8 @@ class DroneMonitor(Node):
|
||||
def gps_callback(self, drone_id, msg):
|
||||
self.signals.update_signal.emit('gps', drone_id, {
|
||||
'lat': msg.latitude,
|
||||
'lon': msg.longitude,
|
||||
'alt': msg.altitude
|
||||
'lon': msg.longitude
|
||||
,'alt': msg.altitude
|
||||
})
|
||||
|
||||
def local_vel_callback(self, drone_id, msg):
|
||||
@@ -150,7 +153,9 @@ class DroneMonitor(Node):
|
||||
})
|
||||
|
||||
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):
|
||||
self.signals.update_signal.emit('local_pose', drone_id, {
|
||||
@@ -212,26 +217,43 @@ class ControlStationUI(QMainWindow):
|
||||
left_layout.addWidget(scroll_area)
|
||||
|
||||
# 右侧地图区域
|
||||
self.map_widget = QLabel()
|
||||
self.map_widget.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
self.map_widget.setStyleSheet("""
|
||||
QLabel {
|
||||
background-color: #1A1A1A;
|
||||
color: #AAAAAA;
|
||||
font-size: 18px;
|
||||
border: 2px solid #333;
|
||||
border-radius: 10px;
|
||||
padding: 20px;
|
||||
self.map_view = QWebEngineView()
|
||||
|
||||
inline_html = '''
|
||||
<!DOCTYPE html>
|
||||
<html><head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css"/>
|
||||
<script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
|
||||
<style>html,body,#map{height:100%;margin:0}</style>
|
||||
</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);
|
||||
}
|
||||
""")
|
||||
self.update_map_display("等待GPS數據...")
|
||||
</script>
|
||||
</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(self.map_widget)
|
||||
main_splitter.addWidget(self.map_view)
|
||||
main_splitter.setSizes([400, 1000])
|
||||
|
||||
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):
|
||||
panel = QWidget()
|
||||
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}_battery", "-- V")
|
||||
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}_velocity", "VX: -- VY: -- VZ: --")
|
||||
self.create_data_row(layout, "姿態:", f"{drone_id}_attitude", "Roll: -- Pitch: -- Yaw: --")
|
||||
self.create_data_row(layout, "電壓:", f"{drone_id}_battery", "--")
|
||||
self.create_data_row(layout, "GPS:", f"{drone_id}_gps", "--")
|
||||
self.create_data_row(layout, "高度:", f"{drone_id}_altitude", "--")
|
||||
self.create_data_row(layout, "Local:", f"{drone_id}_local", "--")
|
||||
self.create_data_row(layout, "HUD:", f"{drone_id}_hud", "--")
|
||||
|
||||
return panel
|
||||
|
||||
@@ -317,7 +339,7 @@ class ControlStationUI(QMainWindow):
|
||||
return
|
||||
|
||||
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')
|
||||
|
||||
elif msg_type == 'battery':
|
||||
@@ -327,18 +349,26 @@ class ControlStationUI(QMainWindow):
|
||||
'#FF6464' if voltage < 12 else '#FFFFFF')
|
||||
|
||||
elif msg_type == 'gps':
|
||||
text = (f"緯度: {data['lat']:.6f}\n"
|
||||
f"經度: {data['lon']:.6f}\n"
|
||||
f"高度: {data['alt']:.1f} m")
|
||||
text = (f"緯度: {data['lat']:.6f}°\n"
|
||||
f"經度: {data['lon']:.6f}°")
|
||||
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':
|
||||
text = (f"X: {data['x']:.1f} m\n"
|
||||
f"Y: {data['y']:.1f} m\n"
|
||||
f"Z: {data['z']:.1f} m")
|
||||
text = (f"({data['x']:.1f}, {data['y']:.1f}, {data['z']:.1f})")
|
||||
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':
|
||||
text = (f"VX: {data['vx']:.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"Yaw: {data['yaw']:.1f}°")
|
||||
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):
|
||||
if label := panel.findChild(QLabel, f"{drone_id}_{field}"):
|
||||
label.setText(text)
|
||||
@@ -369,10 +393,6 @@ class ControlStationUI(QMainWindow):
|
||||
self.info_layout.addWidget(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):
|
||||
try:
|
||||
self.executor.spin_once(timeout_sec=0)
|
||||
@@ -386,7 +406,7 @@ class ControlStationUI(QMainWindow):
|
||||
event.accept()
|
||||
|
||||
if __name__ == '__main__':
|
||||
app = QApplication([])
|
||||
app = QApplication(sys.argv)
|
||||
station = ControlStationUI()
|
||||
station.show()
|
||||
app.exec()
|
||||
app.exec(app.exec())
|
||||
Reference in New Issue
Block a user