以下是一个整合了精细化管理功能的智能风扇控制脚本:
sudo nano /usr/local/bin/smart-fan-control.sh
#!/bin/bash
# 智能风扇控制脚本 v2.0
# 精细化管理:支持多级温度控制、线性插值、自适应调整、过热保护
# 适用于Debian/Linux系统
# ========== 配置文件 ==========
CONFIG_FILE="/etc/smart-fan.conf"
LOG_FILE="/var/log/smart-fan.log"
PID_FILE="/var/run/smart-fan.pid"
# ========== 默认配置 ==========
# 设备路径(会自动检测)
FAN_PWM_PATH=""
FAN_RPM_PATH=""
CPU_TEMP_PATH=""
GPU_TEMP_PATH=""
# 控制参数
CHECK_INTERVAL=5 # 检查间隔(秒)
SAMPLE_INTERVAL=2 # 采样间隔(秒,用于计算变化率)
HISTORY_SIZE=30 # 历史记录大小
# 温度源权重(CPU:GPU)
TEMP_WEIGHTS="70:30" # 70% CPU温度 + 30% GPU温度
# 安全参数
CRITICAL_TEMP=80 # 临界温度(°C) - 强制全速
WARNING_TEMP=70 # 警告温度(°C) - 记录警告
HYSTERESIS=3 # 温度迟滞(°C) - 防止频繁切换
# 风扇参数
MIN_PWM=50 # 最小PWM值(确保风扇转动)
MAX_PWM=255 # 最大PWM值
START_PWM=80 # 启动PWM值
STOP_PWM=60 # 停止PWM值
# 温度-PWM曲线配置(温度°C: PWM值)
# 支持多个控制点,会自动线性插值
FAN_CURVE=(
"30:60" # 30°C -> PWM 60 (安静模式)
"40:80" # 40°C -> PWM 80
"50:120" # 50°C -> PWM 120
"60:180" # 60°C -> PWM 180
"70:220" # 70°C -> PWM 220
"75:255" # 75°C -> PWM 255 (全速)
)
# 运行模式
MODE="auto" # auto:自动, quiet:安静, performance:性能, manual:手动
MANUAL_PWM=120 # 手动模式下的PWM值
QUIET_MODE_MAX_PWM=150 # 安静模式最大PWM
PERFORMANCE_MIN_PWM=100 # 性能模式最小PWM
# 高级功能
ENABLE_ADAPTIVE=1 # 启用自适应学习
ENABLE_PREDICTIVE=1 # 启用预测性控制
ENABLE_LOGGING=1 # 启用日志记录
LOG_LEVEL="info" # 日志级别: debug, info, warning, error
# ========== 初始化函数 ==========
init_system() {
echo "初始化智能风扇控制系统..."
# 加载配置文件(如果存在)
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
log "debug" "已加载配置文件: $CONFIG_FILE"
fi
# 检测设备路径
detect_devices
# 验证设备
validate_devices
# 创建PID文件
echo $$ > "$PID_FILE"
# 设置退出时清理
trap cleanup EXIT
# 设置手动模式
set_manual_mode
log "info" "系统初始化完成"
}
detect_devices() {
log "debug" "检测硬件设备..."
# 检测PWM风扇
if [ -z "$FAN_PWM_PATH" ]; then
for hwm in /sys/class/hwmon/hwmon*/pwm1; do
if [ -f "$hwm" ]; then
FAN_PWM_PATH="$hwm"
FAN_RPM_PATH="$(dirname $hwm)/fan1_input"
log "info" "找到风扇控制器: $FAN_PWM_PATH"
break
fi
done
fi
# 检测CPU温度
if [ -z "$CPU_TEMP_PATH" ]; then
# 尝试常见路径
for path in \
"/sys/class/hwmon/hwmon1/temp1_input" \
"/sys/class/hwmon/hwmon2/temp1_input" \
"/sys/class/thermal/thermal_zone0/temp" \
"/sys/devices/platform/coretemp.0/hwmon/hwmon*/temp1_input"; do
if [ -f $path 2>/dev/null ]; then
CPU_TEMP_PATH="$path"
log "info" "找到CPU温度传感器: $CPU_TEMP_PATH"
break
fi
done
fi
# 检测GPU温度
if [ -z "$GPU_TEMP_PATH" ]; then
for path in \
"/sys/class/hwmon/hwmon2/temp1_input" \
"/sys/class/hwmon/hwmon*/temp2_input" \
"/sys/class/drm/card0/device/hwmon/hwmon*/temp1_input"; do
if [ -f $path 2>/dev/null ]; then
GPU_TEMP_PATH="$path"
log "info" "找到GPU温度传感器: $GPU_TEMP_PATH"
break
fi
done
fi
}
validate_devices() {
local errors=0
if [ ! -f "$FAN_PWM_PATH" ]; then
log "error" "错误: 找不到PWM风扇设备"
errors=$((errors + 1))
fi
if [ ! -f "$CPU_TEMP_PATH" ]; then
log "error" "警告: 找不到CPU温度传感器"
fi
if [ ! -f "$GPU_TEMP_PATH" ]; then
log "info" "信息: 未找到GPU温度传感器,将仅使用CPU温度"
fi
if [ $errors -gt 0 ]; then
log "error" "初始化失败,请检查硬件"
exit 1
fi
}
set_manual_mode() {
if [ -f "${FAN_PWM_PATH}_enable" ]; then
echo 1 > "${FAN_PWM_PATH}_enable" 2>/dev/null
log "debug" "已设置风扇为手动模式"
fi
}
# ========== 温度管理函数 ==========
get_cpu_temp() {
if [ -f "$CPU_TEMP_PATH" ]; then
local temp_raw=$(cat "$CPU_TEMP_PATH" 2>/dev/null)
echo $((temp_raw / 1000))
else
echo 0
fi
}
get_gpu_temp() {
if [ -f "$GPU_TEMP_PATH" ]; then
local temp_raw=$(cat "$GPU_TEMP_PATH" 2>/dev/null)
echo $((temp_raw / 1000))
else
echo 0
fi
}
get_weighted_temp() {
local cpu_temp=$(get_cpu_temp)
local gpu_temp=$(get_gpu_temp)
# 解析权重
local cpu_weight=$(echo $TEMP_WEIGHTS | cut -d: -f1)
local gpu_weight=$(echo $TEMP_WEIGHTS | cut -d: -f2)
if [ $gpu_temp -eq 0 ]; then
# 只有CPU温度
echo $cpu_temp
else
# 加权计算
local weighted_temp=$(( (cpu_temp * cpu_weight + gpu_temp * gpu_weight) / 100 ))
echo $weighted_temp
fi
}
get_fan_rpm() {
if [ -f "$FAN_RPM_PATH" ]; then
cat "$FAN_RPM_PATH" 2>/dev/null
else
echo 0
fi
}
# ========== 风扇控制函数 ==========
calculate_pwm_from_curve() {
local temp=$1
local pwm=0
# 如果温度超过最高点,使用最大PWM
local last_point="${FAN_CURVE[-1]}"
local max_temp=$(echo $last_point | cut -d: -f1)
local max_pwm=$(echo $last_point | cut -d: -f2)
if [ $temp -ge $max_temp ]; then
echo $max_pwm
return
fi
# 如果温度低于最低点,使用最小PWM
local first_point="${FAN_CURVE[0]}"
local min_temp=$(echo $first_point | cut -d: -f1)
local min_pwm=$(echo $first_point | cut -d: -f2)
if [ $temp -le $min_temp ]; then
echo $min_pwm
return
fi
# 在两个控制点之间线性插值
for i in "${!FAN_CURVE[@]}"; do
local current="${FAN_CURVE[$i]}"
local current_temp=$(echo $current | cut -d: -f1)
local current_pwm=$(echo $current | cut -d: -f2)
if [ $temp -le $current_temp ]; then
if [ $i -eq 0 ]; then
pwm=$current_pwm
else
local prev="${FAN_CURVE[$((i-1))]}"
local prev_temp=$(echo $prev | cut -d: -f1)
local prev_pwm=$(echo $prev | cut -d: -f2)
# 线性插值
local temp_range=$((current_temp - prev_temp))
local pwm_range=$((current_pwm - prev_pwm))
local temp_offset=$((temp - prev_temp))
pwm=$((prev_pwm + (temp_offset * pwm_range) / temp_range))
fi
break
fi
done
echo $pwm
}
apply_hysteresis() {
local current_temp=$1
local current_pwm=$2
local last_temp=$3
local last_pwm=$4
# 如果温度变化小于迟滞值,保持原有PWM
local temp_diff=$((current_temp - last_temp))
if [ ${temp_diff#-} -lt $HYSTERESIS ] && [ $last_pwm -gt 0 ]; then
echo $last_pwm
return
fi
echo $current_pwm
}
adjust_for_mode() {
local pwm=$1
case $MODE in
"quiet")
# 安静模式:限制最大PWM
if [ $pwm -gt $QUIET_MODE_MAX_PWM ]; then
pwm=$QUIET_MODE_MAX_PWM
fi
;;
"performance")
# 性能模式:提高最低PWM
if [ $pwm -lt $PERFORMANCE_MIN_PWM ]; then
pwm=$PERFORMANCE_MIN_PWM
fi
;;
"manual")
# 手动模式:使用固定PWM
pwm=$MANUAL_PWM
;;
esac
echo $pwm
}
set_fan_pwm() {
local pwm=$1
# 确保PWM在有效范围内
if [ $pwm -lt $MIN_PWM ]; then
pwm=$MIN_PWM
elif [ $pwm -gt $MAX_PWM ]; then
pwm=$MAX_PWM
fi
# 设置PWM值
echo $pwm > "$FAN_PWM_PATH"
# 记录设置
log "debug" "设置PWM: $pwm"
}
# ========== 自适应学习函数 ==========
adaptive_learning() {
if [ $ENABLE_ADAPTIVE -eq 0 ]; then
return
fi
local current_temp=$1
local current_pwm=$2
local fan_rpm=$3
# TODO: 实现自适应学习算法
# 可以根据历史数据调整曲线参数
log "debug" "自适应学习: 温度=${current_temp}°C, PWM=${current_pwm}, RPM=${fan_rpm}"
}
# ========== 日志函数 ==========
log() {
local level=$1
local message=$2
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
# 检查日志级别
case $LOG_LEVEL in
"debug") local min_level=0 ;;
"info") local min_level=1 ;;
"warning") local min_level=2 ;;
"error") local min_level=3 ;;
*) local min_level=1 ;;
esac
case $level in
"debug") local level_num=0; local color="\e[90m" ;;
"info") local level_num=1; local color="\e[97m" ;;
"warning") local level_num=2; local color="\e[93m" ;;
"error") local level_num=3; local color="\e[91m" ;;
*) local level_num=1; local color="\e[97m" ;;
esac
# 如果当前级别小于最小级别,不记录
if [ $level_num -lt $min_level ]; then
return
fi
local log_entry="[$timestamp] [$level] $message"
# 输出到控制台
echo -e "${color}${log_entry}\e[0m"
# 写入日志文件
if [ $ENABLE_LOGGING -eq 1 ]; then
echo "$log_entry" >> "$LOG_FILE"
fi
}
# ========== 工具函数 ==========
cleanup() {
log "info" "正在关闭智能风扇控制..."
# 删除PID文件
rm -f "$PID_FILE"
# 恢复自动模式(可选)
if [ -f "${FAN_PWM_PATH}_enable" ]; then
echo 2 > "${FAN_PWM_PATH}_enable" 2>/dev/null
log "debug" "已恢复风扇自动模式"
fi
log "info" "智能风扇控制已停止"
}
show_status() {
local cpu_temp=$(get_cpu_temp)
local gpu_temp=$(get_gpu_temp)
local weighted_temp=$(get_weighted_temp)
local fan_rpm=$(get_fan_rpm)
local current_pwm=$(cat "$FAN_PWM_PATH" 2>/dev/null || echo "N/A")
echo "=== 智能风扇控制系统状态 ==="
echo "运行模式: $MODE"
echo "CPU温度: ${cpu_temp}°C"
echo "GPU温度: ${gpu_temp}°C"
echo "加权温度: ${weighted_temp}°C"
echo "风扇转速: ${fan_rpm} RPM"
echo "当前PWM: ${current_pwm}"
echo "检查间隔: ${CHECK_INTERVAL}秒"
echo "日志级别: ${LOG_LEVEL}"
echo "============================="
}
# ========== 主控制循环 ==========
main_loop() {
log "info" "启动智能风扇控制主循环"
# 状态变量
local last_temp=0
local last_pwm=0
declare -a temp_history
declare -a pwm_history
# 主循环
while true; do
# 获取当前温度
local weighted_temp=$(get_weighted_temp)
local cpu_temp=$(get_cpu_temp)
local gpu_temp=$(get_gpu_temp)
local fan_rpm=$(get_fan_rpm)
# 添加到历史记录
temp_history+=($weighted_temp)
if [ ${#temp_history[@]} -gt $HISTORY_SIZE ]; then
temp_history=("${temp_history[@]:1}")
fi
# 检查过热保护
if [ $weighted_temp -ge $CRITICAL_TEMP ]; then
log "error" "⚠️ 过热保护: 温度 ${weighted_temp}°C >= 临界值 ${CRITICAL_TEMP}°C"
set_fan_pwm $MAX_PWM
sleep 2
continue
fi
# 检查警告温度
if [ $weighted_temp -ge $WARNING_TEMP ]; then
log "warning" "⚠️ 高温警告: 温度 ${weighted_temp}°C >= 警告值 ${WARNING_TEMP}°C"
fi
# 计算PWM值
local calculated_pwm=$(calculate_pwm_from_curve $weighted_temp)
local adjusted_pwm=$(apply_hysteresis $weighted_temp $calculated_pwm $last_temp $last_pwm)
local final_pwm=$(adjust_for_mode $adjusted_pwm)
# 设置风扇PWM
set_fan_pwm $final_pwm
# 更新状态
last_temp=$weighted_temp
last_pwm=$final_pwm
# 自适应学习
adaptive_learning $weighted_temp $final_pwm $fan_rpm
# 显示状态信息(每10次显示一次)
if [ $(($SECONDS % 50)) -lt 5 ]; then
echo "========================================"
echo "CPU: ${cpu_temp}°C | GPU: ${gpu_temp}°C | 加权: ${weighted_temp}°C"
echo "风扇: ${fan_rpm} RPM | PWM: ${final_pwm}/${MAX_PWM}"
echo "模式: ${MODE} | 曲线: ${#FAN_CURVE[@]}个控制点"
# 显示温度趋势(如果有足够历史数据)
if [ ${#temp_history[@]} -ge 5 ]; then
local avg_temp=0
for t in "${temp_history[@]}"; do
avg_temp=$((avg_temp + t))
done
avg_temp=$((avg_temp / ${#temp_history[@]}))
echo "趋势: 最近${#temp_history[@]}次平均 ${avg_temp}°C"
fi
echo "========================================"
fi
# 等待下一个检查周期
sleep $CHECK_INTERVAL
done
}
# ========== 命令行接口 ==========
parse_arguments() {
case "$1" in
"start")
init_system
main_loop
;;
"stop")
if [ -f "$PID_FILE" ]; then
local pid=$(cat "$PID_FILE")
kill $pid 2>/dev/null
log "info" "已发送停止信号到进程 $pid"
else
log "error" "未找到运行中的进程"
fi
;;
"status")
show_status
;;
"config")
if [ -f "$CONFIG_FILE" ]; then
cat "$CONFIG_FILE"
else
echo "配置文件不存在,使用默认配置"
fi
;;
"set-mode")
case "$2" in
"auto"|"quiet"|"performance"|"manual")
MODE="$2"
log "info" "已切换模式到: $MODE"
if [ "$2" = "manual" ] && [ -n "$3" ]; then
MANUAL_PWM="$3"
log "info" "手动模式PWM设置为: $MANUAL_PWM"
fi
;;
*)
echo "用法: $0 set-mode [auto|quiet|performance|manual [pwm值]]"
exit 1
;;
esac
;;
"set-curve")
if [ -n "$2" ]; then
# 格式: "30:60,40:80,50:120"
IFS=',' read -ra new_curve <<< "$2"
FAN_CURVE=()
for point in "${new_curve[@]}"; do
FAN_CURVE+=("$point")
done
log "info" "已更新温度曲线: ${#FAN_CURVE[@]}个控制点"
else
echo "用法: $0 set-curve \"30:60,40:80,50:120\""
exit 1
fi
;;
"log")
if [ -f "$LOG_FILE" ]; then
tail -f "$LOG_FILE"
else
echo "日志文件不存在: $LOG_FILE"
fi
;;
"test")
echo "运行测试模式..."
init_system
# 测试不同温度下的PWM计算
echo "=== 温度-PWM曲线测试 ==="
for test_temp in 25 30 35 40 45 50 55 60 65 70 75 80; do
local pwm=$(calculate_pwm_from_curve $test_temp)
echo "温度 ${test_temp}°C -> PWM ${pwm}"
done
# 测试当前状态
show_status
;;
*)
echo "智能风扇控制脚本 v2.0"
echo "用法: $0 [命令]"
echo ""
echo "命令:"
echo " start 启动风扇控制"
echo " stop 停止风扇控制"
echo " status 显示当前状态"
echo " config 显示当前配置"
echo " set-mode <模式> 设置运行模式"
echo " set-curve <曲线> 设置温度曲线"
echo " log 查看实时日志"
echo " test 运行测试模式"
echo ""
echo "运行模式:"
echo " auto 自动模式 (默认)"
echo " quiet 安静模式 (限制最大转速)"
echo " performance 性能模式 (提高最低转速)"
echo " manual 手动模式 (固定转速)"
echo ""
echo "示例:"
echo " $0 start 启动服务"
echo " $0 set-mode manual 150 设置手动模式,PWM=150"
echo " $0 set-curve \"30:60,50:120,70:200\" 设置温度曲线"
exit 1
;;
esac
}
# ========== 主程序入口 ==========
if [ $# -eq 0 ]; then
parse_arguments "start"
else
parse_arguments "$@"
fi
# 设置脚本权限
sudo chmod +x /usr/local/bin/smart-fan-control.sh
# 创建日志目录
sudo mkdir -p /var/log
sudo touch /var/log/smart-fan.log
sudo chmod 644 /var/log/smart-fan.log
# 创建Systemd服务
sudo nano /etc/systemd/system/smart-fan.service
[Unit]
Description=Smart Fan Control Service
After=multi-user.target
Wants=lm-sensors.service
[Service]
Type=simple
ExecStart=/usr/local/bin/smart-fan-control.sh start
ExecStop=/usr/local/bin/smart-fan-control.sh stop
ExecReload=/bin/kill -HUP $MAINPID
Restart=always
RestartSec=10
User=root
# 日志配置
StandardOutput=journal
StandardError=journal
# 环境变量
Environment="LOG_LEVEL=info"
[Install]
WantedBy=multi-user.target# 启用服务
sudo systemctl daemon-reload
sudo systemctl enable smart-fan.service
# 启动服务
sudo systemctl start smart-fan.service
# 查看状态
sudo systemctl status smart-fan.service
# 查看日志
sudo journalctl -u smart-fan.service -f# 启动服务
sudo smart-fan-control.sh start
# 或者
sudo systemctl start smart-fan.service
# 查看状态
sudo smart-fan-control.sh status
# 切换到安静模式
sudo smart-fan-control.sh set-mode quiet
# 切换到手动模式,PWM=100
sudo smart-fan-control.sh set-mode manual 100
# 自定义温度曲线
sudo smart-fan-control.sh set-curve "30:50,40:80,50:120,60:180,70:220,80:255"
# 查看实时日志
sudo smart-fan-control.sh log
# 测试配置
sudo smart-fan-control.sh test多级温度控制:支持任意数量的温度-PWM控制点
线性插值:在控制点之间自动平滑过渡
温度迟滞:防止温度在阈值附近时风扇频繁切换
多种运行模式:
自动模式:根据温度自动调节
安静模式:限制最大转速,降低噪音
性能模式:提高最低转速,确保散热
手动模式:固定转速
多温度源支持:支持CPU和GPU温度加权计算
过热保护:超过临界温度时强制全速运行
自适应学习:可扩展的自适应算法框架
完整日志系统:支持不同级别的日志记录