子どもRubyプログラミング講座「ロボット編」芸術衛星特集

2014/03/30

~ 芸術衛星特集 ~

kidsruby今回は2月28日に打ち上げに成功した芸術衛星(げいじゅつえいせい) ARTSAT(アートサット) INVADER(インベーダー)特集です。

無線ロボットのMI100を、宇宙で地球の周りを回っている芸術衛星のINVADERと連動させてみましょう。

芸術衛星INVADER(インベーダー)のくわしい説明はここにあります。

今までのプログラミング講座でまだやっていない見なれない命令(めいれい)がでてきます。でも、今回は、おなじみのrobo.から始まるMI100を動かす命令や、今まで講座でやった命令だけに注目するようにしましょう。見なれない命令については後でかくにんしましょうね。

プログラムを動かしたときのMI100の動画をYouTubeで公開しています。

どんなプログラム?

プログラムされたINVADERを見る場所、観測点(かんそくてん)を調べます。

近所の有名な建物や駅名などを選んで観測点をプログラムします。もちろん実際にその場所に行く必要はありません。プログラムはそこからINVADERを見ているように動きます。例では「東京都現代美術館」を使います。注意)コンピューターに住所や電話番号をいれないようにしましょう。コンピューターに住所や名前、電話番号をむやみに入れないくせを付けておきましょうね。

INVADERの居場所を調べます。

INVADERが地球のどこの真上にいるのかを教えてくれる、ウェブ・アプリケーション・インタフェースとよばれるプログラムのための道具を使って、INVADERの居場所を調べます。INVADERのいる、緯度(いど)と経度(けいど)と呼ばれる、地球の場所をあらわす数(角度)を教えてくれます。INVADERの真下に住所や地名があればputsで画面に出します。

距離を計算します。

「東京都現代美術館」とINVADERとの距離(きょり)を計算します。

方角を調べます。

「東京都現代美術館」からみて、INVADERがどの方角(東西南北)にいるのかを調べます。

MI100を動かします。

距離や方角をモールス信号音(ピピピピー)で話します。

INVADERが近くにいると(真上など)、MI100がダンスします。近いほど激しくダンスします。

INVADERが近いほどフルカラーLEDが点滅する回数が増します。

INVADERが近づいてくるとMI100が少しずつ前進します。近くにいるほど移動速度が速くなります。

INVADERが遠のいていくとMI100が少しずつ後進します。近くにいるほど移動速度が速くなります。

INVADERの方向でフルカラーLEDの色が変わります。次のような色になります。
東:紫 西:黄緑 南:赤 北:青緑

INVADERの真下(ました)に地名や住所があるときは、MI100が”HELLO WORLD!”とモールス信号音で話します。

プログラム

今回は下のプログラムをKidsRubyのエディターに入れて動かしてみましょう。

プログラムを動かす前の準備

動かす前に、距離の計算などを助けてくれる、あらかじめ用意されている命令の集まり(ライブラリ)をパソコンにインストールします。この手順は一度だけやります。

コマンドプロンプト (=>Windowsでコマンドプロンプトを出すには) で次の gem (ジェム) コマンドを実行します。

C:>gem update mi100<Enter (エンター)>

C:>gem install geocoder<Enter (エンター)>

C:>gem install color<Enter (エンター)>

gem

プログラミング

プログラムの下の部分だけを変えます。
LOCAL_POINT = "東京都現代美術館"
COM_PORT    = "COM5"

LOCAL_POINTの” “で囲まれたところに、”東京駅” など近所の有名な建物の名前を入れます。
COM_PORTの” “で囲まれたCOM5の部分は、今までの講座と同じように、MI100の接続の手順で調べた名前に変えましょう。

ノート

ウェブ・アプリケーション・インタフェースでは約6分ごとに、INVADERの居場所を教えてくれます。INVADERは6分で2500kmくらい移動します。北海道の端から九州の端の長さがおよそ2500kmとも言われていますので、たったの6分で北海道の端から九州の端まで移動してしまうことになります。MI100のダンスを見れた人は本当にラッキーですね。

注意

