試玩 Coral USB Accelerator

我不久前買了 Google 出的 Edge TPU 裝置:Coral USB Accelerator 來玩玩看,覺得這玩意非常不錯,台幣兩千多元,可以跑 TensorFlow Lite 格式的模型,我個人是覺得還蠻划算的。

我先用 Raspberry Pi 3 Model B+ 來測試 Pose Detection 模型,一開始使用預設的解析度:640×480,繪製的速度不是很理想,影片如下:

然後我把解析度設定成:480×360,繪製的速度有加快,但還是不盡理想,所以後來我改用一台 2013 年買的小筆電來跑,這次繪製的速度就相當不錯了,影片如下:

這台小筆電的規格為:CPU 是 Intel Celeron(R) CPU 847 @ 1.10GHz,記憶體 4GB,作業系統是 Debian 10 (buster) amd64 版,且有 USB 3 的插孔。

Raspberry Pi 3 Model B+ 除了 CPU 速度較慢之外,也沒有 USB 3 的插孔,所以整體上慢了一大截。但是前陣子出的 Raspberry Pi 4 Model B 有 USB 3 插孔,我已經訂了一台 Pi 4,等收到貨後再測試看看繪製速度是否有提升。

# 2019 年 11月 3 日補充: 我在 Raspberry Pi 4 (4GB 記憶體版本) 上測試了 Coral PoseNet,測試結果請看以下影片,TrueFPS 維持在 20.x 以上:

然後我還有嘗試 Object detection 與 MiniGo 圍棋:

Coral 官方網站也提供了好幾個已編譯好的模型讓人下載:https://coral.withgoogle.com/models/ ,可以先從這些模型開始玩起。

感想:這種 Edge TPU 裝置可以做很多有趣的應用,而且價格不算貴,有興趣的人快去買來玩玩看!

IchigoJam BASIC RPi 初體驗

IchigoJam BASIC RPi 是一個運作在 Raspberry Pi 上的 OS,這個 OS 很有趣,開機後會顯示一個黑白畫面的 BASIC 終端機,完全沒有 GUI,直接寫 BASIC 控制 Raspberry Pi,彷彿就像是 30 多年前的個人電腦。

下方是我嘗試用 IchigoJam BASIC RPi 控制 Pin 輸出的影片:

電子專案》Closed Eye Alarm

我寫了個 Android app,利用 Google Play Services Vision 的 FaceDetector 偵測人臉位置與眼睛打開的機率,然後只要發現有人的眼睛可能是閉上的,就傳送訊息給 micro:bit (透過 BLE) ,micro:bit 上接了一個 speaker,收到訊息就會逼逼叫。上方是 Demo 影片。

圖:micro:bit 與 speaker

在 Raspberry Pi 上安裝 Groovy

打開終端機,用 apt 指令安裝 OpenJDK 8 與 Groovy:

$ sudo apt install openjdk-8-jdk groovy

然後,我們試試是否可執行 groovy:

$ groovy -v
groovy: JAVA_HOME is not defined correctly, can not execute: /usr/lib/jvm/default-java/bin/java

會顯示「groovy: JAVA_HOME is not defined correctly, can not execute: /usr/lib/jvm/default-java/bin/java」這個訊息。

我們先切換到 /usr/lib/jvm 目錄下查看一下:

$ cd /usr/lib/jvm
$ ls -l
lrwxrwxrwx 1 root root   21  6月 17 02:24 java-1.11.0-openjdk-armhf -> java-11-openjdk-armhf
drwxr-xr-x 9 root root 4096  7月  8 01:20 java-11-openjdk-armhf
lrwxrwxrwx 1 root root   20  3月 29 21:54 java-1.8.0-openjdk-armhf -> java-8-openjdk-armhf
drwxr-xr-x 7 root root 4096  8月  2 13:52 java-8-openjdk-armhf
drwxr-xr-x 9 root root 4096  3月 14  2018 jdk-8-oracle-arm32-vfp-hflt

然後幫 java-8-openjdk-armhf/ 做一個名為 default-java 的 symlink:

$ sudo ln -s java-8-openjdk-armhf/ default-java
$ ls -l
lrwxrwxrwx 1 root root   21  8月  2 14:05 default-java -> java-8-openjdk-armhf/
lrwxrwxrwx 1 root root   21  6月 17 02:24 java-1.11.0-openjdk-armhf -> java-11-openjdk-armhf
drwxr-xr-x 9 root root 4096  7月  8 01:20 java-11-openjdk-armhf
lrwxrwxrwx 1 root root   20  3月 29 21:54 java-1.8.0-openjdk-armhf -> java-8-openjdk-armhf
drwxr-xr-x 7 root root 4096  8月  2 13:52 java-8-openjdk-armhf
drwxr-xr-x 9 root root 4096  3月 14  2018 jdk-8-oracle-arm32-vfp-hflt

