在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ iOS/ 導(dǎo)航應(yīng)用
與四軸無(wú)人機(jī)的通訊
在沙盒中編寫(xiě)腳本
結(jié)構(gòu)體和值類(lèi)型
深入理解 CocoaPods
UICollectionView + UIKit 力學(xué)
NSString 與 Unicode
代碼簽名探析
測(cè)試
架構(gòu)
第二期-并發(fā)編程
Metal
自定義控件
iOS 中的行為
行為驅(qū)動(dòng)開(kāi)發(fā)
Collection View 動(dòng)畫(huà)
截圖測(cè)試
MVVM 介紹
使 Mac 應(yīng)用數(shù)據(jù)腳本化
一個(gè)完整的 Core Data 應(yīng)用
插件
字符串
為 iOS 建立 Travis CI
先進(jìn)的自動(dòng)布局工具箱
動(dòng)畫(huà)
為 iOS 7 重新設(shè)計(jì) App
XPC
從 NSURLConnection 到 NSURLSession
Core Data 網(wǎng)絡(luò)應(yīng)用實(shí)例
GPU 加速下的圖像處理
自定義 Core Data 遷移
子類(lèi)
與調(diào)試器共舞 - LLDB 的華爾茲
圖片格式
并發(fā)編程:API 及挑戰(zhàn)
IP,TCP 和 HTTP
動(dòng)畫(huà)解釋
響應(yīng)式 Android 應(yīng)用
初識(shí) TextKit
客戶(hù)端
View-Layer 協(xié)作
回到 Mac
Android
Core Image 介紹
自定義 Formatters
Scene Kit
調(diào)試
項(xiàng)目介紹
Swift 的強(qiáng)大之處
測(cè)試并發(fā)程序
Android 通知中心
調(diào)試:案例學(xué)習(xí)
從 UIKit 到 AppKit
iOS 7 : 隱藏技巧和變通之道
安全
底層并發(fā) API
消息傳遞機(jī)制
更輕量的 View Controllers
用 SQLite 和 FMDB 替代 Core Data
字符串解析
終身學(xué)習(xí)的一代人
視頻
Playground 快速原型制作
Omni 內(nèi)部
同步數(shù)據(jù)
設(shè)計(jì)優(yōu)雅的移動(dòng)游戲
繪制像素到屏幕上
相機(jī)與照片
音頻 API 一覽
交互式動(dòng)畫(huà)
常見(jiàn)的后臺(tái)實(shí)踐
糟糕的測(cè)試
避免濫用單例
數(shù)據(jù)模型和模型對(duì)象
Core Data
字符串本地化
View Controller 轉(zhuǎn)場(chǎng)
照片框架
響應(yīng)式視圖
Square Register 中的擴(kuò)張
DTrace
基礎(chǔ)集合類(lèi)
視頻工具箱和硬件加速
字符串渲染
讓東西變得不那么糟
游戲中的多點(diǎn)互聯(lián)
iCloud 和 Core Data
Views
虛擬音域 - 聲音設(shè)計(jì)的藝術(shù)
導(dǎo)航應(yīng)用
線(xiàn)程安全類(lèi)的設(shè)計(jì)
置換測(cè)試: Mock, Stub 和其他
Build 工具
KVC 和 KVO
Core Image 和視頻
Android Intents
在 iOS 上捕獲視頻
四軸無(wú)人機(jī)項(xiàng)目
Mach-O 可執(zhí)行文件
UI 測(cè)試
值對(duì)象
活動(dòng)追蹤
依賴(lài)注入
Swift
項(xiàng)目管理
整潔的 Table View 代碼
Swift 方法的多面性
為什么今天安全仍然重要
Core Data 概述
Foundation
Swift 的函數(shù)式 API
iOS 7 的多任務(wù)
自定義 Collection View 布局
測(cè)試 View Controllers
訪(fǎng)談
收據(jù)驗(yàn)證
數(shù)據(jù)同步
自定義 ViewController 容器轉(zhuǎn)場(chǎng)
游戲
調(diào)試核對(duì)清單
View Controller 容器
學(xué)無(wú)止境
XCTest 測(cè)試實(shí)戰(zhàn)
iOS 7
Layer 中自定義屬性的動(dòng)畫(huà)
第一期-更輕量的 View Controllers
精通 iCloud 文檔存儲(chǔ)
代碼審查的藝術(shù):Dropbox 的故事
GPU 加速下的圖像視覺(jué)
Artsy
照片擴(kuò)展
理解 Scroll Views
使用 VIPER 構(gòu)建 iOS 應(yīng)用
Android 中的 SQLite 數(shù)據(jù)庫(kù)支持
Fetch 請(qǐng)求
導(dǎo)入大數(shù)據(jù)集
iOS 開(kāi)發(fā)者的 Android 第一課
iOS 上的相機(jī)捕捉
語(yǔ)言標(biāo)簽
同步案例學(xué)習(xí)
依賴(lài)注入和注解,為什么 Java 比你想象的要好
編譯器
基于 OpenCV 的人臉識(shí)別
玩轉(zhuǎn)字符串
相機(jī)工作原理
Build 過(guò)程

