CozmoをSlackで操作するPythonプログラミング

日本でも多くの人が利用しているコミュニケーションツール「Slack」でCozmoを動かすプログラミングをしてみよう

cozmo, slack

Slackとは

Slack(スラック)は、1つのワークスペースを使ってメンバーとコミュニケーションするツールです。

サービスは2014年から始まっています。

すでに世界中で600万人以上のユーザーが利用しています。

日本でも多くの企業や団体で利用されています。

小規模から大規模な環境まで対応していて、無料でも利用でき有料プランも提供されています。

無料と有料は利用する規模や機能によって違います。

小規模で基本的な機能だけの利用であれば無料で十分使えると思います。

Slackサイト

SlackのBot(ボット)を使う

Slackには簡単に利用できるボット機能があります。

そしてslackbotというPythonのライブラリが配布されていて、特定のメッセージを受け取って返事をしたり何らかの処理をすることができます。

この仕組みを利用してslackbotで受け取ったメッセージをCozmoに喋らせてみます。

まずはslackbotを使えるように準備を行います。

Botを使うための準備

SlackのワークスペースにCozmoを操作するためのBotを作成します。

ここではすでにSlackワークスペースがあってBotを作成するための権限のあるユーザーでログインできることを前提とします。(Slackの始め方と使い方についてはこちらを参照してください)

下記のリンクからBot作成ページへ行ってください。

Bot作成ページ

ここでBotの名前を設定します。

フィールドに入力したら下の「Add bot integration」ボタンを押して作成します。

すると以下のページが表示されます。

このページに表示されているAPI Tokenは後ほどプログラムの中で使用するのでメモしておきます。

この画面ではBotのアイコン画像を変更することもできます。

変更したら画面下にある「Save integration」を押して保存します。

Botが作成できるとSlackの画面に以下のようにBotが表示されます。(ここでは「robot」という名前でBotを作成しています)

これでBotを使う準備ができました。

次にBotを動かすためにslackbotライブラリをインストールします。

slackbotライブラリのインストール

作成したBotをカスタマイズするためにslackbotライブラリをインストールします。

SlockのBot機能に「Real Time Messaging API」というAPIがあって、slackbotライブラリはこのAPIを使ってBotをプログラミングしています。

GitHubで公開しているので詳細は以下から確認できます。

