2017年5月23日 星期二

產品經理可以不懂技術嗎? RD/PM角色互換,我跳進來又跳出去了!

高手高手高高手看了我的文章之後丟了一篇文章給我

產品經理可以不懂技術嗎? 

裡面有提到3點PM如何說服RD
1.過去是否有成功的產品經驗
2.是否有非常敏銳的產品直覺
3.是否有強大的邏輯思維能力

第1點代表2個面向
A.這個PM在這間公司很久了,是老鳥,RD聽PM就對了,反正老闆聽PM的,這裡沒RD說話的餘地,提供火力就對了
B.PM在其他公司曾經有成功的產品經驗,但我們公司是提供夜市Hello Jacky牛排攤衛生紙的,你在衛生紙上放Hello Kitty?? 這不是擺明上市被拍照上傳到爆料公社等被吉嗎???

第2點看看偉大的Steve Jobs吧! 相信自傳大家都會背了,這種通常是大PM,在你沒看到的時候也兼做市場調查,以利接收最新資訊

第3點這個...在台灣老公司好像...沒什麼用 ~ 老闆說了算,但多寫程式就好比吃了撒尿牛丸一般,頭腦靈光了很多,每次提案都沒有被丟到垃圾桶

所以產品經理可以不懂技術嗎? 不如這麼說 產品經理是個戲精 還是天橋下說書的

WTF!!! 這什麼爛回答!

我來說個我家浴室漏水的小故事

在1月的假日,冬天的尾巴,溫度還是會讓人四肢僵硬
好想泡溫泉啊! 這個時候如果在舒適的熱水池裡放空,結束後再以大字型躺在柔軟又乾淨的床上...
瞬間回到現實! 這週床單還沒洗,地還沒吸,衣服還沒整理,週一的會議報告還沒準備...

叮咚! 有人按門鈴

來來來...哩來哩來,來我家看一下 原來是樓下阿伯,什麼事這麼急??

你看我家這裡新裝潢的廁所、房間一路到廚房都漏水了,而且壁貼都變色了 阿伯說

我一路笑臉陪不是,回家之後找科技抓漏,用FLIR紅外線熱像儀,畢竟我RD背景,相信儀器找問題 (PM角色)

浴室沖水的地方因為用久了,防水需重做 科技抓漏說

我再請家裡附近的水電老闆來報價 (要有找Second source的準備)

老闆說 因為家裡浴室已超過20年沒重做防水,找不到問題,老闆報價10萬,浴室全打掉重做,順便找問題 (這老闆很威,整條街水電都老闆接的!)

結果老闆比科技抓漏還強,發現是浴室外面的飯廳地板內的水管,轉彎處破了個洞 聽音辯位的概念嗎? (老闆就是RD,技術精深)

報價單上有提供有老式的磁磚讓我們選擇,我當然是要選像飯店一樣豪華又氣派的磁磚啊!

但老闆說新式的他們沒貼過而且沒有合作商進貨  所以我就上網Google和找朋友

老闆!你唬我哦!現在都用這種樣式的,而且比較便宜,如果我家有客人來上廁所
看到磁磚這麼美麗大方,他一定多拉幾泡... 哦抱歉!是印度來的朋友,他會教我拉茶
而且我會說是你們幫我做防水、泥作與乾濕分離哦!

故事就先在這裡打住,在跑專案流程時,PM是比任何人都清楚目前的階段,小弟我做PM有三點很重要

1、看場合堅定的眼神微笑,但不要他好像永遠對著你笑,笑得你心裡發寒的那種
2、Google是最好的朋友,防水、泥作和磁磚我哪會! 沒有特異功能,只能先Google再轉化成自己的內功才能震懾人心!
3、問與答

接下來就是準備充足、規劃後準備了再上,演什麼像什麼
這樣有沒有跟彭于晏很像?  什麼?? 你說我是PM彭于晏,這怎麼好意思,都說我是RD金城武了 咦!?

回到我寫的不只是智能燈座的智能燈座文章上,RD供精且深的火力資訊給PM,讓廣而寬的PM由公司內部及外部的Feedback消化做決策

RD大人 腸胃都要消化了 PM也要消化 腸胃好 人不老

