自動語音導覽 – Adafruit Ultimate GPS & JQ8900

Description

Description
作者:胡均綸

 
創作發想

先來分享為什麼會做這個自動語音導覽的小專案好了。這個故事是這樣,之前在做 NTP 時鐘的時候有陣子真的滿挫敗的 (一直鬼打牆解決不了問題),當時想說要尋找同溫層或許可以能有幫助,也想知道自己現在到底 capable of what,我就決定出去參加一些 maker 的活動 (eg. MakerPRO),而在這個過程中我意外看到 十年前他帶學生親手打造太陽能車,如今門生遍佈Tesla、Gogoro 這篇文章,看完覺得很興奮,覺得台灣已經沒有幾個教授願意帶著學生實作,我就毅然決然寄信給文中主角台大機械系鄭榮和教授,詢問教授我是否能加入他的團隊,一起把手弄髒,完成一些腦中的想法。

原本抱持著教授怎麼可能會理我這個菜鳥,可能信也就石沈大海了,沒想到隔天一早起床就看教授回信,教授不僅很歡迎我加入團隊,還特地約了個時間要我去找他聊聊,當然我興喜若狂答應赴約,也就這樣進了團隊。

而自動語音導覽系統就是在這裡做的第一個小專案。這個東西是為了台大最近那台電動自駕巴士,初步要規劃用在校園導覽時的語音導覽功能,而語音導覽就是到了某個位置開始播放介紹那個位置的語音檔案,所以我就想說我要用我可能會的東西解決這個問題。

解決方式 (組件介紹):

Adafruit Ultimate GPS

一開始我拿到這顆 GPS module 的時候,用了 4 個 pin 腳位 (VIN, GND, TX, RX),(各個腳位的應用與解釋可以參考以上連結),連接 USB 轉 TTL 的元件,用 coolterm (下圖就是 coolterm 視窗) 去看 GPS 抓到的東西到底是什麼。

螢幕快照 2017-06-11 上午1.02.16.png

這個東西很有趣 (一開始不知道是什麼的時候只覺得很酷),詢問 Bird 之後才發現原來這個是一種叫做 NMEA 的通訊傳輸資料的 protocol,裡面的每個 data 都有它所代表的意義,我這裡不細項介紹 GGA 或 RMC 所代表的東西,而是直接分享我可以從 GGA 或 RMC 裡面獲得什麼資訊。參考資料:The NMEA 0183 Protocol

其實我要的東西不外乎經緯度,畢竟我一開始的規劃是要做,假設到了 “某個位置” 就要 trigger “某件事情” (但接著就會發現沒有這麼容易),而這兩個 longitude and latitude 都可以從 GGA 或是 RMC 裡面找到 (上面 GPS 沒有定位到所以當然還看不到,真正定位到的樣子我留到底下用 Arduino UNO 當 MCU 的時候說明)。

而現在 coolterm 裡面從 RMC 可以得知訊息像是有 “接近原子鐘精度級別的時間信息”,“移動速度”,“日月年 (dd/mm/yy) (從這裡可以看到其實我是剛剛才截圖的 因為當時做的時候忘記拍下來)”。

Arduino console 裡面的資訊 & 撈出我需要的資料

知道這些 data 所代表的意義後,我就將 USB 轉 TTL 收起來改用 Arduino UNO 來作為主要發號司令者,之所以要 Arduino UNO 作為發號司令者是為了達成我的目的 -> 定位到 “某個位置” 我要 trigger “某個事件”,那第一步就是得把 GPS module 身上的資料不僅僅是顯示在 console 而已,我要用 console 裡面的資料去做到我要 trigger 某件事件的動作。

測試版硬體接線實體圖:(其上的 LED 是我用來當作被 trigger 後 LED 會亮)

19095882_1407079619385083_347520789_o.jpg

那這個撈資料的動作怎麼完成?Bird 以前常說要站在巨人的肩膀上,因此我 google 了一下,果不其然真的有有人寫好的 library 可以供 user 使用,library 下載連結:library

看完這個 library 大致上了解他的運作方式後,比如說跟 GPS 說我想要獲得什麼資料 (eg. RMC GGA),多久 update 一次 (頻率幾赫茲),以及發現原來也是用 interrupt 的方式去抓資料下來 (所以 for loop 裡面就可以做 trigger 某件事件的動作),在寫好我需要的 funciton 後,真正 GPS module 定位到是長這個樣子 (如圖):

