MINDSTORMSのPythonでは独自ライブりの利用ができないのである程度の規模のプログラムではソースが大きくなって見通しが悪くなるというお話をしました。ただ、MINDSTORMSのHUBでは0〜19までの20種類のプログラムをアップロードできて、LEGO本体(以下HUB)のキー操作で選択して実行することができるようになっています。この機能を使えばLEGOには20種類のPythonプログラム(LEGOアプリではプロジェクト:以下プロジェクト)をアップロードできることになります。
ということでアップロードしたプロジェクトはHUBに保存されているはずなのでライブラリとして作成したものをアップロードしてそれをimportすれば、なんちゃってライブラリとして使えるのではないかなと考えHUBの方を少し調べてみました。
MINDSTORMS HUBの調査
先日MINDSTORMSのMicroPythonのバージョン調査した時は特に説明をしませんでしたがHUBとUSBないしはBluetoothで接続するとPCとシリアル接続ができます。このシリアルポートを使ってMicroPythonをキーボードから操作することができます。
シリアル通信が可能なソフトならなんでも良いのですが私はTeraTermを利用していますが、TeraTermを利用してHUBと接続する方法はこちらの説明が分かり易いと思います。尚、記事の「インストール」については作業する必要はなく「REPL(コンソール/コマンドライン)による実行」の部分だけ参考にすればHUBとの接続は可能です。
HUBとの接続ができると後はMicroPythonのインタプリタに接続した状態になりますので、適時プログラムを入力することでHUBの中身を調査していきます。
>>> import os
>>> os.listdir()
['boot.py', 'bt-lk1.dat', 'bt-lk2.dat', 'main.py', 'util', 'projects', 'runtime', 'system', '_api',
'commands', 'event_loop', 'mindstorms', 'programrunner', 'protocol', 'sounds', 'spike', 'ui',
'hub_runtime.mpy', 'version.py', '.runtime_hash', 'etc', 'extra_files', '.extra_files_hash',
'runtime.log']
注)見やすくする為、行を折り返しています。
>>> def cat(name):
... f = open(name, 'r')
... src = f.read()
... f.close()
... print(src)
...
...
...
>>> cat('main.py')
import gc
import micropython
import hub_runtime
micropython.alloc_emergency_exception_buf(256)
hub_runtime.start()
>>> help(hub_runtime)
object <module 'hub_runtime' from 'hub_runtime.mpy'> is of type module
HubUI -- <class 'HubUI'>
LinegraphMonitorMethods -- <class 'LinegraphMonitorMethods'>
SoundMethods -- <class 'SoundMethods'>
BT_VCP -- BT_VCP(0)
ProgramRunner -- <class 'ProgramRunner'>
__connection_changed -- <function __connection_changed at 0x20038b70>
error_handler -- <ErrorHandler object at 20020e00>
init -- <function init at 0x20038b40>
TIMER_PACE_HIGH -- 16
__file__ -- hub_runtime.mpy
start -- <function start at 0x20038b60>
LightMethods -- <class 'LightMethods'>
ProgramMethods -- <class 'ProgramMethods'>
initialize_ports -- <function initialize_ports at 0x200197b0>
pop_force_reset -- <function pop_force_reset at 0x2001d200>
cleanup_slot_list -- <function cleanup_slot_list at 0x2001d070>
notify_orientation_event -- <function notify_orientation_event at 0x2001d390>
USB_VCP -- USB_VCP(0)
__name__ -- hub_runtime
HubMethods -- <class 'HubMethods'>
MotorMethods -- <class 'MotorMethods'>
hub -- <module 'hub'>
RPCProtocol -- <class 'RPCProtocol'>
system -- <System object at 200296b0>
notify_button_event -- <function notify_button_event at 0x2001d290>
get_event_loop -- <function get_event_loop at 0x200155c0>
notify_gesture_event -- <function notify_gesture_event at 0x2001d380>
WaitMethods -- <class 'WaitMethods'>
RTTimer -- <class 'RTTimer'>
RadioBroadcastMethods -- <class 'RadioBroadcastMethods'>
Timer -- <class 'Timer'>
DeviceMethods -- <class 'DeviceMethods'>
runtime -- <module 'runtime' from 'runtime/__init__.mpy'>
MoveMethods -- <class 'MoveMethods'>
>>>
>>> os.listdir('projects')
['standalone_', 'standalone.mpy', '.slots', '15015', '13191', '21493', '7000']
>>>
>>> cat('projects/.slots')
{
0: {'name': 'UHJvamVjdCAw', 'id': 13191, 'modified': 1641021587911, 'type': 'python', 'project_id': 'E7DDg6MLrjmB', 'created': 1640675556147},
1: {'name': 'UHJvamVjdCAx', 'id': 15015, 'modified': 1641019129550, 'type': 'python', 'project_id': '53cym18n7Gxv', 'created': 1640932342297},
2: {'name': 'UHJvamVjdCAy', 'project_id': 'PTGPVlM9kK14', 'modified': 1641026738726, 'created': 1640934681576, 'id': 21493, 'type': 'python'},
3: {'name': 'UHJvamVjdCAz', 'type': 'scratch', 'modified': 1641028117920, 'id': 7000, 'project_id': 'objY1Z0dw-Ep', 'created': 1641028058971}}
>>>
注)見やすくする為、一部改行しています。
>>> os.listdir('projects/15015')
['__init__.mpy']
>>>
結論として以下の方法でimportすることができるようになりました。
- 別のスロットの「__init__.mpy」を自スロットへ「xxx.mpy」(xxxは任意)としてコピーする。
- 「from .xxx import <識別子名>」で必要なクラスや関数をimportする。
別スロットのプロジェクトをimportする
注)今後LEGOによる仕様変更で以下の方法が利用できなる可能性もあります。
from mindstorms import MSHub, Motor, MotorPair, ColorSensor, DistanceSensor, App
from mindstorms.control import wait_for_seconds, wait_until, Timer
from mindstorms.operator import greater_than, greater_than_or_equal_to, less_than, less_than_or_equal_to, equal_to, not_equal_to
import math
import json
# 別スロットからのimport用コピー
class CopyProject:
def __init__(self):
f = open('projects/.slots', 'r')
data = f.read()
f.close()
self.slots = json.loads(data.replace('\'', '"'))
def getID(self, no):
return self.slots[no]['id']
def copy(self, no_from, no_to, name):
from_path = 'projects/{}/__init__.mpy'.format(self.slots[no_from]['id'])
to_path = 'projects/{}/{}.mpy'.format(self.slots[no_to]['id'], name)
f = open(from_path, 'rb')
data = f.read()
f.close()
f = open(to_path, 'wb')
f.write(data)
f.close()
cp = CopyProject()
cp.copy(1, 0, 'lib1')
from .lib1 import State, StateMachine
# オブジェクトの定義
app = App()
hub = MSHub()
wheels = MotorPair(MSHub.PORT_B, MSHub.PORT_A)
arm = Motor(MSHub.PORT_C)
distance_sensor = DistanceSensor(MSHub.PORT_D)
color_sensor = ColorSensor(MSHub.PORT_E)
# ステートの定義
STATE_EXIT = "exit"
STATE_GO_STRAIGHT = "GoStraight"
# 直進
class GoStraight(State):
def __init__(self, name: String = STATE_GO_STRAIGHT):
super().__init__(name)
def entry(self):
wheels.start(0, -20)
def do(self) -> String:
distance = distance_sensor.get_distance_cm()
if distance and distance < 20: # 測定不能の場合None
return STATE_EXIT
return None
def exit(self):
wheels.stop()
# メイン処理
class Main:
def __init__(self):
self.__stateMachine = StateMachine()
self.__stateMachine.add(State(STATE_EXIT))
self.setup()
self.loop()
def setup(self):
# ステートの登録
self.__stateMachine.add(GoStraight())
# 開始ステートへ切り替える
self.__stateMachine.changeTo(STATE_GO_STRAIGHT)
def loop(self):
while self.__stateMachine.currentState != STATE_EXIT:
nextState = self.__stateMachine.run()
if nextState:
self.__stateMachine.changeTo(nextState)
# 外部よりのステートマシン終了
if hub.right_button.is_pressed():
print("right button is pressed !")
self.__stateMachine.changeTo(STATE_EXIT)
# プログラム開始指示を待つ
hub.left_button.wait_until_pressed()
hub.speaker.beep()
# プログラム開始
Main()
# プログラム終了(メニューに戻る)
raise SystemExit
0 件のコメント:
コメントを投稿