不只是智能燈座的智能燈座是完全無中生有嗎? No! 這是有歐美Retail的回覆PM才想的出來
不然做個市面上不需要的產品要賣給誰? 所以RD一定要在技術上支援PM

什麼? 你們公司的RD不支援PM,拉RD上來做PM! PM先轉去做FAE或採購! 砸鍋就知道誰不行,海水退了就知道誰沒穿褲子了

PM要注意每一個階段要銜接順利,不能滑時程,而且要隨時回覆上頭迎面而來的進擊,RD適時的提供火力

所以RD和PM不是敵對的,你們在同一條船上啊!  雙方都要保持良好的溝通習慣

那如果今天科技業不行了,要轉去做建築業的PM呢? 這是好問題
還是用那三點,微笑、Google、問與答,好重要的! 這樣至少能過試用期...


2017年5月21日 星期日

第十章:結論

大家好,我是Ches拔,也就是本體

當初在構思這個專案的時候,是用業界的開發流程去創作
但計畫趕不上變化,要做的東西實在太多
從產品設計、研發溝通到找料及插件的角度來說好了,在EVT階段至少需要以下步驟

1、產品雛形規畫->修正->討論->無限循環的會議地獄->再度修正->達成協議
2、軟硬韌體規畫,軟體人員、硬體人員及韌體全部叫出來開會討論,有必要的話,連LayoutEMC人員也要叫出來開會
3、外型規劃,這裡是交給工業設計人員與PM一起開會,通常RD Head也要出現,三方交流
4、找料及加工廠溝通,PM與採購同時進行,但現在台灣的狀況,有可能PM兼職採購
5、手工上件,通常是交由公司內部會拿烙鐵的同仁幫忙上件或是有產線的公司用10~30PCS試打件
6、RD內部測試,軟硬韌一開始分頭進行然後又組隊合作,再分頭進行又組隊合作的無間地獄,直到世界的盡頭及初版樣品會動為止

在第6的步驟很有趣,有聽說過在某間公司,如果初版不會動
開發團隊就會被BU Head 在會議中被電到離不開會議室,順便扒掉RD的皮
RD承認能力不足,對不起家人朋友及所讀的學校
之後Bu Head會單獨叫PM到會議室,當面指責他,怎麼連帶專案都不會

殊不知,BU Head是號稱工作經驗30年以上,卻只會出一張嘴
連單步執行、改電路圖與EMC都不會的呵呵主管,罵完開心地覺得打完收工
結果,所有的RDPM在年終後,全都帶著技術開開心心地離開,技術沒有再傳承下去

造成了斷層後,老闆叫BU Head找新人,至於BU Head如何扛起及用什麼手段放下
這又是另一個故事了

但現在很多新創團隊已經沒有這種問題了
人與人還是要互相幫忙,社會才會和諧

這個專案主要是想讓平常去上班上課的朋友也能知道長輩在家的狀態
但又不想把攝影機放在客廳,所以把室內定位演算法帶回家及衍生了其他功能
想要讓跟我有一樣困擾的朋友,一起腦力激盪,看是否能為社會做點貢獻
也就想出了一個結合了長照、保全、室內定位及智能燈泡的多功能專案

我也用這個專案的其中一個功能去報名了KEEP WALKING夢想資助計畫
( https://goo.gl/rD5InY )打進了20強,但最後的Pitch GG了,原因是前10太強大

會用旁觀者的角度去創作這10篇文章,是想讓大家知道我是如何起頭、寫設計邏輯、外型與電路
沒有把安裝步驟及交叉互測寫進去的主因,是因為這樣會超過十篇,就變成不是設計專案的故事,而是教學了
而且寫下來有個好處,可以順便把Code再Review一遍,結果發現相當難以閱讀 

目前這個專案只到EVT,已經會動,但會動與最終產品是差很多
寫程式絕對不是靠天份,而是靠努力,認真在電腦前面Key了多少
之後開發速度就會回饋多少到自身上如學習語言一樣

註:我能力不夠,所以只能學更多,才有辦法跨界開發,需要感謝的人太多了
例如Fon哥、Kenny大、Summit大、Mark哥與Suci哥、還有學弟Simon
大家一路惡補我沒玩過的東西,一個人做到這裡進度很緩慢
希望能找到想一起做對社會有貢獻的伙伴,接下來就是找時間進入DVT Stage!

Good Luck!

註1:可不可以改用MT7697? BLE+WiFi+Cortex M4簡直如虎添翼!!! 
當然可以! 但CC254x+ESP8266量產價格看起來很有競爭力哦!

2:在第2章時,使用AmebaAP mode,再Station mode,發現AP一直沒轉成Station mode,寫法如下

void setup() {
//Initialize serial and wait for port to open:
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for native USB port only
}

// check for the presence of the shield:
if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
while (true);
}
String fv = WiFi.firmwareVersion();
if (fv != "1.1.0") {
Serial.println("Please upgrade the firmware");
}

