news 2026/10/4 18:32:26

HER算法实战:用目标重标注破解稀疏奖励,DDPG训练成功率拉满90%

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
HER算法实战:用目标重标注破解稀疏奖励,DDPG训练成功率拉满90%

“hindsight”——后见之明。英文里常说 hindsight is 20/20,意思是事后看一切都清清楚楚。做强化学习这几年,我对这个词体会最深的地方反而不在日常复盘,而在一个名字就叫 Hindsight Experience Replay 的算法里。它把我一直以来的困惑——稀疏奖励下智能体死活学不会——用一句话点破了:一条轨迹如果没达成原定目标,那就换个目标再看它,它很可能就是一条完美的成功轨迹。

这个项目就是我基于 HER 做的一次完整复现和调参实录。我用它跑通了 OpenAI Gym 里的 Fetch 系列机器人操作环境,从最简单的 FetchReach 一路做到 FetchPickAndPlace,核心结论一句话就能概括:在不做任何奖励塑形的前提下,HER 能把“成功率几乎为 0”的训练曲线直接拉到 90% 以上。如果你正在被稀疏奖励问题折磨,或者想找一个适合练手的连续控制强化学习项目,这篇文章应该能给你省下不少弯路。我会把目标重标注的原理、四种采样策略的取舍、实现细节和踩坑记录都摊开来讲,所有代码思路都是可以直接抄走的。

1. 项目定位:一个小事后视角如何盘活稀疏奖励

有个场景做 RL 的人都熟:你设计了一个机器人抓取任务,机械臂要接近物体、抓住它、再放到目标位置。你给环境的奖励很简单——成功就给 +1,没成功就给 0。结果训练了几十万步,成功率纹丝不动,智能体像瘫痪了一样。问题就出在“稀疏奖励”上:在漫长轨迹里绝大多数状态都是零奖励,智能体根本不知道哪个动作离目标更近,于是梯度信号彻底消失。

1.1 稀疏奖励的本质:学习信号被抽干了

稀疏奖励环境的典型特征是“只有终局才有评价”。这里可以打个不太严谨但很好懂的比方:一场考试不告诉你每道题对错,只有总分及格与否。你要想通过刷题来提升成绩,几乎无从下手,因为没有任何中间反馈告诉你“这道题这么做对不对”。强化学习的核心是最大化累计奖励,如果一条十步的轨迹只有最后一步才可能获得奖励,那么前面九步的 (状态, 动作) 全部带零奖励——对 Q 网络来说,这些转移唯一学到的东西就是“当前状态动作带来的收益为 0”,完全丧失了方向性。

我最初接触这类问题时试过很多“民间偏方”:加大探索噪声、把奖励做成连续的距离函数、调整折扣因子……效果都不理想。尤其手工奖励塑形,虽然能把曲线推上去,但它是最容易埋雷的方案。你设计的距离奖励函数里每一项权重都需要调,稍微不均衡就会诱导智能体学会“假动作”——比如只顾着把手伸到物体旁边但根本不抓取,因为你的塑形奖励让“靠近物体”本身变得太诱人了。

1.2 传统经验回放为什么救不了场

经验回放(Experience Replay)本身是 DQN 时代的经典技巧:把历史转移样本存进大缓冲区,随机采样更新网络,好处是打破时间相关性、提高样本利用率。在奖励密集的环境里这套组合拳很有效,但在稀疏奖励环境里,回放缓冲区里攒下的绝大多数都是失败的“垃圾样本”。你用这些样本来反复更新 Q 网络,学到的是“所有动作都一样差”。再怎么加大回放比例,也只是把“无信号”重复更多遍而已,不会凭空产生学习信号。

当时我甚至怀疑过是否要转用策略梯度类算法,比如 PPO。但这类 on-policy 算法在机器人操作任务里样本效率更低,每一次参数更新后旧的探索数据就报废了,在真实物理环境或高成本仿真里完全跑不动。所以问题的关键不是换算法,而是想办法让每一条“失败轨迹”都能榨出正面的训练信号。

1.3 HER 的切入点:把失败轨迹重新定性

HER 的做法听起来像耍赖:轨迹结束之后,我们“事后”看一眼智能体实际到达的状态,然后把“目标”改写成这个实际状态,这条原本失败的轨迹瞬间就变成了一条成功轨迹。比如抓取任务里机械臂没把物体放到指定位置 B,但它在某个时刻把物体运到了位置 C——那我们就把这条轨迹的目标改成 C,观察它是不是真的达成了 C。如果是,这条轨迹的后续步骤就获得了正奖励,可以用于学习。