導(dǎo)航應(yīng)用

在這篇文章中,我們將把前面提到過(guò)的內(nèi)容組織起來(lái)構(gòu)成我們的導(dǎo)航器應(yīng)用,這個(gè) iPhone 應(yīng)用將裝載在我們的的無(wú)人機(jī)上,你可以在 Github 下載應(yīng)用的源碼,盡管這個(gè)應(yīng)用是計(jì)劃在沒(méi)有直接的交互操作下來(lái)使用的,但在測(cè)試過(guò)程中我們做了一個(gè)簡(jiǎn)單的 UI 界面來(lái)顯示其無(wú)人機(jī)狀態(tài)并方便我們手動(dòng)操作。

概要

在我們的應(yīng)用中,我們有幾個(gè)類(lèi)它們分別是:

  • DroneCommunicator 這個(gè)類(lèi)關(guān)注于利用 UDP 和無(wú)人機(jī)通訊。這個(gè)話(huà)題全部在 Daniel 的文章中詳細(xì)介紹過(guò)

  • RemoteClient 使用 Multipeer Connectivity 技術(shù)和我們的遠(yuǎn)程客戶(hù)端進(jìn)行交互,具體客戶(hù)端的操作,請(qǐng)看 Florian 的文章。
  • Navigator 用來(lái)設(shè)定目標(biāo)位置,計(jì)算飛行航線(xiàn),以及飛行距離。
  • DroneController 用來(lái)把從 Navigator 獲取的導(dǎo)航的距離和方向發(fā)送命令到DroneCommunicator
  • ViewController 有一個(gè)簡(jiǎn)單的界面,用來(lái)初始化其他的類(lèi)并把它們連接起來(lái),這部分應(yīng)該用不同的類(lèi)來(lái)完成,但是在我們的設(shè)想中,我們的app足夠簡(jiǎn)單所以放到一個(gè)類(lèi)就可以了。

View Controller

View Controller 中最重要的一個(gè)部分是初始化方法,在這里我們創(chuàng)建了 DroneCommunicator, NavigatorDroneController 以及RemoteClient 的實(shí)例化對(duì)象,換句話(huà)說(shuō):我們建立了無(wú)人機(jī)和我們的客戶(hù)端應(yīng)用溝通的整個(gè)橋梁。

- (void)setup
{
    self.communicator = [[DroneCommunicator alloc] init];
    [self.communicator setupDefaults];

    self.navigator = [[Navigator alloc] init];
    self.droneController = [[DroneController alloc] initWithCommunicator:self.communicator navigator:self.navigator];
    self.droneController.delegate = self;
    self.remoteClient = [[RemoteClient alloc] init];
    [self.remoteClient startBrowsing];
    self.remoteClient.delegate = self;
}

View Controller 同時(shí)是 RemoteClient 的委托。 這就說(shuō)明無(wú)論我們的客戶(hù)端發(fā)送了一個(gè)新位置或者著陸,重置以及關(guān)機(jī)的命令,我們都需要在這里處理它。舉個(gè)例子,當(dāng)我們收到一個(gè)新的位置的命令的時(shí)候,我們這樣來(lái)做:

