sample_code/soc_pkg/ros_soc.py
2025-04-01 08:52:50 +08:00

126 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
# *- coding: utf-8 --
import sys
import numpy as np
import rospy
from std_msgs.msg import Float32 # 假设电压数据是 Float32 类型
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout
from PyQt5.QtGui import QPainter, QColor, QBrush, QFont
from PyQt5.QtCore import Qt, QTimer
# 已知总电压与电量百分比SoC的对应关系
total_voltages = [25.2, 24.6, 24.0, 23.4, 22.8, 22.2, 21.6, 21.0, 20.4, 19.8, 18.0]
socs = [100, 90, 80, 70, 60, 50, 40, 30, 20, 10, 0]
def get_soc_from_total_voltage(total_voltage):
# 使用线性插值计算电量百分比SoC
soc = np.interp(total_voltage, total_voltages[::-1], socs[::-1])
return max(0, min(100, soc)) # 确保 SoC 在 0% 到 100% 之间
class BatteryWidget(QWidget):
def __init__(self, total_voltage=24.4):
super().__init__()
self.total_voltage = total_voltage
self.soc = get_soc_from_total_voltage(self.total_voltage)
def paintEvent(self, event):
# 自定义绘制电池图标和百分比文字
painter = QPainter(self)
painter.setRenderHint(QPainter.Antialiasing) # 开启抗锯齿
# 绘制电池轮廓(小尺寸以适应固定窗口)
width, height = 80, 40 # 电池图标的宽度和高度
x, y = (self.width() - width) // 2, 20 # 图标垂直居中
painter.setPen(Qt.black) # 设置边框颜色为黑色
painter.setBrush(QColor(200, 200, 200)) # 设置背景颜色为灰色
painter.drawRoundedRect(x, y, width, height, 3, 3) # 绘制圆角矩形
# 绘制电池正极(小尺寸终端)
painter.setBrush(QColor(0, 0, 0)) # 设置正极颜色为黑色
painter.drawRect(x + width, y + height // 4, width // 10, height // 2)
# 根据电量百分比填充电池
fill_height = int(height * 0.8) # 留出上下边距
fill_width = int((width - 4) * (self.soc / 100)) # 根据 SoC 缩放填充宽度
fill_color = self.get_battery_color(self.soc) # 获取电量对应的颜色
painter.setBrush(QBrush(fill_color))
painter.drawRect(x + 2, y + (height - fill_height) // 2, fill_width, fill_height)
# 绘制百分比文字在电池图标下方
font = QFont("Arial", 12) # 设置字体大小
painter.setFont(font)
text = f"{self.soc:.1f}%" # 百分比格式化为一位小数
text_width = painter.fontMetrics().width(text) # 获取文字宽度
text_height = painter.fontMetrics().height() # 获取文字高度
painter.setPen(Qt.black) # 设置文字颜色为黑色
painter.drawText(
(self.width() - text_width) // 2, # 水平居中
y + height + text_height + 5, # 放置在电池图标下方
text
)
def get_battery_color(self, soc):
# 根据电量百分比设置电池颜色
if soc > 80:
return QColor(0, 255, 0) # 绿色
elif soc > 50:
return QColor(255, 255, 0) # 黄色
elif soc > 20:
return QColor(255, 165, 0) # 橙色
else:
return QColor(255, 0, 0) # 红色
def update_voltage(self, total_voltage):
# 更新电池电压并重新绘制
self.total_voltage = total_voltage
self.soc = get_soc_from_total_voltage(self.total_voltage)
self.update() # 触发重新绘制
class MainWindow(QWidget):
def __init__(self):
super().__init__()
# 初始化默认电压
self.battery_voltage = 24.4 # 默认初始电压
self.init_ui()
self.init_ros()
def init_ui(self):
self.setWindowTitle("电池状态") # 设置窗口标题
self.setFixedSize(300, 120) # 设置固定窗口大小
# 创建电池组件
self.battery_widget = BatteryWidget(total_voltage=self.battery_voltage) # 初始电压
layout = QVBoxLayout()
layout.addWidget(self.battery_widget)
self.setLayout(layout)
def init_ros(self):
# 初始化 ROS 节点
rospy.init_node("battery_status_gui", anonymous=True)
rospy.Subscriber("/PowerVoltage", Float32, self.voltage_callback)
self.timer = QTimer(self)
self.timer.timeout.connect(self.ros_spin_once)
self.timer.start(100) # 每 100ms 调用一次 ROS spin
def voltage_callback(self, msg):
# 订阅到电压数据时更新电池电压
self.battery_voltage = msg.data
self.battery_widget.update_voltage(self.battery_voltage)
def ros_spin_once(self):
# 手动调用 ROS spinOnce
rospy.sleep(0.1)
if __name__ == "__main__":
app = QApplication(sys.argv)
window = MainWindow()
window.show()
# 运行 PyQt 主循环
try:
sys.exit(app.exec_())
except rospy.ROSInterruptException:
pass