这就是“后见之明”在算法里的具体化:智能体当时的动作序列虽然没有达成原目标,但它展示了一套能把某个中间状态变成现实的有效策略。我们用这套轨迹去教导 Q 网络——“当目标恰好是这个状态时,这样的动作序列是好的”。无数条失败轨迹的最终状态各不相同,合在一起就构成了一大批“伪成功数据”。

需要强调的是,这套思路有一个严格前提:算法必须是 off-policy 的。因为我们改写了轨迹的目标和奖励,却保留了原来的动作序列,这等于在用另一个分布下产生的数据去训练当前策略。DDPG、DQN、SAC、TD3 这类 off-policy 算法天然吃这种数据,而 PPO 这类 on-policy 算法会用“重要性采样比例”硬生生修正分布差距,重标注后的数据基本派不上用场。

2. 核心机制拆解:目标重标注与采样策略的取舍

HER 的原理一句话能讲完,但落地时魔鬼全在细节里。最关键的环节是“轨迹存储时如何选虚拟目标”,以及“选好目标后奖励和 done 怎么重新计算”。很多复现失败的项目都是卡在这一步。

2.1 k 次重标注:一条轨迹当多条用

实际操作时,我们不是简单地把每条轨迹的目标替换成最终状态就完了。论文里的标准做法是:一条轨迹采集完成后,从中随机抽取 k 个后续时间步的状态作为额外目标,再把原始目标也保留一份。也就是说,一条轨迹会被拆成 (k+1) 份独立样本存入回放缓冲区。k 就是论文里的那个回放比例参数,最常用的值是 4。

k 值决定了样本量的放大倍数。取 4 意味着同样数量的环境交互,能产生 5 倍于普通经验回放的训练样本,这对于稀疏奖励环境来说简直是雪中送炭。但 k 也不是越大越好,我实测过 k=8 的时候缓冲区里伪成功样本比例过高,网络收敛速度反而略有下降。这个后面在调参部分会详细说。

2.2 四种重标注策略对比

论文提出了四种选择虚拟目标的方式,我复现时把它们的区别整理成了一张表:

策略虚拟目标来源特点适用场景
final轨迹最终状态最简单,稳定目标容易达成的简单任务
future当前时间步之后的某个随机状态样本多样性强,最常用大多数机器人操作任务
episode同一条轨迹中随机一个状态目标可能与当前状态重叠,难度偏低可以作为 baseline 对比
random整个训练过程中的随机状态难度高,几乎学不动一般不建议直接用

我在复现时把 future 作为默认策略,效果确实最稳。它的直觉是:从轨迹当前时刻往后的任意一个状态,都可以作为“未来可能达成的目标”。这样选择的虚拟目标既贴近轨迹的真实走向,又因为随机性保留了足够的探索覆盖范围。而 final 策略虽然简单,但每条轨迹只有一个虚拟目标,样本多样性不足,在 FetchPickAndPlace 这类多阶段任务里很容易陷入局部最优。episode 策略我试过一次,因为随机选择的状态可能出现在当前时间步之前,导致目标“太好达成”,训练出来的策略泛化性偏差。

2.3 最容易翻车的点:奖励和 done 都要跟着重标

