techno_memo

個人用の技術メモ。python・ROS・AI系のソフトウェア・ツールなどの情報を記載

トップページ

このブログのコンセプト

業務等で活用しやすい『機能単位』でノウハウをまとめる

  • イラスト1つでやりたいことがわかる構成とする

  • 一般的なPCの開発環境(Windows+Anaconda等)ですぐに試せるコードをgithubに残す

  • 詳細な機能の記事(各種ライブラリに関する使い方説明など) は他サイトのリンクにとどめる

カテゴリ

pythonによるデータ分析

  • 業務等でpythonスクリプトを用いたデータ処理・業務改善を行うことを想定した場合のノウハウまとめ

f:id:sd08419ttic:20190321220444p:plain

データ読み込み

題目 説明 リンク
数値データの読み込み 指定したフォルダの各種ファイル(csv/xlsx/json等)から数値データを読み込む リンク
画像・動画データの
読み込み
指定したフォルダの画像・動画ファイル(jpg/png/avi等)から数値データを読み込む リンク
Webサイトからの
データ取得
WEBサイトから情報を取得する リンク
命名規則・作成日などの
条件付きファイル探索
条件にマッチしたファイルだけを探索して取得する リンク

前処理

題目 説明 リンク
画像データの前処理 画像データを処理しやすいように整える(サイズ・ノイズ除去など) リンク
数値データの前処理 数値データのノイズ・欠損値を処理する リンク
数値データマージ・削減 データの内容に応じてマージ・削減する処理 リンク

特徴抽出/パターン認識

題目 説明 リンク
物体検出① 簡易的な物体検出を行う(テンプレートマッチング/色検出/エッジ検出) リンク
統計処理 入力データの特性を算出して可視化する TBD
機械学習 データの特性を学習し、適切に分類する TBD

GUI出力/ユーザー要求処理

題目 説明 リンク
GUI tkinterを用いたGUI実装 リンク
グラフ表示 matplotlibを用いたデータの可視化 リンク
exe化 pyinstllerを用いたpythonスクリプトの実行ファイル化 リンク
ユーザー入力の受理 pygameを用いたキーボード/ゲームパッド操作取得 リンク

電子工作プロトタイピング

f:id:sd08419ttic:20190221233311p:plain

前提知識

題目 説明 リンク
開発環境 プロトタイプ用ハード・開発環境に関する考え方 リンク
Raspberry Pi開発環境(ハード) 必要なハードウェアについて リンク
Raspberry Pi開発環境(ソフト) Ubuntu mate/ROSの導入方法について リンク
ソフトウェアアーキテクチャ プロトタイピング時のソフトウェアアーキテクチャについて リンク

環境構築

題目 説明 リンク
Pythonソフト構造 複数機能・ファイルから成り立つプロジェクトの構築 リンク
Python処理タイミング 各機能の動作タイミングの設計方法 TBD
Arduinoソフト構造 複数機能・ファイルから成り立つプロジェクトの構築 リンク
Arduino処理タイミング 各機能の動作タイミングの設計方法 リンク

Web機能

題目 説明 リンク
PCとの接続 リモートデスクトップ/カメラ画像の送信/信号の送信 TBD
クラウドサービスとの接続 IFTTT連携 (Google Home/スマートフォン) TBD

ROS開発

題目 説明 リンク
ROS基礎 概念/用語/コマンドの使い方 リンク
ROS基礎2 プロジェクト構築 リンク
PCとの通信 Ubuntu PCとRaspberry piとの接続/制御信号の通信 TBD

センシング

題目 説明 リンク
カメラセンシング Raspberry pi カメラを使った画像処理 TBD
各種センサ実装 各種センサの実装 TBD

pygameを用いたゲームパッド操作機能の実装

この記事の目的

 pythonのライブラリpygameを用いてゲームパッドでユーザーの入力を読み込む処理を実装する。

使用したゲームパッド


ゲームパッド Logicool ロジクール F310r 国内正規品 3年間メーカー保証

参考となるサイト https://glorificatio.org/archives/1398

pygame

pygameとは

pythonのゲーム制作用ライブラリ 2Dの画面描画/音楽再生機能/ユーザー操作入力(マウス/キーボード/ゲームパッド)が充実しており、ゲーム以外にも処理結果の描画やUIとして利用することができる。

wiki

ja.wikipedia.org

機能/使い方の詳細

aidiary.hatenablog.com

インストール

windows (anacondaの場合)

pip install pygame

Ubuntu mateの場合

sudo apt-get install python-pygame

GUI描画(Hello World) とキーボード読み込み

初期化処理でスクリーンサイズなどを設定したあとで、30msec間隔をあけながらキーボードの状態を取得して 赤丸の位置を動かす処理は、下記のように記述できる。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygame
from pygame.locals import *
import sys
 
SCREEN_SIZE = (640, 480)  # 画面サイズ (横/縦)
STEP = 10       #キーボード/ハットスイッチの反応の良さ

