植田 研究室

島根大学 先端マテリアル研究開発協創機構

研究を行う際には,適切なツールを使うと,実験やデータ処理を楽に行うことができる. その例として,データ処理のためのプログラム,測定を自動化するための装置制御の方法,デジタル制御をするためのマイコンの使い方を紹介する.

プログラム

実験データの整理などに,プログラムは不可欠である. 世の中には様々なプログラムがあり,それらを用途に応じて使い分ける必要があるが,多くの言語を学習するのは大変である. そこで,私が使っている主な四つの言語について,用途ごとに書き方を比較した表を以下に示す. それぞれプログラムの初歩をかじれば,これらの表から必要な書き方を見つけて,簡単なプログラムを書くことができる.
まず,使い方を紹介する四つのプログラム言語の特徴を簡単に説明する. Rは数値の処理が得意で,ベクトルをデータの基本として使っているため,一度に多くの要素を処理することができ,私は数値の処理やグラフを書く際によく使われている. juliaは数学者が作った言語で,演算にユニコードの記号を使ったり,挙動が引数の種類に依存するような関数を簡単に作ることができるという特徴を持つ. rubyは松江市在住の日本人が作った言語で,オブジェクト指向を骨子としており,自由度が高いために同じことをするのに様々な書き方ができ,コードが比較的短くなることが多い. pythonはインデントでブロックの範囲を表す言語で,誰が書いても似たようなコードになることが多いが,一般には長くなる.

[実行]
R julia ruby python3
コマンドライン Rscript test.R julia test.jl ruby test.rb python3 test.py
対話モード R julia irb python3
終了 q() exit() exit exit()
typeof(1) typeof(1) 1.class type(1)
読み込み source("file.r") include("file.jl") require "./file.rb" import file
代入 a<-1
assign("a",1)
a=1 a=1 a=1
表示 cat(1) print(1) print 1 print(1,end="")
改行付表示 cat(2,"\n",sep="") print(2,"\n") puts 1 print(2)
ヘルプ ?cat ?print help
print
help(print)
実行時間 system.time( sum(1:10000) ) @timed sum(1:10000)

[数]
R julia ruby python3
1+2 1+2 1+2 1+2
1-2 1-2 1-2 1-2
1*2 1*2 1*2 1*2
5/2 5/2
2\5
5.0/2
5.fdiv(2)
5/2
冪乗 2^0.5 2^0.5 2**0.5 2**0.5
絶対値 abs(-1) abs(-1) -1.abs abs(-1)
四捨五入 round(3.14) round(3.14) 3.14.round round(3.14)
切り捨て floor(3.14) floor(3.14) 3.14.floor import math;math.floor(3.14)
切り上げ ceiling(3.14) ceil(3.14) 3.14.ceil import math;math.ceil(3.14)
平方根 sqrt(2) √2 Math.sqrt(2) import math;math.sqrt(2)
立方根 2^(1/3) ∛2 Math.cbrt(2) import numpy;numpy.cbrt(2)
指数関数 exp(2) exp(2) Math.exp(2) import math;math.exp(2)
自然対数 log(2) log(2) Math.log(2) import math;math.log(2)
常用対数 log10(2) log10(2) Math.log10(2) import math;math.log10(2)
三角関数 sin(2)
cos(2)
tan(2)
sin(2)
cos(2)
tan(2)
Math.sin(2)
Math.cos(2)
Math.tan(2)
import math
math.sin(2)
math.cos(2)
math.tan(2)
逆三角関数 asin(1)
acos(1)
atan2(1,0)
asin(1)
acos(1)
atan2(1,0)
Math.asin(1)
Math.acos(1)
Math.atan2(1,0)
import math
math.asin(1)
math.acos(1)
math.atan2(1,0)
双曲線関数 sinh(2)
cosh(2)
tanh(2)
sinh(2)
cosh(2)
tanh(2)
Math.sinh(2)
Math.cosh(2)
Math.tanh(2)
import math
math.sinh(2)
math.cosh(2)
math.tanh(2)
円周率 pi π Math::PI import math;math.pi
Euler数 exp(1) Math::E import math;math.e

[整数]
R julia ruby python3
整数商 5%/%2 5.0÷2.0 5/2
5.0.div(2.0)
5.0//2.0
5%%2 5.0%2.0 5.0%2.0 5.0%2.0
not bitwNot(3) ~3 ~3 ~3
and bitwAnd(3,5) 3&5 3&5 3&5
or bitwOr(3,5) 3|5 3|5 3|5
xor bitwXor(3,5) 3⊻5 3^5 3^5
左シフト bitwShiftL(3,2) 3<<2 3<<2 3<<2
右シフト bitwShiftR(3,1) 3>>1 3>>1 3>>1
分数 library(MASS);fractions(5/2) 5//2 5r/2 import fractions;fractions.Fraction(5,2)

[複素数]
R julia ruby python3
実部 Re(1+2i) real(1+2im) (1+2i).real (1+2j).real
虚部 Im(1+2i) imag(1+2im) (1+2i).imag (1+2j).imag
複素共役 Conj(1+2i) conj(1+2im) (1+2i).conj
(1+2j).conjugate
(1+2j).conjugate()
絶対値 abs(-1i) abs(-1i) -1i.abs abs(-1j)
偏角 Arg(1+2i) angle(1+2im) (1+2i).arg
(1+2i).angle
(1+2i).phase
import cmath;cmath.phase(1+2j)
平方根 sqrt(1+2i) √(1+2im) Math.sqrt(1+2i) import cmath;cmath.sqrt(1+2j)
指数関数
自然対数
三角関数など
exp(1i)
log(1+2i)
sin(1+2i)
exp(1im)
log(1+2im)
sin(1+2im)
require "cmath"
CMath.exp(1i)
CMath.log(1+2i)
CMath.sin(1+2i)
import cmath
cmath.exp(1j)
cmath.log(1+2j)
cmath.sin(1+2j)
冪乗 (1+2i)^0.5 (1+2im)^0.5 (1+2i)**0.5 (1+2j)**0.5
ruby2.7以降でCMathを使うには,gem install cmathとしてインストールする必要があります.

