觸摸和去

利用 Windows Phone 羅盤找出方位

Charles Petzold

下載代碼示例

Charles Petzold
我們人類很少使用我們的感官完全從彼此隔離:視力和聽力的結合使我們能夠構建我們的環境 ; 心理形象 混合的味覺、 嗅覺、 觸覺影響我們享受時吃的食物 ; 我們使用的觸摸、 視力和聽力混合時玩一種樂器。

它是一款智慧手機與相同:一款智慧手機可以通過其相機鏡頭"請參閱"、"聽"通過其麥克風、 通過其觸控式螢幕,"感覺"和知道它使用 GPS 和定位感應器的世界中的位置。但開始組合來自這些感應器的輸入,並沒有一個詞語來形容結果但協同作用。

加速度計問題

加速度計在 Windows Phone 是一個感應器,提供一些基本的資訊,但變得更有價值,另一個感應器,具體的羅盤結合時的一個好例子。

加速度計的硬體其實措施強制,但我們知道從物理學,迫使等於品質乘以加速度,所以加速度感應器回應任何種類的加速度。電話舉行時仍,加速度計重力的措施,並提供了指向地球的中心向 3D 向量。此向量是相對於 3D 的座標系統中,如中所示圖 1。無論您是編碼 Silverlight 或新華社的程式,還是在縱向或橫向模式中運行此坐標系是相同的。

The Phone’s Sensor Coordinate System
圖 1 手機的感應器座標系統

加速度計類提供了加速度向量,Vector3 的值的形式。這是新華社類型,所以如果您需要在 Silverlight 的程式中使用它,您需要對 Microsoft.Xna.Framework 的程式集的引用。

儘管加速度向量允許在手機上確定的電話與地球的方向運行的程式,但它缺少一些重要的資訊。讓我證明我在說什麼。

為此列的可下載代碼中 (可在 archive.msdn.microsoft.com/mag201206TouchAndGo) 是一個名為 3DPointers,其中包含四個新華社 3D 專案,所有這一切看上去有點類似的 Visual Studio 解決方案。加速度計 3D 程式繪製 3D"pin"漂浮在太空中的加速度向量的方向,如中所示圖 2

The Accelerometer 3D Display
圖 2 的加速度計的 3D 顯示

任何程式都使用一個 Windows Phone 感應器需要對 Microsoft.Devices.Sensors 的程式集的引用。通常在電話上的新華社程式運行在景觀模式中,,這可能是個問題,因為它不會匹配使用感應器的座標系統。若要使事情容易為我自己,我調整新華社座標系統的程式的遊戲衍生物的建構函式中的縱向模式:

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

在 Windows Phone 7.1,感應器 API 已更改位,提供各類感應器之間的一致性。 加速度計的 3D 程式建構函式使用這個新的 API 來創建一個保存為一個欄位的加速度計實例:

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

預設的 TimeBetweenUpdates 屬性為 25 毫秒 ; 在這裡,它被設置為該程式的畫面播放速率,是 33 毫秒。

程式使用 OnActivated 方法的重寫來啟動加速度感應器:

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

雖然很難想像那裡啟動方法將在此時失敗的情形,但建議你把它放在 try 塊中。 指南針被停在 OnDeactivated 中:

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

該程式使用 LoadContent 方法來生成 3D 頂點的"針"和 BasicEffect 定義用於存儲相機和照明的資訊。 針腳定義以便基地是在原點,它擴展了正面的 Y 軸的一個單位。 直接從正面的 Z 軸原點的攝像點。

該程式的更新方法然後使用加速度向量定義世界變換。 這世界變換有效地將移動相對於原點的 pin。 圖 3 顯示的代碼。

圖 3 中加速度計 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);
    // Special case for magnetometer equal to (0, 1, 0) or (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);
}

該方法直接從加速度計的物件獲取加速度值,如果 IsDataValid 屬性為 true。 Pin 必須旋轉基於上的加速度向量和積極的 Y 軸之間的角度。 這兩個向量的內積提供的角度來看,而由跨產品提供的旋轉軸。

但嘗試此操作:站起來,在一個特定的角度,手中握手機。 下針點。 現在,正站在的地方,轉 360 度左右。 雖然你轉,pin 仍在相同的位置 (或幾乎如此)。 在軸平行的加速度向量行動電話,不能辨別加速度計。 如果您要編寫的應用程式需要知道電話的完整 3D 方向、 加速度計提供只有您的需要的一部分。

拯救指南針

當 Windows Phone 第一次發佈時,它不清楚手機是否甚至包含一個羅盤。 沒有人似乎有一個明確的答案。 但是,應用程式的程式師,情況是非常簡單:沒有程式設計介面的指南針,所以即使它存在,我們不能使用它。

釋放的 Windows Phone 7.1 澄清這一問題:儘管 Windows Phone 設備不需要有一個指南針,某些 Windows Phone 設備始終對之一,包括運行 Windows Phone,我在 2010 年 12 月購買一款智慧手機。 最重要的是,程式師可以實際使用這個指南針。 Windows Phone 7.1 引入新的羅盤類,它允許應用程式程式師知道是否指南針的存在 (通過靜態的 Compass.IsSupported 屬性),並獲得其讀數。 Compass.IsSupported 將返回 false Windows Phone 模擬器上。