// attempt to start AP:

Serial.print("Attempting to start AP with SSID: ");
Serial.println(apssid);
status = WiFi.apbegin(apssid, appass, channel);
delay(10000);

//AP MODE already started:
Serial.println("AP mode already started");
Serial.println();
printWifiData();
printCurrentNet();

WiFi.disconnect();

while (status != WL_CONNECTED) {
Serial.print("Attempting to connect to WPA SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network:
status = WiFi.begin(ssid, pass);

// wait 10 seconds for connection:
delay(10000);
}
}

3:感謝Realtek的人員後來已修正此issue,但我也進入多方時間壓縮的零度空間,已經沒有空再驗證

以下是朋友間的Q&A--------------------------------------------------------------------------------------------------------------------------

Q:所以這個專案能做什麼呢? Health Care? 例如一堆廠商在做的量測生理資訊? 一堆人在做了,切入進來倘混水???

A:別忘了我們有Lora、BLE與WiFi,都說它是Gateway能做的事就多了,還可幫助廠商把產品變成只需一顆CR2032

Q:還是搞不太懂這裡長照是怎麼長照法?

A:例如平常上廁所時間是5-15分鐘 這次不到洗澡時間卻超過20分鐘,且沒有移動(哈利波特的劫盜地圖你懂的),系統會被觸發送推播或打電話到家人的手機,也就是說全家人都是照顧者

Q:但家人正在上班上課呢?

A:所以才說我想去選里長! 把推播訊息全接到里長辦公室服務獨居/獨處長輩也是很理所當然的事
但說也奇怪,為什麼我家里長不住在這個里,這樣也選的上里長? 黑人問號???

Q:所以它是Tracker?

A:應該是說,Tracker是它其中之一的功能,但它不是Tracker

Q:保全怎麼做?

A:都說燈座是Gateway了,加個PIR、Door sensor不就得了? 全家人都是保安

Q:這麼搞剛,我在家裡放個攝影機就好了

A:可是我在家都光上身穿內褲走來走去耶~ 被駭客誤認我是金城武截圖上傳到爆料公社怎麼辦

Q:如果長輩在浴室跌倒當時卻沒帶手環呢?

A:你覺得在Gateway加個Echo的環形麥克風陣列上傳到Cloud分析有沒有搞頭?

Q:我家長輩平常不要說手機,連手環都不帶在身上了

A:有沒有宗教信仰? 跟宮廟合作拜拜送BLE平安符好不好? 還可以塞到鞋墊下

Q:這個專案的AI聽起來還很遠,根本垃圾

A:

徵求想改變鄉里的熱血戰士,前方還有沒遇過的冒險在等著
現在放棄的話,比賽就結束了啊!
讓我們奮戰到最後一刻吧!

鄉民:徵什麼人啊!? 也沒說清楚
系列文章寫的亂七八糟,看都看不懂...

本體:I'm sorry...希望能徵到一起來完整這個專案的戰士...

第九章:材料歸位

月光下,一個小小的Idea躺在腦子裡,正等著孵化

星期天早上,暖和的太陽升起來了,『波』一聲,一個Idea變成一條又小又餓的Idea

他要去找一些東西來吃,以提供養份成長

星期一,他吃了一個Assembly,可是,肚子還是好餓

星期二,他吃了兩個C語言,可是,肚子還是好餓

星期三,他吃了三個Java,可是,肚子還是好餓

星期四,他吃了四個Python,可是,肚子還是好餓

星期五,他吃了五個R,可是,肚子還是好餓

星期六,他吃了橡膠果食

星期日,啊!原來是可以伸縮自如的橡膠朋友呢!

當橡膠朋友把購買的材料與PCB3D Printer的模型收集齊全後,就可以向神龍許願