例では、INVADERの居場所を調べて、MI100を動かすことを、約6分間隔で5回繰り返しています。より多くの人が快適にウェブ・アプリケーション・インタフェース使うことができるように、繰り返しの間隔は6分より短くしないようにしましょうね。

# coding: utf-8
# KidsRubyから動かすときはここから下のみでOK
# artsat.rb
# Copyright (c) 2014 Masami Yamakawa
#
# This software is released under the MIT License.
# http://opensource.org/licenses/MIT
#
require 'open-uri'
require 'json'
require 'geocoder'
require 'color'
require 'time'
require 'mi100'

LOCAL_POINT = "東京都現代美術館"
COM_PORT    = "COM5"

MAX_DURATION_MILLIS = 100
SLEEP_TIME_SEC =        3
CIRCUMFERENCE_KM =  40075
MOVE_DISTANCE_KM =  10000
DANCE_DISTANCE_KM =   500
MAX_DISTANCE_LEVEL =   10
MAX_ROCK_LEVEL =       10

ARTSAT_API = 'http://api.artsat.jp/web/v2/invader/sensor_data.json?sensor=lat,lon'
FETCH_PERIOD_SEC_DO_NOT_CHANGE =  (6 * 60) + rand(20) + 10

Geocoder.configure(:language=>:ja, :units=>:km)

local_lat_lon = Geocoder.coordinates(LOCAL_POINT)
uri = URI.parse(ARTSAT_API)

robo = Mi100.new COM_PORT

last_distance = nil

5.times do

json = open(uri).read

invader = JSON.parse(json,{:symbolize_names => true})
invader_time = Time.parse(invader[:results][0][:closest_available_time_iso_string])
next_fetch_time = invader_time + FETCH_PERIOD_SEC_DO_NOT_CHANGE
invader_lat_lon =[]
invader_lat_lon << invader[:results][0][:sensors][:lat][:value]
invader_lat_lon << invader[:results][0][:sensors][:lon][:value]

address = Geocoder.address(invader_lat_lon)
distance = Geocoder::Calculations.distance_between(local_lat_lon, invader_lat_lon,{:units=>:km})
compass = Geocoder::Calculations.bearing_between(local_lat_lon, invader_lat_lon,{:method=>:spherical})
complementary_color = compass >= 180 ? compass - 180 : compass + 180
compass_word = Geocoder::Calculations.compass_point(compass)
compass_hsl = Color::HSL.new complementary_color, 100, 50
compass_rgb = compass_hsl.to_rgb

puts "TIME    : #{invader_time.to_s}"
puts "LOCAL   : lat %f lon %f"%local_lat_lon
puts "INVADER : lat %f lon %f"%invader_lat_lon
puts "ADDRESS : %s"%(address || 'No street address...')
puts "DISTANCE: #{distance.round.to_s} km"
puts "COMPASS : #{compass.to_s} #{compass_word}"
puts ""

robo.talk "ASAS "
robo.talk distance.round.to_s + " " + compass_word + " "

last_distance ||= distance

distance_level = (MAX_DISTANCE_LEVEL - ((MAX_DISTANCE_LEVEL * distance) / (CIRCUMFERENCE_KM / 2))).round

begin
  robo.talk "HELLO WORLD!" if address

  distance_level.times do
    robo.blink compass_rgb.red_p.round, compass_rgb.green_p.round, compass_rgb.blue_p.round, 100
    sleep 0.2
  end

  if distance.round < DANCE_DISTANCE_KM
    rock_level = (MAX_ROCK_LEVEL - ((MAX_ROCK_LEVEL * distance) / DANCE_DISTANCE_KM)).round
    rock_level.times do
      robo.spin_right MAX_DURATION_MILLIS
      robo.spin_left MAX_DURATION_MILLIS
    end
  end

  if distance.round < MOVE_DISTANCE_KM
    duration = (MAX_DURATION_MILLIS - ((MAX_DURATION_MILLIS * distance.round) / MOVE_DISTANCE_KM)).round
    if last_distance - distance > 0 then robo.move_forward duration
    elsif last_distance - distance < 0 then robo.move_backward duration
    end
  end

  sleep SLEEP_TIME_SEC
end while Time.now < next_fetch_time

last_distance = distance

end

robo.close