這款手機的基礎是硬體的羅盤的一塊稱為磁強計。 這磁強計受任何電話,包括那些可能會在你的電腦桌上的揚聲器附近的磁場力。 如果你把這些從電話的磁鐵,磁強計將衡量的強度和地球磁場的方向。

指南針類提供了一種 CompassReading 結構的表單中的資料。 有可用在一個名為 MagnetometerReading,這是另一個 Vector3 屬性中的原始磁強計資料相對於所示的座標系統圖 1。 附近的磁體的缺席,此向量對齊,地球的磁場。 偏北的方向發展,當然,但在北半球大部分地區的向量點,它還指出到地球。 如果你持有手機正面朝上,螢幕,此向量將有一個顯著的 –Z 元件。

3DPointers 解決方案包含命名磁強計 3D 是類似于加速度計的 3D 專案中,除非它使用指南針類,而不是加速度計類的專案。 圖 4顯示此程式顯示時在我面對螢幕曼哈頓的公寓舉行電話和手機指向頂部"住宅區,",即與左側和右側的手機 (近,可以管理) 與對齊紐約城的途徑。 (一張地圖顯示這些途徑來運行約 30 度北)。


圖 4 磁強計 3D 顯示

檔指出 MagnetometerReading 向量是 microteslas 為單位。 對商業的 Windows Phone 設備的兩個我自己,此向量一般有幅度在 30 多歲,是大約正確。 (在中所示的向量值圖 4 有 43 一個複合規模。)然而,對於我自己的第三個電話,MagnetometerReading 向量正常化,總是有震級為 1。

現在試試這個:所以磁強計 3D 向量幾乎與積極 Y 軸的對齊握手機圖 1。 現在旋轉軸平行于該向量周圍的電話。 向量保持不變 (或幾乎如此),該值指示這款手機的磁強計要麼沒有完整的知識,這款手機的定位。

羅盤的標題

通常當我們使用一個指南針,我們不想與地球磁場對齊 3D 向量。 有用得多,將會是與地球表面相切的 2D 向量。

您可能已經知道,地球磁場與地球的旋轉的軸不一致。 地球的軸方向被稱為地理北或真正的北方,,這是用於地圖和幾乎所有其他目的的北部。 二維表面上的角度通常用於表示北的方向。 這個方向,通常稱為一個標題或軸承。

在全球各地不同磁北與真正的北方之間的差異。 在紐約城,必須從磁性軸承的獲取真實的軸承,減去 13 度左右,但在西雅圖,21 度必須添加到磁懸浮軸承。 指南針類執行這些計算為您基於手機的位置。 除了 MagnetometerReading 向量,CompassReading 還提供雙名為 MagneticHeading 和 TrueHeading 類型的兩個屬性。 這些都是這兩個角度以度為單位從 0 到 360 從中所示的正面 Y 軸逆時針方向測量圖 1

TrueHeading 始終應被解釋為一個近似的值,並即使在當時它完全不應該被信任。 關於兩個 TrueHeading 的權利,通常是自己的手機,但在另一個電話上它好 70 度關閉。

我還沒有能夠做出的 MagneticHeading 價值感。 對於特定的位置,值的 TrueHeading 和 MagneticHeading 之間的區別應該是一個常數。 例如,我住的地方,價值減去磁 TrueHeading 的­標題應該 - 13 名度。 對所有三種我電話,TrueHeading 和 MagneticHeading 之間的區別將零星根據手機的方向的兩個值之間跳轉。 差異有時是 –12 (這是正確的),但主要的區別是 92。 這些都是區別的我見過的只有兩個值。 無我的電話是 MagneticHeading 符合從 MagnetometerReading 向量的 X 和 Y 的值派生的角度。

在新華社程式中,如您所見,您可以簡單地獲得當前值從感應器期間的更新方法。 當 Silverlight 的程式中使用感應器的類,您會想要設置的 CurrentValueChanged 事件的處理常式。 然後,您可以獲取感應器讀取從事件參數的物件。

這篇文章的下載代碼包含兩個 Silverlight 程式 — — 箭頭指南針和撥號指南針 — —,使用 TrueHeading 屬性來顯示北的方向。 所有的圖形是在 XAML 中定義的。 在與新華社的程式,這些 Silverlight 程式羅盤物件中創建它們的建構函式,但他們還設置 CurrentValueChanged 屬性的處理常式:

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

箭頭羅盤在此處理程式附加到箭頭圖形的 RotateTransform 物件上設置的角度:

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

CurrentValueChanged 處理常式稱為一個單獨的執行緒,所以您需要使用調度程式來更新任何使用介面物件。 因為 TrueHeading 角指示一個逆時針方向偏移量和 Silverlight 輪換是順時針,該代碼將使用旋轉的航向角的負值。

