Поиск на сайте: Расширенный поиск


Новые программы oszone.net Читать ленту новостей RSS
Утилита для полного удаления из системы драйверов видео от AMD и NVIDIA. С помощью DDU вы сможете удалить драйверы полно...
Программа для определения занятого места на жестком диске. TreeSize позволяет найти файлы и папки, которые занимают боль...
XviD4PSP - удобный и качественный мультиформатный конвертер на основе AviSynth. Конвертирует файлы для PSP, PS3, XBOX 36...
Многофункциональный календарь-планировщик с функциями напоминателя, будильника, синхронизатора с КПК на базе Windows Mob...
Утилита, расширяющая возможности стандартного буфера обмена. Программа запоминает наиболее часто копируемую в буфер инфо...
OSzone.net Microsoft Разработка приложений Windows Phone Ориентируйтесь на местности с помощью компаса Windows Phone RSS

Ориентируйтесь на местности с помощью компаса Windows Phone

Текущий рейтинг: 4 (проголосовало 1)
 Посетителей: 3117 | Просмотров: 3714 (сегодня 0)  Шрифт: - +
Мы, люди, редко пользуемся органами чувств изолированно друг от друга: сочетание зрения и слуха позволяет нам выстраивать мысленную картину окружения, комбинация обоняния, вкусовых ощущений и осязания влияет на наше восприятие еды, а при игре на музыкальном инструменте в ход идут осязание, зрение и слух.

То же самое и со смартфоном: он может «видеть» линзами своей камеры, «слышать» микрофоном, «осязать» сенсорным экраном и знать о своем местонахождении в мире с помощью GPS и датчика ориентации. Попробуйте комбинировать ввод от этих датчиков, и результат не опишешь иначе, как синергизм.

Проблема акселерометра

Акселерометр в Windows Phone — хороший пример датчика, который дает кое-какую существенную информацию, но становится гораздо ценнее в сочетании с другим датчиком, особенно с компасом.

Аппаратная часть акселерометра на самом деле измеряет силу, но, как мы знаем из школьного курса физики, сила равна массе, умноженной на ускорение, так что акселерометр реагирует на любой вид ускорения. Когда смартфон находится в покое, акселерометр измеряет силу притяжения и предоставляет трехмерный вектор, указывающий на центр Земли. Этот вектор относителен системе трехмерных координат, как показано на рис. 1. Эта система координат остается одинаковой независимо от того, кодируете вы с применением Silverlight или XNA, и от того, в каком режиме находится смартфон — портретном или альбомном.

*
Рис. 1. Система координат датчика смартфона

Класс Accelerometer предоставляет вектор ускорения в виде значения типа Vector3. Это XNA-тип, поэтому, если вам нужно использовать его в программе Silverlight, вы должны добавить ссылку на сборку Microsoft.Xna.Framework.

Хотя вектор ускорения позволяет программе, выполняемой на смартфоне, определять ориентацию устройства относительно Земли, кое-какой важной информации недостает. Позвольте мне показать, о чем я говорю.

В исходном коде, который можно скачать для этой статьи, есть решение Visual Studio с названием 3DPointers, в котором содержатся четыре проекта XNA 3D, и все они выглядят довольно похоже. Программа Accelerometer 3D рисует трехмерную «булавку», которая парит в космосе и направлена в сторону, которую указывает вектор ускорения (рис. 2).

*
Рис. 2. Экран программы Accelerometer 3D

Любой программе, использующей какой-либо датчик Windows Phone, нужна ссылка на сборку Microsoft.Devices.Sensors. Обычно XNA-программы на смартфоне выполняются в альбомном режиме, и это может быть проблемой, так как их система координат не соответствует таковой, используемой датчиками. Чтобы упростить себе некоторые вещи, я переориентировал систему координат XNA для портретного режима в конструкторе класса, производного от Game:

graphics.IsFullScreen = true;
graphics.PreferredBackBufferWidth = 480;
graphics.PreferredBackBufferHeight = 800;

В Windows Phone 7.1 API датчиков немного изменили, чтобы обеспечить согласованность между различными датчиками. Конструктор программы Accelerometer 3D использует этот новый API, чтобы создать экземпляр Accelerometer, сохраняемый как поле:

