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処理タイミング 各機能の動作タイミングの設計方法 リンク

通信機能

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

ROS開発

題目 説明 リンク
ROS基礎 概念/用語/コマンドの使い方 リンク
ROS基礎2 プロジェクト構築 リンク
PCとの通信 Ubuntu PCとRaspberry piとの接続/制御信号の通信 [リンク](https://sd08419ttic.hatenablog.com/entry/2019/12/02/004456)

センシング

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

Virtual Box (Ubuntu) の導入

この記事の目的

 Windows 10 PCにVirtual Box + Ubuntu 16を導入する方法をまとめる  (グラフィック関係のトラブルシューティングと共有フォルダ・コピペ設定などを含む)

1. 仮想PCツールについて

VmwareとVirtualboxではどちらがレスポンスが上?。低スペックでもとりあえず使える最近のVM | アロエのおうち

代表的な仮想PCツールとしてVirtual BoxとVMWareがある。

上述の記事のように性能的にはややVMWareが優勢。 ただし、Virtual Boxは拡張機能をインストールしなければ商用でも無料で利用できる。 上記を考慮してVirtual Boxをインストールする。

2. VirtualBox導入方法

1. OSのISOファイルの取得

通常のUbuntuのインストールと一緒 (日本語版のISOを下記からダウンロード) www.ubuntulinux.jp

2. VirtualBoxのダウンロードとインストール

下記からWindows用のインストーラーをダウンロードしてインストール

www.oracle.com

メモリ容量などはPCスペックに応じて設定する

3. Ubuntuのインストール

上記でダウンロードしたISOを下記から設定して読み込ませる。

f:id:sd08419ttic:20200223003839p:plain

仮想PCを起動し、通常のUbuntuと同様にインストールする。 インストール後再起動するときには、ISOファイルの設定を元に戻しておく。

画面が正しく表示されない場合は下記コマンドを実行

sudo apt-get update
sudo apt-get upgrade

再起動後、low-graphic mode error が出た場合はCtrl+Alt+F1でコンソールモードに移行して下記コマンドを実行

sudo apt -y purge ubuntu-desktop
apt -y purge lightdm
sudo apt-get install tasksel
sudo tasksel install ubuntu-desktop

4. ネットワーク設定

インストール後はWindows PCのネットワークが設定されている場合はUbuntuからもネット接続できる。 ただし、Proxyがある場合はシステム設定から別途設定する必要がある。

5. Windows/Ubuntu間の共有フォルダの設定

WindowsUbuntuでファイルの受け渡しができるように共有フォルダを設定できる。 まず、共有フォルダの場所をVirtual Boxの下記画面で設定する。

f:id:sd08419ttic:20200223004345p:plain

次に、Ubuntuに追加のドライバをインストールする。Virtual Boxの『Guest Addtions CDイメージの挿入』を選択する

f:id:sd08419ttic:20200223004615p:plain

ディスク挿入時にインストールを促されるので、ドライバをインストールする。

その後下記コマンドを入力し、共有フォルダをマウントする。

sudo su -
sudo gpasswd -a [アカウント名] vboxsf
#  (アカウント名は users コマンドで確認できる)

再起動後にWindowsUbuntuで共有フォルダが設定できていることを確認できる。 f:id:sd08419ttic:20200223225430p:plain

6. コピーアンドペーストの設定

WindowsUbuntu間でコピーアンドペーストを許可する設定はVirtualBoxの設定⇒一般⇒高度で変更できる。 f:id:sd08419ttic:20200223225650p:plain

『ホストOSからゲストOSへ』でWindowsからUbuntuへコピペができる。 『双方向』だとその逆も可能となる。

Raspberry pi でWebカメラ映像を処理する方法(OpenCV)

この記事の目的

 Raspberry piWebカメラを用いて撮影したカメラ動画をストリーミング配信して、他端末からの画像の確認/データ処理する方法をまとめる

1. Webカメラ動画のストリーミング配信

Raspberry pi に接続したWebカメラの映像を他デバイスに配信することで低コストで監視カメラや移動ロボット用カメラとして利用することができる。 本記事では、下記の環境で動画のストリーミングを行った。

項目 バージョン
Raspberry pi Raspberry pi 3
OS Ubuntu Mate 16.04
Camera Logicool C615
OpenCV 3.1 (Python)

Webカメラのストリーミングはmjpg-streamerというソフトを利用する。下記のコマンドでインストールできる。

sudo apt-get update
sudo apt-get install -y cmake libv4l-dev libjpeg-dev imagemagick   #mjpg-streamerに必要なパッケージのインストール
svn co https://svn.code.sf.net/p/mjpg-streamer/code/mjpg-streamer mjpg-streamer   #mjpg-streamerのダウンロード
cd mjpg-streamer   #ディレクトリの移動
sudo make install   #makeとインストールの実施

次に、Webカメラの起動とストリーミング配信を行う。Webカメラが正しく接続されているかを下記コマンドで確認する。

lsusb
#正しくWebカメラが認識できていれば下記のようにカメラ名が表示される
#Bus 001 Device 005: ID XXXXXXXXX Logitech, Inc. Webcam C615

上記で正しくWebカメラが認識できていた場合、下記コマンドでストリーミングの開始ができる

sudo ./mjpg_streamer -i "./input_uvc.so -f 10 -r 320x240 -d /dev/video0 -y -n" -o "./output_http.so -w ./www -p 8080"

#フレームレート10fps サイズ 320×240 の画像をポート8080から配信

この際、ストリーミングのオプションを下記のように設定することができる。

引数 オプションの内容 設定例
-d バイス名(複数接続時の識別用) /dev/video0
-r ビデオ解像度 320x240
-f フレームレート 10
-y YUVフォーマットの有効・無効 追記時に有効
-q ポート番号 8080
-p 画質YUVフォーマット時のみ有効 80

繰り返し使う処理とする場合には、下記サイトに紹介されているようにshファイルとして作成しておくと管理しやすい

nyabot.hatenablog.com

正しく実行できたら、LAN上の他端末でブラウザからRaspberry pi のアドレス:ポート番号のurlにアクセスできる。

http://192.168.0.X:8080

ストリーミングを終了するには、起動させたターミナルでctrl + c を押す。

2. Webカメラの画像をOpenCVで処理する

上記によりRaspberry pi からWebカメラの画像を配信することはできたが、実際にはこれらの画像を入力として各種の画像処理(物体検出など)を加えたい場合も多い。 そこで、Raspberry pi 上でOpenCVで画像を処理する場合とPCでストリーミングした画像を取得してOpenCVで処理する2パターンについて実装する。 物体検出などの計算量の多い処理をさせるのであれば、Raspberry piWebカメラで撮影データをストリーミングさせるだけにしておいた方が良い。

2.1 Raspberry pi 上でWebカメラ画像を処理する

OpenCVWebカメラにアクセスする機能(VideoCapture)を用いて画像を処理する。

サンプルコードは下記のようになる。

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

cap = cv2.VideoCapture(0) # VideoCaptureのインスタンスを作成する。(複数のカメラがあるときは引数で識別)

while True:
    ret, frame = cap.read()     # 1フレーム読み込む

    # 画像を表示する
    cv2.imshow('Image', frame)

    k = cv2.waitKey(1)  #引数は待ち時間(ms)
    if k == 27: #Esc入力時は終了
        break

# 終了処理
cap.release()
cv2.destroyAllWindows()

2.2 ストリーミングした画像を取得してOpenCVで処理をする

ストリーミングされた動画を別PCで受信して処理する方法

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

 #mjpg-streamerを動作させているPC・ポートを入力
URL = "http://192.168.0.10:8080/?action=stream"
s_video = cv2.VideoCapture(URL)

while True:
    ret, img = s_video.read()
    cv2.imshow("WebCamera form Raspberry pi",img)
    key = cv2.waitKey(1)
    if k == 27: #Esc入力時は終了
        break

ROS端末間のTopic通信 (デスクトップPCとRaspberry pi )

この記事の目的

 ROS 端末間 (デスクトップPCとRaspberry pi )のTopic 通信についてまとめる

1. デスクトップ PC とRaspberry pi 間のTopic 通信

ROSでは複数の端末間でのTopic通信を行う機能が提供されている。(下記参考サイト参照)

wiki.ros.org

qiita.com

 上記2つ目のサイトに説明があるようにROSは『Master』端末が『roscore』を実行し、それ以外の『Slave 』端末がMaster からの調停に従ってTopicを送受信する。(複数のSlaveを存在させることも可能) 一般的にはMasterをPC (システム全体を管理するPC)、Slaveを移動ロボット、小型機器などで構成することが多い。

 同一のLANにROSを搭載したデスクトップPCとRaspberry pi が接続されている場合は下記の手順でROS Topic通信を行うことが可能である。

今回利用している環境

  • Master PC (Desktop) : Ubuntu 18.04 + ROS melodic (有線接続)
  • Slave PC (Raspberry pi) : Ubuntu Mate 16.04 + ROS kinetic (無線接続)

1.1 IPアドレスの確認/設定

最初に接続対象となる機器のIPアドレスを確認する必要がある。IPアドレスはターミナルを起動して下記コマンドを実行すると確認できる。

ifconfig #出力される文字列の "inet "の値を確認

LANの設定によってはPCのIPアドレスが毎回変動してしまい以降のROS設定を毎回修正する必要があるため固定IPとしておくと良い。 IPの固定はUbuntuのネットワーク設定から可能である。(下記はUbuntu18.04における設定画面とUbuntu Mate 16.04における設定画面)

f:id:sd08419ttic:20191201004344p:plain

f:id:sd08419ttic:20191202004426p:plain

1.2 SSHの設定

Slave端末を移動ロボットに搭載して通信する場合はSSHの設定をしてMaster端末からアクセスできるようにしておくと便利である。 SSHの導入は下記コマンドから行う。(Master/Slave共に実施)

sudo apt-get install openssh-server

その後、SlaveのRaspberry pi で下記設定を実施してPort 22を開放する。

sudo ufw allow 22  #Port 22のFireWall許可
sudo /etc/init.d/ssh restart # sshの再起動
sudo systemctl enable ssh # 起動時にsshが起動するように設定

上記設定後、ターミナルに下記コマンドを入力するとMasterからSlave PCの処理を実施できるようになる。

ssh -p 22 ユーザー名@ローカルアドレス名

1.3 ROS設定の修正

ROSで端末間通信を行うために、MasterとSlaveでそれぞれ下記コマンドを実行する。

export ROS_MASTER_URI=http://192.168.0.2:11311
export ROS_IP=192.168.0.2
export ROS_MASTER_URI=http://192.168.0.2:11311
export ROS_IP=192.168.0.3

いずれも利用時に毎回書くのは面倒なので.bashrcに記載しておくと良い。

Ubuntuにファイアフォール(ufw)を導入済みの場合下記コマンドで無効化しておく。

sudo ufw allow 11311
sudo ufw reload

1.4 ROSノードの起動

上記設定を実施した状態でROSノードを立ち上げ、TopicをPublishすると相互にデータがやり取りできる。

(動作確認には下記記事のtalker.pyとlistener.pyなどを用いると良い)

sd08419ttic.hatenablog.com

正しく動作するとSlaveで起動したtalker.pyのtopicをMasterで取得(もしくはその逆)できることが確認できる。

次回はArduinoUbuntu PCの通信方法についてまとめる。

Ubuntu18.04用 ROS環境開発構築

この記事の目的

 Ubuntu18.04 LTS の開発環境(ROS/Python/VS Codeなど)構築手順についてまとめる (Ubuntu16.04からのアップデート時の防備録)

1.Ubuntu18.04 LTSについて

Ubuntuは2年おきにLTS (Long Term Support)という長期サポート(5年)の安定版をリリースする。

これまで使っていた16.04は2021年にサポート切れとなるのと、Ubuntu 18.04を使用しているネット上の事例が増えてきたのでアップデートを試みる。

参考サイト

www.sejuku.net

qiita.com

2. Ubuntu16.04からのアップデート手順

Ubuntu16.04をすでに利用している場合アップデート用のコマンドを利用すると設定やアプリを引き継いでUbuntuの更新を実施することができる。

アップデートのコマンドは下記のようになる。

sudo apt update
sudo apt upgrade 
sudo apt autoremove            # 不要パッケージの削除
sudo apt dist-upgrade           # 保留パッケージの更新
sudo apt install update-manager-core           # アップデートマネージャのインストール
sudo do-release-upgrade           # アップデートの実施 -d を付与すると開発中のバージョンへの更新も行う

ただし、aptコマンドの依存関係が正しく判定されず保留中のパッケージが残る場合は正しくインストールできない。 原因となっているパッケージを apt-get purge xxx などで削除してすべてのパッケージを最新として保留パッケージがない状態にする必要がある。

参考サイト

paloma69.hatenablog.com

3. nvidiaGPUドライバの更新

Ubuntu 16.04ではnvidiaのグラフィックボードドライバを公式サイトから手動でダウンロードしてインストールする必要があった。 (これによりUbuntuのパッチ更新でログインループが発生するなどの問題も多く起きていた)

Ubuntu18.04ではnvidia用ドライバの更新を下記コマンドのみで実施できる。

sudo ubuntu-drivers autoinstall  #nvidia GPU用ドライバの更新 (推奨ドライバが自動的にインストールされる)
sudo reboot #再起動
nvidia-smi #ドライバがインストールされていること/どのバージョンがインストールされているかの確認

続けてCUDA/cuDNNをインストールする場合はnvidiaの公式サイトの案内(debファイルをローカルに落としてインストールする場合)下記のようにできる。(CUDA10.2の場合)

wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/cuda-ubuntu1804.pin
sudo mv cuda-ubuntu1804.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget http://developer.download.nvidia.com/compute/cuda/10.2/Prod/local_installers/cuda-repo-ubuntu1804-10-2-local-10.2.89-440.33.01_1.0-1_amd64.deb
sudo dpkg -i cuda-repo-ubuntu1804-10-2-local-10.2.89-440.33.01_1.0-1_amd64.deb
sudo apt-key add /var/cuda-repo-10-2-local-10.2.89-440.33.01/7fa2af80.pub
sudo apt-get update
sudo apt-get -y install cuda

インストール後に.bashrcにCUDAのインストールパスを追加しておく

export PATH="/usr/local/cuda/bin:$PATH" export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"

CUDNNについては公式サイトからダウンロードする (要ユーザー登録) その際、CUDAのバージョンにあった『cuDNN Library for Linux』をダウンロードし、展開後に下記コマンドでコピーする。

sudo cp -a cuda/lib64/* /usr/lib/cuda/lib64/
sudo cp -a cuda/include/* /usr/lib/cuda/include/

参考サイト taktak.jp

qiita.com

4. ROS melodicのインストール

Ubuntu 18.04ではUbuntu 16.04のROS kineticに変わりROS melodicをインストールする。

インストール手順はROS公式のインストール手順に従えば良い。

ただし、Ubuntu16.04からアップデートした場合ROS kineticの一部のパッケージが残っていることでmelodicで導入しようとするパッケージとの依存関係の干渉が発生してしまう可能性がある。

そのため下記コマンドを実施して古いROS関連パッケージを削除してからmelodicを導入したほうが良い。

sudo apt-get purge ros-* # ROS関連のaptパッケージの削除

5. その他の開発環境のインストール

Arduino IDE

Arduino公式サイトから"Linux 64 bits" を選択してダウンロードし、ファイルを展開後下記コマンドでインストール

sudo sh install.sh

Visual Studio Code と Pycharm

Ubuntu Softwareを起動し、Visual Studio Code/Pycharmで検索してインストール

ArduinoとPCの通信(シリアル通信)

この記事の目的

 ArduinoとPC間でのシリアル通信についてまとめる

1.Arduinoのシリアル通信機能

ArduinoでPCと信号の送受信を行う場合、USBケーブルを使ったシリアル通信が最も便利な手法である。

Arduino標準のSerialライブラリを使い下記の関数で信号の受け渡しを行うことができる。

参考サイト https://garretlab.web.fc2.com/arduino_reference/language/functions/communication/serial/

API リンク 概要
Serial.begin() リンク シリアル通信の初期化/bpsの指定
Serial.end() リンク シリアル通信の終了
Serial.setTimeout() リンク シリアル通信タイムアウト時間の指定
Serial.readStringUntil() リンク 指定した終端文字まで文字列を読み込む
Serial.println() リンク 指定した文字列を出力する

通信の速度はbpsで選択する。(300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200 など。送受信に必要な周期とデータ量を考慮して設定する。)

上記の表で挙げたのは送受信するデータは文字列であることを想定している。数値データは文字列に変換しカンマ区切りのデータで一括して送信することができる。下記にサンプルコードを示す。下記はデータの桁数をあらかじめ定めておき、終端が区切り文字";"で出力されるデータの送受信をすることを想定している。

/* serial_com_test.cpp */

/* header file */
#include "stdafx.h"
#include "serial_com_test.h"

/* static variable */
static int apl_cyc_count = 0;

#define READ_BUFSIZE  (7)   //受信バッファサイズ x.x,x.x; を想定(終端文字は含まない)
#define SEND_BUFSIZE  (50)  //送信用バッファサイズ
#define SEND_DATA_SIZE (6)  //整数桁3 小数点1 小数点以下2 を想定

//デバッグ用変数
static float deb_val1 = 0.0;
static float deb_val2 = 0.0;
static int keta = 0;

//通信の初期化用関数
void com_init (void)
{
     //Serial Communication initialization
    Serial.begin(115200);      //boudrate 115200
    Serial.setTimeout(50);      //read timeout for serial communication
    Serial.print("Serial Communication Start!");
    return;
}

//シリアル通信読み込み用関数
void com_read (void)
{
    char buff_A[4];
    char buff_B[4];
    if(Serial.available())  //読み込むデータが存在する場合
    {
        //終端データ";"までのすべてのデータを読み込む
        String str_buf = Serial.readStringUntil(';');
        keta = str_buf.length();
        if (str_buf.length() == READ_BUFSIZE )
        {
            str_buf.toCharArray(buff_A,4);
            deb_val1 = atoi(buff_A);
            Serial.println(deb_val1);

            str_buf.toCharArray(buff_B,4,4);  //先頭バイトの指定
            deb_val2 = atof(buff_B);
            Serial.println(deb_val2);
        }
        Serial.println(buff_A);
        Serial.println(buff_B);
    }
    return;
}

//シリアル通信読み込み用関数
void com_send (void)
{
    float deb_send_val1;
    float deb_send_val2;

    char test_str[SEND_BUFSIZE]={'\0'}; //配列を初期化用データで埋める
    char *ptr = test_str;

    deb_send_val1 = deb_val1 + 10.0;
    deb_send_val2 = deb_val2 - deb_val1;

    /*Send Acceralation information*/
    dtostrf(deb_send_val1,SEND_DATA_SIZE,2,ptr);
    ptr = ptr + SEND_DATA_SIZE;
    *ptr=',';  //区切り文字_token 
    ptr = ptr + 1;
    dtostrf(deb_send_val2,SEND_DATA_SIZE,2,ptr);
    ptr = ptr + SEND_DATA_SIZE;
    *ptr=';';  //end_token 
    Serial.println(test_str);
    return;
}

/*arduino 初期化処理*/
void setup()
{
    /*Initialize Communication */
    com_init();
}

/*arduinoメインループ*/
void loop()
{
     //50msec 周期実行
    interval<5000>::run([]{
        switch (apl_cyc_count)
        {
            case 0:
                com_read();
                break;
            case 1:
                //アクチュエータの制御などを実施(仮)
                break;        
            case 2:
                com_send();;
                break;
            default:
                //no action
                //Serial.println("Default");
                break; 
        }
        if (apl_cyc_count>=2)
        {
            apl_cyc_count = 0;
        }
        else
        {
            apl_cyc_count++;
        }
    });
}

 上記スクリプトで送受信した結果はArduino IDE / Teraterm などで確認することができる。

ここで、 PCのPythonスクリプトArduinoを通信させたい場合はライブラリ『Pyserial』を利用すると便利である。

Pyserialは下記コマンドでインストールすることができる。

 pip install pyserial

Pyserialではポート名とボーレート、タイムアウト時間を指定して関数readlineでバッファ読み込み、writeでバッファ書き込みを行う。

読み込みについては、通信状態によって不安定になることがあるため区切り文字までの文字数を明示的にしておくほうが良い。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
import serial
import threading
import time
import re

###########################################
###  Serial Socket Communication Class  ###
###########################################
class Class_Serial_Socket(threading.Thread):

    ###################################
    #Connection information ##########
    COM_PORT_NAME = 'COM4'           #for Windows
    #COM_PORT_NAME = '/dev/ttyACM0'  #for Ubuntu
    BAURATE = 115200

    #############################################
    ###  Serial Communication Initialization  ###
    #############################################
    def __init__(self):
        #Initialize value for threading
        threading.Thread.__init__(self)
        self.terminate_request = False

        #Vehicle Control Signals sending to Arduino
        self.debval1 = 0.0  #initialize debag value 1
        self.debval2 = 0.0  #initialize debag value 2

        self.counter = 1.0;

        #initialize serial communication socket information
        self.ser_sock = serial.Serial(self.COM_PORT_NAME,self.BAURATE, timeout=0.01, writeTimeout=0.01)
        self.send_flg = True
        self.RECEIVE_LENGTH = 16;

        time.sleep(3)

    ####################################
    ###  Update RX data from Arduino ###
    ####################################
    def update_RX_data_from_arduino(self,str_b):
        try:
            str = str_b.decode()                 #Arduinoから受信した文字列のデコード
            #str_splitted =str.split(',')
            if (len(str) == self.RECEIVE_LENGTH):
                str_splitted = re.split('[,;]', str) 
                self.debval1 = float(str_splitted[0])      #1つ目の数字の取得
                print('debval1',self.debval1)
                self.debval2 = float(str_splitted[1])      #2つ目の数字の取得
                print('debval2',self.debval2)
        except:
            pass

    ##################################
    ###  Update TX data to Arduino ###
    ##################################
    def update_TX_buffer_to_arduino(self):
        #Generate sending buffer to Arduino
        #debug
        send_buffer = str(self.counter + 2.0)
        tempbuf = str(self.counter)
        send_buffer = send_buffer + "," + tempbuf
        send_buffer = send_buffer + ";" #for charactor end token
        self.counter = self.counter + 1.0
        if self.counter > 5.0:
            self.counter = 1.0
        try:
            self.ser_sock.write(str.encode(send_buffer))
        except Exception as e:
            print("例外args:", e.args)

        #    pass
            #print("write_error\n")

    ###############################
    ###  Main Periodic Function ###
    ###############################
    def run(self):
        while(1):
            if self.terminate_request == True:
                break;  #Finish  (User request)
            if self.send_flg == True:
                #try:
                #    self.update_TX_buffer_to_arduino()
                #except:
                #    print("Serial Communication Error (PC->Arduino)")
                self.update_TX_buffer_to_arduino()
                self.send_flg = False
            else:
                str = ""
                try:
                    str = self.ser_sock.readline()
                    self.update_RX_data_from_arduino(str)
                except:
                    print("Serial Communication Error (Arduino->PC)")
                self.send_flg = True
            time.sleep(0.050)

if __name__ == '__main__':
        sock_controler = Class_Serial_Socket()                 #Initialize Serial Communication
        sock_controler.start()

サンプルコードはgithubで公開している。

github.com

github.com

次回はROS TopicをArduinoと送受信する方法について説明する。

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を行いながらトピックの通信を行うスクリプトについて記述する。