然後再執行 groovy 試試看:

$ groovy -v
Groovy Version: 2.4.16 JVM: 1.8.0_212 Vendor: Oracle Corporation OS: Linux

寫個程式測試看看:

$ cd ~
$ vim hello.groovy
def hello(name) {
    println("Hi, " + name)
} 
hello("Pi")

執行程式:

$ groovy hello.groovy
Hi, Pi

R 語言筆記》一切運算皆是函數調用

最近在讀《高級 R 語言編程指南》(Advanced R) 這本書,讀到第 6 章時,發現 R 的語法其實很有趣,有些 Lisp 的味道 🙂

在 R 裡頭:

  • 一切皆是物件 (object)
  • 一切運算皆是函數調用

所以像是四則運算 (+-*/) 等符號也是函數 (屬於 中綴函數)。

然後,我們可以像這樣使用四則運算符,讓四則運算符以 前綴函數 的方式調用:

> `+`(2, 3)
[1] 5
> `*`(5, `+`(2, 3))
[1] 25
>

接下來,我們可以來惡搞一下 R:

> `(` <- function(e) e + 1
> `*`(5, (3 + 2))
[1] 30
> (3 + 2)
[1] 6
> (3 * 2)
[1] 7
> 

惡搞完 ( 符號後,接下來玩弄一下 + 運算符… XDD:

> add <- `+`
> add(2, 3)
[1] 5
> add(2, 100)
[1] 102
> `+` <- function(e1, e2) {
+   x <- add(e1, e2)
+   if (x <= 87) {
+     x
+   } else {
+     x <- 87
+     cat("87分不能再高了!\n")
+     x
+   }
+ }
> 
> 2 + 3
[1] 5
> 5 + 81
[1] 86
> 2 + 100
87分不能再高了!
[1] 87
> 2 + 10000
87分不能再高了!
[1] 87
> 

根據維基百科上的描述,R 的語法結合了 S 語言與 Scheme,經過上述的惡搞法後,各位有沒有感受到 R 有一點點 Lisp 的 () 味道呢? XD (Lisp 粉絲不要戰我)

iOS 筆記》用座標查詢地址

在寫地圖應用的 App 時,有時候需要用到利用座標(經緯度)去查詢那個點的地址,這時可以這麼寫:

#pragma mark - MKMapViewDelegate

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated {
    // 取得 MapView 中心點座標:
    CLLocationCoordinate2D center = [self.mapView centerCoordinate];
    NSLog(@"緯度:%f, 經度:%f", center.latitude, center.longitude);
    
    CLGeocoder *ceo = [[CLGeocoder alloc] init];
    CLLocation *loc = [[CLLocation alloc] initWithLatitude:center.latitude longitude:center.longitude];
    
    [ceo reverseGeocodeLocation:loc completionHandler:^(NSArray *placemarks, NSError *error) {
        CLPlacemark *placemark = [placemarks objectAtIndex:0];
        if (placemark) {
            // 這個 Dictionary 有地址的相關資訊:
            NSLog(@"addressDictionary %@", placemark.addressDictionary);
                      
            NSLog(@"region: %@",placemark.region);
            NSLog(@"country: %@",placemark.country); 
            NSLog(@"locality: %@",placemark.locality);
            NSLog(@"name: %@", placemark.name);
            NSLog(@"ocean: %@",placemark.ocean);
            NSLog(@"postalCode: %@",placemark.postalCode);
            NSLog(@"subLocality: %@",placemark.subLocality);                 
                      
            NSLog(@"完整地址: %@", placemark.addressDictionary[@"FormattedAddressLines"][0]);
            NSLog(@"City: %@", placemark.addressDictionary[@"City"]);
            NSLog(@"Name: %@", placemark.addressDictionary[@"Name"]);
        } else {
            NSLog(@"Could not locate");
        }
    }];
}

如果是想輸入地址,然後用地址查出座標呢?可以這麼寫:

CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:@"台灣臺北市信義區市府路1號" completionHandler:^(NSArray *placemarks, NSError *error) {
    if (error) {
        NSLog(@"%@", error);
    } else {
        CLPlacemark *placemark = [placemarks lastObject];
        NSLog(@"緯度:%f, 經度:%f", placemark.location.coordinate.latitude, placemark.location.coordinate.longitude);
    }
}];

參考來源:

  1. http://stackoverflow.com/questions/12599316/i-want-to-get-the-location-name-from-the-coordinate-value-in-mapkit-for-iphone
  2. http://stackoverflow.com/questions/18767351/find-latitude-and-longitude-using-clgeocoder