哦不! 是如下圖

But…因為有一堆的Bug在修改,包括軟體與硬體

為了方便Debug,我買了一個3.3V的變壓器加上跳線,如下圖


再經過一連串的Debug地獄之後,將來會把DVT的外型改成這樣

正面圖

側面圖

最後會把所有材料塞到燈座內,搖身一變成為第0章所提的不只是智能燈座的智能燈座

PM腦:人類行為研究呢?

RD腦:原本打算用Spark去分析,後來使用IBMWatson也有不錯的效果,但只是來驗證雛形是ok的,就猶如清田信長的指甲劃到三井壽的球一樣

所以將來的重點除了DVT Stage外,人類行為研究的演算法,的確需要好好思考如何完成








第八章:PCB Layout設計

RD腦:接下來就是用Toollayout!

PM腦:哦哦哦! 終於到要出Gerber送板廠這一步了,可以告訴我,你用什麼軟體設計的嗎?

RD腦:我在業界都用Mentor/Allergo/Pads、學校用Altium Design,但這裡我不會告訴你我用什麼,你以為本體有軟體可讓我用嗎? 想害我被查水錶!? 沒這麼容易!!!

經過一連串的PCB擺件後


正面圖

背面圖

PM腦:什麼! 你又想用第六章那一招嗎?

RD腦:是!


第七章:電源電路設計

由於電路需要調光,發現這個文章做的挺不錯


簡單的說PIN3是輸入用來判斷ACzero-crossing,而PIN10是輸出,但完整Code需要找時間完成 T T,所以我們將來會改成Cortex M4而不是用Arduino


而在AC/DC的部份,EVT我們使用的是一種叫作Transformerless的設計https://circuitdigest.com/electronic-circuits/transformerless-power-supply


但因為沒有二次側,雖然燈座在天花板,還是有點危險

後來考慮AC-DC電源模組,是一種做成磚塊的模組,如下圖是台灣廠商做的
這個好處在於CEUL都過了,可以省很多事

But…人生就這個But… 我們也在考慮TIST或台廠的標準AC/DC電路
因為可以用EE13的小型Transformer,這樣可以省很多空間

所以,在EVT的版本,我們就用了Transformerlesspower supply,如下圖

但這個不能拿來賣歐美,實在太危險,飛太遠,電路爆炸對你做鬼臉
故在DVT版本,我們會把AC/DC電路與調光電路做一個比大平台還大的大更動




第六章:3D建模設計

本體:123D Design做燈座建模真的很簡單
我用過年的連假一邊帶小孩一邊增肥一邊用油膩的手敲鍵盤與滑鼠
只用了4天就學會了,太神奇啦! 高手只要5分鐘就畫完了吧?

RD腦:終於解決軟韌體的部份了,接下來輕輕鬆鬆用123D Design畫外觀吧!
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
簡單的說,它最後就長這樣


側面圖

正面圖

PM腦:RD腦,你想這樣就結束這一章節嗎?

RD腦:是啊! 畫圖嘛! 這就跟跑步游泳一般,不就你做什麼他做什麼

PM腦:你難道不覺得這樣對不起你的讀者嗎?

RD腦:不會!




第五章:MCU + Lora 與 Android室內平面顯示

前面說過,我們的架構是這樣


代表著MCU掌握了所有溝通技能,一步錯就會導致系統崩潰

PM腦說:哦哦哦! 那不就跟我的角色一樣嗎?

RD腦皺著眉回覆:這有難回答,我先去抽根菸

PM腦接著說:醒醒吧! 你根本不會抽菸,還想多出這一句拖稿! 你是不是不把我奔雷手文泰放在眼裡!?

RD腦指著PM腦說:你
查了幾家做Lora的廠商,台灣有正文、群登和iFROG LAB,國外有Pycom出的LoPy

而群登是Cortex M4 + Lora,這我從來沒試過這麼清新脫俗的感覺,就猶如強大MCU的鮮、與Lora遠距離的甜,混在一起的味道竟然比AlphaGo橫空出世有過之而無不及

又這麼剛好有2UART可以搭上WiFiBLE
也難怪PM腦會說:爭什麼呀! 摻在一起做不只是智能燈座功能的智能燈座啊!笨蛋!

