【问题标题】:Flocking Algorithm crashes on 200+ boids植绒算法在 200 多个 boid 上崩溃
【发布时间】:2011-10-10 20:21:02
【问题描述】:

我正在将群集算法实施到更大的系统中。 OGRE 用于渲染,luabind 用于能够与 LUA 通信,yatta yatta,这些东西应该不重要。

我基本上按照雷诺兹的 boids 模型实现了该算法。这意味着,一个 Boid(如“群中的一条鱼”)根据它的邻居在一定半径内移动。事实上,它的基本复杂度是 O(n²),因为每个 boid 都必须检查它所有的 flockmate 是否在范围内,然后考虑一些因素来计算自己的运动。

算法本身已经实现并且运行流畅。它接受所有不同尺寸的模型,它可以在 2D 和 3D 空间中工作,它可以很好地聚集等等,我已经在研究它一段时间了。

我的问题是,一旦我在 boid 数字中遇到一种“障碍”,算法就会在运行时崩溃,它大约是 200-250,甚至会变化。现在,如果它会变得越来越慢,直到我被打破到 1 fps,我可以理解算法的简单性能就是问题所在。但是,例如,199 个 boid 可以正常工作,201 个根本不工作并在运行时崩溃。这让我非常惊讶。

我实现了 2 个类:“Swarm”和“Boid”。 Swarm 用于保存指向 swarm 中所有 boid 的指针,但计算量不大,运动发生在 Boid 中。

swarm.h:

#ifndef __SWARM_H__
#define __SWARM_H__

#include <vector>
#include "boid.h"

namespace Core {

    class swarm {

    public:
    swarm();
    ~swarm();
    bool move(float timeSinceLastFrame);
    bool addBoid(boid *thisBoid);
    bool removeBoidByName(std::string boidName);
    bool writeAllBoidNames();
    std::vector<boid*> getFlockMates();

private:
    std::vector<boid*> flock;
    float timePassed;

};

}


#endif

boid.h:

#ifndef __BOID_H__
#define __BOID_H__

#include "Ogre.h"
#include <vector>
#include <stdlib.h>

namespace Core {

class swarm;

class boid {

    public:
        boid(Ogre::SceneNode *thisNode, Ogre::String orientation, swarm *thisSwarm);            // Constructor - direkter Node-Zugriff
        boid(Ogre::MovableObject *thisObject, Ogre::String orientation, swarm *thisSwarm);      // Constructor - Node-Zugriff über das Objekt
        ~boid();                                                                                // Destructor
        Ogre::Vector3 getBoidPosition();                                                        // gibt die derzeitige Position des Boids zurück - nötig für Cohesion und Separation
        Ogre::Vector3 getBoidVelocity();                                                        // gibt die derzeitige Geschwindigkeit und Richtung des Boids zurück - nötig für Alignement
        std::string getBoidName();                                                              // gibt den Namen eines Boids zurück
        bool move(float timeSinceLastFrame);                                                    // bewegt das Boid

    private:
        swarm *flockMates;                                                                      // pointer auf den Schwarm
        float boidSize;                                                                         // die Größe des Boids
        Ogre::Vector3 boidOrientation;                                                          // Grundlegende Orientierung des Meshes eines Boids
        Ogre::SceneNode *boidNode;                                                              // Pointer auf die SceneNode des Boids - das Objekt, das tatsächlich bewegt wird
        Ogre::Vector3 velocity;                                                                 // derzeitige, bzw. letzte Geschwindigkeit

};
}

#endif

如您所见,我正在使用指向 swarm 中的 boid 对象的指针向量。在运行时,swarm::move() 被调用,它遍历向量并为每个 boid 调用 boid::move()。

bool swarm::move(float timeSinceLastFrame) {
    std::vector<boid*>::iterator iter;
    for ( iter = flock.begin(); iter != flock.end(); iter++ ) {
        (*iter)->move(timeSinceLastFrame);
    }
    return true;
}

