Xamarin Датчик направления вектора угла
Я использую Xamarin.Essentials.OrientationSensor
в моем проекте, и у меня возникают проблемы с интерпретацией Quaternion
API дает мне новые показания.
описание проблемы
Вот рисунок, описывающий мою проблему:
Мне нужно определить вектор ориентации v
быть нормальным / ортогональным к плоскости телефона (т.е. вектор, выходящий из задней части телефона). Затем мне нужно преобразовать этот вектор v
в трехмерном пространстве состояний (широта, долгота, высота). Это позволит мне вычислить угол между ориентацией телефона и вектором расстояния d
до определенной точки F
, где d = F - P
, Угол может быть получен с помощью скалярного произведения, косинуса и величин v
а также d
,
Что у меня пока
Согласно официальным документам, локальная система координат телефона описывается следующим образом:
Устройство (как правило, телефон или планшет) имеет 3D-систему координат со следующими осями: Положительная ось 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;
}
}
}
Буду признателен за любую подсказку или мысль по этой проблеме.