iOS 筆記》UIWebView 調用 JavaScript 函數時,呼叫 Objective-C 方法

有時候開發 iOS App 時,因為某些考量,會讓整個頁面使用 HTML 頁面,而不是使用原生的 iOS 畫面,但是此 HTML 頁面又必須與原生的 code 互動。例如:我們現在有一個會員註冊頁面使用 HTML 頁面,當註冊成功時,必須通知 App 跳轉頁面,這時我們可以讓 HTML 頁面在註冊成功時,呼叫一個叫做 RegisterOK() 的 JavaScript 函數,當這個函數被調用時,我們呼叫一個 Objective-C 函數或是方法,然後開啟其他的 iOS 頁面,這時可以這麼做:

先包含 JavaScriptCore.h 標頭檔:

#import <JavaScriptCore/JavaScriptCore.h>

使用 JavaScriptCore 跟 UIWebView 互動:

#pragma mark - UIWebView Delegate

//...

- (void) webViewDidFinishLoad:(UIWebView *)webView {
    // 透過 KVC 取得 JavaScriptContext
    JSContext *context =  [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
    
    context[@"RegisterOK"]= ^() {
        NSLog(@"Call RegisterOK()");
        // 執行一些動作,例如跳轉頁面:
        //...
    };
}

參考來源:http://stackoverflow.com/questions/28827317/how-to-listen-to-in-objective-c-uiwebview-when-the-javascript-function-is-trigge

《出家及其弟子》

最近看了倉田百三的《出家及其弟子》,真的很好看,難怪李登輝會說這本書是對他青春時期思考發展影響最大的三本書之一!

作者倉田百三是大正、昭和初期的日本劇作家,在22歲時感染肺結核,被迫輟學,一直到40歲都無法擺脫病榻生活,或許是長期臥病在床的因素,從其作品裡可以看到他對生命有極深刻的體會。

我第一次知道《出家及其弟子》這本書,是從李登輝的《武士道解題》裡看到的。前陣子在複習《武士道解題》時,想到可以查查看這本書有沒有中譯本,所以就上網搜尋了一下,查到2013年有出版了一本中譯本(大牌出版),譯者是毛丹青。

過了幾天,利用週末去三民書局找這本書,本來猜這本書應該會放在日本文學的區域,但是找不到,後來在禪學的區域找到這本書。也許是因為內容是描寫日本淨土真宗創始人–親鸞的故事,所以才會被歸類到禪學類吧!(不過,一般認為,《出家及其弟子》融入了基督教思想。)

《出家及其弟子》的故事有兩個主軸,一個是描寫大師親鸞與他的兒子善鸞之間難解的心結,另一個是描寫親鸞的弟子唯圓與藝伎的愛情故事。

善鸞原本有一戀人,但是女方因故被迫嫁給他人,善鸞對她仍然念念不忘,因而造成悲戀。女方後來病死,女方的丈夫又哭又怒,詛咒著善鸞。親鸞也因此跟善鸞斷絕關係。之後,善鸞也自我放棄,沈溺在酒家間。

親鸞的弟子唯圓知道了這件事後,想促成親鸞與善鸞的和解。但是,唯圓不但沒有促成親鸞與善鸞和解,自己也與藝伎陷入熱戀…。親鸞知道後,並沒有責怪唯圓,反而在親鸞的促成下,最終唯圓與楓結了婚。

故事最後,親鸞臥病臨終之際,善鸞趕回來見父親最後一面:

善鸞:(流淚)終於見面了……請原諒我,我……
親鸞:我們都會被原諒的,誰也不能對誰制裁。

然後,親鸞問了善鸞「你信佛嗎?」。
善鸞無語。

親鸞:你可別拒絕慈悲啊。你跟我說你信……給我一份安心吧,讓我的靈魂能夠返回到天上……

這些話讓靈魂痛苦的善鸞臉色發青,過了一會才絕望的說出:

善鸞:我實在太淺薄了……我不懂……我決定不了。

善鸞的回答讓親鸞有點沮喪,但是過一會兒,親鸞平靜了下來,說出:

親鸞:這也不錯,大家都得救了……這是友善而和諧的世界。啊,和平!那是最遠的,也是最內在的,南無阿彌陀佛!

上面這段親鸞最後說的話,在《武士道解題》裡是這樣翻譯的:

「好吧,你決定就好。任何人只要真心就可得助……世界是善良的、調和的。期許能有永遠美麗光輝的和平。永遠。內心也一樣。南無阿彌陀佛。」

最終,親鸞對善鸞由「全部否定」轉為「全部肯定」,故事也有了圓滿的結局。