if (Accelerometer.IsSupported)
{
  accelerometer = new Accelerometer
  {
    TimeBetweenUpdates = this.TargetElapsedTime
  };
}

Значение свойства TimeBetweenUpdates по умолчанию — 25 мс; в данном случае оно устанавливается в соответствии с частотой кадров программы и равно 33 мс.

Программа использует переопределенную версию метода OnActivated для запуска Accelerometer:

if (accelerometer != null)
{
  try { accelerometer.Start(); }
  catch { }
}

Хотя трудно вообразить сценарий, где метод Start потерпит неудачу на этом этапе, рекомендуется помещать его вызов в блок try. Компас останавливается в OnDeactivated:

if (accelerometer != null)
    accelerometer.Stop();

В программе применяется метод LoadContent для построения 3D-вершин для «булавки» и определяется BasicEffect для хранения информации камеры и освещения. Булавка определяется так, чтобы ее основание находилось в начале координат и расширялось на одну единицу вверх по положительной оси Y. Камера смотрит непосредственно на начало координат с положительной Z-оси.

Затем в методе Update используется вектор ускорения, чтобы определить преобразование мировых координат (world transform). Это преобразование в конечном счете смещает булавку относительно начала координат. Код показан на рис. 3.

Рис. 3. Метод Update в программе Accelerometer 3D

protected override void Update(GameTime gameTime)
{
  if (GamePad.GetState(PlayerIndex.One).Buttons.Back ==
    ButtonState.Pressed)
      this.Exit();
  if (accelerometer != null && accelerometer.IsDataValid)
  {
    Vector3 acceleration = accelerometer.CurrentValue.Acceleration;
    text = String.Format("X = {0:F3}\nY = {1:F3}\nZ = {2:F3}",
                            acceleration.X,
                            acceleration.Y,
                            acceleration.Z);
    textPosition = new Vector2(0, this.GraphicsDevice.Viewport.Height -
      segoe24Font.MeasureString(text).Y);
    acceleration.Normalize();
    Vector3 axis = Vector3.Cross(acceleration, Vector3.UnitY);
    // Особый случай для магнитометра (0, 1, 0) или (0, -1, 0)
    if (axis.LengthSquared() == 0)
        axis = Vector3.UnitX;
    else
        axis.Normalize();
    float angle = -(float)Math.Acos(Vector3.Dot(Vector3.UnitY,
      acceleration));
    basicEffect.World = Matrix.CreateFromAxisAngle(axis, angle);
  }
  else
  {
    basicEffect.World = Matrix.Identity;
    text = "";
  }
  base.Update(gameTime);
}

Этот метод получает значение Acceleration напрямую от объекта Accelerometer, если свойство IsDataValid равно true. Булавку нужно вращать, исходя из угла между вектором Acceleration и положительной Y-осью. Скалярное произведение этих двух векторов дает этот угол, а векторное произведение — ось вращения.

Но попробуйте следующее: встаньте и держите смартфон в руке под каким-то углом. Булавка указывает вниз. Теперь, стоя на одном месте, сделайте оборот на 360 градусов. При повороте булавка остается в той же позиции (или близкой к ней). То есть Accelerometer не реагирует, когда смартфон смещается по оси, параллельной вектору ускорения. Если вы пишете приложение, где нужно знать полную трехмерную ориентацию смартфона, то, увы, акселерометр предоставляет лишь часть требуемой информации.

Компас спешит на помощь

Когда Windows Phone выпустили впервые, было не совсем ясно, есть ли вообще в этих смартфонах компас. Казалось, что точного ответа не знал никто. Однако для прикладных программистов ситуация была очень проста: интерфейс программирования для компаса отсутствовал, поэтому, даже если компас и существовал, мы не могли им воспользоваться.

Выпуск Windows Phone 7.1 прояснил ситуацию: хотя устройство Windows Phone не должно обязательно содержать компас, некоторые устройства Windows Phone всегда оборудованы им, в том числе смартфон, купленный мной в декабре 2010 г. И самое главное — программисты на самом деле могут использовать этот компас. В Windows Phone 7.1 введен новый класс Compass, который позволяет прикладному программисту выяснить наличие компаса (через статическое свойство Compass.IsSupported) и получить доступ к его показаниям. В эмуляторе Windows Phone свойство Compass.IsSupported всегда возвращает false.

