2021年4月19日 星期一

Raspberry Pi Sky Camera

 


總之來記錄一下我的戶外天空相機。

我一直很想要做的是一個能夠照天空的縮時攝影機,不過最大的問題一直都在怎麼做到防水,卡在我的機械結構嚴重欠缺正常的知識。所以當初第一版長的是這樣:

買了一個盜版但是有WiFi的運動攝影機SJ4000再加上防水盒,然後改裝加上降壓電路直接從5V降成鋰電池3.8V,電池空出來的地方再放一個MCU讓開機的時候直接打開WiFi。
沒錯,處理防水跟改電路我選後者。SJ4000的WiFi連上RPI,然後RPI控制照相跟抓檔案/刪檔案,只是問題在於這台的韌體非常不穩定,長期用下來他的WebServer會有機率的死當,再加上照出來效果真的還好,就沒繼續弄了。

基本上就是一個給RPI Camera V2用的防水盒
原廠也有出一個改裝魚眼的套件,只是我沒買。主要是因為買的到IMX219的M12可替換鏡頭模組,可以直接裝上RPI Camera V2的電路板上 (如上圖),所以只要找的到合適的M12鏡頭就可以用了。但是我當時沒想到1/4"的魚眼鏡頭有那麼難找,上圖的設計是給1/2",鏡頭大到裝不下不說,照出來用1/4"的也沒有到全景,只好退回原本的廣角鏡頭。


然後又因為照天空很無聊所以改照101,角度調完之後就丟出去了,排線想說是是看能撐多久所以沒做甚麼特殊的保護。

不過照片當然沒有那麼簡單,魚眼鏡頭很容易造成鏡頭色散,嚴重的話就需要手動去校正,什麼叫做色散? 讓我用這張照片解釋:
校正前
校正後



可以看到圖有很明顯的中心偏綠,外側偏紅的情形,這是因為這個色散超出了RPI ISP的校正範圍,所以這時候就需要自己生出校正的矩陣:
1.首先要來照一張全白的畫面:
2.用https://github.com/6by9/lens_shading 這個來生出ls_table.h
再重新compile raspistill之後就好了

校正完畢之後照出來效果應該就會好很多,接下來要做的就是考慮日夜變化。
因為晚上的時候我希望曝光時間可以拉到10s (Raspberry Pi Camera V2的上限),白天我希望能夠照幾張不同的曝光來合成HDR,所以這裡我先用了這個: https://github.com/risacher/sunwait
來判斷現在的Strategy是哪個,我設定的是太陽落到-7度之後變成晚上的照相模式。

之後照相把raw檔也記錄下來,但是之後處裡的時候把jpg和raw切開,這樣如果我之後懶得碰raw,至少我可以只下載jpg的部分。
head -c -10237440 /home/pi/photo/ramdisk/$FILEDATE.jpgraw > /home/pi/photo/ramdisk/$FILEDATE.jpg
大概長這樣,順帶一提我開了一個小的ramdisk來放暫存檔案,最後資料會直接傳到現在已經消失的無上限教育版Google Drive。

從去年回美國之前架設好到今年二月初掛掉,這是今年二月的照片:
排線斷得很慘,而且還進了一點水,所以是時候來升級這台,並且把之前的資料來整理一下 (加上Google把無上限空間收回,我也得要把資料重新載下來)。

因為半年的資料不少,為了我的硬碟著想(timelapse大概有8T),我沒有用raw檔來做處理,還是直接抓jpg檔案(1.8T)。用PIL寫上照片的時間,然後處理HDR,只是可能因為我只用了jpg來合成,HDR的效果其實還好:

當然也有可能是因為白天的曝光Strategy的級距太小,只有ev-3,0,+3這三個級距。

這時候剛好看到一個合成完畢的Frame長這樣:
還滿好看的,雖然我不知道為什麼這原始檔案就有那個奇怪的亮度階梯在那邊

最後附上兩種縮時,一個是有HDR另一個沒有:


如果我把同個時間點但是不同天的照片抓出來排縮時:


你可以很明顯地看到背景的星星,金星,月亮移動方式都不一樣


大概是這樣,希望接下來的版本換掉排線之後可以用久一點。