很多第一次写 HER 的人都以为只需把 (s, g, a, r, s') 里的 g 换成虚拟目标就行,这是个典型的隐蔽错误。目标一旦变了,这条转移的奖励必须重新判定:在新目标下,如果下一状态 s' 真的与虚拟目标足够接近,奖励就给 0(或正值),否则仍然是稀疏惩罚。同时,如果这一判定成立,done 标志也要置为 True,因为“这一条伪轨迹已经完成了任务”,后续动作不再有意义。

这个细节的重要性怎么强调都不过分。如果只换目标不换奖励,Q 网络会学到“任意动作在新目标下都没奖励”,重标注就白做了;如果只看奖励不纠正 done,时序差分更新会把一条伪成功轨迹的后续动作也当成有效经验,导致目标价值被高估,训练末期出现严重的策略震荡。我见过不少开源实现就是栽在这两个标志位的不同步上。

以下是训练时单条轨迹重标注并入库的核心伪代码片段,我按自己的习惯做了简化和注释:

def hindsight_store(episode_transitions, achieved_states, original_goal, k=4): """ episode_transitions: list of (s, a, r, s_next, done, goal) achieved_states: 轨迹中每个时间步实际到达的状态集合 """ # 原始目标样本先入库 replay_buffer.add_all(episode_transitions) # 从轨迹中选 k 个虚拟目标 # future 策略:只允许选择当前时间步之后的状态 for _ in range(k): virtual_goal = random.choice(achieved_states) new_transitions = [] for t, (s, a, _, s_next, _, _) in enumerate(episode_transitions): # 在新目标下重算稀疏奖励与 done reward = 0.0 if is_goal_reached(s_next, virtual_goal) else -1.0 done = is_goal_reached(s_next, virtual_goal) # 注意:这里存的是新目标,不是原目标 new_transitions.append((s, a, reward, s_next, done, virtual_goal)) if done: # 伪轨迹提前结束,后续状态不再使用 break replay_buffer.add_all(new_transitions)

注意代码里那个if done: break。虚拟目标是从轨迹后续状态里选的,所以很可能在某个时间步就已经达成了。如果达成了还继续把后面的转移存进缓冲区,人为制造了“轨迹成功后又变失败”的矛盾信号,网络会被带偏。这个细节在我早期复现里没有处理,训练曲线一度非常诡异,后面排查了很久才发现是这儿的问题。

3. 实操要点:关键参数与训练调校

复现 HER 并不难,但要复现到“论文级别”的成功率,参数调校比想象中更考验耐心。这一部分我会按自己的实验流程来写,重点说算法选型、网络结构、目标空间处理和几个关键超参数的实测感受。

3.1 算法底座:为什么我选 DDPG 而不是 SAC

HER 的核心价值在于“给 off-policy 算法提供有效样本”,所以底层算法可以自由搭配。我最终选择 DDPG 主要有三个考虑。第一,DDPG 是 actor-critic 结构,适合连续动作空间,Fetch 系列的机械臂控制输出就是四维连续力矩/位置增量,DQN 这类离散动作算法根本用不了。第二,DDPG 的结构比 SAC 简单,网络少,训练稳定性的变量更容易控制,我复现的初衷就是先把 HER 本身跑透,不希望底层算法引入太多额外不确定性。第三,在稀疏奖励任务上 DDPG+HER 有大量公开实验数据可对照,方便我在每步实验里确认自己没有跑偏。

如果你追求更高样本效率,换成 SAC 完全可以,HER 的重标注逻辑完全不变。我的建议是:如果你是第一次上手,先用 DDPG 把 HER 的主干逻辑跑通,再考虑换 SAC 锦上添花。

3.2 网络输入:状态和目标拼在一起

Fetch 环境里智能体观测通常包含:机械臂末端位置、物体位置、相对目标的距离等。HER 要求我们显式区分“状态”和“目标”。实现时我的输入是状态和目标直接拼接成一个向量,喂给 Q 网络和策略网络。

import torch.nn as nn class Actor(nn.Module): def __init__(self, obs_dim, goal_dim, action_dim): super().__init__() self.fc1 = nn.Linear(obs_dim + goal_dim, 256) self.fc2 = nn.Linear(256, 256) self.mu = nn.Linear(256, action_dim) def forward(self, obs, goal): x = torch.cat([obs, goal], dim=-1) x = torch.relu(self.fc1(x)) x = torch.relu(self.fc2(x)) return torch.tanh(self.mu(x))

有一个新手容易踩的坑:没有把目标拼接进网络输入,而是把目标信息当成奖励的一部分写进环境。这样做等于让网络只从标量奖励里间接推断目标,信息量严重不足,顶层特征根本无法区分“不同目标下应该采取不同策略”。我在最初实验时就走过这条路,训练出来的机械臂只会做同一个动作模式,换个目标位置就彻底失灵。正确做法是让目标作为输入特征直接参与网络前向传播,让网络自己去学习“目标位置变化和动作输出之间的关系”。

3.3 关键超参数表

下面这组参数是我在多个环境上试过都比较稳的组合,可以直接作为起点:

参数取值说明
目标重标注比例 k4每条轨迹额外生成 4 条重标注样本
虚拟目标采样策略future取当前时间步之后的随机状态
回放缓冲区大小1e6要能容纳足够多的重标注样本
每次更新 batch 数40每步环境交互后更新 40 次网络
batch size512偏大的 batch 能让重标注样本更稳定
actor 学习率1e-3使用 Adam 优化器
critic 学习率1e-3使用 Adam 优化器
折扣因子 gamma0.98Fetch 任务轨迹短,不用太大
探索噪声OU 噪声或高斯噪声,标准差 0.2高斯噪声更省事
目标判定阈值0.05 米与官方环境一致

看这个表你会发现每次更新的网络次数非常多,40 次更新对一条环境交互而言是很大的比例。这是 HER 的刻意设计:重标注本来就是靠多倍样本堆出来的,所以训练时要把样本利用率拉满。如果算力紧张,可以降到 20 次,但训练曲线会明显变慢。

3.4 奖励函数设计:二值奖励就够

HY 最大的好处就是让你摆脱对奖励塑形的依赖。在 Fetch 系列里我只用了最简单的方式:机械臂末端或物体与目标的距离小于 0.05 米,奖励 0;否则奖励 -1。全程没有距离惩罚、没有动作惩罚、没有分阶段奖励。

有些人会担心这样学习效率太低,实际跑下来完全不用担心。HER 靠重标注制造的海量伪成功样本,天然弥补了稀疏二值奖励缺乏梯度的缺陷。手工塑形奖励反而可能引入偏差:比如你设计了一个“越靠近目标奖励越高”的函数,智能体可能会学到停在目标附近但不完成任务的最优解,因为这样既拿到接近奖励又避免了操作失败惩罚。这类“奖励黑客”行为我见过不止一次。

4. 复现全流程与实验记录

这一部分我会把完整的复现过程写下来,从环境搭建到实验结果对比。整个过程我用的是 MuJoCo 物理引擎和 OpenAI Gym 的 Fetch 环境,硬件只是一张消费级显卡,整个训练大概跑了一天多。

4.1 环境准备与基线设定

首先是环境版本。Fetch 系列环境对 MuJoCo 版本有兼容要求,建议直接用 Gymnasium 里维护的新版本接口,避免老版本接口在 Python 3.8+ 下报各种弃用警告。安装依赖时注意不要手滑装错版本,我之前因为 MuJoCo 版本过新导致模型加载失败,白折腾了一个下午。

基线实验我做了两个:一个完全没有 HER,纯 DDPG;另一个是 HER+DDPG。两者使用完全相同的网络结构和超参数,唯一的变量就是是否开启目标重标注。这样的对照实验很有必要,否则你很难判断训练曲线的提升到底来自 HER 还是来自其他调参运气。

4.2 核心模块实现:Future 策略与缓冲区入库

在 2.3 节我已经给出了重标注的核心伪代码,这里补一下实际训练主循环的骨架。每个 episode 结束后,先提取轨迹里实现过的状态集合和转移列表,然后同时完成“原始样本入库”和“重标注样本入库”。

def train_one_epoch(env, agent, replay_buffer, k=4, noise_std=0.2): episode_transitions = [] achieved_states = [] obs, _ = env.reset() goal = obs["desired_goal"] obs = obs["observation"] done = False while not done: action = agent.select_action(obs, goal, noise_std=noise_std) next_obs, reward, terminated, truncated, info = env.step(action) done = terminated or truncated episode_transitions.append((obs, action, reward, next_obs, done, goal)) achieved_states.append(info["achieved_goal"]) obs = next_obs if done: break hindsight_store(episode_transitions, achieved_states, goal, k=k) for _ in range(40): batch = replay_buffer.sample(512) agent.update(batch)

现实里还需要处理 truncated 和 terminated 的区分:环境因为超时被截断时,轨迹不完整,done 置 True 会导致价值估计偏差,所以我在重标注入库时做了专门处理,只有真正达成虚拟目标才置 done,超时截断一律不当作成功。这个小细节对末端阶段收敛很关键。

4.3 实验曲线解读:三个环境的差异

FetchReach 是最简单的任务,机械臂只需要把末端移动到目标点。HER+DDPG 大约 10 万步就已经能稳定达到接近 100% 的成功率。这个环境适合拿来验证整套代码逻辑是否正确,因为耗时短,改任何参数都能快速看到结果。

FetchPush 要稍微复杂一些,机械臂需要把桌面上的物体推到目标位置。因为存在物体和机械臂之间的接触动力学,前半段训练曲线会有较长的“平台期”,大约在 30 万步之前成功率一直是 0,然后是突然的上升。这种现象在稀疏奖励任务里很典型,很多人会在平台期误判为训练失败提前终止,实际上只要网络没有发散,再坚持一下曲线就会起来。

FetchPickAndPlace 是三个环境里最难的,机械臂要抓起物体再放到目标点。我实验里大约 120 万步才突破 20% 成功率,最终稳定在 80% 左右。这个任务对目标重标注的依赖最大,因为“抓起物体”是一个中间技能,如果不用事后视角,纯靠稀疏奖励几乎不可能学会这种多阶段操作。后期我又把 k 提高到 6,成功率还能再往上走几个点。

4.4 对比实验:HER 开关的效果差异

我的对比实验里,没有 HER 的 DDPG 在三个环境上全部“报废”:FetchReach 跑 20 万步成功率仍为 0,FetchPush 和 FetchPickAndPlace 直到我终止训练都在 0 附近徘徊。而加了 HER 之后,同样的网络、同样的探索噪声,全部环境都能跑出有效学习信号。这个对照组直观地说明了 HER 的价值:它做的不是“改进网络”,而是“改造数据”。网络架构和优化器都没变,只是数据里多了重标注目标的伪成功样本,训练就从“什么都学不到”变成了“稳定收敛”。

5. 踩坑记录与问题排查实录

复现 HER 的过程不可能一帆风顺。我把自己遇到过、且在实际群里也看到别人反复踩的坑整理成了这一节,每条都附带了排查方式和最终解决方案。

5.1 重标注目标导致训练方差爆炸

第一次把 k 调到 8 时,我观察到训练前期的成功率曲线抖动非常剧烈,甚至有时候已经收敛到 60% 又突然跌回 10%。复盘后我意识到问题不在网络不稳定,而在重标注比例过高。k=8 意味着缓冲区里伪目标样本占比极大,这些样本的目标分布和真实智能体正在探索的目标分布严重不一致,相当于频繁切换训练任务,任何网络都会被晃晕。

解决办法是降回 k=4,同时把训练目标函数里的 critic 更新频率适当降低,给网络多一点时间适应新数据。如果你在项目里必须用更大的 k,可以考虑把伪成功样本的优先级稍微调低,让它们不要过于拥挤地在每次 batch 里集中出现。

5.2 缓冲区里的“旧目标污染”

另一类隐蔽问题是:HER 把重标注样本和原始样本存在同一个缓冲区,但不同时间点采样的样本目标分布差异很大。前期智能体还没有学会任何技能时,重标注样本的目标大多是随机位置,后期智能体已经会抓取时,新样本的目标则贴近真实任务区域。旧的随机目标样本如果一直留在缓冲区里,会拖慢后期收敛速度。

一个简单有效的做法是限制缓冲区里重标注样本的保存时间,或分两个缓冲区分别存原始样本和重标注样本,采样时按比例混合。我后来在工程实现里采用了后者,效果不错,而且可配置性更强,方便对不同目标分布做独立控制。

5.3 训练中途 loss 飞到 NaN

这个坑与 HER 本身无关,但在 MuJoCo 环境里尤其常见:当机械臂末端和物体接触时,物理仿真可能出现瞬时不稳定,导致观测值里出现极大数值。这些极大数值经过网络前向传播和梯度反传,很容易把权重变成 NaN。

排查时要先确认 NaN 到底是网络结构问题还是输入数据问题。我的经验是:如果在训练脚本里加一条观测值范围检查,发现 NaN 之前 env.step 返回的观测里就已经有 inf,那基本就是物理仿真的问题。解决办法是给观测值做 clip,强制限制在合理范围,比如机械臂位置和速度都限制在 [-10, 10] 以内。轻微 clip 不会影响学习精度,却能极大提升训练稳定性。

5.4 常见问题速查表

现象可能原因检查方式与解决
训练曲线长期为 0目标没有拼进网络输入检查 actor/critic 输入维度
前期抖动,后期掉坑缓冲区伪目标比例过高降低 k,或分区缓冲采样
后期收敛但不够高done 标志没有跟着重算检查重标注样本的 done 字段
loss 为 NaN观测值包含 inf对观测值做 clip 和合法范围检查
成功率反弹慢超时截断被当成成功区分 terminated 和 truncated
换随机种子效果差异大模型敏感度偏高固定随机种子,或调整目标判定阈值

6. 个人体会与后续扩展方向

HER 这个项目给我最大的启发,其实已经超出了算法本身。“事后视角”这个思路在现实工程里同样好用:代码出了 bug,与其盯着报错信息反复试,不如先复盘一下“实际发生的状态”和“预想中的状态”到底差在哪里。很多问题在事后看都是清晰的,这句话是真的。

在强化学习的框架里,HER 也和另一个经典概念“课程学习”很契合。因为虚拟目标是从轨迹实际状态里抽出来的,天然构成了一条从易到难的课程:早期智能体只会做随机动作,它的“事后目标”就停留在很简单的状态;随着能力提升,事后目标也自动变得更接近真实任务目标。你可以把这个特性理解成一种无需人工设计的自动课程,这一点特别适合前端训练冷启动。

我后来用同样的思路做过一个扩展实验:把 HER 的重标注逻辑从单智能体搬到一个多智能体协作场景里,让一个智能体的“失败轨迹”成为另一个智能体的“演示教材”,效果意外地不错。如果你有兴趣,建议也从 FetchReach 起步,先把这套机制吃透,再往自己的任务上迁移。

最后分享一个我后来一直沿用的习惯:每次跑 HER 实验之前,先跑一个 10 万步的微型实验,把目标重标注代码里“奖励与 done 是否同步更新”这个逻辑单独写个单元测试。这个检查只需要几分钟,却能避免绝大多数训练几个小时后才发现方向错误的惨案。别问我怎么知道要这么做的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/4 18:21:19

ArcGIS Pro批量替换数据源:从图形界面到ArcPy脚本的完整指南

做 GIS 的人十有八九都碰过这么一种场景:ArcGIS Pro 的工程文件(.aprx)本身不存空间数据,存的是每个图层的“数据源引用”。正因为这样,只要数据挪了窝——比如从旧电脑拷到新电脑、从测试数据库切到正式库、或者文件夹…

作者头像 李华
网站建设 2026/10/4 18:19:20

2026美发店会员管理系统哪个好?刚开店从哪款入手,看准这3点

据GII市场研究报告,全球沙龙和水疗软件市场2026年预计达到13.1亿美元,到2032年将增长至20.4亿美元,年复合增长率7.58%,亚太地区是增速较快的区域,中国和印度的沙龙经营者正从纸质记录向手机应用迁移。小编发现&#xf…

作者头像 李华
网站建设 2026/10/4 18:12:39

店铺运营数据分析是什么?一文讲透核心概念与价值(2026最新)

摘要:店铺运营数据分析,是用数据拆解店铺经营问题、定位根因并指导决策的方法。本文讲清它到底是什么、为什么重要、由哪些模块构成,以及新手最常踩的坑,帮你建立完整认知。 不少人一听到“分析”两个字就发怵,觉得那…

作者头像 李华
网站建设 2026/10/4 18:12:03

Docker Compose部署VictoriaMetrics:Prometheus监控存储与备份恢复实战

1. 部署思路:为什么时序数据库要选 VictoriaMetrics1.1 标题场景拆解:一套方案搞定监控全链路先把这个标题拆开看,它其实覆盖了一套完整的监控数据链路:采集端(Prometheus)→ 存储端(VictoriaMe…

作者头像 李华
网站建设 2026/10/4 18:10:39

清华软院推免备考指南:机试算法与面试实战经验

这篇文章聊聊准备人… 好,直接进入正题。1. 清华软院推免到底在考什么?先搞懂游戏规则再动手1.1 别急着刷题,先看清楚材料审核的隐性门槛清华大学软件学院,简称清华软院,每年推免生的竞争激烈程度在圈内是出了名的。我…

作者头像 李华
网站建设 2026/10/4 18:09:47

STM32+ESP8266仓库环境控制系统:温湿度粉尘监测与远程控制实践

1. 先拆需求:这套仓库环境控制系统到底要做什么前段时间帮一个朋友的物料仓库做了一套环境控制系统。仓库不大,但堆放的全是怕潮怕尘的电子元件和纸质包装,前阵子因为返潮,一批货直接报废,损失够买好几套自动化设备了。…

作者头像 李华