- (void)remoteClient:(RemoteClient *)client didReceiveTargetLocation:(CLLocation *)location
    {
        self.droneController.droneActivity = DroneActivityFlyToTarget;
        self.navigator.targetLocation = location;
    }

這段代碼是用來(lái)確保無(wú)人機(jī)開(kāi)始飛行(而不是徘徊)并且更新目標(biāo)位置。

Navigator

導(dǎo)航類(lèi)用來(lái)指定目標(biāo)位置,并且計(jì)算從當(dāng)前位置到目標(biāo)位置的距離,為了完成整個(gè)工作我們首先需要監(jiān)聽(tīng) core location 的改變:

- (void)startCoreLocation
{
    self.locationManager = [[CLLocationManager alloc] init];
    self.locationManager.delegate = self;

    self.locationManager.distanceFilter = kCLDistanceFilterNone;
    self.locationManager.desiredAccuracy = kCLLocationAccuracyBestForNavigation;
    [self.locationManager startUpdatingLocation];
    [self.locationManager startUpdatingHeading];
}

在我們的導(dǎo)航類(lèi)中,我們有兩種方向,絕對(duì)和相對(duì)方向,絕對(duì)方向是兩個(gè)地點(diǎn)之間的方向。比如說(shuō),阿姆斯特丹和柏林間的絕對(duì)方向幾乎處于同一緯度,相對(duì)位置則是我們?cè)趨⒖贾改厢樅罂梢缘贸龅穆肪€(xiàn)方向,要從阿姆斯特丹一直向東到柏林,兩地之間的相對(duì)方向?yàn)榱?。在操作無(wú)人機(jī)的時(shí)候我們就需要使用相對(duì)方向。方向值為零,飛機(jī)直行;方向角度小于零,飛機(jī)向右傾斜轉(zhuǎn)彎;方向角度大于零,飛機(jī)則向左傾斜轉(zhuǎn)彎。

計(jì)算到目的地的絕對(duì)方向,我們需要?jiǎng)?chuàng)建一個(gè)基于 CLLocation 的Helper方法用來(lái)計(jì)算兩個(gè)點(diǎn)的方向:

- (OBJDirection *)directionToLocation:(CLLocation *)otherLocation;
{
    return [[OBJDirection alloc] initWithFromLocation:self toLocation:otherLocation];
}

由于我們的無(wú)人機(jī)只能飛很小的距離(電池只能支持10分鐘),所以我們需要一個(gè)幾何的假設(shè),我們是在一個(gè)平面而不是在地球表面:

- (double)heading;
{
    double y = self.toLocation.coordinate.longitude - self.fromLocation.coordinate.longitude;
    double x = self.toLocation.coordinate.latitude - self.fromLocation.coordinate.latitude;

    double degree = radiansToDegrees(atan2(y, x));
    return fmod(degree + 360., 360.);
}

在導(dǎo)航器中,我們將得到位置和航向的回調(diào),然后我們把這兩個(gè)值存到屬性中,比如,計(jì)算我們需要飛行的兩點(diǎn)之間的距離,我們需要將絕對(duì)航向減去當(dāng)前航向(這與你看到指南針上的值是一樣的意思),然后將結(jié)果換算到 -180 度和 180 度之間。如果你希望知道為什么我們要減去 90 度,那是因?yàn)槲覀?iPhone 和無(wú)人機(jī)之間有 90 度的夾角。

- (CLLocationDirection)directionDifferenceToTarget;
{
    CLLocationDirection result = (self.direction.heading - self.lastKnownSelfHeading.trueHeading - 90);
    // Make sure the result is in the range -180 -> 180
    result = fmod(result + 180. + 360., 360.) - 180.;
    return result;
}

這就是我們導(dǎo)航做的事情?;诋?dāng)前的位置和航向,計(jì)算出到目標(biāo)的距離和無(wú)人機(jī)應(yīng)當(dāng)飛行的方向。并且監(jiān)聽(tīng)這兩個(gè)屬性。

Drone Controller

