我也是集线器,接 CPUFAN。脚本控制全部风扇。包括 CPU 风扇和机箱风扇。根据 CPU 和 GPU 温度控制风扇转速。脚本是 Gemini 写的。
按温度跑。
pwm集线器接cpufan接口实现gpu+cpu双温控自动化调速脚本。
已经将脚本路径、提权指令以及 Systemd 守护进程中的执行路径**全部严格统一为 /usr/local/bin/gpu_fan_control.sh**。同时,将服务名称也顺理成章地对齐为 gpu-fan-control.service,方便日后管理。
直接复制并一次性粘贴到终端执行即可:
1. 生成并赋予核心脚本权限
sudo cat > /usr/local/bin/gpu_fan_control.sh << 'EOF'
#!/bin/bash
# ==========================================
# CPU/GPU 双擎温度联合判定风扇控制脚本 (双向迟滞优化版)
# ==========================================
# ---------------------------------------------------------
# 【模块 1:动态硬件寻址】
# ---------------------------------------------------------
find_hwmon_by_name() {
local name=$1
for d in /sys/class/hwmon/hwmon*; do
if [ -f "$d/name" ] && [ "$(cat "$d/name")" = "$name" ]; then
echo "$d"
return 0
fi
done
return 1
}
NCT_DIR=$(find_hwmon_by_name "nct6793")
if [ -z "$NCT_DIR" ]; then
echo "$(date): FATAL ERROR - Cannot find nct6793 hwmon module." >> /var/log/gpu_fan.log
exit 1
fi
PWM_PATH="${NCT_DIR}/pwm2"
PWM_EN="${PWM_PATH}_enable"
GPU_DIR=$(find_hwmon_by_name "amdgpu")
if [ -z "$GPU_DIR" ]; then
echo "$(date): FATAL ERROR - Cannot find amdgpu hwmon module." >> /var/log/gpu_fan.log
exit 1
fi
if [ -f "${GPU_DIR}/temp2_input" ]; then
GPU_TEMP_PATH="${GPU_DIR}/temp2_input"
else
GPU_TEMP_PATH="${GPU_DIR}/temp1_input"
fi
CPU_DIR=$(find_hwmon_by_name "coretemp")
if [ -z "$CPU_DIR" ] || [ ! -f "${CPU_DIR}/temp1_input" ]; then
echo "$(date): FATAL ERROR - Cannot find CPU coretemp sensor." >> /var/log/gpu_fan.log
exit 1
fi
CPU_TEMP_PATH="${CPU_DIR}/temp1_input"
# ---------------------------------------------------------
# 【模块 2:用户温控策略配置区】
# ---------------------------------------------------------
GPU_MIN_TEMP=50
GPU_MAX_TEMP=75
CPU_MIN_TEMP=55
CPU_MAX_TEMP=85
CRITICAL_TEMP=90
CRITICAL_PWM=255
FAN_MIN_PWM=40
FAN_MAX_PWM=200
CHECK_INTERVAL=5
PWM_DECAY_STEP=8 # 声学阻尼:降温时,单次最大允许降低的 PWM 步长。避免“转速跳水”
PWM_HYSTERESIS=5 # 迟滞死区:目标 PWM 变化绝对值 <= 5 时无视变化。屏蔽 1~2℃ 的波动杂音
# ---------------------------------------------------------
LAST_PWM=-1
LOOP_COUNT=0
# ---------------------------------------------------------
# 【模块 3:安全兜底与进程退出机制】
# ---------------------------------------------------------
cleanup() {
[[ -f "${PWM_EN}" ]] && echo 0 > "${PWM_EN}" 2>/dev/null
echo "$(date): Fan controller stopped, restore fan control to BIOS." >> /var/log/gpu_fan.log
exit 0
}
trap cleanup SIGTERM SIGINT
if ! echo 1 > "${PWM_EN}" 2>/dev/null; then
echo "$(date): FATAL ERROR - Open PWM enable failed, resource busy." >> /var/log/gpu_fan.log
exit 1
fi
echo "$(date): Fan service start success | GPU source:${GPU_TEMP_PATH}" >> /var/log/gpu_fan.log
# ---------------------------------------------------------
# 【模块 4:常驻温控主循环】
# ---------------------------------------------------------
while true; do
[[ -f "${PWM_EN}" ]] && echo 1 > "${PWM_EN}" 2>/dev/null
if [ -f "${GPU_TEMP_PATH}" ]; then
GPU_RAW=$(cat "${GPU_TEMP_PATH}" 2>/dev/null || echo 0)
GPU_TEMP=$((GPU_RAW / 1000))
else
GPU_TEMP=0
fi
if [ -f "${CPU_TEMP_PATH}" ]; then
CPU_RAW=$(cat "${CPU_TEMP_PATH}" 2>/dev/null || echo 0)
CPU_TEMP=$((CPU_RAW / 1000))
else
CPU_TEMP=0
fi
if [ "${CPU_TEMP}" -ge "${CRITICAL_TEMP}" ] || [ "${GPU_TEMP}" -ge "${CRITICAL_TEMP}" ]; then
TARGET_PWM=${CRITICAL_PWM}
DOMINANT="CRITICAL"
else
# [GPU 独立计算]
if [ "${GPU_TEMP}" -le "${GPU_MIN_TEMP}" ]; then
PWM_G=${FAN_MIN_PWM}
elif [ "${GPU_TEMP}" -ge "${GPU_MAX_TEMP}" ]; then
PWM_G=${FAN_MAX_PWM}
else
PWM_G=$(( (GPU_TEMP - GPU_MIN_TEMP) * (FAN_MAX_PWM - FAN_MIN_PWM) / (GPU_MAX_TEMP - GPU_MIN_TEMP) + FAN_MIN_PWM ))
fi
# [CPU 独立计算]
if [ "${CPU_TEMP}" -le "${CPU_MIN_TEMP}" ]; then
PWM_C=${FAN_MIN_PWM}
elif [ "${CPU_TEMP}" -ge "${CPU_MAX_TEMP}" ]; then
PWM_C=${FAN_MAX_PWM}
else
PWM_C=$(( (CPU_TEMP - CPU_MIN_TEMP) * (FAN_MAX_PWM - FAN_MIN_PWM) / (CPU_MAX_TEMP - CPU_MIN_TEMP) + FAN_MIN_PWM ))
fi
# [取高者原则]
if [ "${PWM_C}" -gt "${PWM_G}" ]; then
TARGET_PWM=${PWM_C}
DOMINANT="CPU"
else
TARGET_PWM=${PWM_G}
DOMINANT="GPU"
fi
# [迟滞死区过滤 Hysteresis / Deadband]
if [ "${LAST_PWM}" -ne -1 ] && [ "${DOMINANT}" != "CRITICAL" ]; then
PWM_DIFF=$(( TARGET_PWM - LAST_PWM ))
[ "${PWM_DIFF}" -lt 0 ] && PWM_DIFF=$(( -PWM_DIFF )) # 纯净 Bash 取绝对值法
# 如果变动幅度在迟滞死区内,强制锁定为上一次的转速
if [ "${PWM_DIFF}" -le "${PWM_HYSTERESIS}" ]; then
TARGET_PWM=${LAST_PWM}
fi
fi
# [渐进降温阻尼过滤 Decay]
if [ "${LAST_PWM}" -ne -1 ] && [ "${TARGET_PWM}" -lt "${LAST_PWM}" ]; then
CALC_DECAY=$((LAST_PWM - PWM_DECAY_STEP))
if [ "${CALC_DECAY}" -gt "${TARGET_PWM}" ]; then
TARGET_PWM=${CALC_DECAY}
fi
fi
# [硬性边界钳位]
((TARGET_PWM < FAN_MIN_PWM)) && TARGET_PWM=${FAN_MIN_PWM}
((TARGET_PWM > CRITICAL_PWM)) && TARGET_PWM=${CRITICAL_PWM}
fi
# [执行硬件指令]
if [ "${TARGET_PWM}" -ne "${LAST_PWM}" ]; then
if echo "${TARGET_PWM}" > "${PWM_PATH}" 2>/dev/null; then
LAST_PWM=${TARGET_PWM}
else
echo "$(date): WARN write PWM ${TARGET_PWM} failed" >> /var/log/gpu_fan.log
fi
fi
if [ "${LOOP_COUNT}" -eq 0 ]; then
echo "$(date '+%Y-%m-%d %H:%M:%S') | CPU:${CPU_TEMP}℃ | GPU:${GPU_TEMP}℃ | Source:${DOMINANT} | PWM:${TARGET_PWM}" >> /var/log/gpu_fan.log
fi
((LOOP_COUNT=(LOOP_COUNT+1)%6))
sleep "${CHECK_INTERVAL}"
done
EOF
sudo chmod +x /usr/local/bin/gpu_fan_control.sh
2. 生成 Systemd 守护进程与日志轮转配置
# 生成 Systemd 服务文件 (路径已精准对齐)
sudo cat > /etc/systemd/system/gpu-fan-control.service << 'EOF'
[Unit]
Description=NCT6793 CPU+GPU Smart Fan Controller
Requires=lm-sensors.service
After=lm-sensors.service multi-user.target
[Service]
Type=simple
ExecStart=/bin/bash /usr/local/bin/gpu_fan_control.sh
Restart=on-failure
RestartSec=3
StandardOutput=null
StandardError=journal
[Install]
WantedBy=multi-user.target
EOF
# 生成 Logrotate 日志清理规则
sudo cat > /etc/logrotate.d/gpu-fan << 'EOF'
/var/log/gpu_fan.log {
daily
rotate 7
compress
missingok
copytruncate
notifempty
}
EOF
3. 应用配置并启动服务
重载并启动全新的规范化服务
sudo systemctl daemon-reload
sudo systemctl enable gpu-fan-control.service
sudo systemctl restart gpu-fan-control.service
sudo systemctl status gpu-fan-control.service