[文字列]
R julia ruby python3
連結 paste("abc","def",sep="") "abc"*"def" "abc"+"def" "abc"+"def"
繰返 paste(rep("abc",3),collapse="") "abc"^3 "abc"*3 "abc"*3
結合 paste(1:5,collapse=", ") join(1:5,", ") [*1..5]*", "
[*1..5].join(",")
", ".join(map(str,range(1,6)))
取出 substr("πℯ^2",2,2) collect("πℯ^2")[2] "πℯ^2"[1] "πℯ^2"[1]
部分 substr("πℯ^2",2,3) String(collect("πℯ^2")[2:3]) "πℯ^2"[1..2]
"πℯ^2"[1,2]
"πℯ^2"[1:3]
文字数 nchar("πℯ^2") length("πℯ^2") "πℯ^2".length
"πℯ^2".size
len("πℯ^2")
大文字 toupper("This") uppercase("This") "This".upcase "This".upper()
小文字 tolower("This") lowercase("This") "This".downcase "This".lower()
入れ替え chartr("A-Za-z","a-zA-Z","This") "This".swapcase "This".swapcase()
埋込 s="world"
"Hello $(s)!"
s="world"
"Hello #{s}!"
s="world"
f"Hello {s}!"
"Hello {}!".format(s)
書式 sprintf("%d %s",3,"times") using Printf;@sprintf("%d %s",3,"times") sprintf("%d %s",3,"times")
"%d %s"%[3,"times"]
"%d %s"%(3,"times")
文字列 as.character(123) string(123) 123.to_s str(123)
整数 as.integer("1") parse(Int,"1") "1".to_i int("1")
浮動小数点 as.numeric("1") parse(Float64,"1") "1".to_f float("1")
複素数 as.complex("1+2i") parse(Complex{Int},"1+2im") "1+2i".to_c complex("1+2j")
コード変換 utf8ToInt("A") Int('A') "A".ord ord("A")
文字変換 intToUtf8(960) Char(960) 960.chr(Encoding::UTF_8) chr(960)
検索 regexpr("[\\d\\.]+","pi=3.14",perl=TRUE) match(r"[\d\.]+","pi=3.14") "pi=3.14"=~/[\d\.]+/ import re;re.search("[\d\.]+","pi=3.14")
置換 sub("(\\d)\\.",".\\1","pi=3.14") replace("pi=3.14", r"(\d)\." => s".\1",count=1) "pi=3.14".sub(/(\d)\./,'.\1') import re;re.sub("(\d)\.",".\\1","pi=3.14",1)
全置換 gsub("(\\d)\\.",".\\1","3.14,2.71") replace("3.14,2.71", r"(\d)\." => s".\1") "3.14,2.71".gsub(/(\d)\./,'.\1') import re;re.sub("(\d)\.",".\\1","3.14,2.71")
分割 strsplit("1,2, 3",",\\s*")[[1]] split("1,2, 3", r",\s*") "1,2, 3".split(/,\s*/) import re;re.split(",\s*","1,2, 3")

[論理演算, フロー制御]
R julia ruby python3
if a<-3
if(a==1){cat(1)
}else if(a==2){cat(2)
}else{cat(3)}
a=3
if a==1 print(1)
elseif a==2 print(2)
else print(3)
end
a=3
if a==1 then p 1
elsif a==2 then p 2
else p 3
end
a=3
if a==1:print(1)
elif a==2:print(2)
else:print(3)
三項演算子 a<-2
cat(ifelse(a>0,"+","-"))
a=2
print(a>0 ? "+" : "-")
a=2
print(a>0?"+":"-")
a=2
print('+' if a>0 else '-')
switch i<-"a"
cat(switch(i,a=1,b=2))
i="a"
p case i
when "a" then 1
when "b" then 2
end
for for(i in 1:10){cat(i);cat("\n")} for i=1:10 println(i) end for i in 1..10 do p i end
(1..10).each{|i| p i}
for i in range(1,11):print(i)
while i<-0
while(i<10){cat(i<-i+1);cat("\n")}
let i=0
while i<10 println(i+=1) end
end
i=0
while i<10 do p i+=1 end
p i-=1 until i==0
p i+=1 while i<10
i=0
while i<10:i+=1;print(i)
無限ループ repeat{cat(1);cat("\n")} while true println(1) end loop{p 1} while True:print(1)
ループを抜ける break break break break
次のループ next continue continue next
例外処理 tryCatch(cat("1"/0),
error=function(e)cat(-1))
try print("1"/0)
catch
print(-1)
end
begin
p "1"/0
rescue
p -1
end
try:print("1"/0)
except:print(-1)
終了処理 tryCatch({cat(1);cat("\n")},
finally=cat(2))
try println(1)
finally
println(2)
end
begin
p 1
ensure p 2
end
try:print(1)
finally:print(2)
複文 a<-{cat(1);2} a=(print(1);2) a=(p 1;2)
juliaとpythonはswitch文に相当する命令が無いので,if文などで書き下す必要があります.