PM腦平常都在看市場,很注重客戶的使用狀態回覆,也難怪他程式學不好,我錯怪他了 RD腦心想著

我們使用AcsipAI127x來做主系統,SDK可以看github上面的
https://github.com/AcSiP/AI127x_SDK

這裡就不寫貼整程式碼了,只寫概念,設計流程如下
1、UART1WiFi使用,UART2BLE使用
2、UART1UART2要在最後一個byte]才會進判斷式
3、進入判斷式後,用\做分割,所以進來的數據會長這樣
UART1 RXà\Smart phoneMQTT傳來的訊息判斷\]
UART1 TXà\上傳BLE的訊號到Cloud\]

UART2 RXà\自身的UD\掃瞄到的BLE ID\BLE IDRSSI\RSSI轉為距離\]
UART2 TXà\GO\]  - 這是告訴BLE可以開始掃瞄
4、main()內寫UART1UART2的流程

所以先著手UART設計
void USART2_IRQHandler(void) {
       
        if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {
               
                /* Read one byte from the receive data register */
                uartReceBuffer[uartReceIndex] = USART_ReceiveData(USART2);
                /* Clear the USART2 Receive interrupt */
                USART_ClearITPendingBit(USART2, USART_IT_RXNE);
               
    
     uartReceIndex++;
     if(uartReceIndex >= 50 )  //buffer只有50
       uartReceIndex =0;

       uartReceCount++;  //count+1
  }
       
}

再新增一個readRxBuffer
void readRxBuffer(void)
{

   if(uartReceCount == 0)
    return;
   uartReceCount--; //???ring????byte
             
         myData[myDataCount] = uartReceBuffer[uartReadIndex];
   uartReadIndex++;
   if(uartReadIndex >= 50)
      uartReadIndex = 0;

  

         if(myData[myDataCount] ==  0x5D) //只要最後一個為]就進判斷式
    {
                       
                        myData[myDataCount] = NULL; //]設為空值
                        myDataCount = 0;
     uartReceComplete = 1;
j=0;
     return;
    }
               
if(myData[myDataCount]== 0x5c) //只要是反斜線就分割
         {
          split[j] = main_count;
                 
                 j++;
                 return;
         }
                myDataCount++;
  main_count++;
}

Uart架構完成後,回到main()內做設定

    while(1)
        {
                readRxBuffer_uart1(); //wifi的輸入
if(uartReceComplete_uart1 == 1)
 {
  uartReceComplete_uart1 = 0;


         if(myData_uart1[0] == 'G' && myData_uart1[1] ==  'O') //如果傳來的是GO,就叫BLE開始Scan
         {
       
                 WiFi_RX_Flag = 1;
                 wifi_coming_flag=1;

                main_count_uart1=0;
USART2_UartWrite((uint8_t *)"AT+GO", strlen("AT+GO")); //AT+GOBLE

         USB_OTG_BSP_mDelay (3000);
 }
         
 if(wifi_coming_flag==1)//WIFI的回傳,也就是ESP8266subscribe
 {
//還沒開始寫 但預計這裡是手機控制燈座的狀態
//例如開關燈、保全功能的設徹防以及配件如人體感應器或門磁的狀態
//MQTT的架構應該是這樣 à UID/Bulb/Status UID/Accessory/Status
}


}
                readRxBuffer(); //BLE的輸入

                 if(uartReceComplete == 1)
 {
  uartReceComplete = 0;

         if(CentralID_flag==1)//取得BLE的唯一碼當燈座的唯一碼
         {
                 CentralID_flag=0;
                 USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                 for(i=split[0];i<=split[1]-1;i++)
                 {
            CentralID[i] = myData[i];
                sprintf(str1, "%c", CentralID[i]);
          USART1_UartWrite((char *)str1, strlen((char *)str1));
           }
                 USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                 main_count=0;
         }
         
         else{
                 
                if( WiFi_RX_Flag == 1)//BLE掃瞄到的資料全往上傳
                {
                        USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                        for(i=split[0];i<=split[1]-1;i++)
                        {
                         sprintf(str1, "%c", myData[i]);
                USART1_UartWrite((char *)str1, strlen((char *)str1));
                        }
                        USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                        for(i=split[1];i<=split[2]-1;i++)
                        {
                         sprintf(str1, "%c", myData[i]);
                USART1_UartWrite((char *)str1, strlen((char *)str1));
                        }
                        USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                        for(i=split[2];i<=split[3]-1;i++)
                        {
                         sprintf(str1, "%c", myData[i]);
                USART1_UartWrite((char *)str1, strlen((char *)str1));
                        }

                //由於RSSIUART收進來是ASCII,要轉為數字
                        char string1[5];
                        int l=0;
                       
                        for(i=split[2];i<=split[3]-1;i++)
                        {
                                string1[l] = myData[i];// 把收到的放到string1
                                l++;
                        }
                       
                        float bb = atoi ( string1 ); // string1轉為數字
               
                        float cc = calculateAccuracy( -54.0, bb ); //計算有多少米距離
                        USART1_UartWrite((uint8_t *)"\\", strlen("\\"));
                        sprintf(str1, "%.5f", cc); //取到小數點第五位
                USART1_UartWrite((float *)str1, strlen((float *)str1));
                       
                        USART1_UartWrite((uint8_t *)"\r\n", strlen("\r\n"));

                                USB_OTG_BSP_mDelay (50);
                        main_count_uart1=0;
                        main_count=0;
                }
        }
 }
}

