Я использую Xamarin.Essentials.OrientationSensor
в своем проекте, и у меня возникают проблемы при интерпретации Quaternion
, который API дает мне при новых показаниях.
Описание проблемы
Вот рисунок, описывающиймоя проблема:
Мне нужно определить вектор ориентации v
, являющийся нормальным / ортогональным к плоскости телефона (то есть вектор, выходящий из задней части телефона).Затем мне нужно преобразовать этот вектор v
в трехмерное пространство состояний, равное (широта, долгота, высота) .Это позволит мне вычислить угол между ориентацией телефона и вектором расстояния d
до определенной фиксированной точки F
, где d = F - P
.Угол может быть получен с помощью скалярного произведения, косинуса и величин v
и d
.
Что у меня есть
Согласно официальным документам , локальная система координат телефона описывается следующим образом:
Устройство (обычно телефон или планшет) имеет трехмерную систему координат со следующими осями: положительная ось X указывает справа отдисплей в портретном режиме.Положительная ось Y указывает на верхнюю часть устройства в портретном режиме.Положительная ось Z направлена за пределы экрана.
Это означает, что искомый вектор v
будет v = (0,0, -1) в локальной системе координат телефона.
В этот вопрос , я уже узнал, что данные кватернионы являются относительными, и мне нужен эталонный кватернион centerQ
для моих преобразований:
void onNewOrientation(Quaternion q)
{
if (centerQ == Quaternion.Identity)
{
centerQ = Quaternion.Inverse(q);
return;
}
}
И тогда я мог бы использовать Quaternion.Transform(v, centerQ)
для преобразования v
в земную систему координат, которая в соответствии с документами определяется как
Трехмерная система координат Земли имеетследующие оси: положительная ось X касается поверхности Земли и указывает на восток.Положительная ось Y также касается поверхности Земли и указывает на север.Положительная ось Z перпендикулярна поверхности Земли и направлена вверх.
Однако мне нужно преобразовать v
в высоту WGS +, и здесь она становится сложной.
Я попытался определить два дополнительных кватерниона для преобразований из земной системы координат - как описано в документации - в WGS следующим образом:
var phi = -1 * lastloc.Latitude * Util.DEGTORAD;
var lam = lastloc.Longitude * Util.DEGTORAD;
var qLat = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), (float)phi);
var qLon = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), (float)lam);
Поскольку широта - это в основном вращениевокруг оси x и долготы вокруг оси y соответственно.
И тогда мне придется умножить три кватерниона centerQ
, qLat
и qLon
.Тем не менее, я перепробовал все возможные комбинации заказов между тремя, и результаты не имеют никакого смысла.Я знаю, что умножение кватернионов не является коммутативным.
Вот мой код до сих пор
namespace Foo.ViewModels
{
public class DebugViewModel : BaseViewModel
{
private Quaternion centerQ;
private static Vector3 vec3out = new Vector3(0f, 0f, -1f);
private static Location fixPos;
private Location lastloc;
public DebugViewModel()
{
centerQ = Quaternion.Identity;
lastloc = null;
fixPos = new Location()
{
Latitude = 0, // anonymized
Longitude = 0, // anonymized
Altitude = 0 // anonymized
};
// Create reactive observable
Observable.FromEventPattern<OrientationSensorChangedEventArgs>(
ev => OrientationSensor.ReadingChanged += ev,
ev => OrientationSensor.ReadingChanged -= ev)
.Select(eventPattern => eventPattern.EventArgs.Reading.Orientation)
//.Throttle(TimeSpan.FromMilliseconds(20))
.Subscribe(this.onNewOrientation);
OrientationSensor.Start(SensorSpeed.UI);
}
void onNewOrientation(Quaternion q)
{
if (centerQ == Quaternion.Identity)
{
centerQ = Quaternion.Inverse(q);
return;
}
if (lastloc == null)
{
return;
}
var qi = Quaternion.Inverse(q);
var qq = Quaternion.Multiply(centerQ, q);
// get local quaternion
var phi = -1 * lastloc.Latitude * Util.DEGTORAD;
var lam = lastloc.Longitude * Util.DEGTORAD;
var qLat = Quaternion.CreateFromAxisAngle(new Vector3(1, 0, 0), (float)phi);
var qLon = Quaternion.CreateFromAxisAngle(new Vector3(0, 1, 0), (float)lam);
var qLati = Quaternion.Inverse(qLat);
var qLoni = Quaternion.Inverse(qLon);
var q1 = Quaternion.Multiply(qLat, qLon);
var q2 = Quaternion.Multiply(q1, qq);
Vector3 aa = Vector3.Transform(vec3out, q2);
Vector3 myP = new Vector3(
(float)lastloc.Latitude,
(float)lastloc.Longitude,
(float)lastloc.Altitude);
Vector3 drP = new Vector3(
(float)twrPos.Latitude,
(float)twrPos.Longitude,
(float)twrPos.Altitude);
Vector3 d = Vector3.Normalize(drP - myP);
float alpha = Util.vecAngle(d, aa) * (float)Util.RADTODEG;
}
}
}
Буду признателен за любой намек или мысль по этой проблеме.