找回密码
 立即注册
搜索
热搜: 活动 交友
查看: 122|回复: 0

太空大逃亡AI思路——势场法

[复制链接]

10

主题

15

回帖

848

积分

版主

积分
848
发表于 前天 23:11 | 显示全部楼层 |阅读模式
本帖最后由 littleblackLB 于 7-23-2025 10:17 编辑

这篇帖子,我将复盘一下在前日719RT年度派对上用势场法所实现的AI.

很荣幸能成为四组中存活时间最长的AI,同时算法高效,是O(N)的线性时间复杂度(N为障碍物).


(绿色方块为AI)

为什么选势场法
这个方法更加自然,同时有一定的直观性,因为你可以将障碍物类比成一个个小山坡(或者是电场中的正电荷,高度为场强大小)。同时易于拓展,比如希望 AI 更倾向于前往稀疏区域,同时尽可能不靠近边界。对于这类需求,我们就可以简单的叠加一个来实现目标。

怎么建模?
思路很简单,将所有障碍物的斥力作用于AI的合成为一个力,然后再分解为x, y两个分量即可
令 \(|d_i|\) 为 AI 到 i 编号障碍物的欧氏距离,\(d_{0i}\)为到 i 编号障碍物的单位方向矢量, \(f_i\)为 AI 受到到 i 编号障碍物的斥力,下标为x或y的变量均为标量,k 为基础斥力系数
\(|f_i| = \frac{k}{|d_i|}\)
\(tan \theta = \frac{d_y}{d_x}\)
\(f = \sum_{i=1}^{n}|f_i| \cdot d_{0i}\)
\(f_x = \sum_{i=0}^{n}|f_i| \cdot cos\theta = \sum_{i=0}^{n}|f_i| \cdot \frac{d_x}{\sqrt{d_x^2+d_y^2}}\)
\(f_y = \sum_{i=0}^{n}|f_i| \cdot sin\theta = \sum_{i=0}^{n}|f_i| \cdot \frac{d_y}{\sqrt{d_x^2+d_y^2}}\)
  1. for obs in boxList:
  2.     ox1, oy1 = obs.rect.left,  obs.rect.top
  3.     ox2, oy2 = obs.rect.right, obs.rect.bottom
  4.     cx = min(max(bx, ox1), ox2)
  5.     cy = min(max(by, oy1), oy2)
  6.     dx, dy = bx - cx , by - cy
  7.     dist_sq = max(dx*dx + dy*dy, 1.0)
  8.     strength = k_obs / dist_sq
  9.     inv_d = 1.0 / math.sqrt(dist_sq)
  10.     fx += strength * (dx * inv_d)
  11.     fy += strength * (dy * inv_d)
复制代码
于是问题出现了,当 AI 跑到四边后便会卡在那里不出去,我们便可以给四边设置一个"屏障场",让 AI 尽可能不靠近边界
  1. fx +=  k_border / max(dist_left*dist_left, 1.0)
  2.     fx += -k_border / max(dist_right*dist_right, 1.0)
  3.     fy +=  k_border / max(dist_top*dist_top,    1.0)
  4.     fy += -k_border / max(dist_bottom*dist_bottom,1.0)
复制代码
---



这里的 max 函数是为了防止无穷大无效化了其他斥力.
同时我给场强做了可视化(用了 Numpy 后性能好多了), 蓝 -> 红, 越红强度越大(没有渲染边界的场强),可以发现 AI 会倾向于移动至蓝色区域.


动态调参
我们注意到已经有两个可以调节的参数了,分别是 k_obs 和 k_border。由于其是超参数,所以并不简单的存在全局最优值,因此我们便可以在 AI 运行的过程中动态调节:
  1. def compute_dynamic_k(base_k, current_dist, d0=100.0, min_k=100.0, max_k=5000.0):
  2.     """
  3.     根据飞船与最近障碍物的距离 current_dist 动态缩放斥力系数。
  4.     - base_k:    基础斥力系数
  5.     - current_dist: 与最近障碍物的实际距离
  6.     - d0:        参考距离(当 current_dist == d0 时,k == base_k)
  7.     - min_k, max_k: 系数上下界,防止过小/过大
  8.     """
  9.     # 比例因子:当越靠近(dist < d0),因子 >1;越远时因子 <1
  10.     factor = d0 / max(current_dist, 1.0)
  11.     k = base_k * factor
  12.     # 限制在 [min_k, max_k] 范围内
  13.     return max(min_k, min(max_k, k))
复制代码

向稀疏的地方进发!
我们可以利用高斯密度分布核来生成"密度场"
  1. sigma = 100.0            # 核宽度
  2. k_sparse = 100.0         # 稀疏吸引强度系数
  3. for obs in boxList:
  4.     area = obs.rect.width * obs.rect.height
  5.     # 障碍物中心
  6.     ox = obs.rect.centerx
  7.     oy = obs.rect.centery
  8.     dx = bx - ox
  9.     dy = by - oy
  10.     dist_sq = dx*dx + dy*dy
  11.     # area_factor = obs.rect.width * obs.rect.height / total_area * 10 + 1
  12.     area_factor = 1
  13.     # 高斯核密度: exp(-dist_sq / (2*sigma^2))
  14.     gauss = area_factor * math.exp(-dist_sq / (2 * sigma * sigma))
  15.     # 密度梯度 = ∇(gauss) = (-dx / sigma^2 * gauss, -dy / sigma^2 * gauss)
  16.     # 稀疏吸引 = -密度梯度
  17.     fx += k_sparse * ( dx * gauss / (sigma*sigma) )
  18.     fy += k_sparse * ( dy * gauss / (sigma*sigma) )
复制代码
sigma:控制感知“密度”的范围,相当于你在多远距离上能“感知”障碍物。
k_sparse:决定飞船有多强的意愿走向稀疏区域。

蓝 -> 绿,越绿表面该区域越密集

感想
正因为有了 AI 的协助,我们才能在项目中实现“边用边学”的循环提升。从最初的构想到逐步实现,虽然大量代码是由 AI 生成的,但每一段代码背后所蕴含的算法思想与工程逻辑,都在实践中被不断理解和内化。
这种“即学即用、即用即精”的方式,不仅提高了效率,也让我们的思维方式发生了转变:不再畏惧复杂的技术细节,而是勇于提出问题、尝试解决,并在反馈中不断优化。
在未来,我们或许会更依赖 AI,但更重要的,是我们学会了如何与 AI 一起思考、一起成长。



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×

评分

参与人数 1金钱 +50 收起 理由
Ovo + 50 太强了,膜拜大佬o(* ̄▽ ̄*)ブ ...

查看全部评分

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|RealDevClub ( 沪ICP备2024093864号-1 )

GMT+8, 7-24-2025 03:53 , Processed in 0.064359 second(s), 22 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表