if __name__ == '__main__':
    # Pygameを初期化
    pygame.init()
    screen = pygame.display.set_mode(SCREEN_SIZE)   # SCREEN_SIZEの画面を作成
    pygame.display.set_caption("window test")       # Windowタイトルの設定
    X_CENTER = int(SCREEN_SIZE[0]/2)
    Y_CENTER = int(SCREEN_SIZE[1]/2)
    [circle_x, circle_y] = [X_CENTER, Y_CENTER]     #円の初期位置を設定

    # 画面描画ループ
    while True:
        screen.fill((0,0,0))     # 画面を青色で塗りつぶす
        # イベント処理
        for event in pygame.event.get():    #×ボタンによる終了
            if event.type == QUIT:  # 終了イベント
                sys.exit()
        #  キーボード状態の取得
        pressed_keys = pygame.key.get_pressed()
        # 押されているキーに応じて画像を移動
        if pressed_keys[K_LEFT]:
            circle_x = circle_x - STEP
        if pressed_keys[K_RIGHT]:
            circle_x = circle_x + STEP
        if pressed_keys[K_UP]:
            circle_y = circle_y - STEP
        if pressed_keys[K_DOWN]:
            circle_y = circle_y + STEP

        #描画範囲の上下限チェック
        if circle_x< 0:
            circle_x = 0
        elif circle_x > SCREEN_SIZE[0]:
            circle_x = SCREEN_SIZE[0]

        if circle_y< 0:
            circle_y = 0
        elif circle_y > SCREEN_SIZE[1]:
            circle_y = SCREEN_SIZE[1]

        #描画
        pygame.draw.circle(screen, (255,0,0), (circle_x,circle_y), 10)
        pygame.display.update()  # 画面を更新
        pygame.time.wait(30)     # 30msec 待ち
        

コントローラの読み込み

ジョイスティックの初期化

下記処理を初期化部分に追加するとpygameジョイスティックを初期化できる。

    pygame.joystick.init()
    try:
        joy = pygame.joystick.Joystick(0) # create a joystick instance
        joy.init() # init instance
        print("Joystick Name: " + joy.get_name())
        print("Number of Button : " + str(joy.get_numbuttons()))
        print("Number of Axis : " + str(joy.get_numaxes()))
        print("Number of Hats : " + str(joy.get_numhats()))

F310の場合、軸やボタン数を下記のように取得できる。

Joystick Name: Controller (Gamepad F310)
Number of Button : 10
Number of Axis : 5
Number of Hats : 1

ジョイスティック(アナログバー)の読み込み

アナログバーは各軸について -1~1の小数値で得られる。 F310の場合axis(2)はLRキーの強さに対応する軸なので注意。 (下記は左スティックのみ有効としている)

            #ジョイスティック(アナログバー左スティック)状態の取得
            circle_x = int((joy.get_axis(0)+1) * X_CENTER)    #joystick(横軸)の方向キーはは-1~1の範囲で取得できる
            circle_y = int((joy.get_axis(1)+1) * Y_CENTER)    #joystick(縦軸)の方向キーはは-1~1の範囲で取得できる
            # axis(2)はL2/R2キーに対応
            #ジョイスティック(アナログバー右スティック)状態の取得
            #circle_x = int((joy.get_axis(3)+1) * X_CENTER)    #joystick(横軸)の方向キーはは-1~1の範囲で取得できる
            #circle_y = int((joy.get_axis(4)+1) * Y_CENTER)    #joystick(縦軸)の方向キーはは-1~1の範囲で取得できる

方向ボタン(ハットスイッチ)読み込み

方向ボタン(ハットスイッチ)は縦横軸それぞれを-1,0,1の離散値として取得できる

            #方向ボタン (ハットスイッチ)
            hat_input = joy.get_hat(0)

            if hat_input[0] == -1:  #
                circle_x = circle_x - STEP
            elif hat_input[0] == 1:
                circle_x = circle_x + STEP
            if hat_input[1] == 1:
                circle_y = circle_y - STEP
            elif hat_input[1] == -1:
                circle_y = circle_y + STEP

上記の3つの入力手段を切り替えることができるpygameスクリプトgithubに保存した。

github.com

次回は上記の処理をROSと結合して目標速度指令を送信し、turtlesimを動かす機能を実装する。

ROS入門 ワークスペース/パッケージ/ノード (python開発環境/vscode導入)

この記事の目的

pythonを用いたROSのパッケージの作成・ノードの実装・トピックの送受信についてまとめる。 Visual studio Codeを導入し、pythonスクリプトを実装しやすい環境を構築する。

pythonを用いたROSプロジェクト構築

ROSでは処理を実装して動作させるまでに下記のワークスペース/パッケージ/ノードを準備する必要がある。 以下に最低限のコマンドなどをまとめて記述する。

用語 内容
workspace ROSプロジェクト開発の作業用フォルダ
package 機能の集合体(開発時にmakeするプロジェクトの最小単位)
node プロセスとして相互にメッセージを通信する単位

1 ワークスペースの作成

下記チュートリアルに従ってcatkin_wsを作成する

wiki.ros.org

ターミナルを開きホームフォルダ直下にcatkin_wsを作成する ここで、setup.bashはcatkin_ws内に作成したパッケージの読み込みに必要となる設定である。下記スクリプトを記載しておくと起動時に自動的に読み込まれる。