螢幕快照 2017-06-11 上午1.45.28.png

上圖可以看到我要的東西有四個,分別是緯度、經度、連線到的衛星數,以及 HDOP (等等會說明),所以我當時所在的位置的定位即 latitude 為 2502.58,longitude 為 12133.83 (這個單位跟 google map 所用的單位不同,稍後分享),連接到的衛星數為 3 顆衛星,HDOP (Horizontal Dilution of precision) 為 3.76。

而可以在 console 裡面看到這些資訊最重要的一點是,我成功利用一些 functions 透過 Serial.print 的方式把 GGA 或是 RMC 這些 data 中我需要的資訊給正確的抓了出來,這樣也就離我想達成的目標更近了一步。

DOP (Dilution of precision)

而回到剛剛提到的 HDOP,這其實是一個精度準確度的判斷值,而 HDOP 只是其中一種 DOP 而已 (即水平也就是所謂的二維),還有 GDOP (三維座標與時間的精度準確度)、PDOP (三維位置)、TDOP (時間)、HDOP (水平二維)、VDOP (高程及高度) 等等,之間的關係以及意義不多做說明,附連結有興趣的人可以參考:GPS 觀測中的的參數「DOP值」是什麽概念?

我在這裡要分享的東西是 HDOP (水平二維) 所代表的誤差值,我們利用 GPS 定位最少要有三顆衛星連線,數字越代表越準確,如果看到 1.00 表示非常非常準 (也可以從連接到的衛星數看出端倪 <連接到的衛星數和 HDOP 的值大致成反比>,1.00 通常都 7 顆 8 顆了),越高代表所得到的經緯度資訊越不可靠,也就是說 DOP 這個東西是告訴我們獲得資料的可靠信。

但可靠信這件東西要怎麼應用又是另外一個故事,畢竟我要做的事情是 “定位到某個位置 trigger 某件事件”,也就是說我所獲得的定位資訊的可信度要夠高我才能 trigger 某件事件 (一開始我還蠢蠢的以為 HDOP 是可以直接拿來和經緯度 “加減” <一個正負 range 的概念>),所以說我設定 HDOP 在小於 3 的情況下才能 trigger 某的事件 (當然還有別的條件,只是這是第一個),至於為何只要 HDOP 是因為我不需要用到海拔高度所以用水平二維的資訊即可。

longitude and latitude 資訊 convert & 大地座標系

資料正確抓出來,以及了解 DOP 的問題後,接下來就是要清楚的知道抓出來的資料的意義是什麼。從上面的圖片可以看到 latitude 為 2502.58,longitude 為 12133.83,好奇如我立馬把這個經緯度複製貼往 google map 看看定位在哪裡 (我當然預期會在 AppWorks 附近)。

但失敗了,根本沒有這個地方。後來觀察經緯度的數字才發現,原來我所獲得的經緯度的表示方式和 google map 所用的經緯度是不同的,我的這個是 Decimal Degrees (十進位表示法),而 google map 用的是 Degrees, Minutes & Seconds (度分秒表示法),而這之間的換算可以輕鬆地做到,有興趣的這裡有連結:GPS converter

做好換算後再貼到 google map 一次,發現成功了,的確有顯示位置,但顯示的位置卻在饒河夜市附近,後來詢問 Bird 原因才知道,原來可能是因為所使用的大地座標系不同而導致這樣的結果 (覺得博大精深,連做這種小 project 都可以牽涉到這麼廣的知識)。

不過沒關係,最後成品出來後,我可以直接去台大定位,取得這個 GPS module 自已本身的數字也不需要 converter 去做換算了。但其實 google map 對我的 project 還是有它的價值存在,雖然沒有辦法定在正確的位置上,我還是可以用 google map 來做與我獲得的經緯度和 HDOP 之間的關係。意思是說,在某個數值的 HDOP 的情況下 (其實不是定值,但變化相對小),我把每次獲得的經緯度都丟到 google map 上看在哪裡 (比如說抓 10 次, 10 個點的分佈狀況),就可以大概知道可信度與資料間的關係 (比如說 HDOP 為 1.73 時,10 次位置大概是怎麼分佈)。