再加下計算距離的公式
float calculateAccuracy( float txPower, float rssi )
{
       if (rssi == 0) {
           return -2.0; // if we cannot determine accuracy, return -1.
       }
       float ratio = rssi * 1.0 / txPower;
       if (ratio < 1.0) {
           return pow(ratio, 10);
       } else {
           float accuracy = (0.42093) * pow(ratio, 6.9476) + 0.54992;
           return accuracy;
       }
}

如此就完成MCU的寫法了

簡單說明一下,calculateAccuracy裡頭有個txPower
這裡我們在mian()是寫-54Beacon為了計算距離,會有個txPower
指的是一公尺的RSSI值,而Beacon會帶這個值
但我們用的是小米及Fitbit這種常見的產品,它不是Beacon

經測試後,通常會在-54-64之間,而在這個EVT Stage,我們選擇的是-54
但將來做成產品會讓使用者去校正
既然得到了手環與燈座的距離,就由Android去顯示三角定位的圖示
所以雛形大約長這樣,藍點代表定位的那一位,燈泡圖形就代表智能燈座

PM腦說:零分!!!

RD腦回:為什麼??? 我的禾花雀平面圖,設定可說是百中挑一的三角定位演算法,無論速度、解析度,都是一等一的

PM腦說:我指得不是演算法,我指的是禾花雀平面圖設計的太醜了!!!

RD腦回:設計醜?喂! 醜不醜關專案什麼事啊?

PM腦說:有事!挑男女朋友都挑長的帥或漂亮順眼的,使用者在下載APP也選美觀大方的,誰還有胃口來選你這個醜不拉嘰的APP呢?所以我看了一眼就把早餐吐出來了!

RD腦只好先把Android交代完畢,之後再想要怎麼修改這平面圖的設計

RD腦說:ㄜ其實我看了幾篇文章才寫出來的Code,但時間有點久,貼這2篇文章也許能代表我想說什麼

https://www.ece.lsu.edu/scalzo/FDR_Final.pdf page 11頁的Trilateration in 2D

Android的寫法如下:
import java.math.BigDecimal;
import java.text.DecimalFormat;

import android.app.Activity;

import android.graphics.Point;
import android.os.Bundle;