需要改進的大概就是白天曝光的級距加大,晚上的要縮小,raw檔案可能要先壓縮再來上傳,以及要處理raw轉dng的相機校正檔案。還有日落跟日出可能要有個階梯式的Strategy調整方式,尤其是固定awb。希望我有空來修改raspistill直接把不同的曝光frame抓出來。

2021年2月15日 星期一

BLE Dashboard - LS060S2UD01 Sharp Memory LCD

 
有次網路逛街看到了好便宜的6" Sharp Memory LCD,手癢就買來玩玩看。因為這次螢幕解析度和大小都滿大的,所以想說來做兩個東西: 顯示天氣圖還有如上圖的PC儀表板。

首先要決定的是要用哪個MCU跟架構,因為Memory LCD本來就低耗電,設計目標自然是帶電池的無線螢幕,所以挑選的項目自然只剩下有無線功能的MCU。

WiFi or BLE? 老實說我想了一陣子,因為800x600資料不少,我原本是很想要用WiFi因為資料下載比較快,但是ESP32的耗電量控制真的慘,不適合這種一直更新的用途,所以還是用BLE。

先來講這片螢幕 LS060S2UD01: 

6" 800x600的解析度,而和之前的Memory LCD差異最大的在於每個pixel是兩個單色sub-pixel組成,所以每個pixel實際上能組成四種黑/白色,資料可以寫4bit的資料,然後控制兩個單色的sub-pixel你就有4色,也可以讓螢幕幫你做dithering擴展成16色階,不過效果在複雜的圖文底下不佳(aka天氣圖)。

這是單色我老婆

這是16色階dithering的我老婆,其實效果還不錯

這是16色階dithering的天氣圖,文字和線條就很慘了

控制介面則是parallel bus,有16bit和8bit兩種寬度,資料刷新的步驟基本上是:

資料傳到螢幕的Frame Buffer => 讓Frame Buffer寫資料進到Panel => 等資料寫完

其中寫資料可以指定寫入部分的螢幕(類似E-ink的partial update),還可以從frame buffer把資料讀出來(為了支援小容量記憶體的MCU我猜)。然後把資料從frame buffer轉移完之後透過ACK pin通知MCU寫入完畢。另外也可以開Video mode讓LCD自己用30Hz的速度自動轉移Frame buffer到Panel,這時候SYNC pin會有Frame Sync通知MCU。

我為了方便,這邊用的是8bit bus + 單色 + Full Update + 自己下指令轉移buffer。可能之後換成4色。因為800x600單色已經是56K的資料量了,開成4bit=>224K,有點肥。而資料寫入的Bus則是透過把parallel bus安排到GPIO 0 ~ 7 + 直接register set/clr,速度可以拉到10Hz。


我的Driver基本上是為了這個MCU設計的,所以僅供參考: https://github.com/will127534/Adafruit_SHARP_Memory_Display

接下來就是MCU,基本上nRF系列也不錯,但是我這次想來試試 SparkFun Artemis Module,因為這個Module用的不是常見的BLE SoC,用的是Apollo3b from Ambiq,他們家的MCU非常暴力省電。大學修電子學的時候略有耳聞有些IC工作點是在課本沒有教的sub-threshold region,因為不是在飽和區,工作的電流可以壓到非常低,通常用在手錶這種非常低功耗的IC上面。
而Ambiq把它用在MCU上,整個模組我的測試功能全開的狀況下Active power只有1mA,非常狂。

不過缺點也很明顯,因為用的人少,軟體的Support其實不怎麼樣,Sparkfun有Arduino Support,但是它是基於Mbed OS,等於是他們包了Mbed OS bridge + Mbed OS去Support Arduino。老實說如果只有這樣的話那就算了,反正我可以直接寫MCU register,但是好死不死他們包的Mbed OS沒有Sleep Mode,也就是說模組會一直耗掉1mA,這我真的不能忍。只好改用Mbed OS來寫,結果發現把Sleep打開會一睡不醒...... 為了能讓我用BLE+Sleep,只好切回去原廠的SDK。
可是原廠的SDK BLE超難用,最後只好開一個BLE example然後硬改成我要的。附帶一提BLE功率不高。

模組的pinout也非常的糟糕,即便是parallel bus特別安排到GPIO0~7,實際上模組腳位可能東一個西一個,搞得Layout上複雜了不少。

模組附近Layout的近照,這三小pinout

原本打算用兩個CR2032拚幾個月,結果因為螢幕本身就要吃掉500uA了其實做不到,所以還是回頭用Li-ion。電路板其他的基本上就是UART隔離+Sparkfun Bootloader的電路,還有鋰電池充電+降壓模組,最後就是幾個電流的測試點。降壓模組我這邊用的是TPS82740A,看了很久想說來試試看,這個在低耗電量(數百uA)的範圍轉換效率真的很高,Iq也耗很少電,BGA封裝但是很好Layout和焊接,最高頂多輸出200mA但是這種用途非常足夠了。
鋰電池電壓監測照慣例用隔離電路,平常斷開分壓電阻,只有需要的時候才打開一下。

EN pin低到高會打開這個MOS 5ms,這時候Sample VBAT。

Apollo3b 其實內建了類似的電路,而且還有一個500歐姆的load resistor,透過比較有load/無load的電壓去計算電池內阻來判定電池容量,滿狂的。只可惜螢幕只能跑3.3V,我也不想另外放電平轉換電路在8bit匯流排上面。

接下來就是畫圖軟體了,先來介紹第一種用途:畫天氣圖
天氣圖基本上就是RPI資料載下來-->轉檔-->BLE傳Frame Buffer到板子上。
剛剛提到一張Frame也將近56K,所以這裡勢必是需要一點壓縮,原本找了老半天看了老半天的黑白影像壓縮,看來大家都很愛RLE(running length encoding)就跟著.....沒有,用RLP壓完變得更肥。最後覺得乾脆gzip算了結果壓縮比還不錯,上面這張圖壓完只剩下21Kb。
MCU上面的解壓縮用的是這個Library: https://github.com/pfalcon/uzlib
解壓縮基本上照著example就好,只是我開了32K給他放dictionary: 
    uzlib_uncompress_init(&d,(void*)dict, 32768);

解壓縮直接解到Frame buffer然後寫進去Flash,因為天氣圖有未來兩天半的資料一共七張圖,所以我直接把Flash後面的512K都拿來寫,每次傳之前就把這512K一次清掉。

資料抓NOAA Short Range Forecasts: https://www.wpc.ncep.noaa.gov/basicwx/basicwx_ndfd.php
透過Python把時間點和圖片抓下來,另外多虧這個圖片是256色的gif,透過替換顏色的方式把地圖,等壓線,文字,鋒面給擷取出來。

這邊算是貼給我自己看得,這段Python包含爬蟲,下載,畫圖,壓縮,上傳BLE
from bluepy import btle
from bluepy.btle import Scanner, DefaultDelegate
import os
import numpy
from PIL import Image
import PIL.ImageOps as ImageOps
import binascii
from io import BytesIO
import gzip
import time
import requests
from PIL import ImageDraw
from PIL import ImageFont
import requests
from pyquery import PyQuery

os.system('rm *.gif')

class ScanDelegate(DefaultDelegate):
    def __init__(self):
        DefaultDelegate.__init__(self)

scanner = Scanner().withDelegate(ScanDelegate())
devices = scanner.scan(8.0)

nameToMacAddress = dict()
for dev in devices:
    #print("Device %s (%s), RSSI=%d dB" % (dev.addr, dev.addrType, dev.rssi))
    for (adtype, desc, value) in dev.getScanData():
        #print("  %s = %s" % (desc, value))
        if desc == "Complete Local Name":
            nameToMacAddress[value] = dev.addr;

print("Connecting...")

dev = btle.Peripheral(nameToMacAddress["SharpMeMoryLC"])
time.sleep(1)
dev.setMTU(2049)
lightSensor = btle.UUID("00002760-08c2-11e1-9073-0e8ac72e1011")
DataService = dev.getServiceByUUID(lightSensor)
uuidConfig = btle.UUID("00002760-08c2-11e1-9073-0e8ac72e0011")
BLE_data = DataService.getCharacteristics(uuidConfig)[0]