在 Adafruit Ultimate GPS module 的背後,其實有一個可以裝上電池的地方,如下圖:

螢幕快照 2017-06-12 下午5.03.28

上圖可以看到已經有電池裝在上面了 (因為還沒裝的時候甚至還沒將電池外殼焊上去的時候又忘記拍照了……)。這顆電池叫 CR1220 (圖中右上方有這個電池的型號),很不常見,便利商店也買不到,最後 Bird 說公司對面打鑰匙的店鋪可能會有,果然在那裡找到了這個型號的電池 (一顆 100 超貴……)。

而這裡要分享的重點是這顆電池對於 GPS 的作用為何。原本以為這顆電池是用來供電,在不外接任何電源的情況下使 GPS 仍然能夠正常運作 (即定位),後來聽到 Bird 說這顆電池可以撐個一兩年才發現自己大錯特錯。

原來這顆電池是用來記憶的,所謂記憶的意思是,假設我現在定位成功,我獲得的資訊 (像是經緯度,HDOP 等等的資料),因為有了 CR1220 這顆電池的存在所以能在斷電後 (即拔掉 USB 電源) 將斷電前的資訊紀錄在 GPS module 的記憶體中,等到下一次重新接電時,因為有了上一次的記憶 (在 GPS module 的記憶體中),若要定位的地點和上次雷同就可以比較快速的連接上衛星而獲得資訊,這也就是所謂的 Warm start

而與 Warm start 相對應的即是 Cold start。Cold start 與 Warm start 的差異就在於 Cold  start 沒有辦法拿到前一次斷電前的資料,每一次重新插電就是全新的一次,也就是說只要一斷電,任何收到的資訊都會被洗掉歸零。

所以說有了這顆電池,往後需要重新連接衛星時,可以較快成功定位。

JQ8900 (mp3 player shield)

搞定所有 GPS 的部分,接著要處理的即是 mp3 player shield 的部分。JQ8900 這個東西其實不難,講白了他就是一個可以解碼 WAV、mp3 檔案且含有一個 4.2 MB 的記憶體可供存放 mp3 檔案的元件。

至於怎麼使用也不是件太困難的事情,如下圖可以看到各個 pin 腳:

螢幕快照 2017-06-12 下午6.01.25.png

這裡我會分享我有用到的腳位,對其他腳位有興趣的朋友這裡有附連結:JQ8900-16P語音模塊。IO1 ~ IO7 是 7 隻對地觸發 (腳位的 state 為 LOW 時才運作) 的 pin 腳位,也就是說在 JQ8900 中可以放 7 首 mp3 檔案;GND 不用多做解釋,仍是地地相連到天邊的概念;DC-5V 其實就是常常看到的 VIN;SPK- & SPK+ 就分別接到 speaker 的兩端。

了解以上的事情後,我現在要做的就是把這幾個已經分別解決處理好的小模組,把他們拼裝起來並且能夠互相溝通。

成為硬漢的第一步

現在手邊的東西已經有:Arduino UNO、Adafruit Ultimate GPS module 還有 JQ8900 這三樣東西,Bird 問我我打算怎麼把他們搞在一起,畢竟總不可能成品仍然掛著長長的跳線吧 (想知道我說的長長的跳線是什麼可以查閱上一篇自動語音導覽 – Adafruit Ultimate GPS & JQ8900-16P (一) 裡面的圖片),為了這個問題我在腦中想出了各種奇形怪狀的方式,事實上解法其實很單純:設計電路。

設計電路前,要解決的問題是我要怎麼把東西都搞在同一張板子上 (洞洞板)。在經過 Bird 提點後,決定將 Arduino UNO 改成 Arduino nano,問題便迎刃而解 (nano 版很小等等有圖片 demo),如圖 (尚未焊接,只是先想想要怎麼排比較合適):

19096191_1408211525938559_125241456_o.jpg

其實上圖的狀況已經是 Bird 要我先把連接各 module 形式以及接線都先畫下來給他看後,我才拼裝的樣子 (畢竟我這個菜鳥是第一次自己設計這種東西)。

設計完整體形式後,接下來就是焊接。而要成爲一名硬漢,最重要的武器就是 “烙鐵”、“各式鉗子”、“銲錫” 等等工具,這也是我第一次自己動手焊接電路與元件 (真的有難度,Bird 總是可以焊到跟工廠出品的一樣我也不知道為什麼……)。