import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class IndoorLocation extends Activity{

         
          private SurfaceView sfv;
          private SurfaceHolder sfh;
         
          private Point greenXY = new Point(0, -13);                                               
          private Point blueXY = new Point(-8, 13);
          private Point purpleXY = new Point(8, 13);
          public static Point[] beacons;
          private int beaconsNum =3 ;                                                                     
          static double locationX = 0;
          static double locationY = 0;
         
          private void init(){
                  beacons = new Point[beaconsNum];
                  beacons[0] = greenXY;
                  beacons[1] = blueXY;
                  beacons[2] = purpleXY;
                 

          }
         
          @Override
          protected void onCreate(Bundle savedInstanceState) {
                  init();
                  super.onCreate(savedInstanceState);
                  setContentView(R.layout.indoorsurfaceview);

                  sfv = (SurfaceView) findViewById(R.id.surfaceView1);
                  sfh = sfv.getHolder();
                  sfh.addCallback(new MySfvCallback(IndoorLocation.this));


                 
                  double xypoint[] = getMeetingPoints(4.0, 4.0, 9.0, 7.0, 9.0, 1.0, 0.0025, 1.42, 6.96);
                         
                  locationX = xypoint[0];
                  locationY = -xypoint[1];//因為不知道為什麼y的相限剛好是相反的

          }


          double norm (double x,double y) // get the norm of a vector
          {
              return (float) Math.pow(Math.pow(x,2)+Math.pow(y,2),0.5);
          }
         
          private double[] getMeetingPoints(double x1, double y1, double x2, double y2, double x3, double y3, double r1, double r2, double r3)
          {

                  double p2p1Distance = (Math.pow(Math.pow(x2-x1,2) + Math.pow(y2-y1,2),0.5));

                 
                  double ex[] = {(x2-x1)/p2p1Distance, (y2-y1)/p2p1Distance};

                  double aux[] = {x3-x1,y3-y1}; //correct
             
              //signed magnitude of the x component
                  double i = ex[0] * aux[0] + ex[1] * aux[1]; //correct

               
                  double aux2[] = { x3-x1-i*ex[0], y3-y1-i*ex[1]};//跑了一點
                 
                    DecimalFormat df = new DecimalFormat("##.000");//四捨五入小數點第三位
                    double d1 = Double.parseDouble(df.format(aux2[0]));
                    double d2 = Double.parseDouble(df.format(aux2[1]));
                   
                        double ey[] = { d1 / norm (d1,d2), d2 / norm (d1,d2) };
                    DecimalFormat df1 = new DecimalFormat("##.000");//四捨五入小數點第三位
                    double d11 = Double.parseDouble(df1.format(ey[0]));
                    double d21 = Double.parseDouble(df1.format(ey[1]));
                         
                    double j = d11 * d1 + d21 * d2;
                    DecimalFormat jj = new DecimalFormat("##.000");//四捨五入小數點第三位
                    double jj1 = Double.parseDouble(jj.format(j));

              //coordinates
                  //double x = ((Math.pow(r1,2) - Math.pow(r2,2) + Math.pow(p2p1Distance,2))/ (2 * p2p1Distance));
              //double y = ((Math.pow(r1,2) - Math.pow(r3,2) + Math.pow(i,2) + Math.pow(j,2))/(2*j) - i*x/j);
                  double x = ((Math.pow(r1,2) - Math.pow(r2,2) + Math.pow(p2p1Distance,2))/ (2 * p2p1Distance));
                  DecimalFormat xx = new DecimalFormat("##.000");//四捨五入小數點第三位
                  double xx1 = Double.parseDouble(xx.format(x));
                 
                 
                  double y = ((Math.pow(r1,2) - Math.pow(r3,2) + Math.pow(i,2) + Math.pow(jj1,2))/((2*jj1) - (i*x/jj1)));
                  DecimalFormat yy = new DecimalFormat("##.000");//四捨五入小數點第三位
                  double yy1 = Double.parseDouble(yy.format(y));
                 


                  //result coordinates
                  double finalXY[] = {x1+ x*ex[0] + y*d11, y1+ x*ex[1] + y*d21};
                 
                  DecimalFormat xy = new DecimalFormat("##.000");//四捨五入小數點第三位
                  double xy1 = Double.parseDouble(xy.format(finalXY[0]));
                  double xy2 = Double.parseDouble(xy.format(finalXY[1]));
                 
                  double finalXY_return[]={xy1,xy2};
                 
              return finalXY_return;

          }
}

layout的部份如下
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res/com.csform.android.uiapptemplate"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_gravity="center"
    android:gravity="center" >
   
    <SurfaceView
        android:id="@+id/surfaceView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true" />

</RelativeLayout>

本體:有沒有高手可以跳出來解釋一下2D3D室內定位的方法?
忘了之前是在哪裡看到的方法,接下來DVT Stage還要修正,才能讓室內定位更精確

FB設定搶先看的方式

設定搶先看的兩種方式 A1. 先到我家的日常粉絲團按下 …( 紅框處 ) A2. 按下追蹤中 ( 紅框處 ) A3. 按下搶先看 ( 紅框處 ) A4. 完成!!! 另一種方式 ...