<jupyter_start><jupyter_text>Basic Motion - JetBotの基本制御JetBotブラウザベースのプログラム実行環境へようこそ。テキストやソースコード、グラフィックの表示が1つにまとまった*Jupyter Notebook*です。*Jupyter*に馴染みがない人は、トップバーから、``Help``のドロップダウンメニューをクリックしてください。そこに、*Jupyer*上でプログラミングをするために役に立つリファレンスが記載されています。このノートブックは、JetBotの基本的な制御について説明します。 Importing the Robot class(Robotクラスをメモリに読み込む)JetBotのプログラミングを始めるにあたって、``Robot``クラスをImportします。[Robotクラス](https://github.com/NVIDIA-AI-IOT/jetbot/blob/master/jetbot/robot.py)はJetBotのモーター制御をおこなうためにPythonコードで実装したクラスになります。このクラスを取り込む事で、Robotのモーター制御を簡単におこなえます。``Robot``クラスは、``jetbot``パッケージに含まれています。``Robot``クラスをimportするために、セルを選択し、``ctrl + center``を入力するか``play``アイコンを選択します。これで、セルの中に含まれるコードが実行されます。<jupyter_code>from jetbot import Robot<jupyter_output><empty_output><jupyter_text>これで、``Robot``クラスが読み込まれました。  クラスは、例外はありますが基本的にはそのままでは使えないため、**インスタンス化**することで利用可能になります。  次は下記のコードを実行して、クラスを**インスタンス化**して、`robot`変数としてクラスを扱えるようにします。<jupyter_code>robot = Robot()<jupyter_output><empty_output><jupyter_text>Robotをコマンドで制御 ``Robot``インスタンスを作成し、`robot`という変数で名付けました。JetBotを制御するために、このインスタンスを使います。最大出力のの30%で反時計回りに回転させるために、下記のように変数を呼び出します。> 注意: 下記のコマンドで**JetBotが動きだします。**JetBotの周りにスペースがある事を確認してください。<jupyter_code>robot.left(speed=0.3)  # 30%の出力で左に旋回する<jupyter_output><empty_output><jupyter_text>すばらしい、あなたはJetBotを反時計回りに回転できました。> もし、JetBotが左側に回転しなかったら、モーターの配線が逆になっているかもしれません。Robotの電源を落とし、間違っている側の``赤``と``黒``の配線の指し先を逆にしてみてください。>> ケーブルのチェックは常に気をつけてください。システムが起動中は、ケーブルの配線は変えないように！それでは、``stop``メソッドを呼び出し、JetBotを停止します。<jupyter_code>robot.stop()  # モーターを停止する<jupyter_output><empty_output><jupyter_text>少しだけ動かして止めたい場合は、Pythonの``time``パッケージを使用します。<jupyter_code>import time<jupyter_output><empty_output><jupyter_text>このパッケージには、次のコマンドの実行まで、指定した秒数の間だけコード実行を止める事ができる``sleep``関数が定義されています。  0.5秒の間だけ、JetBotを右回転させるために、下記コードを実行します。<jupyter_code>robot.left(0.3)  # 30%の出力で左に旋回する
time.sleep(0.5)  # 0.5秒間コードが次に進むことを止める
robot.stop()  # モーターを停止する<jupyter_output><empty_output><jupyter_text>すばらしい、これで、ちょっとの間、JetBotを左回転し、止める事ができるようになりましたね。> ``left``メソッドの引数の``speed=``はPythonでは省略が可能です。[Robot](https://github.com/NVIDIA-AI-IOT/jetbot/blob/master/jetbot/robot.py)クラスは、``right``や``forward``や``backward``メソッドを使用できます。1秒間、50%のスピードで前方に移動するために、新しいセルを作成しましょう。``b``キーを押すか、``+``アイコンを選択すると、下側に、セルが選択された状態の新しいセルが生成されます。セルが生成されたら、JetBotを1秒間 50%のスピードで前方に進ませるためのコードをタイプしてみましょう。  左右のモーターを個別に制御するここまでは、``left``や``right``などのコマンドを使ってRobotを制御する方法をやってみました。  左右のモーターのSpeedをそれぞれ設定したら、どうなるでしょう？ それには、2つのやり方があります。1つめの方法は、``set_motors``メソッドを呼び出す方法です。例として、左30%、右60%にでモーターの値を設定し、2秒間ほど、左方向にアーチを描くように動かしてみます。<jupyter_code>robot.set_motors(0.3, 0.6)
time.sleep(1.0)
robot.stop()<jupyter_output><empty_output><jupyter_text>すばらしい! 右方向にアーチを描くように動かせましたね、しかし、実際は、同じ事を達成するのに、違う方法をつかう事もできます。``Robot``クラスは、2つのそれぞれのモーターを形取る``left_motor``と``right_motor``という属性を持っています。これらの属性は、``Motor``クラスのインタンスで、それぞれに、``value``という属性を含んでいます。この``value``という属性は、新しい値がアサインされたときに、``events``が生成される[traitlet](https://github.com/ipython/traitlets)(https://github.com/ipython/traitlets) で定義されています。モータークラスの中で、値が変更されるとモーターコマンドがUpdateされる関数がアタッチされます。そのため、下記を実行すると、まったく同じ事が達成できます。下記を実行しましょう。<jupyter_code>robot.left_motor.value = 0.3
robot.right_motor.value = 0.6
time.sleep(1.0)
robot.left_motor.value = 0.0
robot.right_motor.value = 0.0<jupyter_output><empty_output><jupyter_text>同じ方法で、JetBotを動かしていきましょう。  Traitletsライブラリを使ってスライダーウィジェットの値とモーター出力を連動させる [traitlets](https://github.com/ipython/traitlets)の本当にいけている機能として、他のtraitletsと連動できる事にあります！  Jupyter Notebook内で、trailetsを使う事で、グラフィカルな``widgets``を作ることが可能になり、とても便利です。ブラウザからコントロールする``widgets``とモーター出力を連動させたり、ウィジェットに値を反映してビジュアル的に表示することが可能となります。何を言っているのかわからない？まずはやってみましょう。モーターをコントロール可能なスライダーを作成し、モーター制御と連動させて表示します。<jupyter_code>import ipywidgets.widgets as widgets
from IPython.display import display

# 左右モーター用の垂直スライダーを、値の範囲 [-1.0, 1.0] で作成します。
left_slider = widgets.FloatSlider(description='left', min=-1.0, max=1.0, step=0.01, orientation='vertical')
right_slider = widgets.FloatSlider(description='right', min=-1.0, max=1.0, step=0.01, orientation='vertical')

# スライダーを隣り合わせに配置する水平ボックスコンテナーを作成します
slider_container = widgets.HBox([left_slider, right_slider])

# スライダーを配置したコンテナを表示します
display(slider_container)<jupyter_output><empty_output><jupyter_text>2つの垂直なスライダーが表示されているはずです。>Jupyterの機能：Jupyter Labでは、分割したウィンドウにセルの出力を表示できます。これは、他の場所で実行したコードの出力をスクロールすることなく確認する場合に役立ちます。>これを行うには、セルの出力を右クリックして、「Create New View for Output」を選択します。 その後、新しいウィンドウをドラッグしてウィンドウの表示位置を変更できます。今はまだスライダーをクリックして上下に動かしても、何も起こらないです。  なぜならスライダーの値とモーター出力は連動していないためです。この連動はtraitletsパッケージの``link``関数で実現できます。<jupyter_code>import traitlets

# スライダーとモーター制御を連動させます
left_link = traitlets.link((left_slider, 'value'), (robot.left_motor, 'value'))
right_link = traitlets.link((right_slider, 'value'), (robot.right_motor, 'value'))<jupyter_output><empty_output><jupyter_text>スライダーを動かすと、それぞれのモーターが回転し始めます。実際に生成された``link``関数は、双方向リンクになっています。つまり、モーターの値を変更したら、スライダーの表示も変更されます。  次は`robot`インスタンスでモーター出力を変更して、実際にスライダーに反映されるところをみてみましょう。<jupyter_code>robot.forward(0.3)
time.sleep(1.0)
robot.stop()<jupyter_output><empty_output><jupyter_text>もし、スライダーとモーター制御の連動を解除したい場合は、``unlink``メソッドを呼び出します。<jupyter_code>left_link.unlink()
right_link.unlink()<jupyter_output><empty_output><jupyter_text>双方向リンクが必要ない場合、つまりスライダーはモーターの値を表示するだけとして、スライダーの操作をモーターに反映しない場合にどうするかを試してみましょう。  この場合はdlink関数で実現できます。 左側の入力がソースで、右側の入力がターゲットです。<jupyter_code>left_link = traitlets.dlink((robot.left_motor, 'value'), (left_slider, 'value'))
right_link = traitlets.dlink((robot.right_motor, 'value'), (right_slider, 'value'))<jupyter_output><empty_output><jupyter_text>スライダーを動かしてもモーターは反応しなくなりますが、プログラムでモーターの値を変更すると、スライダーに反映されます。  ボタンイベントと関数を連動させるtraitletsを使うと、値と値の連動だけではなく、関数とイベントの連動も実現できます。これらの関数は、objectが変更されるたびに、呼び出されます。``old``や``new``のように変化の情報も送られます。JetBotを操作するボタンを作成し、表示しましょう。<jupyter_code># ボタンウィジェットを作成します。
button_layout = widgets.Layout(width='100px', height='80px', align_self='center')
stop_button = widgets.Button(description='stop', button_style='danger', layout=button_layout)
forward_button = widgets.Button(description='forward', layout=button_layout)
backward_button = widgets.Button(description='backward', layout=button_layout)
left_button = widgets.Button(description='left', layout=button_layout)
right_button = widgets.Button(description='right', layout=button_layout)

# ボタンウィジェットを表示します。
middle_box = widgets.HBox([left_button, stop_button, right_button], layout=widgets.Layout(align_self='center'))
controls_box = widgets.VBox([forward_button, middle_box, backward_button])
display(controls_box)<jupyter_output><empty_output><jupyter_text>十字に配置されたボタンが表示されました。でも、まだ何も反応はしません。  ボタンの``on_click``イベントに関連するいくつかの関数を作成する必要があります。<jupyter_code>def stop(change):
    robot.stop()
    
def step_forward(change):
    robot.forward(0.4)
    time.sleep(0.5)
    robot.stop()

def step_backward(change):
    robot.backward(0.4)
    time.sleep(0.5)
    robot.stop()

def step_left(change):
    robot.left(0.3)
    time.sleep(0.5)
    robot.stop()

def step_right(change):
    robot.right(0.3)
    time.sleep(0.5)
    robot.stop()<jupyter_output><empty_output><jupyter_text>これで関数が定義できました。ボタンのon-clickイベントと関数を対応づけます。<jupyter_code># on-clickイベント発生時、指定した関数を実行するように設定します。
stop_button.on_click(stop)
forward_button.on_click(step_forward)
backward_button.on_click(step_backward)
left_button.on_click(step_left)
right_button.on_click(step_right)<jupyter_output><empty_output><jupyter_text>これで、先ほど表示された十字ボタンをクリックすると、JetBotが動きます！    Heartbeat Killswitch(通信不良時にJetBotを停止する)ここでは、PC(ブラウザ)とJetBotの通信が途切れてしまった時に、動いているJetBotを止めるための方法について説明します。  PCとJetBotの通信確認には[Heartbeat](https://github.com/NVIDIA-AI-IOT/jetbot/blob/master/jetbot/heartbeat.py)を使います。これは、JetBotとの通信が可能かどうかをシンプルに特定する方法です。  Heartbeatによる通信確認の間隔は、スライダーで操作できるようにします。  Heartbeatが通信不良と判断した場合は、Heartbeatの'`status`'属性が、``dead``にセットされます。通信が復元されるとすぐに、heartbeatの'`status`'属性が、``alive``に変わります。  下記では通信不良になった時に、`robot.stop()`を実行します。通信が復活した場合は何もしません。<jupyter_code>from jetbot import Heartbeat

heartbeat = Heartbeat()

# この関数は、heartbeatの「alive」ステータスが変化したときに呼び出されます。
def handle_heartbeat_status(change):
    if change['new'] == Heartbeat.Status.dead:  # ステータスがdeadになったら、モーターを停止します。
        robot.stop()
        
heartbeat.observe(handle_heartbeat_status, names='status')

period_slider = widgets.FloatSlider(description='period', min=0.001, max=0.5, step=0.01, value=0.5)
traitlets.dlink((period_slider, 'value'), (heartbeat, 'period'))

display(period_slider, heartbeat.pulseout)<jupyter_output><empty_output><jupyter_text>下記のコードを実行し、sliderを下げるとどうなるかみてみましょう。  また、JetBotとPC間の通信を切断するとどうなるかみてみましょう。<jupyter_code>robot.left(0.2) 

# それでは、「period」スライダーの値を小さくして、heartbeatがdeadになるようにしてみましょう。<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.13031293787949555, 'fraction_numerical': 0.012143858010275572, 'mean_word_length': 15.129943502824858, 'pattern_counts': {'":': 0, '<': 73, '<?xml version=': 0, '>': 80, 'https://': 6, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '18', 'dir': 'jupyter-structured-clean-dedup', 'id': '78815', 'license': 'MIT', 'n_tokens_mistral': 5424, 'n_tokens_neox': 5097, 'n_words': 211, 'path': 'notebooks/basic_motion/01_basic_motion_JP.ipynb', 'repo_name': 'FaBoPlatform/jetbot', 'size': '8564'}