Основу компаса образует устройство, называемое магнитометром. На него влияют любые магнитные поля рядом со смартфоном, включая то, которое генерируется акустическими колонками вашего настольного компьютера. Если вы уберете все такие источники магнитных полей от смартфона, магнитометр будет измерять силу и направление магнитного поля Земли.

Класс Compass предоставляет данные в виде структуры CompassReading. Неструктурированные данные от магнитометра доступны через свойство MagnetometerReading, которое является еще одним Vector3, относительным системе координат, показанной на рис. 1. В отсутствие близлежащих магнитов этот вектор соответствует магнитному полю Земли. Вектор указывает на север, естественно, но в большинстве мест в северном полушарии он также указывает в землю. Если вы держите смартфон так, что его экран смотрит вверх, у этого вектора появляется значительный компонент –Z.

Решение 3DPointers содержит проект Magnetometer 3D, аналогичный проекту Accelerometer 3D с тем исключением, что использует класс Compass вместо Accelerometer. На рис. 4 показан экран этой программы, когда смартфон находится в моей квартире на Манхеттене дисплеем вверх и верхняя часть смартфона указывает в «центр», т. е. левая и правая стороны устройства выровнены (насколько я смог) с магистральными дорогами Нью-Йорка. (Карта показывает, что эти дороги направлены под углом примерно 30 градусов на восток от севера.)

*
Рис. 4. Экран программы Magnetometer 3D

В документации утверждается, что вектор MagnetometerReading выражается в микротеслах. На двух коммерческих устройств Windows Phone, которыми я владею, этот вектор обычно имеет магнитуду порядка 30, что приблизительно правильно. (Значения вектора на рис. 4 имеют составную магнитуду 43.) Однако в моем третьем смартфоне вектор MagnetometerReading нормализуется и всегда имеет магнитуду, равную 1.

Теперь попробуйте следующее: держите смартфон так, чтобы вектор Magnetometer 3D был примерно выровнен с положительной Y-осью на рис. 1. Затем поворачивайте смартфон вокруг оси, параллельной этому вектору. Вектор остается тем же самым (или около того), т. е. магнитометр смартфона тоже не дает полной картины об ориентации устройства.

Направления по компасу

Обычно, когда мы пользуемся компасом, нам не нужно выравнивать трехмерный вектор с магнитным полем Земли. Гораздо полезнее был бы двухмерный вектор, тангенциальный поверхности Земли.

Как вы, возможно, знаете, магнитное поле Земли не совпадает с ее осью вращения. Направление оси Земли называется географическим, или истинным, севером, и это тот самый север, который используется на картах и практически для всех остальных целей. Углы на двухмерной поверхности часто применяются для того, чтобы представлять направление на север. Его часто называют направлением или азимутом по компасу.

Разница между магнитным и истинным севером варьируется по всему земному шару. В Нью-Йорке, чтобы получить истинный азимут, нужно вычесть примерно 13 градусов из магнитного, но в Сиэтле, напротив, к магнитному азимуту требуется добавить 21 градус. Класс Compass выполняет эти вычисления за вас, основываясь на местонахождении смартфона. Кроме вектора MagnetometerReading, CompassReading также предоставляет два свойства типа double с именами MagneticHeading и True­Heading. Это углы в градусах в диапазоне от 0 до 360, измеряемые против часовой стрелки от положительной Y-оси, показанной на рис. 1.

TrueHeading следует всегда интерпретировать как приблизительное значение, и даже тогда ему нельзя всецело доверять. На двух моих устройствах TrueHeading обычно дает примерно правильное значение, но на другом смартфоне отклонение составляет добрых 70 градусов!

Я не сумел вникнуть в смысл значения MagneticHeading. В конкретном месторасположении разница между значениями TrueHeading и MagneticHeading должна быть постоянной. Например, там, где я живу, значение TrueHeading за вычетом Magnetic­Heading должно быть –13 градусов. На всех трех моих устройствах эта разница спорадически скакала в зависимости от ориентации смартфона. Иногда она составляла –12 (что почти верно), но по большей части была 92. И это всегда были только эти два значения. Ни на одном из моих смартфонов значение MagneticHeading не согласовалось с углом, полученных из значений X и Y вектора MagnetometerReading.