GitHub – lins05/slackbot: A cht bot for Slack (英語)(https://slack.com).

ここではPython3系の実行環境でslackbotライブラリを使えるようにします。

もしまだPythonが実行できない環境の場合はPython3をインストールしてください。

slackbotライブラリはpip3で使えるようにします。

Python3をインストールするとpip3も自動的にインストールされます。

Python3の環境が整えば以下のコマンドでslackbotライブラリを使えるようにします。

$ pip3 install slackbot

そして、以下のディレクトリ構成でPythonプログラムのファイルを作成します。

ディレクトリ名は自由に変えられますが、ここではslackbotという名前のディレクトリにファイルを作成します。

slackbotディレクトリ配下にrun.pyとslackbot_settings.pyを作成します。

$ mkdir slackbot
$ cd slackbot
$ touch run.py slackbot_setting.py

※touchコマンドで中身が空のファイルが作成されます。

エディタを使ってこれから説明する内容を編集する際にファイルを新規作成してもいいです。

特にWidowsのコマンドプロンプトはtouchコマンドがないのでエディタを使ってファイルを作成してください。

そして、このライブラリはプラグインとして機能を実装できるようになっているので、プラグインの仕組みを使ってCozmoにコマンドを送ります。

そのためのpluginsディレクトリを作成します。

pluginsディレクトリ配下に__init__.pyとrobot_ctrl.pyを作成します。

pluginsディレクトリをプラグインとして読み込ませるために、このディレクトリはパッケージにする必要があるので__init__.pyを作成しています。

ファイルの中身は空で作成しても問題ないです。

$ mkdir plugins
$ touch __init__.py robot_ctrl.py

※先ほどと同じようにtouchコマンドで中身が空のファイルが作成されます。

これもエディタでこれから説明する内容を編集する際にファイルを新規作成してもいいです。

__init__.pyはファイルの中身は空で作成します。

robot_ctrl.pyは後ほどCozmoを操作するプログラムを記載していきます。

run.pyとslackbot_settings.pyの編集

エディタでrun.pyとslackbot_settings.pyをそれぞれ以下の内容で編集してファイルを作成してください。

run.py

# coding: utf-8

from slackbot.bot import Bot

def main():
    bot = Bot()
    bot.run()

if __name__ == "__main__":
    print('start slackbot')
    main()

slackbot_settings.py

# coding: utf-8

# Bot作成時に作られたAPI Tokenを設定(これでBotを特定)
API_TOKEN = "xxxx-xxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxx"

# このBot宛にメッセージを送るとデフォルトで返信する文字列
DEFAULT_REPLY = "ボットのぼっちゃんです"

# プラグインであるサブディレクトリ名のリスト
PLUGINS = ['plugins']

slackbotを起動する

上記の編集したらrun.pyを起動します。

$ python3 run.py

そしてSlackの画面で先程作成したBot(ここでは「robot」という名前)へ何か適当にダイレクトメッセージを送信すると、「ボットのぼっちゃんです」と返信したかと思います。

これでBotが動作することを確認できました。 終了するには、「Control + C」キーをします。

robot_ctrl.pyの編集

作成したBotへ機能を追加してCozmoを操作します。

ここではCozmoを操作するプログラムをrobot_ctrl.pyへ書いていきます。

まずはCozmoを喋らせてみましょう。

以下のコードをrobot_ctrl.pyへ記述します。

# coding: utf-8
import sys
import cozmo
from cozmo.util import degrees
from slackbot.bot import default_reply
from slackbot.bot import respond_to
from slackbot.bot import listen_to

# Slackからコマンド以外のものはCozmoが喋る
@default_reply()
def default_func(message):
    global msg
    msg = message.body['text'] # メッセージを取り出す
    cozmo.run_program(cozmo_say)

# sayコマンド:Cozomoが発話する言葉を指定
@respond_to(r'^say (.*)')
@listen_to(r'^say (.*)')
def cozmo_say(message, args):
    global msg
    msg = args
    cozmo.run_program(cozmo_say)

def cozmo_say(robot: cozmo.robot.Robot):
    if (msg is not None) and (len(msg) > 0):
        robot.say_text(msg).wait_for_completed()

slackbotライブラリのdefault_replay、respond_to、listen_toのデコーダを使ってCozmoを操作します。

それぞれのデコーダはSlackからメッセージを送信したときに呼ばれます。

タイミングは以下です。

  • default_reply:どのデコーダにも当てはまらない場合に呼ばれます(ダイレクトメッセージのみ)
  • respond_to:Botに対してメッセージ(@botname)されたときに呼ばれます
  • listen_to:Botが参加しているチャネル内にメッセージが送られると呼ばれます

上記コードの場合、@respond_toも@listen_toも呼ばれない場合@default_replyが呼ばれて、def default_func(message)関数が実行されます。

引数にはslackbotライブラリで定義されているMessageクラスのオブジェクトが渡されます。

message.body[‘text’]でSlackで送信されたメッセージを取り出します。

そのメッセージをCozmoに喋らせるためにmsg変数へ代入してdef cozmo_say(robot: cozmo.robot.Robot)関数を実行しています。

@respond_toと@listen_toが呼ばれれば、def cozmo_say(message, args)関数が実行されます。

@respond_toと@listen_toが呼ばれる条件は「(r’^say (.*)’)」となります。

この意味は、正規表現でメッセージの先頭が「say」で半角スペースの後に何らかの文字があった場合となります。

つまりSlackでBot宛のダイレクトメッセージかBotが参加しているチャットに「say こんにちは」のようなメッセージがあった場合に呼ばれて、def cozmo_say(message, args)関数が実行されます。

引数にはMessageクラスのオブジェクトと「say」と半角スペースの後の何らかの文字が渡されます。

つまり、第2引数のargsがCozmoに喋らせる文字列になります。

それをmsg変数へ代入してdef cozmo_say(robot: cozmo.robot.Robot)関数を実行しています。

Cozmo SDKを有効にする

SDKを使ってCozmoと接続します。

まずはデバイスのWiFi設定でCozmoのSSIDを選んで、Cozmoアプリを起動してCozmoと接続します。

接続後に表示されるメインメニュー画面右上の一般設定アイコンをタップします。

一般設定画面のメニューから、「SDKを有効にする」ボタンを押します。

画面を左にスワイプすると表示されると思います。

SDKが有効になると次の画面になってプログラム実行待ちの状態になります。

この状態でターミナルからCozmoに向けたPythonプログラムを実行することができます。

run.pyを起動したあとに、Bot宛にsayコマンドを入力するとCozmoが喋ります。

$ python run.py

Slackの入力例)say こんにちは

以下はCozmoを操作するコマンドとそのプログラムの一覧です。