def binarize_array(numpy_array):
    RED = numpy.array([255, 0, 0])
    BLUE = numpy.array([0, 0, 255])
    BLACK = numpy.array([0, 0, 0])
    GRAY = numpy.array([220, 220, 220])
    RED2 = numpy.array([238, 44, 44])
    PURPLE = numpy.array([145, 44, 238])
    WHITE = numpy.array([255, 255, 255])
    red, green, blue = numpy_array.T
    redAreas = (red == 255) & (blue == 0) & (green == 0)
    blueAreas = (red == 0) & (blue == 255) & (green == 0)
    blackAreas = (red == 0) & (blue == 0) & (green == 0)
    grayreas = (red == blue) & (blue == green) & (green<255)
    red2Areas = (red == 238) & (blue == 44) & (green == 44)
    purpleAreas = (red == 145) & (blue == 44) & (green == 238)
    output = numpy.full(numpy_array.shape,255, dtype=numpy.uint8)
    output[redAreas.T] = (0, 0, 0)
    output[blueAreas.T] = (0, 0, 0)
    output[blackAreas.T] = (0, 0, 0)
    output[grayreas.T] = (0, 0, 0)
    output[red2Areas.T] = (0, 0, 0)
    output[purpleAreas.T] = (0, 0, 0)
    return output

def round_corner(radius):
    """Draw a round corner"""
    corner = Image.new('1', (radius, radius), (1))
    draw = ImageDraw.Draw(corner)
    draw.arc((0, 0, radius * 2, radius * 2), 180, 270, fill=(0),width=3)
    return corner

def round_rectangle(size, radius,timeText):
    """Draw a rounded rectangle"""
    width, height = size
    rectangle = Image.new('1', size, (1))
    corner = round_corner(radius)
    rectangle.paste(corner, (0, 0))
    rectangle.paste(corner.rotate(90), (0, height - radius - 3))  # Rotate the corner and paste it
    rectangle.paste(corner.rotate(180), (width - radius -3, height - radius -3))
    rectangle.paste(corner.rotate(270), (width - radius-3, 0))
    ImageDraw.Draw(rectangle).line((radius,3, width - radius, 3), fill=(0),width=3)
    ImageDraw.Draw(rectangle).line((radius,height - 3, width - radius, height - 3), fill=(0),width=3)
    ImageDraw.Draw(rectangle).line((3,radius, 3, height - radius), fill=(0),width=3)
    ImageDraw.Draw(rectangle).line((width - 3,radius, width - 3, height - radius), fill=(0),width=3)
    fnt = ImageFont.truetype('./FreeMonoBold.ttf', 22)
    x,y = ImageDraw.Draw(rectangle).textsize(timeText,font=fnt)
    ImageDraw.Draw(rectangle).text((int((width-x)/2), int((height-y)/2)),timeText,(0),font=fnt)
    return rectangle


baseURL = 'https://www.wpc.ncep.noaa.gov'
r = requests.get('https://www.wpc.ncep.noaa.gov/basicwx/basicwx_ndfd.php')
pq = PyQuery(r.text)
periodList = ['period1','period2','period3','period4','period5','period6','period7']
filelist = []
timeList = []
for period in periodList:
    tag = pq('div#'+period+' img')
    if len(tag) == 0:
        continue
    fileName = tag.attr['src'].split('/')[2]
    timeName = tag.attr['alt'].split(' ')[-1] + " " + tag.attr['alt'].split(' ')[-2][0:3]
    print('FileName: %s, Time: %s' % (fileName,timeName))
    os.system('wget ' + baseURL + tag.attr['src'])
    filelist.append(fileName)
    timeList.append(timeName)

count = 0
for filename in filelist:
    print(filename)
    image = Image.open(filename)
    #label = image.crop((393, 659, 393+250, 712))
    label = image.crop((437, 684, 437+55, 684+12))
    #print(pytesseract.image_to_string(label, lang='eng'))
    #print(pytesseract.image_to_data(label))
    label = label.resize((100,21))
    label = label.convert('L')
    label = label.convert('1')
    image = image.resize((800,558))
    image = image.convert('RGB')
    pix = numpy.array(image)
    pix = binarize_array(pix)
    image = Image.fromarray(pix)
    image = image.convert('L')
    image = image.convert('1')
    image2 = Image.new('1',(800,600),(1))
    image2.paste(image, (0, 41))
    rect = round_rectangle((110,42),10,timeList[count])
    image2.paste(rect, ((800/len(filelist))*count, 0))
    #image2.paste(label,(110*count + 5, 10))
    count = count + 1
    image2.save(filename + '.bmp')
    pix = numpy.array(image2)
    bitArray = bytearray()
    for x in pix:
        row_int = numpy.packbits(numpy.uint8(x))
        for y in row_int:
            bitArray.append(y)
            pass
    out = open(filename+'.gz','wb')#BytesIO()
    with gzip.GzipFile(fileobj=out, mode="wb") as f:
      f.write(bitArray)
    out.close()

BLE_data.write(b'\xFE')

for filename in filelist:
    print("Sending %s" % filename)
    with open(filename + ".gz", "rb") as f:
        byte = f.read(240)
        print(len(byte))
        while byte != b"":
            BLE_data.write(len(byte).to_bytes(1,'big')+byte)
            byte = f.read(240)
            #time.sleep(0.1)
        BLE_data.write(b'\x00')
    time.sleep(10)



以上就是畫天氣圖的部分,接下來就是Dashboard:

畫圖基本上還是Adafruit GFX,只是因為SDK是純C所以改了一下把原本的C++變成C。
而Dashboard的Prototype是靠AIDA64大致上抓一下感覺。
不過最難的在於自己寫這一個widget:


大概噴了兩個多小時寫了這個簡單的widget,因為Arc不好搞。
struct arc_struct {
    char plotName[15];
    float value;

    char unit[2];

    int innerR;
    int outerR;

    float max;
    float min;

    float angle;

    int origin_x;
    int origin_y;

};  
//https://stackoverflow.com/questions/8887686/arc-subdivision-algorithm/8889666#8889666
void f(float cx, float cy, float px, float py, float theta, int N)
{
    float dx = px - cx;
    float dy = py - cy;
    float r2 = dx * dx + dy * dy;
    float r = sqrt(r2);
    float ctheta = cos(theta/(N-1));
    float stheta = sin(theta/(N-1));
    //std::cout << cx + dx << "," << cy + dy << std::endl;
    for(int i = 1; i != N; ++i)
    {
        float dxtemp = ctheta * dx - stheta * dy;
        dy = stheta * dx + ctheta * dy;
        dx = dxtemp;
        writePixel(cx + dx, cy + dy, BLACK);
        //std::cout << cx + dx << "," << cy + dy << std::endl;
    }
}
void plotArc(struct arc_struct *plot){

    plot->angle = plot->value / (plot->max - plot->min) * 360;

    for(float x=plot->innerR;x<plot->outerR;x+=0.5){
        f(plot->origin_x, plot->origin_y, plot->origin_x, plot->origin_y-x, plot->angle/180*3.14,350);
    }
    drawCircle(plot->origin_x, plot->origin_y,plot->innerR-2,BLACK);
    drawCircle(plot->origin_x, plot->origin_y,plot->outerR+1,BLACK);
    setTextColor(BLACK);
    setTextSize(3);
    char buf[20]={0};
    int n = sprintf(buf,"%.0f%s",plot->value,plot->unit);
    int16_t x1,y1,w,h;
    getTextBounds(buf,0,0,&x1, &y1,&w,&h);
    setCursor(plot->origin_x-w/2,plot->origin_y-h/2);
    for(int i=0;i<n;i++){
        LCDwrite(buf[i]);
    }

    setTextSize(2);
    n = sprintf(buf,"%s",plot->plotName);
    getTextBounds(buf,0,0,&x1, &y1,&w,&h);
    setCursor(plot->origin_x-w/2,plot->origin_y+plot->outerR+18-h/2);
    for(int i=0;i<n;i++){
        LCDwrite(buf[i]);
    }

}

其實效率非常差,我的畫法是一堆Arc填滿,但是應該要用Boundary + Fill algorithm,之後再來改進吧。其他Widget就還好了,都還算簡單,Code滿亂的改天整理完再Po。

耗電量這邊也可以看到畫圖花了非常久,大概400ms才畫完。
平均耗電量約莫1.3mA,不低但是搭配2500mAh的電池也是有兩個多月的使用時間。