結果將顯示在圖 5,再用指向住宅區在紐約城的手機。

The Arrow Compass Display
圖 5 顯示箭頭指南針

在撥號的指南針,箭頭仍固定同時撥號旋轉指示方向,如中所示圖 6。 如果你想要知道,指向頂部的電話,而不相對於電話北的方向的方向,則使用這種變化。

The Dial Compass Display
圖 6 撥號羅盤顯示

如果您運行這兩個程式之一,以便在螢幕面臨地球握手機指南針不再正常運行。 旋轉是對面那應該是。 如果您需要為此更正,你會想要積極的加速度向量的 Z 值時使用的 TrueHeading 值的正面價值。

校準指南針

在右下角,箭頭羅盤程式顯示的 CompassReading 值的 HeadingAccuracy 屬性。 在理論上,這將提供標題值的準確性。 在實踐中,我見過 HeadingAccuracy 值介於 5%到 30%。 (但我只看過 5%的熄滅方式的電話 !)

指南針類還定義了一個名為 HeadingAccuracy 的值超過 20%時,將觸發的校準的事件。

您可以執行校準手法,以減少此 HeadingAccuracy:握手機從螢幕指向您身體左側或右側,然後再打掃你的手臂無窮大模式中好幾次。 一些示例代碼提供的 MSDN 教程 (在 bit.ly/yYrHrL) 甚至有指南針需要校準時通知您的使用者,可以顯示的圖形。

結合指南針和加速計

這款手機的羅盤是一個完美的例子,一個感應器,顯然具有本身的一些實用的 — — 尤其是如果你迷失在樹林裡 — — 被轉換成其它的感應器,尤其是加速度計結合時的值錢得多。 在一起,這兩個感應器可以提供一個完整的導向,在 3D 空間中的電話。

事實上,Windows Phone 7.1 定義一個新類,正是這樣做。 除了以三種不同的方式提供的手機定位、 運動類還納入了資訊從新的陀螺儀類中,如果存在在電話上。

此外,運動類的加速度計和指南針的資料進行平滑處理做額外的工作。 如果已經運行的程式提交到目前為止,您可能已經注意重大抖動的這些類中的資料。 那抖動是從運動類都走了。

然而,因為我是那種享有很好的挑戰的人,以為"手動,"結合的加速度計和指南針資料槍會了。 必須承認,經驗離開我的運動類的更深讚賞 !

指南針 3D 程式會顯示四個不同顏色的針腳排列在一個圓點北 (銀)、 東 (紅色)、 南 (綠色) 和西 (藍色)。 該程式將嘗試以顯示此平面平行于地球表面和正確地面向指南針的四個要點的針腳。

我拿了政府的策略是派生歐拉角。 這些代表圍繞 X、 Y 和 Z 軸旋轉的三個角度,在一起他們描述在 3D 空間中的定位。 飛行動力學中的三個角度標有俯仰、 卷和偏航。 從飛機的角度看,螺距指示是否鼻子是向上或向下和減多少,而卷表示任何銀行業的飛機向右或向左。 這些旋轉角度可以視覺化為相對於兩個軸:攤位是圍繞貫穿的翅膀,而卷基於從平面的前端到後端軸的軸旋轉。 偏航繞垂直于地球表面的軸的旋轉角度,指示飛機的羅盤標題。

直觀顯示這些手機的坐標系中的角度圖 1,想像自己騎像魔毯,坐到你前面電話和到你後面的三個按鈕的頂部與螢幕上的電話。 攤位是繞 X 軸旋轉、 輥是圍繞 Y 軸的旋轉和偏航是圍繞 Z 軸旋轉。

計算輥和瀝青的加速度向量從原來是相當容易,涉及標準公式:

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

坐在桌上平一起面對其螢幕的電話,卷及攤位都為零。 輥範圍從 –π/2 π/2,和值返回到零,電話是轉身朝下。 當手機朝下螺距範圍從 –π 到 π 的最大值。 有正面朝上的這款手機的螢幕,偏航應更多或更少的指南針,TrueHeading 屬性值相同,但轉換為弧度新華社目的:

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

然而,正如你看到與這兩個的 Silverlight 羅盤程式,TrueHeading 將停止正常工作向地球,其螢幕手機處於打開狀態時如此偏航需要予以糾正的。 我謹此陳後入主題一些理論與實證的遠足、 離開它,並建造從三個角度的世界變換:

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

結果將顯示在圖 7

The Compass 3D Program
圖 7 羅盤 3D 程式

我還包含了一個定位 3D 程式,從運動類獲取這些三個角度。 你可以看到自己多少更平滑的結果是 (除了工作更好地當電話顛倒)。

運動類是太重要了,只是此單個程式的感應器的 API。 此列的下一部分中,你就會看到,類可以其實作為一個入口網站進入一個 3D 的世界。

Charles Petzold 是 MSDN 雜誌長期貢獻。他的網站是 charlespetzold.com

由於下面的技術專家,檢討這篇文章:Drew Batchelor