【发布时间】: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++