植绒算法在200多个boids上崩溃

时间:2011-10-10 20:21:02

标签: c++

我正在努力将植绒算法实施到更大的系统中。 OGRE用于渲染,luabind用于能够从LUA,yatta yatta进行通信,这些东西不应该太重要。

我基本上按雷诺'boids-model实现了算法。这意味着,一个Boid(如“群中的一条鱼”)根据它在某个半径内的邻居移动。事情就是这样,它的基本复杂性是O(n²),因为如果它们在范围内,那么每个boid必须检查所有它们的flockmates,然后考虑一些因素来计算自己的运动。

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

我的问题是,一旦我在boid数字中遇到一种“障碍”,算法就会在运行时崩溃,大约200-250甚至变化。现在,如果它变得越来越慢,直到我被打破到1 fps,我就能理解,算法的简单性能就是问题所在。但是,例如,199 boids完美运行,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中的对象。在运行时,调用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;
};

因此,正如您所看到的,基本算法非常重,非常重要。我运行了一个boids向量,调用每个boid的方法然后再次遍历向量。其他计算是基本的,只是抛出变量,以便一切看起来都很好,没有任何指数增加复杂性。

现在,正如已经说过的那样,我预计渲染会在大量的boid中变慢。我希望帧率下降等等,但这根本不会发生。相反,算法运行得非常精细,具有高和流畅的帧速率,直到我大约200个boid +/-,然后一旦调用swarm :: move()就会立即崩溃。

我已经检查了几个松散的结尾。向量容器有足够的空间用于> 10亿个元素,所以不是这样。我还可以使用10000,20000个boid初始化所有内容,因此它也不是基本的内存分配问题。只要调用swarm :: move(),它就会崩溃。

那么,为什么这会导致200和几个boid崩溃?为什么帧率没有随着时间而下降?感谢您的帮助。我想我会提供所有必要的信息,但是,如果您需要额外的代码(或其他),请随时提出。

通过编辑的其他信息: 如果我通过点击而不是通过帧率手动触发swarm :: move,它不会改变。它仍适用于&lt; 200ish boids,它仍然无法使用更多。

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

Edit³: 有趣的是,调试内容让我感到困惑,远远超过实际指向问题所在。我再次更新了boid :: move(),我将抛出swarm :: getFlockMates的代码,我将在一秒钟内解释原因。

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

令我困惑的是以下内容。在我将距离的计算更改为Ben voigt建议的内容之后,代码在最终的运动中崩溃了,值不应该崩溃。相反,我有一个1.000百万的cohesionCount,而alignementCount和separationCount仍然是0.这再次 - 对我来说根本没有意义。 cohesionCount不能高于boids的总量,此刻为1000(因此崩溃)。即使所有Boids都在内聚力范围内(距离<10 * boidSize),它的最大值也不能高于Flock中存储的boid总量。

这个说法,我是如何迭代我的羊群的?

4 个答案:

答案 0 :(得分:3)

事实证明,我在这里提出的算法没有任何导致这个问题的缺陷。内存泄漏是我必须使用的一个Lua-Files,因为这个引擎(也就是这个算法)就是通过它来使用的。谢谢你们所有人的帮助!

答案 1 :(得分:2)

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

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

答案 2 :(得分:0)

这是一个评论而非答案,但我不能写评论......我的猜测是,你的问题不在swarm::move(),而是在此之前。如果你的一个指针没有指向boid而是指向其他记忆,那么崩溃将是最明智的事情。

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

答案 3 :(得分:0)

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

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