[関数]
R julia ruby python3
定義 f<-function(x){2*x}
f(2)
f(x)=2x
function f(x) 2x end
f(2)
def f(x) 2*x end
f(2)
def f(x):return 2*x
f(2)
省略値 f<-function(x=1){2*x}
f()
f(x=1)=2x
f()
def f(x=1) 2*x end
f
def f(x=1):return 2*x
f()
キーワード引数 f<-function(x,y){x^y}
f(y=1,x=2)
f(;x,y)=x^y
f(y=1,x=2)
def f(x:,y:) x**y end
f(y:1,x:2)
def f(x,y):return x**y
f(y=1,x=2)
可変長引数 f<-function(x,...){x+length(c(...))}
f(3,2,1)
f(x,a...)=x+length(a)
f(3,2,1)
def f(x,*a) x+a.size end
f(3,2,1)
def f(x,*a):return x+len(a)
f(3,2,1)
引数展開 f<-function(x,y){x+y}
a<-list(1,2)
do.call(f,a)
f(x,y)=x+y
a=[1,2]
f(a...)
def f(x,y) x+y end
a=[1,2]
f(*a)
def f(x,y):return x+y
a=[1,2]
f(*a)
無名関数 (function(x){2*x})(2) (x->2x)(2) proc{|x|2*x}.(2) (lambda x:2*x)(2)
配列 f<-function(x){2*x}
f(1:10)
f(x)=2x
f.(1:10)
def f(x) 2*x end
[*1..10].map{|x|f(x)}
def f(x):return 2*x
list(map(f,range(1,11)))
合成 f<-function(x){2*x}
g<-function(x){x+1}
g(f(2))
f(x)=2x
g(x)=x+1
(g∘f)(2)
2|>f|>g
f=proc{|x|2*x}
g=proc{|x|x+1}
(g<<f).(2)
(f>>g).(2)
import toolz
def f(x):return 2*x
def g(x):return x+1
toolz.compose(g,f)(2)
toolz.pipe(2,f, g,)
rubyでは,通常の関数のように使うものはmethodで,関数自体をobjectとして使いたいときにはprocedureを用いる.

[配列]
R julia ruby python3
定義 a<-c(1,2,4,8,4) a=[1,2,4,8,4] a=[1,2,4,8,4] a=[1,2,4,8,4]
連続 b<-1:10 b=Vector(1:10) b=[*1..10] b=list(range(1,11))
等差数列 b<-seq(1,20,2) b=Vector(1:2:20) b=[*(1..20).step(2)] b=list(range(1,21,2))
内包表記 [i^2 for i in 1:5] [i**2 for i in range(1,6)]
map sapply(1:10,function(x){2*x}) map(x->2x,1:10) [*1..10].map{|x|2*x} list(map(lambda x:2*x,range(1,11)))
最初 a[1]
head(a,1)
a[1]
a[begin]
first(a)
a[0]
a.first
a[0]
要素 a[2] a[2] a[1] a[1]
最後 tail(a,1) a[end]
last(a)
a[-1]
a.last
a[-1]
最後から tail(a,2)[1] a[end-1] a[-2] a[-2]
範囲 a[1:3] a[1:3] a[0..2]
a[0,3]
a[0:3]
追加 a<-append(a,3)
a<-c(a,3)
push!(a,3) a.push(3)
a<<3
a.append(3)
取出 a<-head(a,-1) pop!(a) a.pop a.pop()
挿入 a<-append(a,9,2) insert!(a,3,9) a.insert(2,9) a.insert(2,9)
削除 a<-a[-3] deleteat!(a,3) a.delete_at(2) a.pop(2)
長さ length(a) length(a) a.length
a.size
len(a)
逆順 rev(a) reverse(a) a.reverse list(reversed(a))
上書き逆順 a<-rev(a) reverse!(a) a.reverse! a.reverse()
重複除去 unique(a) unique(a) a.uniq set(a)
ソート sort(a) sort(a) a.sort sorted(a)
上書きソート a<-sort(a) sort!(a) a.sort! a.sort()
結合 c(a,b) vcat(a,b) a+b a+b
含有 2%in%a 2∈a
a∋2
a.include?(2) 2 in a
部分集合 all(a%in%b) a⊆b
b⊇a
require 'set'
Set[*a]<=Set[*b]
Set[*b]>=Set[*a]
Set[*a].subset?(Set[*b])
Set[*b].supset?(Set[*a])
set(a)<=set(b)
set(b)>=set(a)
set(a).issubset(b)
set(b).issuperset(a)
積集合 intersect(a,b) a∩b a&b set(a)&set(b)
和集合 union(a,b) a∪b a|b set(a)|set(b)
差集合 setdiff(a,b) setdiff(a,b) a-b set(a)-set(b)
対称差集合 symdiff(a,b) require 'set'
Set[*a]^Set[*b]
set(a)^set(b)
最大 max(a) maximum(a) a.max max(a)
最小 min(a) minimum(a) a.min min(a)
総和 sum(a) sum(a) a.sum sum(a)