前前後後練習了好幾次終於看起來有點樣子 (其實還是很醜,手很抖),最後我把元件該固定在洞洞板上的點以及 module 之間應該要相連的 pin 腳用 OK 線焊接好,如圖:

19074810_1407079499385095_1555533511_o.jpg

(真的滿醜的,被 Bird 嫌棄哈哈,不過能用且功能正常,至於焊接技術只能再接再厲) 再附一張焊接好後接電的樣子,如圖:

19125294_1407079522718426_476082514_o.jpg

目前看起來沒有什麼問題,成爲硬漢的第一步成功,接下來要做的就是在 nano 裡面 program,讓我達成 “到某個位置 trigger 某個事件” 這個目標。回顧一下,因為 GPS 是利用 interrupt 的形式,以每秒一次 (可以自己挑) 的頻率向衛星收資料,並顯示在 console 裡面,所以說我的 for loop 就可以用來做我想做的事 (即到某個位置 trigger 某個事件)。

在這次的成品需求下,我只需要用到 JQ8900-16P 的 5 隻 IO 腳,分別接到 nano GPIO2 – GPIO6,由於 JQ8900 的 IO 腳是接地觸發,所以 nano GPIO2 – GPIO6 pin 腳位的 initial state (即 digitalWrite()) 當然就要設為 HIGH。

此外,另外一件重要的事情是,一旦到了某個特定位置 (eg. latitude : 2502.56 longitude : 12133.89) 觸發了某個事件 (eg. GPIO2 state 為 LOW 後又變回 HIGH -> 音樂就播放了) 後,我要在一段時間間隔內不再觸發,不然我一秒收衛星資料一次,如果還在我設的範圍內豈不是又會再觸發一次 (即重播),所以我得設定時間間隔避免這樣的悲劇發生。

以下是部份程式碼供大家參考:

#define pinNUM 5
int location[5] = {2, 3, 4, 5, 6};
unsigned long triggerTime;
unsigned long lastTriggerTime[pinNUM] = {};
void setup()
{
for (int i = 0; i < 5; i++)
{
pinMode(location[i], OUTPUT);
digitalWrite(location[i], HIGH);
}
GPS.begin(9600);
//.
//.
//.
//there are lots of code above and it’s about GPS, but not the point so skip it
}
//here also has interrupt function for GPS to grab the info
void loop()
{
triggerTime = millis();
if (GPS.newNMEAreceived())
{
GPS.parse(GPS.lastNMEA());
if (GPS.fix)
{
Serial.print(Latitude: );
Serial.println(GPS.latitude);
Serial.print(Longitude: );
Serial.println(GPS.longitude);
Serial.print(numebr of satellities: );
Serial.println(GPS.satellites);
Serial.print(HDOP: );
Serial.println(GPS.HDOP);
if (GPS.HDOP < 3)
{
if (triggerTime – lastTriggerTime[0] > 15000)// longitude and latitude of the first location will add up later
{
lastTriggerTime[0] = triggerTime;
digitalWrite(location[0], LOW);
delay(1000);
digitalWrite(location[0], HIGH);
Serial.println(song1 started!);
}
}
}
}
}
view rawGPS_nano_mp3.cpp hosted with ❤ by GitHub

最後分享這次成品的 demo 影片:

 
Ryan Hu

Ryan Hu

政大休學生,對於 IOT 軟硬整合相關有極大興趣,鍾愛無人機,目前獨自完成的專案像是瓦力號、GPS 自動語音導覽系統、NTP 網路自動校時時鐘,喜歡流浪。
個人網站:https://makeryan.wordpress.com/
個人信箱:ryanhu0306@gmail.com
Ryan Hu

Contact

Contact
  • Category
    語音介面

Project簡述

Project簡述
  • 簡述
    自動語音導覽系統是為了台大那台電動自駕巴士,初步要規劃用在校園導覽時的語音導覽功能,而語音導覽就是到了某個位置開始播放介紹那個位置的語音檔案,所以我就想說我要用我可能會的東西解決這個問題。
  • 作者
    胡均綸

您的姓名 〈需填寫〉

您的電子郵件信箱 〈需填寫〉

主旨

您的信件內容