読者です 読者をやめる 読者になる 読者になる

Yura YuLife

ITエンジニアの覚え書き

python の multiprocecssing.Pool.map で複数の引数を持つ関数を扱う

python の multiprocessing を用いてマルチスレッド処理を行う際に、複数の引数を持つ関数を扱うときの Tips です。

動作環境

方法

やり方は簡単で、目的となる関数をラップするだけです。

# -*- coding: utf-8 -*-

from multiprocessing import Pool


def calc(a, b, c):
    result = a + b * c
    print("%d + %d * %d = %d" % (a, b, c, result))
    return result


def wrap_calc(num):
    return calc(*num)


def main():
    pool = Pool(processes=4)
    args = [(1, 2, 3), (2, 3, 4), (3, 4, 5), (4, 5, 6)]
    print("result = %s" % pool.map(wrap_calc, args))


if __name__ == '__main__':
    main()

結果

$ python pool.py 
1 + 2 * 3 = 7
2 + 3 * 4 = 14
4 + 5 * 6 = 34
3 + 4 * 5 = 23
result = [7, 14, 23, 34]

上記のように、wrap_calc メソッドにリスト型で引数を渡し calc メソッドに展開してあげれば解決します。

参考URL

ansible でネストされた item に対してループを回す

動作環境

  • ansible 2.1

with_subelements でネストされたループを回す

with_items のかわりに with_subelements を利用することで、ネストされた item に対してループを回すことが出来ます。

vars/main.yml

users:
  - sam:
    email:
      - sam@example.com
      - sam@example.jp
  - mary:
    email:
      - mary@example.com
  - mike:
    email:
      - mike@example.com
      - mike@example.net

tasks/main.yml

- name: print email addresses
  debug: msg="{{ item[1] }}"
  with_subelements:
    - "{{ users }}"
    - email

実行結果

ok: [localhost] => (item=({u'sam': None}, u'sam@example.com')) => {
    "item": [
        {
            "sam": null
        }, 
        "sam@example.com"
    ], 
    "msg": "sam@example.com"
}
ok: [localhost] => (item=({u'sam': None}, u'sam@example.jp')) => {
    "item": [
        {
            "sam": null
        }, 
        "sam@example.jp"
    ], 
    "msg": "sam@example.jp"
}
ok: [localhost] => (item=({u'mary': None}, u'mary@example.com')) => {
    "item": [
        {
            "mary": null
        }, 
        "mary@example.com"
    ], 
    "msg": "mary@example.com"
}
ok: [localhost] => (item=({u'mike': None}, u'mike@example.com')) => {
    "item": [
        {
            "mike": null
        }, 
        "mike@example.com"
    ], 
    "msg": "mike@example.com"
}
ok: [localhost] => (item=({u'mike': None}, u'mike@example.net')) => {
    "item": [
        {
            "mike": null
        }, 
        "mike@example.net"
    ], 
    "msg": "mike@example.net"
}

参考URL

Mac で ROS の環境構築

Mac OSX で ROS を動かすまでの環境構築手順です。

環境

  • Mac OSX El Capitan
  • ROS Kinetic

ROS の環境構築

GUI 系のツールを動かそうとすると Qt5 周りの依存関係でハマったので、とりあえず ROS の基本パッケージのみ(ROS-Comm)の環境構築を行いました。

基本的には公式ページの手順 に則るだけです。

Homebrew の設定

Homebrew を最新版にし cmake をインストールしておきます。

$ brew update
$ brew install cmake

ROS のインストールに必要な依存パッケージをインストールできるようにします。

$ brew tap ros/deps
$ brew tap osrf/simulation
$ brew tap homebrew/versions
$ brew tap homebrew/science

Python, Pip の設定

Homebrew でインストールしたパッケージを Python から使えるようにします。

$ mkdir -p ~/Library/Python/2.7/lib/python/site-packages
$ echo "$(brew --prefix)/lib/python2.7/site-packages" >> ~/Library/Python/2.7/lib/python/site-packages/homebrew.pth

