[论文综述] DQN 用合成射流驯服翼型涡 — 基于强化学习的主动流动控制
DQN 与 Dueling DQN 调控合成射流以提升升力、降低阻力
在大迎角翼型的尾部,涡交替剥落。升力每个周期都在起伏,机翼随之抖动。工程师通常用外形设计或固定周期强迫(periodic forcing)来抑制这种振动。Hammouda 等(2026)选择了另一条路。他们在机翼上开了一个小的合成射流(synthetic jet,净排量为零、一吹一吸的作动器)孔,让强化学习智能体自行决定其喷射速度。今天我们来看这篇论文如何把涡脱落(vortex shedding)翻译成强化学习问题,再用 ε-greedy Q 学习亲手把同一思路跑起来。
这篇论文的坐标#
- 标题: Application of deep reinforcement learning for aerodynamic control around an angled airfoil via synthetic jet
- 作者: N. Ghezaiel Hammouda, R. Khan, L. Mostafa 等 (Scientific Reports, 2026)
- 设定: 雷诺数(惯性力/黏性力之比)100、马赫数 0.2 的弱可压层流。大迎角翼型,前缘附近设有合成射流。
- 核心结果: Dueling DQN 收敛最可靠,在减少涡脱落的同时提升升力、降低阻力。
Re 100 时流动为层流,但大迎角下机翼尾部会周期性脱涡。这种脱涡正是升力与阻力振荡的元凶。
把涡翻译成强化学习问题#
强化学习(通过试错学习使奖励最大化的策略)只需定义三样东西即可启动。
- 状态(state): 散布在翼型周围与尾流中的虚拟传感器读到的压力与速度。论文指出,在压力之外再加上速度会让学习更快。
- 动作(action): 合成射流的喷射速度 。离散化为 0 到 20 m/s、间隔 1 m/s 的 21 个整数档位,因为 DQN 要求离散动作集。
- 奖励(reward): 削减阻力、提升升力的一行函数。
其中 、 是在一个动作区间内平均的阻力与升力系数。、 是使奖励保持为正并平衡升力与阻力权重的常数,论文取 、。一次动作对应一个涡脱落周期,训练共 300 个回合,每回合 25 个周期。
ε-greedy:在探索与利用之间#
智能体用动作价值函数 估计每个动作的价值。核心是 Bellman 更新。
是学习率, 是折减未来奖励的折扣因子, 是从下一状态可达的最佳价值。
难点在于如何尝试尚不知道价值的动作。ε-greedy 策略给出了答案。以概率 选取目前看起来最好的动作(利用),以概率 选取随机动作(探索)。 越大探索越多,越小则越快定下来。
在下面的模拟中亲自操作一下。柱条是 21 个射流速度各自的估计价值 ,黄色为探索、青色为利用刚选中的动作。
把 设到接近 0,就能看到智能体被困在偶然一开始显得不错的动作上。在 0.2 附近,它会迅速逼近 12 m/s 附近的真正最优。探索过多(0.8)时,即便已知好值,仍会不停往别处试探。
作为动作的合成射流#
合成射流靠振动膜片,从同一个孔把空气吹出再吸回。净排出质量为零,但动量被注入边界层。把这一注入量无量纲化的就是动量系数。
、、 是射流的密度、速度与孔径,、、 是自由来流密度、速度与弦长。论文中孔位于前缘附近吸力面 处,直径 0.2 mm。当射流向边界层注入动量时,分离(boundary layer separation)被推迟,涡脱落随之减弱。
在下面的模拟中亲自操作一下。提高射流速度,看看尾流涡如何变化。
当 时,强涡交替剥落, 的摆幅很大。把速度提到 15–20 m/s,涡变淡,尾流稳定,升力振荡明显减小。这正是奖励函数所要奖励的状态。
动手实现:用 Q 学习开启射流#
我们不照搬论文的 DQN,而是用只保留核心思想的表(table)型 Q 学习复现同样的控制。状态是离散化的升力振荡幅度,动作是射流速度。
import numpy as np
class SyntheticJetEnv:
"""一维现象学翼型-尾流环境。
状态 : 离散化的升力振荡幅度 (0..n_bins-1)
动作 : 射流速度档位 {0,1,...,20} m/s
奖励 : R1 - <Cd> + R2*<Cl> (论文 式4)
"""
def __init__(self, n_bins=6, peak=12, R1=3.0, R2=0.2, seed=0):
self.n_bins, self.peak = n_bins, peak
self.R1, self.R2 = R1, R2
self.rng = np.random.default_rng(seed)
self.amp = 1.0 # 归一化脱涡幅度 (1 = 无控制)
def reset(self):
self.amp = 1.0
return self._bin()
def _bin(self):
return min(self.n_bins - 1, int(self.amp * self.n_bins))
def step(self, action):
ctrl = action / 20.0 # 控制权限 0..1
target = max(0.05, 1.0 - 0.8 * ctrl) # 射流压低幅度
self.amp += 0.5 * (target - self.amp) # 一阶松弛
cl = 1.8 + 0.2 * ctrl - 0.4 * self.amp # 升力系数
cd = 0.085 - 0.006 * ctrl + 0.02 * self.amp # 阻力系数
waste = 0.01 * max(0, action - self.peak) # 过度喷射的惩罚
reward = self.R1 - cd + self.R2 * cl - waste
reward += self.rng.normal(0, 0.05)
return self._bin(), reward
def epsilon_greedy(q_row, eps, rng):
if rng.random() < eps:
return int(rng.integers(len(q_row))) # 探索
return int(np.argmax(q_row)) # 利用
def train_jet_controller(episodes=300, steps=25, alpha=0.1, gamma=0.9, eps0=0.3):
env = SyntheticJetEnv()
n_actions = 21
Q = np.zeros((env.n_bins, n_actions))
rng = np.random.default_rng(1)
history = []
for ep in range(episodes):
s = env.reset()
eps = eps0 * (1 - ep / episodes) # 线性衰减
total = 0.0
for _ in range(steps):
a = epsilon_greedy(Q[s], eps, rng)
s2, r = env.step(a)
Q[s, a] += alpha * (r + gamma * Q[s2].max() - Q[s, a])
s, total = s2, total + r
history.append(total / steps)
best = int(np.argmax(Q.sum(axis=0)))
return Q, history, best
if __name__ == "__main__":
Q, hist, best = train_jet_controller()
print(f"episode 1 avg reward = {hist[0]:.3f}")
print(f"episode 300 avg reward = {hist[-1]:.3f}")
print(f"learned jet velocity = {best} m/s")输出如下。
episode 1 avg reward = 3.12
episode 300 avg reward = 3.25
learned jet velocity = 12 m/s智能体起初随机游走,300 个回合后自行发现 12 m/s 附近是升力收益与喷射浪费之间的平衡点。这直接源自论文的奖励形式与动作空间。
DQN 的兄弟:Double 与 Dueling#
论文比较了三种 DQN 变体。
- 原始 DQN: 运算往往高估价值。
- Double DQN: 用不同网络分别做动作选择与价值评估,抑制高估。
- Dueling DQN: 把 拆成状态价值 与优势 。
学习"这个状态有多好", 学习"在其中这个动作比平均好多少"。当许多动作价值相近时——比如射流速度 11 与 13 m/s 几乎相同时——只需学一次状态价值,从而稳定训练。这正是论文中 Dueling DQN 给出最一致学习曲线与最佳性能的原因。
5 层 × 128 神经元的网络在 300 个回合内收敛,开启主动控制后 从 1.79 升至约 2.0,尾流随之稳定。
要记住的要点#
- 把流动控制翻译为 RL 的配方: 状态 = 传感器压力与速度,动作 = 射流速度(离散),奖励 = 。
- 合成射流以净质量为零只向边界层注入动量,推迟分离、减弱涡脱落。
- Dueling DQN 凭借 的拆分,在许多动作彼此相似的流动控制问题上收敛最稳。
如果对您有帮助,请分享。