2015年2月7日 星期六

Arduino動手做 - 使用PocketGeiger偵測游離輻射劑量

核能議題在台灣一直是個爭論不休的話題
不過今天不聊核能爭議,來想想怎麼利用arduino偵測核輻射



傳統上偵測輻射的方法最常見就是所謂的蓋格計數器(Geiger counter)
基本的原理就是使用一根內涵非常稀薄惰性氣體,接近真空的管子
同時內有施予一定電壓的電極
當高能粒子也就是游離輻射穿過時就會激發內部的氣體放電而導通迴路
此時就計數(count)+1
以此計算每分鐘有多少高能粒子通過就成了常見的輻射劑量單位CPM(counts per minutes)

而因為每種高能粒子的特性以及人體暴露輻射源的位置不同等因素
再經由一連串的計算就變成了更具有健康防護參考價值的劑量單位: 西弗 (sv)


所以從今天介紹的偵測器命名來看不難看出其用意
但是從上圖不難發現,這個偵測器看起來......說好的管子勒?

這就要講到人類半導體科技發展的偉大
PocketGeiger使用的偵測元件為光電二極體(PIN photodiode)

其實原理就和太陽能板很像,藉由光電效應來偵測高能粒子打在感測板上的訊號同樣算出CPM
優點是比傳統偵測器成本低廉很多
缺點就是靈敏度較差且容易受到干擾


講這麼多,先來開箱


其實官方賣的type for Embedded (Arduino) Type 5
就等於是Type 4 for ios的無組裝板

只要把這些組裝起來就跟攜帶式的ios版一模一樣還附電池盒呢!

所以先接來ipad上試試啦
下載好app把PocketGeiger接上耳機孔,設定選擇type 4
設定完就會開始量測,一但有偵測到高能粒子出現就開始計算CPM並換算出毫西弗


請原諒我很偷懶的沒有蓋上銅板以隔絕beta粒子的干擾

說到這裡,必須解釋一下

因為PocketGeiger的設計原意是要偵測核災的輻射
所以感測器必須要用鋁箔,銅板來隔絕alpha, beta粒子的干擾
所以是著重於gamma射線的偵測


經過五分鐘後就算是測量完畢






不過其實我們的重點是要搭配arduino來偵測XD

事不宜遲就挽起袖子開工啦

官方的datasheet截圖:

接線方法簡單得不得了
除了5v, GND之外

只要再將輻射波(SIG)與干擾波(NS)個別接上數位D2和D5就完成啦


程式碼的部分如下

//////////////////////////////////////////////////
// Radiation-Watch.org
// URL http://www.radiation-watch.org/
//////////////////////////////////////////////////

/// Digital I/O PIN Settings ///
int signPin = 2; //Radiation Pulse (Yellow)
int noisePin = 5; //Vibration Noise Pulse (White)
//VCC 5V (Red)
//GND (Blue)
////////////////////////////////

const double alpha=53.032; // cpm = uSv x alpha

int index=0; //Number of loops
char msg[256]=""; //Message buffer for serial output

int signCount=0;  //Counter for Radiation Pulse
int noiseCount=0;  //Counter for Noise Pulse

int sON=0;//Lock flag for Radiation Pulse
int nON=0;//Lock flag for Noise Puls

double cpm = 0; //Count rate [cpm] of current
double cpmHistory[200]; //History of count rates
int cpmIndex=0;//Position of current count rate on cpmHistory[]
int cpmIndexPrev=0;//Flag to prevent duplicative counting

//Timing Settings for Loop Interval
int prevTime=0;
int currTime=0; 

int totalSec=0; //Elapsed time of measurement [sec]
int totalHour=0; //Elapsed time of measurement [hour]

//Time settings for CPM calcuaration
int cpmTimeMSec=0;
int cpmTimeSec=0;
int cpmTimeMin=0;

//String buffers of float values for serial output
char cpmBuff[20];
char uSvBuff[20];
char uSvdBuff[20];

void setup()
{
  //Serial setup
  //9600bps
  Serial.begin(9600);
  
  //PIN setting for Radiation Pulse
  pinMode(signPin,INPUT);
  digitalWrite(signPin,HIGH);

  //PIN setting for Noise Pulse
  pinMode(noisePin,INPUT);
  digitalWrite(noisePin,HIGH);

  //CSV-formatting for serial output (substitute , for _)
  Serial.println("hour[h]_sec[s]_count_cpm_uSv/h_uSv/hError");
  
  //Initialize cpmHistory[]
  for(int i=0; i<200;i++ )
  {
    cpmHistory[i]=0;
  }
  
  //Get start time of a loop
  prevTime = millis();
}