資料靠HWiNFO + Python Script,HWiNFO 可以把資料導向一個UDP port,然後Python去收。
這邊有人寫好了一個範例: https://medium.com/swlh/reverse-engineering-a-tcp-protocol-455d248d68fa,我只是拿來改

只是BLE連線我目前暫時是用RPI Zero 來處理,然後透過USB Ethernet device直接連到我的PC。

大概就是這樣,順帶一提買了一台新的3DP MK3S+真的好好用,按鍵一按下去電腦的3D模型就生出來了呢,這次的外殼就是新3D印表機弄的,連螺絲都是組裝剩下的備料XD

2021年1月1日 星期五

Solar Eclipse 2020

 


2020年算是我個人變動比較大的一年,去年出國念書今年畢業在國外上工,經歷了Remote Learning的下學期,沒有畢業典禮+計畫被打亂的畢業,在空蕩蕩的機場搭飛機回台灣隔離14天,然後又飛回去搬家準備上工。一直沒什麼時間寫下這篇記錄,趕在2020年要過了之前寫完這篇。

這篇算是在防疫旅館做完的Project,目標是做出一個自動追蹤太陽的照相機來照日環蝕。通常要追蹤星體都是用赤道儀,透過跟地球自轉同步的旋轉軸達到跟星體同步,但是沒有用赤道儀有兩個原因:

  1. 一個是赤道儀貴得要死......貴到不好意思跟老師借來玩
  2. 第二個是由於地球自轉軸是傾斜的,赤道儀使用之前需要手動對齊北極星+周圍幾個參考星體(因為北極星不是正北),我不確定白天是要怎麼對齊

所以就變這樣了:


CCD(CMOS? lol)是Raspberry Pi HQ Camera,雖然比不上單眼但是很夠用了,加上因為底片大小比較小(1/2.3)所以長焦的鏡頭也比較小,勉強可以用便宜的兩軸伺服器馬達來移動。
長焦鏡頭是Canon FD 100-200 MM f5.6 Lens,RPI HQ Camera一個特色是因為是C/CS mount,所以可以轉接不少老/新鏡頭,我翻了一下Ebay找Cannon FD的長焦就這個好像還可以,買起來才13USD。

好,所以到此照相/機械架構有了,接下來就軟體的架構: 目標是做出一個能夠收集完整解析度之下最高FPS最原始的資料+自動追蹤的架構。

RPI相機資料有四種輸出: 1. MJEPG Encoder 2.H.264 Encoder 3. Raw 4.YUV
大致上整體影像的Pipe Line如下: RAW----->Debayer---->MJEPG or H.264 Encoder
四種輸出就是看你從哪一個Process接出來用

其中H.264有FHD解析度限制,所以只剩下1和3,MJEPG的問題是RPI的Video Encoder不知道為啥Quality調得很低,壓出來的畫質慘不忍睹,第二個問題是MJEPG Encoder雖然沒有解析度限制,但是也是有總pixel的處理限制,雖然解析度不至於頂到但是假如我開VNC調用MJEPG的時候就會撞到。於是只剩下RAW和YUV,RAW檔案的問題是因為是相機底層的資料,我需要先生出相機校正的參數,包含鏡頭變形和色散等等的,老實說我在旅館做不來,加上資料量大小真的恐怖,所以最後試了一下還是用YUV資料。

但是YUV資料量也很恐怖,假設以頂到RPI HQ Camera的CSI-2頻寬上限來算,10 FPS x 4056 x 3040 x 2 = 30Mb/s,要記錄全程大概數小時的資料至少就要數百GB,所以勢必是需要有壓縮的方式,加上太陽濾鏡之後的資料其實就一個大圓以外都黑的,簡單的JPEG就很能壓縮。

我試了在RPI上面跑FFMPEG的結論就是RPI4做不來,那只剩下一個方式:把資料Dump到我的筆電處裡,透過1G的有線網路的話至少可以跑到5FPS,於是就用了raspividyuv+FFMPEG在我的電腦收RPI Dump的資料。

這邊還有一個需要留意的,RPI的ISP生出Frame的時候會帶有timestamp,通常是為了送到Encoder做時間軸用而raspividyuv不會用到,但是因為日蝕是有精確的時間點的,我需要知道每個Frame的時間才好處理,所以我稍微修改了一下raspividyuv把pts紀錄到另外一個檔案裡,事後再把Frame+PTS一同處理。