[辞書]
R julia ruby python3
定義 h<-c("a"=1,"b"=2) h=Dict("a"=>1,"b"=>2) h={"a"=>1,"b"=>2} h={"a":1,"b":2}
内包表記 d<-sapply(as.character(1:5),function(x)as.numeric(x)^2) d=Dict((string(k),k^2) for k=1:5) d=(1..5).to_h{|i|[i.to_s,i**2]} d={str(k):k**2 for k in range(1,6)}
テキスト x<-scan(text="H 1\nHe 4", what=list("",1))
names(x[[2]])<-x[[1]]
x<-x[[2]]
x=Dict(split(i) for i in split("H 1\nHe 4","\n")) x=Hash[*"H 1\nHe 4".split] x=dict(i.split() for i in "H 1\nHe 4".split("\n"))
アクセス h["a"] h["a"] h["a"] h["a"]
追加 h["c"]<-3 h["c"]=3 h["c"]=3 h["c"]=3
キー names(h) keys(h) h.keys h.keys()
h values(h) h.values h.values()
キーの存在 "a"%in%names(h) haskey(h,"a") h.key?("a") "a" in h
値の存在 1%in%h 1∈values(h) h.value?(1) 1 in h.values()
長さ length(h) length(h) h.length
h.size
len(h)
結合 d<-c(x,h) d=merge(h,x) d=h.merge(x) d={**h,**x}
上書き結合 h<-c(x,h) merge!(h,x) h.merge!(x) h.update(x)
ハッシュまたはディクショナリなどと呼ばれるデータ. 厳密にはRではこのようなデータはできないが,いくつかの手法で似たようなことができ,data.frameを使っても良く, x<-read.table(text="H 1\nHe 4",row.names=1);x["H",] とすれば良いが,呼び出しに余分なカンマが必要になるので,表では名前付きのベクトルを使った.

[行列]
R julia ruby/matrix ruby/gsl ruby/numo python3
初期化 using LinearAlgebra require "matrix" require "gsl" require "numo/linalg" import numpy as np
定義 a<-matrix(c(1,3,2,4),2,2) a=[1 2; 3 4] a=Matrix[[1,2],[3,4]] a=GSL::Matrix[[1,2],[3,4]] a=Numo::NArray[[1,2],[3,4]] a=np.array([[1,2],[3,4]])
単位行列 b<-diag(2) b=I(2) b=Matrix.identity(2)
b=Matrix.unit(2)
b=Matrix.I(2)
b=GSL::Matrix::Complex.identity(3)
b=GSL::Matrix::Complex.unit(3)
b=GSL::Matrix::Complex.I(3)
b=GSL::Matrix::Complex.eye(3)
b=GSL::Matrix::Complex.scalar(3)
b=Numo::DFloat.eye(2) b=np.eye(2)
b=np.identity(2)
対角行列 d<-diag(c(1,2)) d=diagm([1,2]) d=Matrix.diagonal(1,2) d=GSL::Matrix.diagonal(1,2) d=Numo::NArray[1,2].diag d=np.diag([1,2])
対角要素 diag(a) diag(a) a.map(:diagonal).to_a a.diagonal
a.diag
a.diagonal np.diag(a)
np.diagonal(a)
a.diagonal()
dim(a) size(a) [a.row_size,a.colum_size] a.size
a.shape
a.shape a.shape
要素 a[1,2] a[1,2] a[0,1] a[0,1] a[0,1] a[0,1]
a[1,] a[1,:] a.row(0) a.row(0)
a.submatrix(0,nil)
a[0,true] a[0,:]
a[,1] a[:,1] a.column(0) a.column(0)
a.col(0)
a.submatrix(nil,0)
a[true,0] a[:,0]
転置 t(a) transpose(a) a.transpose
a.t
a.transpose a.transpose a.T
a.transpose()
随伴行列 Conj(t(a)) a'
adjoint(a)
a.adjoint a.transpose.map(&:conj) a.transpose.conj a.T.conjugate()
sum(diag(a)) tr(a) a.trace
a.tr
a.trace a.trace a.trace()
行列式 det(a) det(a) a.determinant
a.det
a.det Numo::Linalg.det(a) np.linalg.det(a)
a+b a+b a+b a+b a+b a+b
a-b a-b a-b a-b a-b a-b
スカラー倍 2*a
a*2
2*a
a*2
2*a
a*2
2*a
a*2
2*a
a*2
2*a
a*2
要素毎の積 a*b a.*b a.hadamard_product(b) a*b a*b
a%*%b a*b a*b a*b Numo::Linalg.matmul(a,b)
a.dot(b)
a@b
np.matmul(a,b)
a.dot(b)
冪乗 Reduce("%*%",replicate(3,a,FALSE)) a^3 a**3 a**3
a^3
Numo::Linalg.matrix_power(a,3) np.linalg.matrix_power(a,3)
逆行列 solve(a) inv(a)
I/a
a\I
a.inverse
a.inv
a.invert
a.inv
Numo::Linalg.inv(a) np.linalg.inv(a)
対角化 eigen(a) eigen(a) a.eigensystem
a.eigen
a.clone.eigen
a.clone.eig
Numo::Linalg.eig(a) np.linalg.eig(a)
固有値 eigen(a)$values eigvals(a) a.eigen.eigenvalues a.clone.eig[0] Numo::Linalg.eigvals(a) np.linalg.eigvals(a)
固有ベクトル eigen(a)$vectors eigvecs(a) a.eigen.eigenvector_matrix
a.eigen.v
a.clone.eig[1] Numo::Linalg.eigvecs(a) np.linalg.eig(a)[1]
rubyでは,いくつかの方法で行列を扱うことができ,その代表的ないくつかを紹介したが, matrixは複素数の対角化が苦手で,gslは複素数の取り扱いにくせがある.
Rでは列を並べた順に要素が並ぶことに注意する必要がある.

[ファイル]
R julia ruby python3
読み込み f<-file("index.html","r")
d<-readLines(f)
close(f)
f=open("index.html","r")
d=readlines(f)
close(f)
f=open("index.html","r")
d=f.readlines
f.close
f=open("index.html","r")
d=f.readlines()
f.close()
ブロック d=open("index.html","r") do f readlines(f) end d=open("index.html","r"){|f|f.readlines} with open("index.html","r") as f: d=f.readlines()
ファイル名 d<-readLines("index.html") d=readlines("index.html")
全体読み込み d<-readChar("index.html",file.info("index.html")$size,TRUE) d=open(f->read(f,String),"index.html") d=open("index.html","r"){|f|f.read} with open("index.html","r") as f: d=f.read()
書き込み cat("Hello, world!",file="out.txt") open(f->print(f,"Hello, world!"),"out.txt","w") open("out.txt","w"){|f|f.print "Hello, world!"} with open("out.txt","w") as f: f.write("Hello, world!")
追加 cat("Hello, world!",file="out.txt",append=TRUE) open(f->print(f,"Hello, world!"),"out.txt","a") open("out.txt","a"){|f|f.print "Hello, world!"} with open("out.txt","a") as f: f.write("Hello, world!")

[特殊関数]
R julia ruby python3
導入 julia
using Pkg
Pkg.add("SpecialFunctions")
aptitude install ruby-gsl aptitude install python3-scipy
初期化 using SpecialFunctions require "gsl" from scipy import special
ガンマ関数 gamma(3) gamma(3) Math.gamma(3) special.gamma(3)
ダイガンマ関数 digamma(3) digamma(3) GSL::Sf::psi(3) special.psi(3)
ベッセル関数 besselJ(0.1,1) besselj(1,0.1) GSL::Sf::bessel_Jn(1,0.1) special.jv(1,0.1)

[特殊多項式]
R julia ruby python3
導入 aptitude install r-cran-polynom
R
install.packages("orthopolynom")
julia
using Pkg
Pkg.add("SpecialPolynomials")
aptitude install ruby-gsl aptitude install python3-scipy
初期化 library(polynom);library(orthopolynom) using SpecialPolynomials require "gsl" from scipy import special
エルミート多項式 predict(hermite.h.polynomials(3)[[3]],0.8) basis(Hermite,2)(0.8) GSL::Poly.hermite(2).eval(0.8) special.hermite(2)(0.8)

[WignerSymbols]
R julia ruby python3
導入 aptitude install r-cran-gsl julia
using Pkg
Pkg.add("WignerSymbols")
aptitude install ruby-gsl aptitude install python3-sympy
初期化 library(gsl) using WignerSymbols require "gsl" from sympy.physics.wigner import wigner_3j
厳密 wigner3j(0.5,1,1.5,0.5,0,-0.5) wigner_3j(0.5,1,1.5,0.5,0,-0.5)
浮動小数 coupling_3j(1,2,3,1,0,-1) wigner3j(Float64,0.5,1,1.5,0.5,0,-0.5) GSL::Sf::coupling_3j(1,2,3,1,0,-1) float(wigner_3j(0.5,1,1.5,0.5,0,-0.5))

装置制御

ここでは,Rubyを用いて計測装置の制御を行う方法を解説する. Rubyは様々なところで使われているが,計測装置の通信や制御に使われていることは少ないようである. ここの情報を元に,rubyで計測装置の制御を行う人が少しでも増えると幸いである. RubyとGPIBに関しては, 昔のホームページ も役に立つかも知れないので,ここに残しておく.
装置をPCから制御する方法は,PCのOSによって多少異なる. OSとしては,主にWindows, Mac, Linuxなどがある. 長期間に渡って安定して動作させるためには,サポート期間の長いOSが望ましく,私は装置制御にはLinuxを使うようにしており,以下ではLinuxについて説明するが,他のOSを使う場合にも,多少の変更で動かすことができる場合が多い. MacのOS Xは基本的にはUnixなので,多少の違いがあるが,Linuxとほぼ同様に扱うことができ,基本的にはデバイスのファイル名を適切に変更すれば良い. WindowsはLinuxとはかなり違うシステムであるが,以下で説明するインターフェースmoduleは書き換える必要があるが,装置classはそのままで使えるはずである. 特にシリアルポートは,デバイスマネージャーで適切に設定してファイル名をCOM1などとすれば良い.

インターフェース

計測装置と通信するためには,インターフェースを選択する必要があり,様々なものが使われている. 時代と共に流行り廃れがあり,代表的なものを表に示して,その特徴を説明する. これらのインターフェースを使って測定装置と通信するためには,それぞれのインターフェースに特有の書き方をする必要がある. そのため,一つの装置が複数のインターフェースを持っている場合には,インターフェース毎に微妙に異なるプログラムを使う必要がある.
名称 特徴
RS232C シリアルポートとも呼ばれる.コネクタは9ピンまたは25ピンのD-subであり,以前のPCには標準でついていた.最近のPCでは,マザーボードに端子がある場合には,増設ケーブルで使うことができるが,USB-シリアル変換ケーブルを用いて変換して用いることが多い.基本的には装置と一対一の通信を行うための規格であり,送信と受信にそれぞれ一本の導線を用いている.その他の制御線使い方や通信速度やデータのビット数やパリティの指定など,様々な条件で通信が行われるが,送受信の双方でそれらの条件を揃えなければならない.ケーブルについてもストレートとクロスに加えて,制御線の接続方法による違いがある.
プリンタポート パラレルポートとも呼ばれていた,一般的にはプリンターと通信するために使われていた.コネクタは25ピンのD-subまたは36ピンのアンフェノールである.古いPCには標準で付いていた.最近のPCでは,マザーボードに端子がある場合には,増設ケーブルで使うことができる.一対一の通信を行い,データの送受信に8本のデータ線と9本の制御線を用いて通信を行う.
GPIB 複数の装置と通信するために開発されたインターフェースである.コネクタは24ピンのアンフェノールであり,オスの後ろにメスのついたコネクタが両側にあるケーブルを使って,数珠つなぎに複数の装置と接続できる.以前はよく用いていたが,最近はあまり見かけなくなってきた.PCには標準では搭載されていないので,拡張ボードとして増設するか,USBから変換ケーブルを使う必要がある.しかし,これらは一般にはそれなりに高価である.データの送受信に8本のデータ線と8本の制御線を用いて通信を行う.エラーチェックの仕組みが無いので,通信エラーが起きた場合に注意が必要である.
LAN ネットワークのために使われており,コネクタは8ピンのRJ45で,最近のデスクトップPCには標準で付いている.ハブを用いることによって,複数の装置とも通信できる.
USBTMC USBを使った計測装置のインターフェースの一つである.USBは最近のPCにはほぼ必ずついている.ハブを用いることによってポートを増設することもできる.

インターフェースmodule

以前は,インターフェースクラスを作って,そのサブクラスとして装置クラスを作っていた. しかし,別のインターフェースを使う場合には,そのたびに装置クラスを定義したファイル自体を書き換える必要があるという欠点があった. できるだけ共通のコードを使うために, インターフェースに関係する部分をmoduleとして, 装置に関する部分をclassとして,定義した. すると,ある装置をあるインターフェースで使う時には,装置classにそのインターフェースmoduleを組み込むだけで良くなる. しかし,同じ種類の複数の装置を別のインターフェースで通信することはできず,一種類の装置は一種類のインターフェースで通信する必要があり,改善の余地はある. 作成したインターフェースmoduleを表に示す.
インターフェース ファイル名 モジュール名 説明
RS232C rs232c.rb Rs232c USB-シリアルケーブルを使う場合には,ファイルをttyUSB0などとする.
GPIB gpibtty.rb GpibTty RS232CからGPIBに変換して使う場合に用いる.最近は,通常のGPIBを使っていないので,それ用のmoduleは作っていない.
LAN lanport.rb LanPort
USBTMC usbtmc.rb UsbTmc
具体的な使い方は後で説明する.

計測装置

実験装置を構成する計測装置には,様々な種類がある. 装置毎に機能が異なり,決まったコマンドを送ることによって,装置を制御することができる.

装置class

装置毎に仕様が異なるので,それぞれ装置についてclassを作る必要がある. ただし,同じ種類の装置はできるだけ同じmethodで動くように作った方が便利であり,そのように意識して作ったつもりである. これまでに使ったことのある装置のクラスをいくつか公開する. ただし,装置のすべての機能を使えるように実装したわけでは無いし,作った時期によって書き方が一定していないかもしれないが,ご容赦頂きたい.
ファイル名 クラス名 種類 会社名 装置名
instrument.rb Instrument これ以外のすべての装置クラスは,Instrumentクラスのサブクラスとして定義されており,必ず必要であるが,使う際にはこのクラスを意識する必要はほとんど無い.
ptm3500a.rb PtM3500a デジタルマルチメーター PICOTEST M3500A
pg5100a.rb Pg5100A ファンクションジェネレーター PICOTEST G5100A
yk7651.rb Yk7651 カレントソース YOKOGAWA 7651
ykgs200.rb YkGs200 カレントソース YOKOGAWA GS200
kt2000.rb Kt2000 デジタルマルチメーター Keithley 2000
kt2400.rb Kt2400 ソースメーター Keithley 2400
kt2182.rb Kt2182 ナノボルトメーター Keithley 2182
kt705.rb Kt705 スキャナー Keithley 705
kt224.rb Kt224 カレントソース Keithley 224
kt6485.rb Kt6485 ピコアンペアメーター Keithley 6485
hp34401a.rb Hp34401a デジタルマルチメーター Keysight 34401A
hp34420a.rb Hp34420a デジタルマルチメーター Keysight 34420A
hp33220a.rb Hp33220a ファンクションジェネレーター Keysight 34420A
hp3478a.rb Hp3478a カレントソース Keysight 3478A
tds200.rb Tds200 オシロスコープ Tektronix TDS2000
ls218.rb Ls218 温度モニター Lake Shore 218
ltc11.rb Ltc11 温度コントローラー Neocera LTC-11
cryocon22.rb Cryocon22 温度コントローラー CRYO-CON 22
egg7265.rb Egg7265 ロックインアンプ Signal Recovery 7265
sds7000.rb Sds7000 オシロスコープ OWON SDS7202
xds2000.rb Xds2000 オシロスコープ OWON XDS102A
wsta2012.rb WSta2012 ファンクションジェネレーター Teledyne Lecroy WaveStation 2012
装置毎にどのようなmethodで,どのような動作をするのかは,それぞれのclassの中身を見て推測して下さい. 今後改良するとすれば,同じ種類の装置についてclassを作って,それぞれの装置はそのサブクラスとして定義することもできるだろう.

装置との通信の例

それでは,実際に上のmoduleやclassを使って,rubyで装置を制御する方法を,説明しよう. まず,上のファイルの内,必要なものをdeviceというフォルダに入れておく. プログラムの中では,instrument.rbと使用する装置のclassと通信するインタフェースのmoduleを読み込んだ後に,装置クラスにインターフェースmoduleを組み込むように再定義して,その装置classのオプジェクトを作り,それに行いたい動作のmethodを実行すれば良い.

PICOTEST社のG5100AをUSBTMCから制御する場合の例を下に示す. 3行目まででclassとmoduleを読み込んでいる. 4行目で装置classにインターフェースmoduleを組み込んでいる. 5行目で装置classのオブジェクトを作って,6行目でコマンドを実行させている.
require "./device/instrument"
require "./device/pg5100a"
require "./device/usbtmc"
class Pg5100a include UsbTmc end
a=Pg5100a.new(0)
a.on()
装置オブジェクトを作る際には,その装置を指定するための引数を指定する必要がある. USBTMCの場合には,繋いだ順にusbtmc0,usbtmc1などと番号がついていくので,その番号を指定する. この番号が分からない時には,UsbTmcInst::Listとすると,usbtmc.rbを組み込んだ時点で接続されている装置の識別文字列が配列として得られ,UsbTmcInst.find("5100")とすると,5100という文字列を含む識別文字列をもつ番号が配列として得られる. それが唯一である場合には,UsbTmcInst.find("5100")[0]を指定すれば良い. GPIBの場合にはGPIBアドレスを, LANの場合にはIPアドレスとポート番号を, RS232Cの場合にはシリアルの番号とボーレートなどを指定する.

マイコン

マイコンというのは,マイクロコンピューターまたはマイクロコントローラーの略で,一つのICを小さなコンピュータのように使って,プログラムを実行させることができる. 研究では様々な電子機器が必要になるが,簡単な機器や市販されていないような機器が必要になった場合には,私はそれらをマイコンを使って自作して使っている. ここでは,マイコンの特徴や,簡単な使い方,作ったものなどを紹介したい.

マイコンの基礎

いくつかのICを組み合わせた基板のマイコンをマイコンボードと呼ぶが,近年のマイコンは実質的に一つのICであり,ワンチップマイコンとも呼ばれる. 通常のコンピュータとは違い,ディスプレイもキーボードも無く,基本的には電気信号の入出力しか出来ないが, デジタルの入出力をするだけでは無く,アナログデジタルコンバーター(ADC)や外部の機器との通信をするためのインターフェースなどが組み込まれている場合もある. プログラムはPCで作り,マイコンに書き込んで,それをマイコンが実行するという形で利用する. プログラムを書き込むためには,専用の書込機が必要なことが多いが,マイコンボードの一部ではUSBなどでPCと繋いで書き込みができる.
プログラムは,機械語やC言語などで書くことができる. 機械語を使えば,そのマイコンの能力を最大限に引き出すことが出来るが,少し複雑なことを実現しようとすると長いコードとなり,マイコンの種類毎にコマンドが異なっているためにそれぞれを学習する必要がある. C言語などを用いると,マイコンの種類にあまり依存しない書き方をすることができ,コードも比較的短くなる.しかし,マイコンの型番によって,使い方は細かく違っており,それらを把握する必要がある. Arduino IDEに代表される統合開発環境では,マイコンの種類や型番に依存しないようなプログラムの書き方ができる.もちろん,そのマイコンで使えない機能のプログラムは無理だが.

マイコンの種類と特徴

これまでに,AVR, ESP, stm, wchなどのいくつかのマイコンを私は使って来た. よく用いられるマイコンの例を,以下の表に挙げる. マイコンを始めるなら,書込機などの初期投資が少なくて済むAVRやCHシリーズか, 開発環境が整っているマイコンボードなどがお勧めできる.
通称 開発会社 型番 特徴 有名なマイコンボード
PIC Microchip Technology PIC12, PIC16, PIC18, PIC32 書込機が高価.
AVR Atmel ATtiny, ATmega 様々な書込機があり,安価. Arduino
STM STMicroelectronics STM8, STM32 組み込み機器によく利用されている. Blue pill
CH WCH CH32, CH552 チップ自体が非常に安価. Blue pill plus
ESP Espressif Systems ESP8266, ESP32 WiFiやBluetoothが使える. M5Stack


AVR

AVRを使うためには,書込機を用意する必要がある. 以前は,プリンターポートを使った書込機が簡単に安く作れたので,私はこれを使ってAVRを使い始めたが,最近のPCにはプリンターポートが無いので,使えなくなった. USB接続の様々な書込機が販売されているので,これを購入して使うこともできる. 後述のArduinoを用いると,プログラムの例にあるArduinoISPを使うだけで,書込機を作ることもできる. FTDI社のFT232RLというUSBシリアル変換ICを使うと,コネクタとコンデンサと配線するだけで,Bitbang書込機を作ることもできる. 私個人としては,最近は最後の2つを主に使っている.

ESP

CH32

マイコンを使った装置

私がマイコンを使ってこれまでに作った装置のいくつかを紹介する.

GPIBアダプタ

GPIBを用いてPCから装置と通信するときに用いるGPIBアダプタは,比較的高価なものが多く,私もいろいろな種類のアダプタを自作して来たが,様々な人によって様々なアダプタが発表されている.
(1)プリンターポートを利用したもの
プリンターポートとGPIBは,パラレルな双方向通信が可能という意味で,似ていることに気がついて, 2004年には プリンターポート-GPIBアダプタ を使っていました.ICなどを使わずにGPIBと通信ができるという点で,優れていたと思うが,近年はプリンターポートを見かけなくなって来て,実用性は無くなった気がする.ちなみに,その後,トランジスタ技術2005年10月号230ページでは,同じようなプリンターポートを使ったアダプタが紹介されている.
(2)AVR ATtiny2013を利用したもの
AVRを用いたGPIBアダプタとしては,トランジスタ技術2001年2月号321ページでは,AVRのAT90S1200を使ったGPIBが紹介されている. 私は,2006年にはAT90S2013を使って,GPIBからRS232Cへの変換を行い,リスナーのみのGPIB機器を作っており,2007年にはトーカーにもなれるGPIB機器を作っている.そして,2011年には,tiny2313を使って,GPIBアダプターを作っている.tiny2313は安価だけど多くのピンがIOとして使えるので,GPIBアダプタの作成に合っていると言える.しかし,基板を作ってハンダ付けをする必要があり,作成はそれなりに面倒である.
(3)arduinoを利用したもの
AVRのハンダ付けが面倒だと思っていたころ,それならarduinoを使えば良いということに気がついた.多少は高くなるが,arduinoはAVRがすでに基板の上にハンダ付けされた状態で,シリアル通信を介してUSBでも通信できるようになっており,GPIBコネクタを取り付けて,プログラムを書き込めば,GPIBアダプターとなる.実物が手元に残っているので,それらを写真と共に紹介する.

