コンピュータを楽しもう!!

今、自分が面白くていろいろやってみたことを書き綴りたいと思います。連絡先はtarosa.yでgmail.comです。

Luaridaの回転命令サンプル

ドロイド君が回転するサンプルを作りました。Androidマーケットでも公開しています。とりあえず、動画から。

加速度センサのX,Yの値を使って、重力の方向を計算してドロイド君がまっすぐ向くように表示するサンプルです。せっかくのセンサなので、取得データに適当なフィルタを掛けるようにしました。フィルタは一次のIIRフィルタと移動平均を取るものです。
サンプルのダウンロードはこちらから(rotatesample.apk /sdcard/luarida/rotatesample/フォルダに保存されます)。

フィルタの選択画面

最初にドロイド君の画像とどのフィルタかを選ばせます。

------------------------------------------
--メインプログラム
------------------------------------------
function main()
local w,h
local wb, hb
local k
 --ワークエリアをクリアする
 canvas.workCls()
 --ワークエリアの(0,0)-(79,93)に、アンドロイド画像を読み込みます
 if( canvas.loadBmp( Path.."droidkun.png", 0, 0, 79, 93 )==-1)then
   dialog( Path.."droidkun.png", "ロードに失敗しました",1 )
   do return end
 end
 --画面サイズ取得
 w,h = canvas.getviewSize()
 w = w-1
 h = h-1
 wb = 80+w
 hb = h
 --背景を白にする
 canvas.drawCls( color(255,255,255) )
 --画面を背景にするために、ワークエリアに取り込む
 canvas.getg( 0, 0, w, h, 80, 0, wb, hb )
 while(true)do
   item.clear()
   item.add( "一次フィルタ", 0 )
   item.add( "移動平均", 0 )
   item.add( "フィルタ無し", 0 )
   item.add( "終 了", 0 )
   k = item.list( "どのフィルタを選びますか" )
   if( k==1 )then
     filter()
   elseif( k==2 )then
     moveave()
   elseif( k==3 )then
     rawdata()
   elseif( k==4 )then
     break
   end
 end
end

一次フィルタ

測定した加速度に重み(omomi)を掛けて鈍らせています。単純なフィルタです。

 axo = axo*omomi + ax*(1-omomi)
 ayo = ayo*omomi + ay*(1-omomi)

回転のコマンドは下記の2つです。中心にドロイド君、中心から半径70ピクセル離れたところに角度を表示しています。

 canvas.putrotg( cx, cy, angle, 0, 0, 79, 93 )
 canvas.putflush()
 canvas.drawTextRotate("θ="..angle, cx-70*x, cy+70*y, angle, 24, color(255,0,0))

回転角度は、アークタンジェントatan2()で計算しています。2つの引数が加速度XとYになります。ここでは縦がX軸、横がY軸となっているので、角度計算にXとYの値を入れ替えています。

関数は下記。

------------------------------------------
-- 一次フィルタ
------------------------------------------
function filter()
local ax,ay,az,axo,ayo
local aax,aay
local w,h
local wb, hb
local kaku
local omomi = 0.85
local cx, cy
local pi = 3.141592
local x,y,s
local angle
local moji
 --画面サイズ取得
 w,h = canvas.getviewSize()
 w = w-1
 h = h-1
 wb = 80+w
 hb = h
 cx = w/2
 cy = h/2
 moji,s = editText("フィルタ係数を入力してください(0<=〜<1)")
 if( s==nil or moji==nil or moji=="" or tonumber(moji)==nil )then
   omomi = 0.8
 elseif( s==1 )then
   omomi = tonumber( moji )
   if( omomi<0 or omomi>=1 )then omomi = 0.8 end
 else
   omomi = 0.8
 end
 toast( "フィルタ係数を "..omomi.."に設定しました", 0 )
 toast("ドロイドをタッチすると終了します", 0 )
 axo, ayo, az = sensor.getAccel()
 while(true)do
   --加速度を取得
   ax, ay, az = sensor.getAccel()
   axo = axo*omomi + ax*(1-omomi)
   ayo = ayo*omomi + ay*(1-omomi)
   kaku = math.atan2( -ayo, axo )
   angle = math.floor(kaku/pi*180)
   x = math.sin(kaku)
   y = math.cos(kaku)
   canvas.putg( 0, 0, w, h, 80, 0, wb, hb )
   canvas.putrotg( cx, cy, angle, 0, 0, 79, 93 )
   canvas.putflush()
   canvas.drawTextRotate("θ="..angle, cx-70*x, cy+70*y, angle, 24, color(255,0,0))
   --画面タッチで終了
   x,y,s = touch()
   if( s~=1 )then
     if( x>=cx-79/2 and x<=cx+79/2 and y>=cy-93/2 and y<=cy+93/2 )then
       break
     end
   end
 end
end

移動平均

移動平均は、データの平均を取っているだけです。ax、ayの配列を作って、リングバッファに使っています。平均と言いながら、今回は足し込むだけで、nで割っていません。それは、atan2の計算でXとYの比になるので、割る必要がないからです。画像の表示は一次フィルタと同じです。

------------------------------------------
-- 移動平均
------------------------------------------
function moveave()
local ax={}
local ay={}
local az
local axt, ayt
local i
local n = 10
local aax,aay
local w,h
local wb, hb
local kaku
local cx, cy
local pi = 3.141592
local x,y,s
local angle
local moji
 moji,s = editText("移動平均を取る数を入力してください(n=1〜)")
 if( s==nil or moji==nil or moji=="" or tonumber(moji)==nil )then
   n = 4
 elseif( s==1 )then
   n = tonumber( moji )
 else
   n = 4
 end
 toast( n.."個の移動平均を取ります", 0 )
 --画面サイズ取得
 w,h = canvas.getviewSize()
 w = w-1
 h = h-1
 wb = 80+w
 hb = h
 cx = w/2
 cy = h/2
 axt = 0
 ayt = 0
 for i=1,n do
   ax[i], ay[i] = sensor.getAccel()
   axt = axt + ax[i]
   ayt = ayt + ay[i]
 end
 toast("ドロイドをタッチすると終了します", 0 )
 i = 1
 while(true)do
   axt = axt - ax[i]
   ayt = ayt - ay[i]
   --加速度を取得
   ax[i], ay[i] = sensor.getAccel()
   axt = axt + ax[i]
   ayt = ayt + ay[i]
   kaku = math.atan2( -ayt, axt )
   angle = math.floor(kaku/pi*180)
   x = math.sin(kaku)
   y = math.cos(kaku)
   i = i + 1
   if( i>n )then i=1 end
   canvas.putg( 0, 0, w, h, 80, 0, wb, hb )
   canvas.putrotg( cx, cy, angle, 0, 0, 79, 93 )
   canvas.putflush()
   canvas.drawTextRotate("θ="..angle, cx-70*x, cy+70*y, angle, 24, color(255,0,0))
   --画面タッチで終了
   x,y,s = touch()
   if( s~=1 )then
     if( x>=cx-79/2 and x<=cx+79/2 and y>=cy-93/2 and y<=cy+93/2 )then
       break
     end
   end
 end
end

生データ

生データは加速度のデータをそのまま使って角度を計算しているだけです。ソース添付は省略したいと思います。
以上、サンプル紹介を終わります。