こんにちは!ふるまいチームのエンジニア、市川です!
以前、下記の記事でLOVOTのふるまいづくりについてご紹介させて頂きました。
そこでも言及しましたが、わたしたちが呼んでいる「ふるまい」とは下記の事を指します。
- 感情(学術的な理論に基づく心のモデル)
- 感情や認識情報に基づいた意思決定(何をするか?)
- 意思決定に基づいた体への動作指示(手・足・目・瞼などの動き)
これらの処理は、NeoDM(Neo Decision Maker)と呼ばれる一つのサービスで担っています。 NeoDMでは他にも下記の事をやっています。
- 自身の姿勢の推定(抱っこされている?転けている?など)
- 画像認識結果のフィルタリング
- 障害物・崖の検知
- 経路計画
- 歌の認識
- その他
これら複数の事をできるだけ同時にこなさなければいけません。
「100個の事を同時に考えてください」と言われても困りますよね*1。
人間の場合は1つずつこなしていくかもしれませんが、例えば1個目の処理に「10秒待つ」という処理がある場合、2個目の処理が始まるのは少なくとも10秒以上後になってしまいますね。 待ってる間に他の処理をしておきたいです。
そんな良い感じのスケジューリングをしてくれるtrioというライブラリを導入しています。
NeoDMはpythonで書かれていて、trioはpythonの並行処理ライブラリ*2です。
このtrioにより、次のように記述されています(説明のために簡略化してあります)。
# main関数を開始する(並行処理の開始) trio.run(main) async def main(): # 並行処理させる様々なクラスをインスタンス化 # (実際はグローバル変数のようになっていて、インスタンス間で相互にアクセスできる) poller = Poller() physical_state_updater = PhysicalStateUpdater() ... # 各インスタンスのrunメソッドを子タスクとして開始する async with trio.open_nursery() as nursery: nursery.start_soon(poller.run) nursery.start_soon(physical_state_updater.run) ... # 機体内の情報をかき集めるクラス class Poller: async def run(self): # 一定周期でループさせるfor文(25Hz) async for _ in trio_util.periodic(1/25): # redisというデータベースに機体内の情報があるのでそれをかき集める # 例えば電池残量、各種センサの情報など ... # 自身の姿勢を推定するクラス class PhysicalStateUpdater: async def run(self): # 一定周期でループさせるfor文(25Hz) async for _ in trio_util.periodic(1/25): # Pollerで集めた各種センサ情報を元に自身の姿勢を推定する ...
雰囲気が伝わったでしょうか?
trioの使い方に関しては下記にまとめておりますので、良かったらご参考ください。
また、PyConJP 2019にて、弊社のJohn BelmonteさんがLOVOTでtrioを使っている事について発表しました。 trioの導入理由に関しても触れられています。 興味がある方はご覧ください。
おわりに
NeoDMの並行処理についてご紹介しました。 弊社ではそんなNeoDMの開発メンバーを募集しています。
また、ふるまい以外の分野でも募集しておりますので、LOVOTに関わるお仕事に興味ある方は是非ご検討ください。