void loop()
{
  // Raw data of Radiation Pulse: Not-detected -> High, Detected -> Low
  int sign = digitalRead(signPin);

  // Raw data of Noise Pulse: Not-detected -> Low, Detected -> High
  int noise = digitalRead(noisePin);

  //Radiation Pulse normally keeps low for about 100[usec]
  if(sign==0 && sON==0)
  {//Deactivate Radiation Pulse counting for a while
    sON = 1;
    signCount++;
  }else if(sign==1 && sON==1){
    sON = 0;
  }

  //Noise Pulse normally keeps high for about 100[usec]
  if(noise==1 && nON==0)
  {//Deactivate Noise Pulse counting for a while
    nON = 1;
    noiseCount++;
  }else if(noise==0 && nON==1){
    nON = 0;
  }

  //Output readings to serial port, after 10000 loops
  if(index==10000) //About 160-170 msec in Arduino Nano(ATmega328)
  {
    //Get current time
    currTime = millis();
    
    //No noise detected in 10000 loops
    if(noiseCount == 0)
    {
      //Shift an array for counting log for each 6 sec.
      if( totalSec % 6 == 0 && cpmIndexPrev != totalSec)
      {
        cpmIndexPrev = totalSec;
        cpmIndex++;
        
        if(cpmIndex >= 200)
        {
          cpmIndex = 0;
        }
        
        if(cpmHistory[cpmIndex] > 0)
        {
          cpm -= cpmHistory[cpmIndex];
        }
        cpmHistory[cpmIndex]=0;
      }
      
      //Store count log
      cpmHistory[cpmIndex] += signCount;
      //Add number of counts
      cpm += signCount;
      
      //Get ready time for 10000 loops
      cpmTimeMSec += abs(currTime - prevTime);
      //Transform from msec. to sec. (to prevent overflow)
      if(cpmTimeMSec >= 1000)
      {
        cpmTimeMSec -= 1000;
        //Add measurement time to calcurate cpm readings (max=20min.)
        if( cpmTimeSec >= 20*60 )
        {
          cpmTimeSec = 20*60;
        }else{
          cpmTimeSec++;
        }
        
        //Total measurement time
        totalSec++;
        //Transform from sec. to hour. (to prevent overflow)
        if(totalSec >= 3600)
        {
          totalSec -= 3600;
          totalHour++;
        }
      }
      
      //Elapsed time of measurement (max=20min.)
      double min = cpmTimeSec / 60.0;
      if(min!=0)
      {
        //Calculate cpm, uSv/h and error of uSv/h
        dtostrf(cpm / min, -1, 3, cpmBuff);
        dtostrf(cpm / min / alpha, -1, 3, uSvBuff);
        dtostrf(sqrt(cpm) / min / alpha, -1, 3, uSvdBuff);
      }else{
        //Devision by zero
        dtostrf(0, -1, 3, cpmBuff);
        dtostrf(0, -1, 3, uSvBuff);
        dtostrf(0, -1, 3, uSvdBuff);
      }
        
      //Create message for serial port
      sprintf(msg, "%d,%d.%03d,%d,%s,%s,%s",
      totalHour,totalSec,
      cpmTimeMSec,
      signCount,
      cpmBuff,
      uSvBuff,
      uSvdBuff
        );
        
      //Send message to serial port
      Serial.println(msg);
      
      index=0;
    }
    
    //Initialization for next 10000 loops
    prevTime = currTime;
    signCount=0;
    noiseCount=0;
  }
  index++;
}


傳至arduino後打開serail應該就能看到如下畫面

可以看到在3.948秒的時候偵測到了一個有效訊號
而得以換算出當下的CPM以及毫西弗/小時


需要注意的是這個感測器對微震相當敏感
最好放在絕對沒有震動疑慮的平台上偵測 (不能放在地上)


最後不得不說,全世界核輻射偵測相關資料就真的是日本最多
沒實際遇過的災難,大部分人是不會有所警惕的

PocketGeiger官網
http://www.radiation-watch.org/

1 則留言: