ROS講座31 python基礎

# 環境
この記事は以下の環境で動いています。

| 項目 | 値 |
|:-:|:-:|
| CPU | Core i5-8250U |
| Ubuntu | 20.04 |
| ROS | Noetic |
| python | 3.8.10 |

インストールについては[ROS講座02 インストール](https://qiita.com/srs/items/e0e0a9dc3f94c2d3348e)を参照してください。
またこの記事のプログラムはgithubにアップロードされています。[ROS講座11 gitリポジトリ](https://qiita.com/srs/items/25a56a6abda41dcf396e)を参照してください。

# 概要
ここまでROSノードをC++で作成していましたがpythonでも作成をすることができます。C++との違いを解説していきます。

# ソースコード

## talker
String型のROSメッセージをpublishするノードの記述です。

```python:py_lecture/scripts/python_talker.py
#!/usr/bin/env python3
import rospy
import rosparam
from std_msgs.msg import String

def talker():
    rospy.init_node('talker')
    word = rospy.get_param("~content", "default")
    pub = rospy.Publisher('chatter', String, queue_size=10)

    r = rospy.Rate(1) # 10hz
    while not rospy.is_shutdown():
        str = "send: %s" % word
        rospy.loginfo(str)
        pub.publish(str)
        r.sleep()

if __name__ == '__main__':
    try:
        talker()
    except rospy.ROSInterruptException: pass
```

* msgファイルのインクルード  
`from std_msgs.msg import String`と記述します。(パッケージ名).msgの中を読み込みます。
* privateパラメーターの読み込みは  
`word = rospy.get_param("~content", "default")`と記述します。`~(パラメーター)`という名前でアクセスします。
* publisherの宣言  
`pub = rospy.Publisher('chatter', String, queue_size=10)`と記述します。
* loggerの実行  
`rospy.loginfo(str)`で`ROS_INFO()`と同等のloggerを出力します。
* C++と違いspinOnce()を実行する必要はありません。

## listener
```python:py_lecture/scripts/python_listener.py
#!/usr/bin/env python
import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo("recieved %s", data.data)
    now = rospy.Time.now()
    rospy.loginfo("now: %f", now.to_sec())
    
def listener():
    rospy.init_node('listener')
    rospy.Subscriber("chatter", String, callback)
    rospy.spin()
        
if __name__ == '__main__':
    listener()
```

* subscriverの宣言  
`rospy.Subscriber("chatter", String, callback)`で宣言します。
* ros time  
`rospy.Time.now()`でC++の`ros::Time::now()`と同等の値を取得します。
* spin  
`rospy.spin()`と記述します。

## timerのサンプル

```python:py_lecture/scripts/python_timer.py
#!/usr/bin/env python3
import rospy

def timerCallback(event):
    rospy.loginfo("timer callback")

def timer():
    rospy.init_node('timer')
    rospy.sleep(2.0)
    rospy.loginfo("sleep 2s")
    
    rospy.Timer(rospy.Duration(2), timerCallback)
    rospy.spin()

if __name__ == '__main__':
    try:
        timer()
    except rospy.ROSInterruptException: pass
```


* sleep  
`rospy.sleep(2.0)`で2秒間のスリープをします。
* tiemrの設定  
`rospy.Timer(rospy.Duration(2), timerCallback)`で2秒ごとにtimerCallbackが起動します。

# 実行権限の付与
pythonはスクリプト言語なのでこれ自体はビルドは必要ありません。ただpythonファイルを実行するためには実行権限を付ける必要があります。

```shell:実行権限の付与
roscd py_lecture/scripts/
chmod +x python_talker.py 
chmod +x python_listener.py 
```

# 実行
各ターミナルごとに実行前に`source ~/catkin_ws/devel/setup.bash`を実行する必要があります。

```shell:1つ目のターミナル
roscore
```

```shell:2つ目のターミナル
rosrun py_lecture python_talker.py _content:=data
```

```shell:3つ目のターミナル
rosrun py_lecture python_listener.py
```

# pythonのパス
pythonのモジュール等を探索するパスは環境変数の`PYTHONPATH`に入っています。`echo $PYTHONPATH`とすると標準では`/home/{user名}/catkin_ws/devel/lib/python2.7/dist-packages:/opt/ros/kinetic/lib/python2.7/dist-packages`となっています。これらのパスにあるpythonモジュールをpythonのノード内で使用することができます。

# コメント
python自体はmakeは必要ありませんがカスタムmsgの生成等でmakeが必要なことはあります。
Noeticから（Ubuntu20.04から）python2は廃止で、python3に移行します。スクリプトの1行目は`#!/usr/bin/env python3`とする必要があります。

# 参考
[ROSwiki pythonノード](http://wiki.ros.org/ja/ROS/Tutorials/WritingPublisherSubscriber%28python%29)
# 目次ページへのリンク
[ROS講座の目次へのリンク](https://qiita.com/srs/items/5f44440afea0eb616b4a)