Heater: force output off in overtemp region (err<0); Kp 4->10

This commit is contained in:
2026-10-10 14:14:09 +08:00
parent 12ba9de05f
commit 974a077e29
4 changed files with 39 additions and 24 deletions
+3
View File
@@ -152,6 +152,9 @@
截图确认);改为纯阶梯(变更时刻垂直落变+水平延伸),数据本身无误。
- v0.2.5:排版调整——串口连接卡片从右列移到左列顶部,左列图表卡/右列日志卡
弹性拉伸填充,两列底边对齐。
- v0.2.6:对齐修复——双列改放同一滚动容器(整页统一滚动):原先两列各自包
QScrollArea 独立滚动,窗口化下底边错位(全屏正常);现两列高度锁定一致,
不足一屏各自拉伸到底、超出一屏整页滚动。
- selftest 全过(累计新增 14 项回归用例);exe 已本地打包冒烟,Gitea 发布
待用户确认后执行。
遗留见其 README"已知事项":应用图标、USB-RS485 适配器更换。
+18 -11
View File
@@ -60,7 +60,7 @@ import config
import filmforge_rs485 as ff
APP_NAME = "FilmForge"
APP_VERSION = "0.2.5"
APP_VERSION = "0.2.6"
REPO_URL = "https://gitea.hulk.wang/hulk/FilmForge"
MONO = QFont("Consolas", 10)
CHIP_IDLE = "#666666"
@@ -470,27 +470,34 @@ class App(QMainWindow):
header.addWidget(self._button("关于", self._about))
outer.addLayout(header)
split = QSplitter(Qt.Horizontal, root)
# 双列放进同一滚动容器: 两列高度锁定一致——不足一屏时各自拉伸到底边,
# 超出一屏时整页一起滚动; 各列独立滚动会让底边在窗口化下错位(实测)
split = QSplitter(Qt.Horizontal)
split.setChildrenCollapsible(False)
split.addWidget(self._scroll_column(self._build_left))
split.addWidget(self._scroll_column(self._build_right))
split.addWidget(self._column_host(self._build_left))
split.addWidget(self._column_host(self._build_right))
split.setStretchFactor(0, 3)
split.setStretchFactor(1, 2)
outer.addWidget(split, 1)
page = QWidget()
page_lay = QHBoxLayout(page)
page_lay.setContentsMargins(0, 0, 0, 0)
page_lay.addWidget(split)
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setFrameShape(QScrollArea.NoFrame)
scroll.setWidget(page)
outer.addWidget(scroll, 1)
self.setCentralWidget(root)
def _scroll_column(self, builder):
def _column_host(self, builder):
"""构建一列的宿主 QWidget(不再各自包 QScrollArea, 整页统一滚动)"""
host = QWidget()
layout = QVBoxLayout(host)
layout.setContentsMargins(2, 2, 8, 2)
layout.setSpacing(10)
builder(layout)
layout.addStretch(1)
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setFrameShape(QScrollArea.NoFrame)
scroll.setWidget(host)
return scroll
return host
# ---------------- 左列: 串口 + 监视 ----------------
def _build_serial_card(self, layout):
+1 -1
View File
@@ -152,7 +152,7 @@ void usart1_irq_handler(void);
#define HEATER_PWM_PERIOD_MS 1000U /* PWM 载波周期(1s,恒 1Hz 开关频率) */
#define HEATER_PWM_STEP_PCT 10U /* 占空比步进(每档脉宽 = 100ms) */
#define HEATER_PID_PERIOD_MS 500U /* PID 计算周期 */
#define HEATER_PID_KP 4.0f /* PID 参数:小 Kp 换取低过冲(接近目标时减速) */
#define HEATER_PID_KP 10.0f /* PID 参数:超温前退功率更快(配合超温强制关断) */
#define HEATER_PID_KI 0.05f
#define HEATER_PID_KD 30.0f /* 微分阻尼:按误差变化率提前刹车(抑制爬升惯性) */
#define HEATER_RAMP_CPS 0.8f /* 目标斜坡速率(℃/s):内部设定值缓升,抑制热滞后过冲 */
+13 -8
View File
@@ -111,21 +111,26 @@ static void pid_compute(void) {
float u_noi = HEATER_PID_KP * err
+ HEATER_PID_KD * (err - s_prev_err); /* 比例 + 微分 */
float integ_new = s_integral + err;
/* 超温区间(实际温度高于内部目标):加热器只能加热、不能制冷,
输出必须为 0(此前积分正残量会算出小占空在超调段续灌加热);
积分按负误差同步回退,为温度回落后的重新加热保留正确初始态 */
if (err < 0.0f) {
s_integral = integ_new;
s_prev_err = err;
s_duty10 = 0;
} else {
/* 误差为正(未达/低于内部目标):正常 PID + 抗积分饱和 */
float u = u_noi + HEATER_PID_KI * integ_new;
if (u > 100.0f) {
u = 100.0f;
if (err < 0.0f) s_integral = integ_new; /* 超上限:仅误差为负(降温方向)才吸收 */
} else if (u < 0.0f) {
u = 0.0f;
if (err > 0.0f) s_integral = integ_new; /* 超下限:仅误差为正(升温方向)才吸收 */
/* 超上限:仅误差为负才吸收——err>=0 分支不会发生,保持积分不动 */
} else {
s_integral = integ_new; /* 未饱和:正常积分 */
s_integral = integ_new;
}
s_prev_err = err;
/* 输出量化到 10% 档位(四舍五入) */
s_duty10 = (uint8_t)((u + (float)HEATER_PWM_STEP_PCT / 2.0f) / (float)HEATER_PWM_STEP_PCT);
}
s_prev_err = err;
}
/******************************************************************************/