左から,arduino UNOを使ったもの,arduino nanoにケーブルを繋いだもの,arduino nanoを使って拡張ボードのところに取り付けられるようにしたもの,arduino pro microをコネクタの中に組み込んだものである. 簡単に作りたいのであれば,UNOかnanoを使うと良く,nanoだとそれなりにコンパクトにできる. ケースを作るのが面倒になって,GPIBコネクタの中にarduinoを入れてしまえないか考えて作ったのが,pro microを使ったもので,micro USBコネクタの部分にUSBケーブルを直接ハンダ付けすると,なんとか入れることができた.pro microはATmega32U4というAVRを搭載しており,USBと直接通信できるので,構成もシンプルになっている.コネクタの中に収めるなら,基板も自作した方が良いのだろうが,それは面倒でやっていない.
arduinoを使ってGPIBを実現している人は沢山いるようで, Emanuele Girlando blogAR488 GPIB Controllergpib-conv-arduino-nano やトランジスタ技術2024年5月号190ページなどに紹介されている. 上記のものは,いずれもUSBからシリアルを介してGPIBに変換しており,PrologixのGPIB-USBコントローラーに準拠しているものや,独自のコマンド体系を用いているものがある. 私が作ったものは,プログラムを簡単に書けるようなコマンド体系で,IFC,SRQ,REN,LLO,DCLはGPIBの対応するコマンド,RDAはRENをoffにするコマンドで,c,lの後に二桁のアドレスがあると,そのデバイスをそれぞれdevice clear,go to localとして,wと二桁のアドレスの後に文字列があると,その文字列をデバイスに送信し,rと二桁のアドレスがあると読み取りとした.ちなみに,コマンドの最後にはデリミタが必要である.装置制御のところで紹介したgpibtty.rbは,このコマンド体系に対応している.arduino UNOやnano用のプログラムは2017/08/26の私のブログで紹介した.ピンの番号は最初の方に定義してあり,繋ぎやすいように配線して,適宜番号を変えてもらって構わないようにした.プログラムを書くのが面倒で,バイナリーモードには対応していないなどの課題はあるが,まだ必要となったことが無いので,書いていない.

粉末充填機

粉末を棒状の風船に充填するときに,アクリルパイプに入れた風船に粉末を詰めた状態で机に打ち付けることによってしっかりと充填させるという作業を,毎日のようにやっていたら,肩が痛くなってしまったので,それを自動化するために作った装置である.漏斗の下にあるエアーシリンダーを上下させるための空圧を,電磁バルブを使って制御しており,その信号をATtiny13Aで作って,トランジスタで増幅している.単純だが役に立つ装置である.今回は箱を小さくしたら,安定性が悪くなってしまった.

ジルコニア式酸素センサー制御

車載用のジルコニア式酸素センサーを使って,酸素濃度を表示する装置である.低濃度の酸素の濃度を測るセンサーは高価なものが多い.自動車の空燃比を調整するためにジルコニア式の酸素センサーが用いられており,大量生産されているために,センサー自体は比較的安価である.酸素濃度を求める際にNernstの式を用いるため,絶対温度を知る必要があるが,このセンサーには温度計は内蔵されていないのが欠点である.検証を行った結果,ヒーターに10.2Wの電力を送って加熱すると,センサーは約735℃となることが分かり,この時には酸素濃度が十分の一になると50mVの電圧が発生する.ADCを使えるtiny861を用いて,ヒーターの電流と電圧から与えた電力を監視して平均で10.2Wとなるようにon/offを行い,センサーの電圧をオペアンプで低インピーダンスとしてから読み取って,酸素濃度を計算して表示するというものである.酸素濃度を計算するのがワンチップでは難しかったので,メモリに覚えさせた値を使って計算するようにした.

FZ用表示装置

arduino nanoを使って作ったFZのロッドの位置や酸素濃度を表示する装置.位置はスライドボリュームの電圧から,酸素濃度はジルコニア式酸素濃度計の電圧から計算して,NTSC信号に変換して出力する.防犯カメラ用の録画装置に取り込んで,記録できるようにしてある.可動範囲は150mmであるが,スライドボリュームは100mmのものしか見つから無かったので,写真のようにスライドボリュームを2つ接続して可動範囲をカバーできるようにしたが,スライドボリューム本体が動くので,長期的には配線が切れないか心配である.NTSC信号は,arduinoのTVout Libraryを使って出力している.

内圧制御装置

グローブボックスの内圧を一定に保つ装置.精製機の再生中や停電中には,内圧の制御が止まるので,その間に内圧を保つための装置である.停電中に電池で駆動することを想定して,消費電力が少なくなるように,ボンベからのガスの流量をニードルバルブで制御しており,バルブはギアボックスを介してDCモーターで回している.ガス圧は歪ゲージ式の圧力センサーを24bitのADCと通信してtiny13Aが読み取り,PWMでモーターを回すようにしている.省電力にするために,表示をすべて無くしたので,状態が分かりにくいのが欠点である.