$ mkdir -p ~/catkin_ws/src
# ホーム直下にcatkin_ws/srcを作成
$ cd ~/catkin_ws/src
# srcに移動
$ catkin_init_workspace
# ワークスペースの初期化を実施
$ cd ~/catkin_ws/
# catkin_wsに移動
$ catkin_make
# ワークスペースのビルドを実施
$ echo "source ~/catkin_ws/devel/setup.bash" >> ~/.bashrc
# bashrc にプロジェクトフォルダのsetup.bashを読み込む設定を追記
$ source bashrc
#bashrcの読み込み (入力したときのみ必要。以降は起動時にbashrcから読み込まれる)

2 パッケージの作成

下記チュートリアルに従ってcatkin_ws内にパッケージを作成する

wiki.ros.org

$ cd ~/catkin_ws/src
# catkin_ws/srcに移動
$ catkin_create_pkg beginner_tutorials std_msgs rospy roscpp
# catkin_create_pkg [パッケージ名] [依存ライブラリ1] [依存ライブラリ2]...
#上記ではROSの標準メッセージ std_msgsとpython開発環境/C開発環境を読み込んでいる 

ここでパッケージ以下には下記のようなファイル・フォルダ構成としておく。(cmakelists.txtとpackage.xml以外は必要に応じて追加する)

フォルダ・ファイル名 説明
CMakeLists.txt パッケージ用のcmakeファイル(ビルドの依存関係の設定など)
package.xml rosプロジェクトの名前/依存関係を記載したファイル
include パッケージのヘッダファイル(外部公開用)
src C言語実装済みソフトウェア(使用するときのみ)
script python実装済みソフトウェア(使用するときのみ)
msg ユーザーが型定義するros message(使用するときのみ)
launch ユーザーが定義するlaunchファイル(使用するときのみ)

3 Visual Studio Codeの導入

上記まででプロジェクトの作成ができたので、pythonでのスクリプトを記述する。

pythonスクリプトの開発は標準インストールされているテキストエディタでもできるがVisual Studio Codeを用いるとコード補完などが動作するので効率が良い。

Ubuntu mate for Raspberry pi 用の Visual Studio Code のインストール手順