В XNA-программе, как вы видели, можно просто получить текущее значение от датчика при выполнении метода Update. Используя класс датчика в программе Silverlight, вы должны подключить обработчик для события CurrentValueChanged. Тогда вы сможете получать объект показаний датчика из аргументов этого события.

В исходном коде для этой статьи содержатся две программы Silverlight — Arrow Compass и Dial Compass, — показывающие направление на север с помощью свойства TrueHeading. Вся графика определена в XAML. Как и в случае XNA-программ, эти программы Silverlight создают объект Compass в своих конструкторах, но, кроме этого, задают обработчик для свойства CurrentValueChanged:

if (Compass.IsSupported)
{
  compass = new Compass();
  compass.TimeBetweenUpdates = TimeSpan.FromMilliseconds(33);
  compass.CurrentValueChanged += OnCompassCurrentValueChanged;
}

В программе Arrow Compass этот обработчик задает угол в объекте RotateTransform, подключенным к символу стрелки (arrow graphic):

{Для верстки: в следующем листинге есть два специальных символа – не потеряйте их.}

this.Dispatcher.BeginInvoke(() =>
{
  arrowRotate.Angle = -args.SensorReading.TrueHeading;
  accuracyText.Text = String.Format("±{0}°",
                      args.SensorReading.HeadingAccuracy);
});

Обработчик CurrentValueChanged вызывается в отдельном потоке, поэтому вам потребуется использовать Dispatcher для обновления любых UI-объектов. Поскольку угол TrueHeading указывает отклонение против часовой стрелки, а повороты в Silverlight осуществляются по часовой стрелке, в коде используется отрицательный угол направления для поворота.

Результат показан на рис. 5, и вновь смартфон указывает на центр Нью-Йорка.

*
Рис. 5. Экран программы Arrow Compass

В программе Dial Compass стрелка остается фиксированной, пока шкала поворачивается, чтобы указать направление (рис. 6). Используйте эту вариацию, если вам нужно знать направление, в котором указывает верх смартфона, а не направление на север относительно ориентации устройства.

*
Рис. 6. Экран программы Dial Compass

Если вы запустите любую из этих двух программ и будете держать смартфон так, чтобы его дисплей смотрел на землю, компас перестанет правильно работать. Поворот будет прямо противоположен тому, который должен быть на самом деле. Если вам нужно исправить эту проблему, используйте положительное значение TrueHeading, когда Z-значение вектора ускорения положительное.

Калибровка компаса

В нижнем правом углу программа Arrow Compass отображает значение HeadingAccuracy из CompassReading. Теоретически оно сообщает точность значений направления по компасу. На практике я видел разброс значений HeadingAccuracy от 5% до 30%.

В классе Compass также определено событие Calibrate, которое срабатывает, когда значение HeadingAccuracy превышает 20%.

Вы можете выполнить маневр с калибровкой, чтобы уменьшить значение HeadingAccuracy: держите в руке смартфон с экраном, смотрящим влево или вправо, а затем несколько раз подряд опишите рукой в воздухе символ бесконечности. В некоторых примерах кода из учебных материалов в MSDN (bit.ly/yYrHrL) даже есть символ, который можно показывать пользователям, чтобы уведомлять их о необходимости калибровки компаса.

Объединение компаса и акселерометра

Компас — отличный пример датчика, который с очевидностью дает некоторую пользу сам по себе, особенно если вы заблудились в лесу, но становится гораздо ценнее в сочетании с другими датчиками, в частности с акселерометром. Совместно эти два датчика могут предоставить полную информацию об ориентации смартфона в трехмерном пространстве.

В Windows Phone 7.1 определен новый класс, который как раз это и делает. Кроме предоставления информации об ориентации смартфона в трех разных направлениях, класс Motion включает информацию от нового класса Gyroscope, если таковой датчик присутствует в смартфоне.

Более того, класс Motion выполняет дополнительную работу по сглаживанию данных от Accelerometer и Compass. Если вы запускали представленные на данный момент программы, то, вероятно, заметили значительный разброс в данных от этих классов. Этот разброс убирается классом Motion.