say以外のプログラムもrobot_ctrl.pyへ追記して実行してみてください。

Cozmo操作コマンド一覧

このサンプルはTwitterとCozmoを接続して、コマンドをツィートすることでCozmoに動作を命令することができるプログラムです。

ツィートで発行できるコマンドは以下です。

say

入力方法:say メッセージ sayコマンドは入力したメッセージをCozmoが発話します。

日本語でも英語でも発話します。

例)say こんにちは

Cozmoは「こんにちは」と発話します。

# sayコマンド:Cozomoが発話する言葉を指定
@respond_to(r'^say (.*)')
@listen_to(r'^say (.*)')
def cozmo_say(message, args):
    global msg
    msg = args
    cozmo.run_program(cozmo_say)

def cozmo_say(robot: cozmo.robot.Robot):
    if (msg is not None) and (len(msg) > 0):
        robot.say_text(msg).wait_for_completed()

Cozmoはデフォルトで日本語を話します。

でも正直あまりキレイではありません。

日本語を話している様子はこちらをご覧ください。

ちなみにCozmoに英語を喋らせてみると、、とても下手くそな英語を喋ります。

英語を日本語で喋ろうとします。

日本語にローカライズしているためなのでしょうか。

以下は、「I am Cozmo. You can control me by Slack.」と話しているつもりのCozmoです。

drive

入力方法:drive 秒数 driveコマンドは指定した秒数でCozmoが移動します。

正の数を指定すると前に進み、負の数を指定すると後ろに進みます。

もしCozmoが障害物を検知したら移動を停止します。

例)drive 3

例)drive -3

# driveコマンド:Cozmoの移動
@respond_to(r'^drive (.*)')
@listen_to(r'^drive (.*)')
def cozmo_drive(message, args):
    global duration
    duration = args
    cozmo.run_program(do_drive)

def do_drive(robot: cozmo.robot.Robot):
    drive_duration = extract_float(duration)
    if drive_duration is not None:
        drive_speed = 50
        drive_dir = "forwards"
        if drive_duration < 0:
            drive_speed = -drive_speed
            drive_duration = -drive_duration
            drive_dir = "backwards"
        robot.drive_wheels(drive_speed, drive_speed, duration=drive_duration)

turn

入力方法:turn 回転する角度 turnコマンドは指定した角度にCozmoが回転します。

正の数を指定すると右回り、負の数を指定すると左回りに回転します。

指定できる値は-360から360です。

例)turn 90

例)turn -90

# turnコマンド:Cozomoが左右に回転する
@respond_to(r'^turn (.*)')
@listen_to(r'^turn (.*)')
def cozmo_turn(message, args):
    global turn
    turn = args
    cozmo.run_program(do_turn)

def do_turn(robot: cozmo.robot.Robot):
    drive_angle = extract_float(turn)

    if drive_angle is not None:
        robot.turn_in_place(degrees(drive_angle)).wait_for_completed()

lift

入力方法:lift 高さの値 liftコマンドは指定した値でCozmoのリフトの位置が変わります。

有効な値の範囲は0から1です。

例えば「lift 0.5」と入力するとリフトは中央の位置まで移動します。

例)lift 1(最も下にある状態で最も上に移動)

# liftコマンド:Cozmoのリフトが上下する
@respond_to(r'^lift (.*)')
@listen_to(r'^lift (.*)')
def cozmo_lift(message, args):
    global lift
    lift = args
    cozmo.run_program(do_lift)

def do_lift(robot: cozmo.robot.Robot):
    lift_height = extract_float(lift)

    if lift_height is not None:
        robot.set_lift_height(height=lift_height).wait_for_completed()

head

入力方法:head 頭の角度 headコマンドは指定した角度にCozmoが頭を動かします。

指定できる有効な値は、-25から44.5です。0を指定すると正面を向きます。

負の数を指定すると正面から下を向き、正の数だと正面から上を向きます。

例)head 40

# headコマンド:Cozmoの頭が上下する
@respond_to(r'^head (.*)')
@listen_to(r'^head (.*)')
def cozmo_head(message, args):
    global head
    head = args
    cozmo.run_program(do_head)

def do_head(robot: cozmo.robot.Robot):
    head_angle = extract_float(head)

    if head_angle is not None:
        head_angle_action = robot.set_head_angle(degrees(head_angle))
        clamped_head_angle = head_angle_action.angle.degrees
        head_angle_action.wait_for_completed()

※Cozmoカメラの画像をSlackに送信できるかどうかは現在調査中です。

ソースコードまとめ

参考にしたサイト