Drone controller 用來(lái)初始化 navigator 和 communicator,并且發(fā)送距離和方向的命令到無(wú)人機(jī),因?yàn)槊钚枰掷m(xù)發(fā)送,所以我們創(chuàng)建一個(gè)計(jì)時(shí)器:

self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.25
                                                    target:self
                                                  selector:@selector(updateTimerFired:)
                                                  userInfo:nil
                                                   repeats:YES];

當(dāng)計(jì)時(shí)器觸發(fā)后,假設(shè)我們飛向一個(gè)目標(biāo),我們需要發(fā)送給無(wú)人機(jī)適當(dāng)?shù)闹噶睿绻覀冏銐蚪?,無(wú)人機(jī)盤(pán)旋,否則,我們轉(zhuǎn)向目標(biāo),在大致方向正確的情況下飛過(guò)去!

- (void)updateDroneCommands;
{
    if (self.navigator.distanceToTarget < 1) {
        self.droneActivity = DroneActivityHover;
    } else {
        static double const rotationSpeedScale = 0.01;
        self.communicator.rotationSpeed = self.navigator.directionDifferenceToTarget * rotationSpeedScale;
        BOOL roughlyInRightDirection = fabs(self.navigator.directionDifferenceToTarget) < 45.;
        self.communicator.forwardSpeed = roughlyInRightDirection ? 0.2 : 0;
    }
}

Remote Client

Remote Client 類(lèi)關(guān)注于和我們的客戶(hù)端通訊,我們利用了一個(gè)很方便 Multipeer Connectivity 框架。首先,我們需要和附近的創(chuàng)建一個(gè)會(huì)話(huà)以及 MCNearbyServiceBrowser :

- (void)startBrowsing
{
    MCPeerID* peerId = [[MCPeerID alloc] initWithDisplayName:@"Drone"];

    self.browser = [[MCNearbyServiceBrowser alloc] initWithPeer:peerId serviceType:@"loc-broadcaster"];
    self.browser.delegate = self;
    [self.browser startBrowsingForPeers];

    self.session = [[MCSession alloc] initWithPeer:peerId];
    self.session.delegate = self;
}

在我們的項(xiàng)目中,我們不需要處理單獨(dú)設(shè)備的安全問(wèn)題,因?yàn)槲覀兛偸茄?qǐng)所有的對(duì)等網(wǎng)絡(luò)的設(shè)備。

- (void)browser:(MCNearbyServiceBrowser *)browser foundPeer:(MCPeerID *)peerID withDiscoveryInfo:(NSDictionary *)info
{
    [browser invitePeer:peerID toSession:self.session withContext:nil timeout:0];
}

我們需要加入 MCNearbyServiceBrowserDelegateMCSessionDelegate 全部的協(xié)議方法,否則這個(gè)應(yīng)用將會(huì)崩潰。唯一一個(gè)方法我們需要實(shí)現(xiàn)的是 session:didReceiveData:fromPeer: 。我們解析對(duì)等客戶(hù)端發(fā)送來(lái)的命令并且調(diào)用合適的委托方法,在我們簡(jiǎn)易的應(yīng)用中,View Controller 實(shí)現(xiàn)了這些委托,當(dāng)我們接收到了新的位置我們更新導(dǎo)航,并且讓無(wú)人機(jī)飛向新的位置。

總結(jié)

這篇文章描述了這個(gè)簡(jiǎn)易的 app ,最初我們把所有的委托和代碼都加入到了 View Controller 中,這是被證明最簡(jiǎn)單的編碼和測(cè)試方式,其實(shí)寫(xiě)代碼是一個(gè)容易的事情,但是閱讀代碼非常困難。因此我們需要重構(gòu)所有的代碼讓其合理的分配到不同類(lèi)中。

硬件方面的工作,測(cè)試非常的耗時(shí),比如,在我們的 quadcopter 項(xiàng)目中,需要一段時(shí)間來(lái)啟動(dòng)設(shè)備,發(fā)送命令,并讓它飛起來(lái)。因此我們盡可能多在離線(xiàn)狀況下測(cè)試。我們還添加了大量的的日志語(yǔ)句,這樣我們調(diào)試起來(lái)更加方便。