【问题标题】:Taking average of multiple Vector3's取多个 Vector3 的平均值
【发布时间】:2019-02-27 18:04:35
【问题描述】:

所以我正在使用像 kinect 一样工作的深度相机。 (这不是 kinect)。并在其后面使用 Nuitrack 进行骨骼跟踪。但是,它为玩家返回的位置是不稳定的。当完全静止时,它返回的数字最多可以上升或下降 10。

示例:用户尽可能地站着,数据在第一帧返回位置 100,下一帧是 102,然后是 97,然后是 100,然后是 106,以此类推。它在更新中返回这些位置我们用它来移动图像。 (因此用户控制图像)但正如您所料,由于数据不一致,该图像移动得非常不稳定。根据 Nuitrack 的说法,这是正确的,用户自己需要为此找到解决方案。

我尝试过从一个位置到另一个位置的 lerp,但这让它感觉不太互动,因为一旦我到达 lerp 实际上是平滑的点,它就会有很大的延迟。我还尝试仅使用新的位置数据,如果它与之前 nuitrack 给我的位置有 4 个像素不同的话,这会更好一些,但会导致图像跳跃,即使我也能看到它。使用此功能:

foreach (User user in frame.Users)
        {
            if (Vector3.Distance(_lastPos, user.Proj.ToVector3()) >4f)
            {
                Vector3 final = ((_lastPos + user.Proj.ToVector3()) /2);
                userData.Add(new UserData(user.ID, user.Real.ToVector3(), final));
                _lastPos = user.Proj.ToVector3();
            }
            else
            {
                userData.Add(new UserData(user.ID, user.Real.ToVector3(), _lastPos));
            }

还有lerp函数:

float _userX = user.ProjPosition.x * (_cameraPos.x *2)- _cameraPos.x;
            Vector3 _newPos = new Vector3(_userX, _basketPos.y, _basketPos.z);
            _basketPrefab.transform.position = Vector3.Lerp(_basketPrefab.transform.position, _newPos, Time.deltaTime * 30f);

编辑:还有其他人吗?

【问题讨论】:

  • 在保持良好响应的同时过滤数据是一门艺术,尝试研究 PID 控制器和卡尔曼滤波
  • 会调查的,谢谢。

标签: c# unity3d


【解决方案1】:

您可以尝试保留最后 n 个位置的列表,然后通过使用 0.5f(中间值)作为 t 将它们合并在一起来计算这些位置的平均值。然后,您可以通过将先前的 le​​rped 位置再次相互 Lerping 来增加“平滑度”的水平,使其在每次迭代时更加平滑。然而,每次迭代都会让人感觉有点迟钝,需要在平滑和反应之间找到平衡。

(未经测试的示例)。

List<Quaternion> lastPosition = new List<Quaternion>(); //Keep a list of the last positions
int smoothing = 16; //Max amount of smoothing, higher means more positions will be used for the smoothness
enum SmoothingLevels { None, Mild, Moderate, Severe, Extreme } //Level of smoothness you want to use
SmoothingLevels smoothingLevel;
Vector3 pos;

//remove the oldest entry from the list 
if(lastPosition.Count > 0)
{
    lastPosition.RemoveAt(0);
}

//Add the newest data to the list
while (lastPosition.Count < smoothing)
{
    lastPosition.Add(transform.position);
}

Vector3 vecA = lastPosition[0];
Vector3 vecB = lastPosition[1];
Vector3 vecC = lastPosition[2];
Vector3 vecD = lastPosition[3];
Vector3 vecE = lastPosition[4];
Vector3 vecF = lastPosition[5];
Vector3 vecG = lastPosition[6];
Vector3 vecH = lastPosition[7];
Vector3 vecI = lastPosition[8];
Vector3 vecJ = lastPosition[9];
Vector3 vecK = lastPosition[10];
Vector3 vecL = lastPosition[11];
Vector3 vecM = lastPosition[12];
Vector3 vecN = lastPosition[13];
Vector3 vecO = lastPosition[14];
Vector3 vecP = lastPosition[15];

//Lerp each subsequent position by t 0.5 to get the average position of the two.
//This is the base smoothing, where smoothing is low and responsiveness is high
Vector3 vecAB = Vector3.Lerp(vecA, vecB, 0.5f);
Vector3 vecCD = Vector3.Lerp(vecC, vecD, 0.5f);
Vector3 vecEF = Vector3.Lerp(vecE, vecF, 0.5f);
Vector3 vecGH = Vector3.Lerp(vecG, vecH, 0.5f);
Vector3 vecIJ = Vector3.Lerp(vecI, vecJ, 0.5f);
Vector3 vecKL = Vector3.Lerp(vecK, vecL, 0.5f);
Vector3 vecMN = Vector3.Lerp(vecM, vecN, 0.5f);
Vector3 vecOP = Vector3.Lerp(vecO, vecP, 0.5f);

//moderate smoothing, Lerp the previously lerped position again with each other to increase the smoothness
Vector3 vecABCD = Vector3.Lerp(vecAB, vecCD, 0.5f);
Vector3 vecEFGH = Vector3.Lerp(vecEF, vecGH, 0.5f);
Vector3 vecIJKL = Vector3.Lerp(vecIJ, vecKL, 0.5f);
Vector3 vecMNOP = Vector3.Lerp(vecMN, vecOP, 0.5f);

//Severe smoothing, High smoothness, lower responsiveness
Vector3 vecABCDEFGH = Vector3.Lerp(vecABCD, vecEFGH, 0.5f);
Vector3 vecIJKLMNOP = Vector3.Lerp(vecIJKL, vecMNOP, 0.5f);

//Extreme smoothing, this will take the average of all 16 positions. Very smooth, feels really sluggish
Vector3 vecABCDEFGHIJKLMNOP = Vector3.Lerp(vecABCDEFGH, vecIJKLMNOP, 0.5f);
switch (smoothingLevel)
{
    case SmoothingLevels.None:
        pos = transform.position;
        break;
    case SmoothingLevels.Mild:
        pos = vecOP;
        break;
    case SmoothingLevels.Moderate:
        pos = vecMNOP;
        break;
    case SmoothingLevels.Severe:
        pos = vecIJKLMNOP;
        break;
    case SmoothingLevels.Extreme:
        pos = vecABCDEFGHIJKLMNOP;
        break;
}

//apply pos to your target object

【讨论】:

  • 首先感谢您的帮助!我试过你的代码,它确实有点用。它仍然有点晃动,但可以修复。问题是,我认为这是一个非常繁重的功能,要为 2-6 个用户执行每一帧。而不是最漂亮的食客。不知道现在该怎么办。我们将来自相机的位置数据放入我们自己的数据类中,然后将其传递给最终的游戏。您的示例有所帮助,但仍然不是我们正在寻找的最终行为。感觉你说的有点迟钝
  • 代码确实可以做得更漂亮一些,但选择了可读性最好的选项。它的性能实际上取决于你用它做的传球次数。根据我的经验,最多 16 次通过(如上例所示)即使在较旧的 android 设备上使用也不会产生明显的性能影响。出于好奇,为什么您需要每帧运行 2-6 次?我会假设每个用户都会在他们自己的设备上运行它,或者只在每帧的对象上运行一次并公开结果
  • 是的,我可能会研究如何改进它,尽管您的解决方案并不完全符合我们的要求,但它是迄今为止最接近的。我跑了这么多,因为镜头前可能有多个人。每一帧 Nuitrack 都会为这些用户中的每一个传递所有数据。最多可达 6 个。
猜你喜欢
  • 1970-01-01
  • 2017-02-06
  • 2022-11-22
  • 2014-09-23
  • 2015-10-23
  • 2021-10-25
  • 1970-01-01
  • 2014-08-22
  • 1970-01-01
相关资源
最近更新 更多