pip が入っていない場合は pip をインストールします。

$ brew install python  # brew Python comes with pip
$ sudo -H python -m pip install -U pip  # Update pip

pip でパッケージをインストールします。

$ sudo -H python -m pip install -U wstool rosdep rosinstall rosinstall_generator rospkg catkin-pkg Distribute sphinx

rosdep の初期化

rosdep をインストールできたら、初期化を行っておきます。

$ sudo -H rosdep init
$ rosdep update

catkin workspace の作成

catkin_ws というディレクトリを作成します。

$ mkdir ~/catkin_ws
$ cd ~/catkin_ws

ROS のインストール

今回は GUI 系のツールなしの、最小限の ROS パッケージのセットをインストールします。

$ rosinstall_generator ros_comm --rosdistro kinetic --deps --wet-only --tar > kinetic-ros_comm-wet.rosinstall
$ wstool init -j8 src kinetic-ros_comm-wet.rosinstall

workspace の初期化が済んだら、依存するパッケージをインストールします。

$ rosdep install --from-paths src --ignore-src --rosdistro kinetic -y

そして workspace 内の各パッケージをビルド、インストールします。

$ ./src/catkin/bin/catkin_make_isolated --install -DCMAKE_BUILD_TYPE=Release

ROSの動作を確認

ターミナルに ROS の環境変数を設定しておきます。

$ source ~/catkin_ws/install_isolated/setup.bash

ROS の動作を確認します。

$ roscore &

$ rosnode list
/rosout

$ rosnode info /rosout
--------------------------------------------------------------------------------
Node [/rosout]
Publications: 
 * /rosout_agg [rosgraph_msgs/Log]

Subscriptions: 
 * /rosout [unknown type]

Services: 
 * /rosout/set_logger_level
 * /rosout/get_loggers


contacting node http://YumaM-iMac-3.local:53388/ ...
Pid: 26340

$ rosparam list
/rosdistro
/rosversion
/run_id

動いた!

参考URL

ROS開発にPyCharmやCLionを使う際のTips

この記事では ROS の開発に Jetbrains 製の IDE である PyCharm や CLion を使う際に、便利に開発を進めるための設定を紹介しています。

環境

  • Ubuntu 16.04 (14.04)
  • ROS Kinetic (Indigo)
  • PyCharm 2016.X
  • CLion 2016.X

環境変数を設定

Ubuntu にインストールした PyCharm や CLion をランチャーから起動すると、ROS の環境変数が読み込まれておらず、ROSの関数やメッセージを import / include すると not found とワーニングが表示されます。

そこで、ROSの環境変数を読み込んだターミナルから IDE を起動してあげることで、メッセージや関数が正しく補完されるようになります。

$ source /opt/ros/kinetic/setup.bash  # ROS Kinetic の場合
$ source /opt/ros/indigo/setup.bash   # ROS Indigo の場合
$ source ~/catkin_ws/devel/setup.bash
$ clion &  # CLion をバックグラウンドで起動
$ charm &  # PyCharm をバックグラウンドで起動

追記(2016/10/27)

以下のように Unity の launcher ファイルを書き換えることで、シェルから IDE を立ち上げる必要がなくなります。

PyCharmの場合 ~/.local/share/applications/jetbrains-pycharm.desktop CLionの場合 ~/.local/share/applications/jetbrains-clion.desktop

[Desktop Entry]
Version=1.0
Type=Application

...

# 以下の行を削除
Exec=bash -c "/path/to/your/clion/bin/pycharm.sh %f"  # PyCharm
Exec=bash -c "/path/to/your/clion/bin/clion.sh %f"  # CLion

# 以下の行を追記する(PyCharm, ROS Kinetic の場合)
Exec=bash -c "source /opt/ros/kinetic/setup.bash; source /path/to/your/catkin_ws/devel/setup.bash; /path/to/your/pycharm/bin/pycharm.sh" %f

# 以下の行を追記する(CLion, ROS Kinetic の場合)
Exec=bash -c "source /opt/ros/kinetic/setup.bash; source /path/to/your/catkin_ws/devel/setup.bash; /path/to/your/clion/bin/clion.sh" %f

...

型ヒントを利用 (PyCharm)

Python の docstring に変数や戻り値の型を記述すると、PyCharm がメンバ変数の補完や型の誤りの指摘をしてくれます。

例えば、PoseStamped 型のトピックのコールバック関数を例に取るとこんな感じです。

import math

import rospy
from geometry_msgs.msg import PoseStamped

def callback_pose(pose):
    u""" PoseStamped のトピックのコールバック

    :param geometry_msgs.msg.PoseStamped pose: 変数 pose に関する説明
    :rtype: float  # 関数の戻り値の型
    :return: pose の原点からの距離  # 関数の戻り値の説明
    """
    # pose. と打つと pose を補完、 pose.pose と打つと position や orientation を補完してくれる
    position = pose.pose.position
    distance = math.sqrt(position.x ** 2 + position.y ** 2 + position.z ** 2)
    return distance


rospy.Subscriber("/some/topic", PoseStamped, callback_pose)

ROS の Pose 型と PoseStamped 型などは間違いやすいうえ、Python だと実行するまで気づけなかったりするので、PyCharm の type hinting を活用するとミスが減って効率アップできます。

参考URL

FTDI FT2232C用のudevルール

FTDIのFT2232C (Dual RS-232)というUSBシリアルを接続した際に、他のデバイスと識別できるよう、/dev/以下にシンボリックリンクを作成するudevのルールです。

通常のUSBデバイスならば、Vendor IdとProduct Idを指定すれば良いのですが、FT2232Cは2つのシリアルポートが存在するため、少し工夫が必要です。

環境

USBデバイスとして認識していることを確認

lsusb コマンドを実行して、 ベンダーIDとプロダクトIDが0403, 6010なるデバイスが存在することを確認します。

$ lsusb | grep "0403:6010"
Bus XXX Device XXX: ID 0403:6010 Future Technology Devices International, Ltd FT2232C Dual USB-UART/FIFO IC

udevルールの作成

99-ft2232c.rulesというファイルを以下の内容で作成します。

# udev rule for FT2232C (Dual USB-UART/FIFO IC)
ACTION=="add", SUBSYSTEMS=="usb", ATTRS{interface}=="Dual RS232", SYMLINK+="ft2232c-%s{bInterfaceNumber}"

作成したファイルを /etc/udev/rules.d/以下にコピーします。

$ sudo cp 99-ft2232c.rules /etc/udev/rules.d/

シンボリックリンクが貼られたことを確認

USBケーブルを抜き差しすると、 /dev/ft2232c-00, /dev/ft2232c-01 として、2つのシリアルポートが認識されていることが確認できます。

$ ls -al /dev/ | grep USB
lrwxrwxrwx  1 root root           7  7月 25 18:48 ft2232c-00 -> ttyUSB0
lrwxrwxrwx  1 root root           7  7月 25 18:48 ft2232c-01 -> ttyUSB1
crw-rw-rw-  1 root dialout 188,   0  7月 25 14:07 ttyUSB0
crw-rw----  1 root dialout 188,   1  7月 25 18:53 ttyUSB1

参考URL

ROS Pythonでオイラー角とクォータニオンの相互変換

ROSでオイラー角とクォータニオンを変換するには、tfパッケージの関数を利用すれば良いのですが、 単体の関数として使うには少々使いづらいので、簡単なラッパー関数を作りました。

オイラー角からクォータニオンへの変換

import tf
from geometry_msgs.msg import Quaternion