boid::move 非常复杂,因为它根据很多东西计算运动。我将在这里发布 - imo - 实际上很重要的点,而不是每一个乘法,因为我不想让你厌烦不需要的东西。编辑:好的,现在大部分代码都在这里了。

bool boid::move(float timeSinceLastFrame) {

    Ogre::Vector3 cohesion = Ogre::Vector3(0, 0, 0);
    Ogre::Vector3 alignement = Ogre::Vector3(0, 0, 0);
    Ogre::Vector3 separation = Ogre::Vector3(0, 0, 0);

    int cohesionCount = 0;
    int alignementCount = 0;
    int separationCount = 0;

    std::vector<boid*>::iterator iter;
    std::vector<boid*> temp = flockMates->getFlockMates();

        for ( iter = temp.begin(); iter != temp.end(); iter++ ) {

        if ( (*iter) != this ) {

            Ogre::Vector3 p1 = boidNode->getPosition();
            Ogre::Vector3 p2 = (*iter)->getBoidPosition();
            Ogre::Real distance = p1.distance(p2);


            if ( distance <= 10*boidSize ) {

                //cohesion
                cohesionCount++;
                cohesion += (*iter)->getBoidPosition();

                //alignement
                alignementCount++;
                alignement += (*iter)->getBoidVelocity();

            }

            if ( distance <= 2.5*boidSize ) {

                //separation
                separationCount++;

                Ogre::Vector3 away = boidNode->getPosition() - (*iter)->getBoidPosition();
                away.normalise();
                away*=boidSize;

                away/=(distance/2);
                separation += away;

            }
        }
    }

    /* do some more stuff */

    /* actually move the boid */
    return true;
};

因此,如您所见,基本算法相当繁重,令人愉快。我穿过一个 boid 向量,调用每个 boid 的一个方法,然后再次穿过该向量。其他计算是基本的,只是抛出变量以使一切看起来都很好,不会以指数方式增加复杂性。

现在,如前所述,我预计在大量 boid 时渲染会变慢。我希望帧率下降等等,但这根本不会发生。相反,该算法在高帧率和流畅的帧率下运行得非常好,直到我大约达到 200 boids +/-,然后在调用 swarm::move() 时立即崩溃。

我已经检查了几个未完成的部分。矢量容器有足够的空间容纳超过 10 亿个元素,所以不是这样。我也可以用 10000、20000 个 boid 初始化所有东西,所以这也不是一个基本的内存分配问题。只要调用 swarm::move() 就会崩溃。

那么,为什么会在 200 和一些 boid 的情况下崩溃?为什么帧率不会随着时间的推移而下降?感谢您对此的任何帮助。我想我提供了所有必要的信息,但是,如果您需要其他代码(或其他),请随时询问。

通过编辑的附加信息: 如果我通过单击而不是通过帧速率手动触发 swarm::move,它不会改变。它仍然适用于

编辑²: 编辑了 boid::move() 方法并记录了调试器捕获崩溃的位置。但是,它并没有捕捉到 boid 1 的崩溃,这对我来说是有意义的,但在 boid 314(在这种情况下)。因此,该算法通过相同的向量完美运行 313 次,然后在第 314 次崩溃。这有意义吗?

编辑³: 有趣的是,调试的东西比实际指出问题所在更让我感到困惑。我再次更新了 boid::move(),我将输入 swarm::getFlockMates 的代码,我会在稍后解释原因。

std::vector<boid*> swarm::getFlockMates() {
    return flock;
}

