从加速度数据创建四元数
Create Quaternion from Acceleration Data
所以我有一个加速度传感器可以给我加速度数据。该设备目前处于某个位置,因此数据如下所示(每个轴都有一些噪声):
ACCELX = 264
ACCELY = -43
ACCELZ = 964
然后有一个3D模型代表设备,"all I want"就是这个3D模型代表真实设备的方向。在我试图理解 quaternions in .NET 的用法时,这是我吞噬的代码:
/* globals */
Vector3D PrevVector = new Vector3D(0, 0, 0);
ModelVisual3D model; // initialized and model file loaded
private async void TimerEvent()
{
RotateTransform3D rot = new RotateTransform3D();
QuaternionRotation3D q = new QuaternionRotation3D();
double x = 0, y = 0, z = 0;
List<Reading> results = await Device.ReadSensor();
foreach (Reading r in results)
{
switch (r.Type)
{
case "RPF_SEN_ACCELX":
x = r.Value;
break;
case "RPF_SEN_ACCELY":
y = r.Value;
break;
case "RPF_SEN_ACCELZ":
z = r.Value;
break;
}
}
double angle = Vector3D.AngleBetween(new Vector3D(x, y, z), PrevVector);
q.Quaternion = new Quaternion(new Vector3D(x, y, z), angle);
rot.Rotation = q;
model.Transform = rot;
PrevVector = new Vector3D(x, y, z);
}
移动我的真实设备确实会在报告值中产生变化,但屏幕上的模型只是在我看来随机的方向抽动,比噪音稍微远一点,而且似乎与我旋转真实设备的方式无关.我相当确定我正在错误地构建和使用四元数。我该怎么做才对?
这是带有 WPF 的 .NET。还有 HelixToolkit.WPF 可用,但我没有看到任何函数可以从那里的加速度数据创建四元数。 Unreal Engine 或 Unity 等更高级别的框架不适用于此项目。
你的传感器输出旋转值是累计值还是差值?有时输出旋转是不同的,你需要以前的旋转值加上差异来计算当前的新值。
您可以尝试保存之前的四元数,将当前四元数与之前的四元数相加,得到新的累计旋转。
原来我的问题性质完全不同:我不得不使用一个名为 Transform3DGroup
的 class。这是必须更改代码以启用绕 Z 轴旋转的方式:
/* globals */
ModelVisual3D model; // initialized and model file loaded
Transform3DGroup tg = new Transform3DGroup();
private async void TimerEvent()
{
RotateTransform3D rot = new RotateTransform3D();
QuaternionRotation3D q = new QuaternionRotation3D();
double x = 0, y = 0, z = 0;
List<Reading> results = await Device.ReadSensor();
foreach (Reading r in results)
{
switch (r.Type)
{
case "RPF_SEN_ACCELX":
x = r.Value;
break;
case "RPF_SEN_ACCELY":
y = r.Value;
break;
case "RPF_SEN_ACCELZ":
z = r.Value;
angle = GetAngle(x, y).ToDegrees();
q.Quaternion = new Quaternion(new Vector3D(0, 0, 1), angle);
break;
}
rot.Rotation = q;
tg.Children.Clear();
tg.Children.Add(rot);
model.Transform = tg; // Use Transform3DGroup!
}
}
我没有找到关于强制使用 Transform3DGroup
的任何文档。
为了完整起见,这里是 GetAngle()
的内部结构,源自 Bill Wallis on Math Overflow:
private double GetAngle(double x, double y)
{
if (x > 0)
{
return 2 * Math.PI - Math.Atan(y / x);
}
else if (x < 0)
{
return Math.PI - Math.Atan(y / x);
}
else // x == 0
{
return 2 * Math.PI - Math.Sign(y) * Math.PI / 2;
}
}
以及用于在弧度和度数之间转换双精度数的双精度类型的扩展([=25= 之外],在命名空间内):
public static class NumericExtensions
{
public static double ToRadians(this double val)
{
return (Math.PI / 180) * val;
}
public static double ToDegrees(this double val)
{
return (180 / Math.PI) * val;
}
}
所以我有一个加速度传感器可以给我加速度数据。该设备目前处于某个位置,因此数据如下所示(每个轴都有一些噪声):
ACCELX = 264
ACCELY = -43
ACCELZ = 964
然后有一个3D模型代表设备,"all I want"就是这个3D模型代表真实设备的方向。在我试图理解 quaternions in .NET 的用法时,这是我吞噬的代码:
/* globals */
Vector3D PrevVector = new Vector3D(0, 0, 0);
ModelVisual3D model; // initialized and model file loaded
private async void TimerEvent()
{
RotateTransform3D rot = new RotateTransform3D();
QuaternionRotation3D q = new QuaternionRotation3D();
double x = 0, y = 0, z = 0;
List<Reading> results = await Device.ReadSensor();
foreach (Reading r in results)
{
switch (r.Type)
{
case "RPF_SEN_ACCELX":
x = r.Value;
break;
case "RPF_SEN_ACCELY":
y = r.Value;
break;
case "RPF_SEN_ACCELZ":
z = r.Value;
break;
}
}
double angle = Vector3D.AngleBetween(new Vector3D(x, y, z), PrevVector);
q.Quaternion = new Quaternion(new Vector3D(x, y, z), angle);
rot.Rotation = q;
model.Transform = rot;
PrevVector = new Vector3D(x, y, z);
}
移动我的真实设备确实会在报告值中产生变化,但屏幕上的模型只是在我看来随机的方向抽动,比噪音稍微远一点,而且似乎与我旋转真实设备的方式无关.我相当确定我正在错误地构建和使用四元数。我该怎么做才对?
这是带有 WPF 的 .NET。还有 HelixToolkit.WPF 可用,但我没有看到任何函数可以从那里的加速度数据创建四元数。 Unreal Engine 或 Unity 等更高级别的框架不适用于此项目。
你的传感器输出旋转值是累计值还是差值?有时输出旋转是不同的,你需要以前的旋转值加上差异来计算当前的新值。
您可以尝试保存之前的四元数,将当前四元数与之前的四元数相加,得到新的累计旋转。
原来我的问题性质完全不同:我不得不使用一个名为 Transform3DGroup
的 class。这是必须更改代码以启用绕 Z 轴旋转的方式:
/* globals */
ModelVisual3D model; // initialized and model file loaded
Transform3DGroup tg = new Transform3DGroup();
private async void TimerEvent()
{
RotateTransform3D rot = new RotateTransform3D();
QuaternionRotation3D q = new QuaternionRotation3D();
double x = 0, y = 0, z = 0;
List<Reading> results = await Device.ReadSensor();
foreach (Reading r in results)
{
switch (r.Type)
{
case "RPF_SEN_ACCELX":
x = r.Value;
break;
case "RPF_SEN_ACCELY":
y = r.Value;
break;
case "RPF_SEN_ACCELZ":
z = r.Value;
angle = GetAngle(x, y).ToDegrees();
q.Quaternion = new Quaternion(new Vector3D(0, 0, 1), angle);
break;
}
rot.Rotation = q;
tg.Children.Clear();
tg.Children.Add(rot);
model.Transform = tg; // Use Transform3DGroup!
}
}
我没有找到关于强制使用 Transform3DGroup
的任何文档。
为了完整起见,这里是 GetAngle()
的内部结构,源自 Bill Wallis on Math Overflow:
private double GetAngle(double x, double y)
{
if (x > 0)
{
return 2 * Math.PI - Math.Atan(y / x);
}
else if (x < 0)
{
return Math.PI - Math.Atan(y / x);
}
else // x == 0
{
return 2 * Math.PI - Math.Sign(y) * Math.PI / 2;
}
}
以及用于在弧度和度数之间转换双精度数的双精度类型的扩展([=25= 之外],在命名空间内):
public static class NumericExtensions
{
public static double ToRadians(this double val)
{
return (Math.PI / 180) * val;
}
public static double ToDegrees(this double val)
{
return (180 / Math.PI) * val;
}
}