Однако, поскольку я отношусь к тем людям, которые не боятся трудных задач, я решил попробовать «вручную» скомбинировать данные от Accelerometer и Compass и должен признать, что этот эксперимент заставил меня с большим уважением относиться к классу Motion!

Программа Compass 3D отображает четыре булавки разных цветов, расположенных по невидимому кругу: серебристая указывает на север, красная — на восток, зеленая — на юг и синяя — на запад. Программа пытается показывать эту плоскость с булавками параллельно поверхности Земли с правильной ориентацией в направлении четырех точек компаса.

Я выбрал стратегию с использованием углов Эйлера. Это три угла, представляющие повороты по осям X, Y и Z, и все вместе они описывают ориентацию в трехмерном пространстве. В механике полета эти три угла обозначаются как тангаж (pitch), крен (roll) и рысканье (yaw). С точки зрения авиации, тангаж указывает, задран или опущен нос самолета и насколько, тогда как крен определяет любой наклон плоскости в правую сторону или левую. Эти углы вращения можно визуализировать как относительные двум осям: тангаж — это вращение вокруг оси, которая проходит через крылья, а крен — вращение вокруг оси, проходящей от носа самолета к хвосту. Рысканье — это вращение вокруг оси, перпендикулярной поверхности Земли, и этот угол указывает направление по компасу для плоскости.

Чтобы визуализировать эти углы относительно системе координат смартфона, показанной на рис. 1, представим, что вы летите на смартфоне как на ковре-самолете, сидя на экране, и при этом верхняя часть устройства находится впереди от вас, а три кнопки — позади. Тангаж — это вращение вокруг X-оси, крен — вращение вокруг Y-оси и рысканье — вращение вокруг Z-оси.

Вычисление крена и тангажа по вектору ускорения оказалось сравнительно простым и требует использования стандартных формул:

float roll = (float)Math.Asin(-acceleration.X);
float pitch = (float)Math.Atan2(acceleration.Y, -acceleration.Z);

{Для верстки: не потеряйте в следующем абзаце 4 символа "пи".}

Если смартфон лежит на столе и его экран смотрит вверх, как крен, так и тангаж равны нулю. Диапазон крена составляет от –π/2 до π/2, и значения возвращаются к нулю, когда смартфон переворачивается экраном вниз. Тангаж варьируется от –π до π с максимальными значениями при расположении смартфона экраном вниз. Когда экран смотрит вверх, рысканье должно быть более-менее тем же значением, что и в свойстве TrueHeading класса Compass, но преобразуется в радианы для XNA:

float yaw = MathHelper.ToRadians((float)compass.CurrentValue.TrueHeading);

Однако, как вы видели в двух программах-компасах Silverlight, TrueHeading перестает правильно работать, когда смартфон поворачивается экраном вниз, в сторону земли, поэтому рысканье нужно скорректировать с учетом этого. После теоретических и эмпирических изысканий в этой области я оставил все, как есть, и сконструировал преобразование мировых координат на основе этих трех углов:

basicEffect.World = Matrix.CreateRotationZ(yaw) *
                    Matrix.CreateRotationY(roll) *
                    Matrix.CreateRotationX(pitch);

Результаты показаны на рис. 7.

*
Рис. 7. Экран программы Compass 3D

Я также включил программу Orientation 3D, которая получает эти три угла от класса Motion. Вы сами можете увидеть, насколько более гладкими получаются результаты (в дополнение к правильной работе при переворачивании смартфона экраном вниз).

Класс Motion — слишком важное дополнение в API датчиков, чтобы использовать его только в этой программе. Как вы увидите в следующей статье, этот класс на самом деле служит порталом в трехмерный мир.

Автор: Чарльз Петцольд  •  Иcточник: MSDN Magazine  •  Опубликована: 26.09.2012
Нашли ошибку в тексте? Сообщите о ней автору: выделите мышкой и нажмите CTRL + ENTER
Теги:   Windows Phone.


Оценить статью:
Вверх
Комментарии посетителей
Комментарии отключены. С вопросами по статьям обращайтесь в форум.