$ wget -qO - https://packagecloud.io/headmelted/codebuilds/gpgkey | sudo apt-key add -
#GPGキーの設定
$ sudo su
#管理者権限の付与
 . <( wget -O - https://code.headmelted.com/installers/apt.sh )
#インストール用スクリプトの読み込み

ただし、下記記事にあるようにUbuntu mate 16.04と最新バージョンのVS codeでは正しく動作しなかったので1.29にデグレした。

www.fabshop.jp

sudo apt-get install code-oss=1.29.0-1539702286 -y --allow-downgrades
#1.29へのデグレ
sudo apt-mark hold code-oss
#バージョンの固定 下記コマンドで解除可能
#sudo apt-mark unhold code-oss

実行後に下記の拡張機能を追加する。

f:id:sd08419ttic:20190625001709p:plain

上記設定後にコマンドパレットでpython2.7に切り替えるとVisual studio codeでROS対応したpythonコードを実行できる

下記チュートリアルに従ってtopicをノード間通信するpythonスクリプトを記述する。

wiki.ros.org

talker.py

#!/usr/bin/env python
# license removed for brevity
import rospy
from std_msgs.msg import String

def talker():
    pub = rospy.Publisher('chatter', String, queue_size=10)
    rospy.init_node('talker', anonymous=True)
    r = rospy.Rate(10) # 10hz
    while not rospy.is_shutdown():
        str = "hello world %s"%rospy.get_time()
        rospy.loginfo(str)
        pub.publish(str)
        r.sleep()

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

listener.py

#!/usr/bin/env python
import rospy
from std_msgs.msg import String

def callback(data):
    rospy.loginfo(rospy.get_caller_id()+"I heard %s",data.data)
    
def listener():

    # in ROS, nodes are unique named. If two nodes with the same
    # node are launched, the previous one is kicked off. The 
    # anonymous=True flag means that rospy will choose a unique
    # name for our 'listener' node so that multiple listeners can
    # run simultaenously.
    rospy.init_node('listener', anonymous=True)

    rospy.Subscriber("chatter", String, callback)

    # spin() simply keeps python from exiting until this node is stopped
    rospy.spin()
        
if __name__ == '__main__':
    listener()

上記の2つのファイルを記述後にそれぞれのpythonファイルを実行するとtopic通信が行われることが確認できる。 次回はpythonジョイスティックやGUIを行いながらトピックの通信を行うスクリプトについて記述する。

ROS入門 (概要/用語/コマンド)

この記事の目的

 ROSを使った開発の考え方、手順(チュートリアルを通じたコマンドの使い方)についてまとめる

ROSの考え方/用語の整理

ROSの特徴や利点については下記サイトに細かく記載されている。

myenigma.hatenablog.com

特に、自動運転・ロボット関連の試作に便利なライブラリ(自己位置推定など)やデバッグ用のツールが充実しており、それらに関する試作を行う場合には非常に便利なフレームワークといえる。

ROSのプロセス/通信の概念・用語

上記のようにROSは便利なフレームワークではあるが使い方や固有の用語がかなり多い。 公式チュートリアル (http://wiki.ros.org/ja/ROS/Tutorials)を参考にしても良いが、 初級の時点でファイルシステムやパッケージファイルの詳細の説明をしており、直観的な 理解が難しいと感じた。そこで、今後説明しやすいように下記にROSでアプリケーションを 構築する際のプロセス/通信に関する概念と用語をまとめてみた。

参考 http://forestofazumino.web.fc2.com/ros/ros_basics.html

用語 内容
Node プロセス(処理)
Message ノード間の送受信
Master ノード・メッセージの名称管理をする
Topic 非同期で送受信するノード間信号
Subscriber Topicを受信する側のノード
Publisher Topicを送信する側のノード
Service 同期型(受信してすぐ処理を実行する)のノード間通信
Service Server Serviceを受けて実行結果を出力するノード
Service Client Serviceを要求して結果を受理するノード

上記については下記参考書の解説を読むとわかりやすい。


ROSではじめるロボットプログラミング―フリーのロボット用「フレームワーク」 (I・O BOOKS)

ROSコマンドの使い方 (チュートリアルを利用)

ROSではノードの起動や通信内容の確認、可視化をROSコマンドを用いて行う。 基本的には、下記の順で使用することが多い。 公式のチュートリアルにあるturtlesimを用いて使い方をまとめる。

1.roscoreの起動

roscore

各ノード・送受信信号の名前の管理、標準入出力処理を行う ROS上で処理を動作させる場合は必ず起動する必要がある (起動していない状態で別ノードを起動させようとするとroscoreの起動待ちになる)

2.ノードの起動

下記の2つの方法で起動できる。 オプションが多い場合はlaunchファイルを作っておくと良い。

rosrunコマンド

rosrun turtlesim turtlesim_node
# rosrun [package_name] [node_name]
rosrun turtlesim turtle_teleop_key
# turtlesim_nodeとは別のターミナルで起動する

コマンド rosrunを使いパッケージ名とノード名を指定して起動する apt-getで入れたライブラリでは問題ないが、自作で作ったパッケージの 場合パスを通しておく必要がある。

複数のノードを立ち上げる場合はそれぞれターミナルを立ち上げて実行する。

roslaunch

roslaunch beginner_tutorials turtlemimic.launch
#事前に下記チュートリアルに従った準備が必要
#roslaunch beginner_tutorials turtlemimic.launch

launch ファイル内に立ち上げたいノードと起動時のオプション (パラメータの有効/無効など)を渡すと1つのターミナルでノードをまとめて起動できる。ライブラリによっては最初からlaunchファイルで起動できるように提供されているものがある。(turtlesimはないのでチュートリアルに従って記述する必要がある)

3.動作結果の確認

ノードを立ち上げたあと、各ノードの状態・通信内容は下記のコマンドで確認する。

コマンドによる確認

送受信中の信号一覧の表示には下記を利用する。 rostopic list では信号の送受信が成立していなくても、 受信待ちの状態となるだけでリストに出力されることに注意する必要がある。 信号が狙いの周期で更新できているかは rostopic hzなどを用いたほうが良い。

# ROSで通信している全信号リスト
rostopic list

# ROSで通信している信号の出力
# rostopic echo [topic名]
rostopic echo /turtle1/pose

#表示例
---
x: 5.544444561
y: 5.544444561
theta: 0.0
linear_velocity: 0.0
angular_velocity: 0.0
---


# ROSで通信している信号の更新周期の出力
# rostopic hz  [topic名]
rostopic hz /turtle1/pose

#表示例
subscribed to [/turtle1/pose]
average rate: 62.515
    min: 0.015s max: 0.017s std dev: 0.00047s window: 61
average rate: 62.519
    min: 0.014s max: 0.017s std dev: 0.00052s window: 123

GUIによる確認

コマンドによる確認の他に、rosのGUI機能rqt_graphでノードとトピックの関係を 可視化して接続関係を確認することができる。

# GUIツール rqt_graphを用いたノードの通信状態の可視化
rosrun rqt_graph rqt_graph

f:id:sd08419ttic:20190619225134p:plain

また、各トピックの波形 (時系列変化)を表示することもできる。

# GUIツール rqt_graphを用いた波形の時間変化の可視化
rosrun rqt_plot rqt_plot

f:id:sd08419ttic:20190619225554p:plain

rvizによる確認

ROSで用意された形式に従って位置や速度情報を出力するとロボットが空間でどのような動きをしているかをrvizを使って確認することができる。 (tfの発行なども必要なので下記チュートリアルに従う)

wiki.ros.org

rviz

f:id:sd08419ttic:20190619231201p:plain

4.動作結果の保存

動作中のノード間でどのような信号の送受信が行われたのかを保存することができる。この機能を用いることで同じカメラ信号のrosbag信号を流しながら自己位置推定処理のデバッグをシミュレーションで行うなどの使い方が可能になる。

#全ての送受信信号の保存 (ターミナルのパスにbagファイルが生成される)
rosbag record -a
#bagファイルを再生する
#再生中は記録時と同じタイムスタンプでトピックの送受信が行われる
rosbag play recorded1.bag  

よく使うコマンドリストのまとめ

https://qiita.com/tomoyafujita/items/13076d37bcac05a83530

https://raspimouse-sim-tutorial.gitbook.io/project/ros_tutorial

用語 内容
roscore マスターノードの起動 (rosを使用する際は必須)
rosrun [ノード名] ノードの起動
roslaunch [launchファイル名] launchファイルを用いたノード起動
rostopic list ros上で通信しているtopicのリスト出力
rostopic echo [topic名] topic名の内容の出力
rostopic hz [topic名] topicの更新周期の出力
rosbag record -a ros上で通信しているtopicをbagファイルに保存
rosrun rqt_graph rqt_graph rqt_graphの起動
rviz rvizの起動

次回はpythonを使ったノードの開発・トピックの通信例についてまとめる。

Raspberry pi 開発環境について(Ubuntu Mate設定/ROS導入)

この記事の目的

 RaspberryPi3(with ROS/Python) を使った電子工作の環境構築手順についてまとめる

 ROSを使ったロボット制御のソフトウェアを実装するため下記を導入する。

  • Ubuntu Mate 16.04

  • ROS Kinetic

必要なハード

下記記事のハードを用い、Raspberry pi 3 model B を使用する。

sd08419ttic.hatenablog.com

下記の手順はWindows PC で必要なソフトのダウンロードやmicroSDカードへの書き込みなどを行うことを前提とする。

環境構築手順

1.SDカードの初期化

Raspberry pimicroSDカードにOSを書き込む必要がある。

公式サイトではツールSDCardFormatterでFAT32に初期化する手順を推奨しているが、SDカードのサイズが64GB以上の場合exFATに自動的に変換してしまうので注意が必要。

下記のIOデータのハードディスクフォーマッタではフォーマット時にFAT32を明示的に選択することができる。

www.iodata.jp

2.OSイメージの焼き込み

現在(2019.6)時点で、公式サイトのUbuntu Mate は 18.04に更新されている。

Ubuntu Mate 18.04 に対してROSを入れることも可能なようであるが、まだリリースされてから日が浅くトラブルも多いと予想できるので、 ここではUbuntu 16.04を導入する。Ubuntu 16.04はすでに公式サイトからはダウンロードできなくなっているが、下記サイトからダウンロード可能である。 (torrent形式なのでBitTorrentなどを用いてダウンロードする必要がある)

ubuntu-pi-flavour-maker.org

ダウンロードしたubuntu-mate-16.04.2-desktop-armhf-raspberry-pi.img.xz は7.zipでimgファイルに展開する

sevenzip.osdn.jp

展開したimgファイルを下記のWin32 Disk ImagerでmicroSDカードに焼きこむ

sourceforge.net

3.初期設定

上記までの手順でUbuntu Mateを焼きこんだmicro SDカードを、Raspberry pi の SDカードスロットに差してmicro USBから電源供給すると Raspberry pi 3が起動する。

Wifi/地域設定/キーボード設定をインストーラーはインストーラーの手順に従って実施すればできる。

システムの更新

Mate Terminalを開いて下記コマンドを実行

sudo apt-get update 
sudo apt-get upgrade

その他初期設定に関する記事(画面の最大化なども便利)

pinky-blog.com

上記を実施するとfirefoxがクラッシュする。(バージョンによって起動しないとのこと)ブラウザにこだわりがなければ下記でchromiumを導入してしまった方が早い。

sudo apt install chromium-browser

日本語入力対応

地域設定をしておくとGUI表示はデフォルトで日本語になる 言語設定が必要なのでシステム⇒設定⇒ユーザー向け⇒言語サポートからインストールし、入力形式をfcitxに変更する 下記に画像付きの解説がされている。

deviceplus.jp

IP固定

Windows PCからリモートでのログインやデータの受け渡しのために、IPを固定しておくと便利である。 Wifiであれば、画面右上のネットワークアイコン以下のGUIを使って設定が可能である

https://qiita.com/dendensho/items/ab63e4b343607d832f21

ファイル共有設定

sambaのインストールと設定行い、Raspberry pi のファイルをWindows PC からアクセス可能にできる

sudo apt-get -y install samba

上記を実施後、下記サイトにある『UI を使って samba を設定する(おまけ)』に従うとGUIを使ってsambaの共有設定ができる。

https://pinky-blog.com/raspberry-pi/nas-samba-server-ubuntu-mate/

リモートデストップの有効化

下記コマンドでxrdpをインストールする

sudo apt-get -y install xrdp

その後、下記サイトに従って暗号化レベルの変更と日本語キーボード設定を追加する (日本語キーボード設定がないと円マークなどが正しく入力できなくなる)

algorithm.joho.info

4. ROSのインストール

公式の手順に従いros-kineticをインストールする。

sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" > /etc/apt/sources.list.d/ros-latest.list'
sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net:80 --recv-key 421C365BD9FF1F717815A3895523BAEEB01FA116
sudo apt-get update
sudo apt-get upgrade
sudo apt-get install ros-kinetic-desktop-full
sudo apt install python-roslaunch

次にrosのパッケージ管理機能の初期化/更新を行う

rosdep init
rosdep update

最後に環境変数にROSのパスを通す (ROSのコマンドを利用するために必要)

echo "source /opt/ros/kinetic/setup.bash" >> ~/.bashrc
source ~/.bashrc

上記まででRaspberry pi でROSを使う準備が整う。 ROSのcatkin_workspaceの作り方などについては今後別記事で説明する。

Raspberry pi 開発環境について(ハードウェア)

この記事の目的

 Raspberry pi の電子工作の開発環境について必要なハードウェアに関する情報を忘備録として整理する。

必須ハード

Raspberry pi 本体

カメラを使った画像認識などをすることを考慮すると、なるべく高スペックなものが望ましい。

2019年6月現在、下記のRaspberry pi 3 model B+が、最新かつ高スペックなRaspberry piである。


Raspberry Pi3 Model B+ ボード&ケースセット

ただし、1つ前のモデルであるRaspberry pi 3 model Bとのスペック差はさほどない。(CPUクロック数のわずかな上昇と通信速度の上昇程度)のと、Pi3の方が発売されてからの期間が長く各種開発環境の動作報告も多いので 以降の記事ではRaspberry pi 3の利用を前提として記載する。


Raspberry Pi3 Model B ボード&ケースセット

AC電源アダプタ

Raspberry Pi 3は micro USB type B端子での電力供給が必要となる。ただし、消費電流が2.5Aとなることからスマホ等のACアダプタでは対応できない可能性が高いため動作確認できているACアダプタを利用することを推奨する。 また、電源ボタンがなくケーブルからの電力供給の有無で起動を判定するため、頻繁な起動/終了の繰り返しで本体の端子が痛む可能性がある。スイッチボタンによる電力の遮断ができる下記のようなACアダプタを使うと便利である。


Raspberry Pi用電源セット(5V 3.0A)-Pi3フル負荷検証済

micro SDカード

Raspberry PiではOS等のソフトウェアをmicroSDカードに保存する必要がある。OSイメージ+基本的なツールを導入するための最低限の必要容量は4GB程度以上だが、今回の用途ではROS環境一式やカメラでの計測データを保存する必要 があるため32GBのものを利用する。 (なお、32GBを上回る容量の場合、SDカードをFAT32でフォーマットする使うツールに気を付ける必要がある。別途記載予定)


東芝 Toshiba microSDHC 32GB + SD アダプター + 保管用クリアケース [バルク品]

また、インストール手順でPCからmicro SDカードへの書き込みをする必要がある。PCにmicro SDを読み込む手段がない場合にはUSBのSDカードリーダーが必要となる。 (速度にこだわらなければ、最近は100均などでも購入可能)


Transcend USB 3.0 Super Speed カードリーダー (SD/SDHC UHS-I/SDXC UHS-I/microSDXC UHS-I 対応) ブラック 2年保証 TS-RDF5K

マウス/キーボード/HDMIケーブル

Raspberry pi の操作に利用する。マウス/キーボードはUSB接続のものであればよほど問題はないが、無線接続したい場合は動作実績があるものを利用する方が無難。

初期設定を済ませると、デスクトップPCからリモート接続して利用することも可能となるためあまりこだわらなくてもよい。

モバイルバッテリ

前述したように、Raspberry pi 3 は micro USB type Bによる給電が可能なのでモバイルバッテリからの電源供給ができる。ただし、最近のモバイルバッテリは消費電流が低い場合に省電力モードになってしまったり、 Raspberry pi のピーク時の2.5Aを出力できないなどの場合があるため、動作実績があるものを選んだほうが良い。下記サイトで実機での確認がされている。実際に私が試したところAnker PowerCore 10000、 cheero Power Plus 10050mAhともに問題はなかった。

消費電力が大きなRaspberry Pi3に最適のモバイルバッテリーは? - 猿まね電子工作

カメラ

Raspberry pi では専用のカメラモジュールによる画像取得とPC等で使われるUSB Webカメラによる画像取得が可能である。

カメラモジュールは小型・軽量で省電力ではあるが、取り付けなどに苦労する。下記のような台座とセットになっているものの方が良い。


For raspberry pi カメラモジュール 5MP Raspberry Pi 3 b+ / Pi Zero Camera とケース500W画素 感光チップOV5647センサー

Webカメラは、台座が安定してHD撮影可能なものとして下記などが良い。


LOGICOOL HDウェブカム フルHD動画対応 C615

ブレッドボード/ジャンパハーネス

Raspberry pi のGPIOに各種の電子部品を接続する際に必要となる。ジャンパハーネスは端子にオス-メスの組み合わせがあるので一定数まとめ買いしておいた方が良い。


ELEGOO 120pcs多色デュポンワイヤー、arduino用ワイヤ—ゲ—ジ28AWG オス-メス オス-オス メス –メス ブレッドボードジャンパーワイヤー

ブレッドボードはいろいろな場所への取り付けに対応できるよう小型/横長タイプで複数用意しておいた方が良い。


ELEGOO 400タイポイント ブレッドボード3PCS 、Arduino用ジャンパーワイヤ 4電源レール

次回はRaspberry pi の環境構築 (OS設定/ROSの導入/開発ツール類の設定)について記述する。

C言語経験者がpythonで開発する際のポイントまとめ

この記事の目的

 C言語(特に組込みソフト)経験者がpythonで開発する際のポイントをまとめる (よく聞かれたこと、聞かれることの整理用)

 * C言語開発者がpythonを開発する際のポイント・・・階層構造をもつpythonスクリプトのインポート方法

 * 別ファイルのimport方法・・・階層構造をもつpythonスクリプトのインポート方法

 * 変数/定数の定義・・・const・#define・グローバル変数・ローカル変数の扱い

C言語開発者がpythonを開発する際のポイント

 最近のデータ処理・AI関連の技術・オープンソースの発展により、組込み制御ソフト・C/C++など他言語の開発者であってもpythonによるプロトタイピングやデータ分析に手を出すケースが多くなっている。 (私もその一人であるが)

 C言語で組み込み等のソフトの開発経験者がpythonを開発する際のポイントは下記参考記事にまとめられている

qiita.com

 特に留意しておく点は

 インデント・・・C言語と異なりインデント(字下げ)が明示的に意味を持つ。if文、for文などはインデントでどこまでが対象かを規定

 型定義・・・動的型付けがされる言語であり、変数宣言時に型は不要。作る際は楽だが意図せぬ代入がされることが多いためデバッグによる確認が必要

 参照渡し・・・pythonにおける代入は基本的に参照渡しとなる (C言語でいうポインタ的な扱い)

 その他上記の参考サイトにはpythonの動作の仕組み(インタプリタ)、型定義、if文など制御文の書式などに一通り触れられているが、C言語でよくある複数ファイル/フォルダからなる構造の構築方法やC言語でよく使うconst/defineなどについては記載されていないので追加でまとめておく。

別ファイルのimport方法

 C言語の制御ソフトでは、機能別に複数ファイル/フォルダに分けて実装する際、include文とmake環境へのインクルードパス設定を記述する。pythonで同様のことをしたい場合、import文で別ファイルの記述内容を参照することが可能である。

f:id:sd08419ttic:20190602183346p:plain

上記の図でルートフォルダにあるスクリプトroot_func.pyから各フォルダのサブファンクションを直接参照する場合は下記のように記述できる。

#1階層下のファイルを呼び出し
from SubA.sub_funcA import func_sub_funcA
from SubB.sub_funcB import func_sub_funcB

#2階層下のファイルを呼び出し
from SubA.Sub01.sub_funcA1a import func_sub_funcA1a
from SubA.Sub01.sub_funcA1b import func_sub_funcA1b
from SubA.Sub02.sub_funcA2 import func_sub_funcA2

#3階層下のファイルを呼び出し
from SubA.Sub01.Sub03.sub_funcA3 import func_sub_funcA3

if __name__ == '__main__':
    func_sub_funcA()
    func_sub_funcB()
    func_sub_funcA1a()
    func_sub_funcA1b()
    func_sub_funcA2()
    func_sub_funcA3()

一方で、サブフォルダSub01にあるsub_funcA1a.pyから上位階層にある関数をインポートしたい場合、相対パスを通すだけだとエラーが出力されてしまう。

(実行フォルダよりも上位パスにあるファイルのインポートを禁止するpythonの仕様のため)

そこで、サブディレクトリからのインポート時にpythonの探索パスにルートディレクトリのパスを加える方法を用いる。

上図でサブフォルダ01から上位階層や他フォルダを参照するためにシステムパスを変更する。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys

#prj_config.pyまでのパスを追加する

#os.path.abspath(__file__) : pythonファイルが存在する絶対パスを取得
#os.path.dirname(): 入力したパスの一つ上の階層のパスを取得

ROOTPATH = os.path.dirname(os.path.abspath(__file__))
CFGPATH = os.path.dirname(os.path.dirname(ROOTPATH))

#sys.path.append() :入力したパスをpythonファイルの探索パスに追加する

sys.path.append(CFGPATH)

#from にルートフォルダからのフォルダ階層/ファイル名を記載する
from SubA.Sub01.sub_funcA1b import func_sub_funcA1b
from SubA.Sub02.sub_funcA2 import func_sub_funcA2
from SubA.Sub01.Sub03.sub_funcA3 import func_sub_funcA3

def func_sub_funcA1a():
    print("sub_funcA1a is called!!")

if __name__ == '__main__':
    func_sub_funcA1b()
    func_sub_funcA2()
    func_sub_funcA3()
    pass

システムパスにルートフォルダを追加することで、開発時に自分のファイルからの相対的なフォルダ関係ではなくルートフォルダからの階層を考慮した記述で実装することが可能である。

変数/定数の定義

const (定数)について

C言語では変数宣言時にconst修飾子を追加することで書き込み不可な定数を利用することができる。pythonには同様の機能はない (完全に書き込みを防ぐ変数を定義できない)

慣習として、全文大文字の変数を定数として扱うことが多いらしい。

 参考サイト www.tohoho-web.com

defineについて

C言語では#defineを用いてよく使うパラメータやコンパイル時に切り替えたい機能のフラグなどを記述することが一般的である。 (特に組込みソフトでは込み入った実装がされることが多い)

pythonでは#defineにあたる機能はないため、上記のような実装は通常の関数/変数を用いて実現する必要がある。

複数ファイルにまたがる機能の開発を行う場合においては、プロジェクト共通で使う定義(機能の切り替え、主要な値の定義など)設定用のコンフィグを記載したpythonファイルをルートフォルダに作成し、他ファイルからimportするようにつくるとC言語に近いスタイルで開発することができる。

global変数・static変数について

pythonではモジュール内のglobal変数を定義し、他関数から参照することができる。しかし、下記の方法では複数ファイルからの更新はできずC言語のような他ファイルの変数をexternで参照して更新という使い方ではない。

ama-ch.hatenablog.com

他機能から利用する変数はクラス内にまとめて記載したほうが開発後の再利用性やデバッグなどの効率が良い。下記のようなクラスを用いるとC言語のイメージに近い実装ができると考えている。

class sub_function_X():

    #初期化用クラス (変数の宣言/初期化)
    def __init__(self):

        #グローバル変数 (他機能からの参照がある変数)
        self.glb_var1 = 0
        self.glb_flg = True

        #スタティック変数 (公開範囲をクラス内だけにしたい変数)
        self.__sts_var1 = 0

    #クラス内ローカル関数
    def __static_func(self):
        self.__sts_var1 = self.__sts_var1 + 1
        print ("This is Private function")

    #グローバル関数
    def global_func(self):
        self.__static_func()
        print ("This is Global function")
        print(self.__sts_var1)

if __name__ == '__main__':
    test_instance = sub_function_X()
    test_instance.global_func()

Arduinoのソフト構造・スケジューリングについて

この記事の目的

 Arduinoで複数の機能を効率よく開発するため、ファイル構造・スケジューリング設定がしやすいプロジェクトの構築方法を記載する

 特に下記に重点を当てて説明する

  • ファイル構造・・・1ファイルを機能単位で構成し、複数のCファイル/Hファイルを扱えるようにする

  • スケジューリング・・・各機能を順序性を保ちながら一定周期で呼び出しやすいソフト構造を取り入れる

Arduinoのソフトの実装方法と課題

 Arduino向けのソフトを開発する場合に最も基本的な方法は公式サイトで配布されているArduino IDEを用いて.inoファイルに処理を記載する方法である。

公式サイトへのリンク

www.arduino.cc

簡易的なスクリプトの開発

n.mtng.org

小規模なプロジェクトであれば上記ツールで記載しても問題はないが、複数人数で開発する処理内容が多いプロジェクト(外部通信/センシング/アクチュエータ制御を同時に実施するなど)の場合

下記のような課題がある。

  • 1ファイル内の処理内容が長くなり複数人同時開発がしにくい

  • 動作タイミングが処理の長さに依存し、一定周期で呼び出す処理などが記載しにくい。 (たとえばPCとの通信が発生するときだけ、センサ処理が遅れるなど)

そこで、下記に示すように複数ファイルに独立した処理を記載して、スケジューラで周期的に呼び出すことが可能なソフトウェアアーキテクチャの構築を試みる。

f:id:sd08419ttic:20190527220839p:plain

機能別ファイル構造の構築

公式IDEを使う場合には気づきにくいが、ArduinoではC言語の書式に従い複数のcpp/hファイルに記載されたファイルのコンパイル・リンクが可能である。

下記サイトに従いVisual Studio CodeC言語の記述に従って開発可能な環境を構築するとintellisenseによるコード補間やジャンプも利用できるため効率が良い。

qiita.com

ポイントは下記となる。

  • "プロジェクト名.ino"ファイルはarduinoプロジェクトのコンパイルに必須である。(処理は記述せずダミーファイルとして残す)

  • 関数setup(初期化関数)、loop(周期関数)は必須関数となる。また、ヘッダArduino.hをインクルードする。(下記のように標準ヘッダにまとめると管理しやすい)

  • その他の機能は任意のファイル名.cpp上に開発し、ヘッダのインクルードによって機能を呼び出す。

  • 関数loopに後述のスケジューリング処理を入れると周期呼び出しがしやすい環境が構築できる。

#ifndef __STDAFX_H__
#define __STDAFX_H__
 
#include "Arduino.h"


void setup();
void loop();

#endif /* __STDAFX_H__ */

処理の周期呼び出しのスケジューリング

 複数の処理を一定周期(数ミリ秒など)で順序に従い呼び出すためには、一定周期の経過をカウントして呼び出すスケジューラを使うと効率化できる。

 下記サイトに公開されてるintervalクラスを用いると上記機能に容易に対応できる。

lowreal.net

 ただしそのまま使うとタイマの単位がミリ秒とやや長い間隔なので、millis()をmicros()にしてlong型でカウンタを保持するとより短い周期にも対応できる。

template <uint16_t time>
class interval {
    unsigned long next_run = 0;

    template <class T>
    void _run(T func) {
        unsigned long now = micros();
        if (next_run < now) {
            func();
            next_run = now + time;
        }
    }

    interval() {}
public:

    template <class T>
    static void run(T func) {
        static interval<time> instance;
        instance._run(func);
    }
};

 上記クラスを使って生成した周期的な動作タイミングに加え、スケジューラ内でも分散カウンタ (switch case文で実装)をすると通信/センシング/アクチュエータなどの各機能が周期条件以内で呼び出されることを確認しながら処理を実装することが可能になる。(下記は5msec毎に種類の違う処理を順版に呼び出す例)

#include "stdafx.h"
#include "communication.h"
#include "sensor.h"
#include "actuator.h"

void setup()
{
    com_init();
    sensor_init();
    actuator_init();
}

void loop()
{
    interval<5000>::run([]{
    switch (cyc_count)
    {
       case 0:
        com_main();
        break;
       case 1:
        sensor_main();
        break;
       case 2:
        actuator_main();
        break;
    }
    if (cyc_count>=2)
    {
            cyc_count = 0;
    }
    else
    {
            cyc_count++;
    }
}

上記をまとめたarduinoの開発用テンプレートをgithubに保存する。

github.com