def euler_to_quaternion(euler):
    """Convert Euler Angles to Quaternion

    euler: geometry_msgs/Vector3
    quaternion: geometry_msgs/Quaternion
    """
    q = tf.transformations.quaternion_from_euler(euler.x, euler.y, euler.z)
    return Quaternion(x=q[0], y=q[1], z=q[2], w=q[3])

実行例

>>> import math
>>> euler_to_quaternion(Vector3(0.0, 0.0, 0.0))
x: 0.0
y: 0.0
z: 0.0
w: 1.0

>>> euler_to_quarternion(Vector3(0.0, 0.0, math.pi / 2.0))
x: 0.0
y: 0.0
z: 0.707106781187
w: 0.707106781187

クォータニオンからオイラー角への変換

import tf
from geometry_msgs.msg import Vector3

def quaternion_to_euler(quaternion):
    """Convert Quaternion to Euler Angles

    quarternion: geometry_msgs/Quaternion
    euler: geometry_msgs/Vector3
    """
    e = tf.transformations.euler_from_quaternion((quaternion.x, quaternion.y, quaternion.z, quaternion.w))
    return Vector3(x=e[0], y=e[1], z=e[2])

実行例

>>> quaternion_to_euler(Quaternion(0.0, 0.0, 0.0, 1.0))
x: 0.0
y: -0.0
z: 0.0

>>> quaternion_to_euler(Quaternion(0.0, 0.0, 0.7071, 0.7071))
x: 0.0
y: -0.0
z: 1.57079632679

参考URL

ROS Pythonにおけるuint8[]の扱い方

ROSでuint8[]型を利用しているトピックをPythonで使う場合にハマった点です。

環境

rospyにおけるuint8[]型の扱い

ROSのメッセージ型に関するリファレンスを参照すると、uint8[]型について以下のような記述があります。

uint8 has special meaning in Python. uint8[] is treated as a Python bytes so that it is compatible with other byte-oriented APIs in Python.

rospy treats uint8[] data as a bytes, which is the Python representation for byte data. In Python 2, this is the same as str.

Pythonでは、ROSのuint8[]型がbytes型(Python 2系ではstr型)として扱われるらしく、これは時に厄介な問題を引き起こします。

発生した問題

とあるセンサー群の測定値を測定するメッセージ型を以下のように定義しました。

foo_package/msg/FooSensor.msg

uint8[] value

そして、以下のようなシンプルなPublisherとSubscriberを作成しました。

publisher.py

import rospy
from foo_package.msg import FooSensor

def publish():
    publisher = rospy.Publisher('/foo', FooSensor, queue_size=1)
    value = FooSensor(value=[100, 110, 120])
    r = rospy.Rate(1.0)
    while not rospy.is_shutdown():
        publisher.publish(value)
        r.sleep()

publish()

subscriber.py

import rospy
from foo_package.msg import FooSensor

def callback(data):
    rospy.loginfo(data)
    rospy.loginfo(data.value)

def subscribe():
    rospy.Subscribe('/foo', FooSensor, callback)
    rospy.spin()

subscribe()

publisher.py, subscriber.pyを実行すると、以下のような2つのINFOレベルのログが出力されます。

  • [INFO] [WallTime: 1461145922.975544] value: [100, 110, 120]
  • [INFO] [WallTime: 1461145941.678116] dnx

data自体を出力すると、valueにint型の数値が入っているのが確認できるのですが、data.valueを出力するとdnxという文字列が出力されてしまい、配列の要素をint型として利用することが出来ません。

対処方法

対処方法はあまり根本的なものではないのですが、valueの各要素のASCIIコードが本来のintの値なので、ord関数で変換してあげることでint型の配列を得ることが出来ます。

def callback(data):
    int_array = [ord(n) for n in data.value]
    rospy.logwarn(int_array)

再度実行すると[100, 110, 120]という配列が出力されます。

ただし、この処理は直感的でなく、uint8[]型以外に対してはエラーを返すため、あまりオススメできません。出来れば、メッセージ型の定義部分でuint16[]等を利用することで回避するのが好ましいと思われます。

参考URL