让我感到困惑的是以下内容。在我将距离的计算更改为 Ben voigt 建议的值后,代码在带有值的最终运动中崩溃了,这不应该崩溃。相反,我的 cohesionCount 为 1.x 百万,而 alignementCount 和 SeparationCount 仍然为 0。这 - 再次 - 对我来说根本没有意义。 cohesionCount 不能高于 boid 的总数,目前为 1000(因此崩溃)。即使所有的 Boid 都在凝聚力范围内(距离

这个蜜蜂说,我如何迭代我的羊群有问题吗?

【问题讨论】:

  • 尝试在调试器中运行它,以便找出导致它崩溃的行。
  • +1 用于调试器。我猜这里有一个空指针取消引用,但它甚至可能与 OGRE 有关。另外,您是否尝试过使用较小的数字(例如 150)运行并让它运行几个小时以查看是否发生崩溃?除非有高密度的 boid,否则可能会有一些罕见的边缘情况。
  • 同意在调试器中运行。我之前已经运行过这类模拟,并且可以相信在高达 200 个代理的情况下仍能获得不错的帧速率 - 计算真的没有那么粗略。如果我不得不猜测错误在哪里,我会说 getFlockMates() 可能会返回一个未初始化的向量,它可能指向垃圾。
  • 现在您的故障排除因模型中存在 OGRE(显示相关)数据类型而变得复杂。我首先从模型中清除视图内容,即时转换为 OGRE 向量以进行显示。然后完全关闭视图,看看模型本身是否会崩溃。
  • 希望您现在可以从这些“乱码”值中获得更多信息。 :) 顺便说一句,在回复某人时,请在您的消息前加上@Name 以通知对方您的回复,否则可能会被忽略。此外,调试器是编码人员拥有的最强大的工具之一,学习使用它! :)

标签: c++


【解决方案1】:

事实证明,我在这里提出的算法没有导致这个问题的任何缺陷。内存泄漏在我必须使用的 Lua 文件之一中,因为这个引擎(以及因此也有这个算法)是通过它使用的。不过还是感谢大家的帮助!

【讨论】:

    【解决方案2】:

    在他的书的第 6.14 章中,Daniel Shiffman 提出了一些算法来提高 boid 性能。这表明它们不应该为所有其他存在的 boid 循环,而只为那些接近它的 boid 循环,就像使用四叉树一样。

    http://natureofcode.com/book/chapter-6-autonomous-agents/

    【讨论】:

      【解决方案3】:

      这更像是一个评论而不是一个答案,但我不能写 cmets...我的猜测是,你的问题不在 swarm::move() 而是在那之前。如果您的一个指针不指向 boid 而是指向其他内存,那么崩溃将是最明智的选择。

      但是,我只能猜测,因为我不知道您的代码或哪种崩溃会终止您的程序。顺便说一句,您应该使用调试器进行检查,大多数调试器会向您显示崩溃发生的确切位置,有时甚至会显示原因。

      【讨论】:

      • 您还不能写 cmets 旨在鼓励您提供问题答案以首先证明您的价值!不要通过发布非答案作为答案来规避系统:(欢迎!
      • 没有 cmets 就不好玩了。 :-( 我为什么要回答已经回答的问题?stackoverflow 上的人太多了。
      • 每隔几分钟就会发布几十个新问题。但不要害怕回答老问题!
      • @TomalakGeret'kal:在许多“帮我找到我的问题”问题中,每个答案都只是猜测,直到最终证明是正确答案。
      • @Ben:确实如此。我实际上并没有标记这个。我想这更多是因为缺乏代表来发布 cmets 而将某些内容发布为非评论的概念,这更多是出于原则性的回应。
      【解决方案4】:

      许多算法都有收敛半径。您的 timeSinceLastFrame 参数是否与渲染速率相关联?如果是这样,那么通过影响渲染速率,人口规模反过来从根本上影响算法的收敛行为。如果您开始遇到稳定性问题,您也可以开始触发浮点异常。

      尝试使timeSinceLastFrame 保持不变,现在您的运行速度将低于实时,但收敛将不再受计算和渲染时间的影响。

      【讨论】:

      • 由于其他一些原因已经使用 timeSinceLastFrame。遗憾的是,将其设置为 1、2 或 100 并没有改变任何东西。
      猜你喜欢
      • 2012-02-10
      • 1970-01-01
      • 1970-01-01
      • 2011-09-06
      • 1970-01-01
      • 1970-01-01
      • 2017-08-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多