估计阅读时长: 7 分钟

Boids算法(也称鸟群/鱼群算法)是Craig Reynolds于1986年提出的群体行为模拟模型,通过三条局部规则模拟鸟类、鱼群等生物群体的自组织运动。在Boids算法中,整个过程通过个体(称为“boid”)的局部交互实现全局有序行为,无需中央控制。每条规则计算个体与邻居的相互作用力,最终合力决定运动方向。Boids算法的精髓在于用局部规则涌现全局智能,其简洁性、可扩展性使其成为连接生物行为与工程控制的桥梁。从《蝙蝠侠》的蝙蝠群到无人机编队表演,从游戏生态到交通优化,Boids持续证明:自然界的简单规则,足以驱动复杂系统的有序演化。

算法原理讲解

Boids算法中,其精髓在于无中央控制,而是依靠个体仅感知局部邻居(如半径内的其他boid),全局行为通过简单规则叠加自然涌现,无需全局路径规划。整个算法可以使用三部分来描述:

1.分离(Separation)

这部分的代码主要是为了避免碰撞,维持个体间距。算法代码主要是按照排斥力原则来进行计算,例如个体排斥过近的邻居,排斥力与距离成反比(距离越小,排斥越强)。

Private Function Avoid(boid As Boid, distance As Double, power As Double) As (Double, Double)
    ' point away as boids get close
    Dim neighbors = grid.SpatialLookup(boid, radius).Where(Function(x) x.GetDistance(boid) < distance)
    Dim sumClosenessX As Double = Nothing, sumClosenessY As Double = Nothing

    For Each neighbor As Boid In neighbors
        Dim closeness = distance - boid.GetDistance(neighbor)
        sumClosenessX += (boid.x - neighbor.x) * closeness
        sumClosenessY += (boid.y - neighbor.y) * closeness
    Next
    Return (sumClosenessX * power, sumClosenessY * power)
End Function

2.对齐(Alignment)

这部分的代码是为了实现群体运动方向同步。代码中主要是通过匹配调整个体调整速度方向,匹配邻居的平均运动方向。

Private Function Align(boid As Boid, neighbors As Boid(), power As Double) As (Double, Double)
    ' point toward the center of the flock (mean flock boid position)
    ' Dim neighbors = grid.SpatialLookup(boid, radius).Where(Function(x) x.GetDistance(boid) < distance).ToArray
    Dim meanXvel As Double = neighbors.Sum(Function(x) x.Xvel) / neighbors.Count()
    Dim meanYvel As Double = neighbors.Sum(Function(x) x.Yvel) / neighbors.Count()
    Dim dXvel = meanXvel - boid.Xvel
    Dim dYvel = meanYvel - boid.Yvel
    Return (dXvel * power, dYvel * power)
End Function

3.聚合(Cohesion)

最后我们需要通过代码来维持群体聚集,防止分散。主要是通过吸引力算法来实现个体向邻居的中心位置(质心)靠拢。

Private Function Flock(boid As Boid, neighbors As Boid(), power As Double) As (Double, Double)
    ' point toward the center of the flock (mean flock boid position)
    Dim meanX As Double = neighbors.Sum(Function(x) x.x) / neighbors.Count()
    Dim meanY As Double = neighbors.Sum(Function(x) x.y) / neighbors.Count()
    Dim deltaCenterX = meanX - boid.x
    Dim deltaCenterY = meanY - boid.y
    Return (deltaCenterX * power, deltaCenterY * power)
End Function

GDI+渲染

在拥有了上面的模拟算法代码之后,我们就可以计算出每一个Boid的速度方向,通过循环进行迭代计算更新位置,即可实现整个模拟计算的逻辑了。在每一次完成了位置的更新后,我们可以通过对应的绘图代码来进行图像的渲染更新。在这里,我们将每一个Boid的速度向量提取出来,按照热图的渲染规则进行颜色索引的计算,通过特定的颜色版对计算出来的每一个颜色索引都赋予对应的热图颜色,就可以得到最终的粒子模拟计算渲染结果了。

Public Module SDRender

    Dim colors As Color()
    Dim n As Integer = 30

    Sub New()
        colors = Designer.GetColors(ScalerPalette.viridis.Description, n)
    End Sub

    Public Function RenderField(field As Field) As Bitmap
        Dim bmp As Bitmap = New Bitmap(CInt(field.Width), CInt(field.Height))
        Dim max As Double = field.MaxSpeed

        Using gfx = Graphics.FromImage(bmp)
            gfx.SmoothingMode = Drawing2D.SmoothingMode.AntiAlias
            gfx.Clear(Color.Black)

            Dim len As Integer = field.Entity.Count

            For i = 0 To len - 1
                Dim boid As Boid = field(i)

                If i < field.PredatorCount Then
                    RenderShape.RenderBoid(gfx, boid.x, boid.y, boid.GetAngle, Color.White)
                Else
                    Dim lv As Integer = ((boid.GetSpeed / max) * n) - 1

                    If lv < 0 Then
                        lv = 0
                    ElseIf lv >= colors.Length Then
                        lv = colors.Length - 1
                    End If

                    RenderShape.RenderBoid(gfx, boid.x, boid.y, boid.GetAngle, colors(lv))
                End If
            Next
        End Using

        Return bmp
    End Function
End Module
谢桂纲

Attachments

2 Responses

  1. Wow! This simulation animation demo is absolutely amazing! How did you manage to render so many particle animation effects at such a high frame rate on WinForm?

    来自菲律宾

Leave a Reply to 谢桂纲 Cancel reply

Your email address will not be published. Required fields are marked *

博客文章
December 2025
S M T W T F S
 123456
78910111213
14151617181920
21222324252627
28293031  
  1. 谢博,您好。阅读了您的博客文章非常受启发!这个基于k-mer数据库的过滤框架,其核心是一个“污染源数据库”和一个“基于覆盖度的决策引擎”。这意味着它的应用远不止于去除宿主reads。 我们可以轻松地将它扩展到其他场景: 例如去除PhiX测序对照:建一个PhiX的k-mer库,可以快速剔除Illumina测序中常见的对照序列。 例如去除常见实验室污染物:比如大肠杆菌、酵母等,建一个联合的污染物k-mer库,可以有效提升样本的纯净度。 例如还可以靶向序列富集:反过来想,如果我们建立一个目标物种(比如某种病原体)的k-mer库,然后用这个算法去“保留”而不是“去除”匹配的reads,这不就实现了一个超快速的靶向序列富集工具吗? 这中基于kmer算法的通用性和扩展性可能会是它的亮点之一。感谢博主提供了这样一个优秀的思想原型

  2. WOW, display an image on a char only console this is really cool, I like this post because so much…

  3. 确实少有, 这么高质量的内容。谢谢作者。;-) 我很乐意阅读 你的这个技术博客网站。关于旅行者上的金唱片对外星朋友的美好愿望,和那个时代科技条件限制下人们做出的努力,激励人心。