插曲就是2.5G有線網路其實差不多要開始流行了,我看到2.5G的USB Dongle價錢開始往下壓就買了兩隻想說這下我終於可以把1G的瓶頸暴力的拉到2.5(幾乎是RPI HQ Cam的CSI-2頻寬),結果就是預設的Linux Driver跑不到2.5,很微妙的在1.2左右,原廠的Driver不知道為啥也安裝不了但是還是比1G好一點就硬著頭皮用了

ISP除了Debayer以外還有其他2A的功能,包含Auto Brightness + AWB,Auto Focus因為RPI Camera沒有調焦的功能,所以只能輸出Focus的數字,所以在Setup的時候是先跑輸出Focus找到對焦點之後再開始資料採集。而由於日蝕的亮度變化,所以Auto Brightness有開,而AWB則是固定,我的Cmd如下:
raspividyuv -t 0 -fps 15 --mode 3 -w 4056 -h 3040 -ss 10000 -ISO 100 -ev -10 -mm average -ex spotlight -awb sun -o tcp://169.254.142.200:6667 -pts $DATE_STRING.txt -v
./ffmpeg.exe -f rawvideo -pix_fmt yuv420p -s 4064x3040 -probesize 89M -r 10 -i tcp://0.0.0.0:6667?listen -q:v 5 ".\$((Get-Date).ToString('yyyy-MM-dd_hh-mm-ss'))\img-%08d.jpg" -vf scale=800:600,fps=1 -q:v 2 -f image2 -update 1 udp://169.254.35.220:13991
加上我懶,所以Setup Stream是用power shell script自動跑這兩隻程式
*Note: YUV輸出的時候會自動Padding所以FFMPEG收的時候是4064x3040 

以上是資料採集,接下來是自動追蹤
我其實以前也做過太陽的追蹤,2017美西有一次Solar Eclipse,雖然我人在加州只看到日偏食,但是還是有照全程,只是那時候我忘記帶腳架,導致事後資料處裡很麻煩,於是就寫了OpenCV的程式自動擷取太陽出來:
2017年LA日偏食

效果不錯,因為太陽濾鏡之後也只剩下一個大圓球很好追,所以就用了OpenCV來追太陽
如果稍微看ffmpeg的指令的話會發現除了壓縮完影像之後,其實我還Copy了Frame然後把解析度調到800x600後送回RPI4,這個資料就是回去給RPI4上面的OpenCV用的,OpenCV抓到太陽的中心點之後就會判斷需不需要移動(因為伺服器馬達有角解析度的限制),然後送指令給伺服器馬達的控制Server。最後為了我自己控制的方便,挖了XBOX的控制器出來讓我能直接手動找太陽+開/關Auto Control。

當然這還是得要先測試個幾次:
很蠢的測試



以下是這次日蝕的OpenCV的追蹤結果

有雲的時候邊緣比較拿抓閥值,但是可以直接指定圓的大小就還好,重點是中心點

當然還是會有跑掉的時候,所以控制Loop需要有一點De-bounce

因為下一張就回去了




當天跑去故宮南院,還搬了冰桶過去XD



從早上待到結束,Some Result:
這張是NEX-5N照底圖+RPI HQ Camera Overlay上去的,照底圖要在太陽來之前/之後照XD

幾張Key Frame

以上,就是在這2020年顛簸的旅程中的一點紀錄





2019年6月1日 星期六

SI8B Pancake Geiger Counter Tube


速戰速決,總之這是新買的一個蓋格管,從圖來看最大的特色就是這個設計的感應面積非常大,通常暱稱叫做Pancake Tube。多虧這個大片的感應面積,這個蓋格管比之前我用管狀的靈敏很多,這款的背景CPM通常可以到~180

而且最有趣的是因為上蓋是透明的雲母片,所以其實可以看的到輻射打過去所激發的光。


大Guy94這樣,驅動版我懶得做就挖了之前高空氣球的那塊板子來用所以沒有特別的點,電壓我還是調成跟之前一樣,連Bais的電阻都是1M而已,短時間照照相可能還好,長時間拿來做監控可能還是要照一下規格書給4.7M。