ロボットを作る

子供のころ、映画「STARWARS」 を観てワクワクし、ガンプラにドキドキしていた。時間が経つのも忘れてしまう、そんな時間をもう一度、取り戻すために、Raspberry pi を使って、オヤジがロボット作りに挑戦する!

やり始めてみると、なかなか思い通りに行かない。時間の経つのも忘れて悪戦苦闘中。 「こんなちっちゃな Raspberry Pi で、こんなことができるんだ」を日々実感している今日この頃。


無料で学べるスキルアップAIキャンプ
https://www.skillupai.com/skillupai-camp/ 


2021年3月24日開催の
「RaspberryPiで始めよう AIとIoT」
で、久しぶりに RaspPi いじってみよう。

カメラ使って、AI使って、それっぽいことできそうだ。


3月中旬に「カラー図解 RaspberryPiで学ぶ機械学習(ブルーバックス)」 が出るらしい。

価格.com で、HP Spectre x360 の価格を引っこ抜いてきて、LINE へ通知させる。

前半は、BeautifulSoup を使って、価格を部分をスクレイピング。
スクレイピングで、抜き出したい情報を成形しようとヘタな書き方になっているが、とりあえず価格のみを出せた。
# pull out "price"  from 価格.com
from bs4 import BeautifulSoup
import urllib as req

url="http://kakaku.com/item/J0000024058"

res=req.urlopen(url)
soup=BeautifulSoup(res, "html.parser")

price=soup.select_one(".colorprice")
price=price.encode('utf_8')
price=price.split('<span>')
price=price[1].split('</span>')


後半は、LINE の LINE Notify を使って、価格情報を通知。

LINE Notify
https://notify-bot.line.me/ja/

ログインするには、予めLINEアプリの「設定」で、アカウント>メールアドレス が[登録完了]になっている必要がある。

headersの Bearer の後ろの半角スペースが必要。
通知文は、payloadにまとめてあるが、msg 部分 と スクレイピングしてきた価格部分
# Notification to LINE
import requests

token="XXXXXXXXXXXXXXXXXXXXXXXXX"
url="https://notify-api.line.me/api/notify"
msg="HP Specre x360: "

payload={"message": msg + price[0]}
headers={"Authorization": "Bearer " + token}
line_notify=requests.post(url, headers=headers, params=payload)
取り敢えず、これでやりたかったことはできた。今は簡単にできてしまう。
IFTTTを使ってみて、連携していくことも考えたい。

Raspberry Pi3 で Google Assistant SDK に入れてみる。 

Google Assistant SDK

以下を参考にさせていただいた。

Raspberry Pi にGoogle Assistant SDKを搭載して「OK Google」してみる。



downloadした client_secret_XXXX.json ファイル(長いファイル名)をもとに、以下を実行して、assistant_credentials.json ファイルを作る。

$ python -m googlesamples.assistant.auth_helpers --client-secrets /home/pi/client_secret_XXXX.json

$ python -m googlesamples.assistant
を実行時に、

「Actually, there are some basic settings that need your permission first...」

となって動かなかったが、 # これが聞き取れたわけでなく、ググっていたらたどり着いただけ。
Troubleshooting
のページの一番上 Permissions issues に出てきた対処を行って解決した。

以前、Amazon Alexa を同じく Raspi3 に入れてみたが、それに比べて、言葉の認識度合が高い気がする。
単なる英語の発音の悪さだけかもしれないが。


LSTM (Long short-term memory) は、RNN (Recurrent Neural Network) の拡張で、時系列データ (Sequential Data)に対するモデル

lstm.py 

#
#import pdb; pdb.set_trace()

import sys
import numpy as np
import pandas as pd

length_of_sequences = 5

def convert_data(values):
   returns = pd.Series(values).pct_change()
   #returns = pd.DataFrame(values).pct_change()
   ret_index = (1 + returns).cumprod()
   ret_index[0] = 1.0
   return ret_index

def create_train_data(values,samples):
   train_x = []
   train_y = []

   for i in np.arange(0, (len(values) - samples - 1)):
      s = i + samples
      feature = values.iloc[i:s]
      if feature[s - 1] < values[s]:
      #if feature.loc[s - 1] < values.loc[s]:
         train_y.append([1])    # up
      else:
         train_y.append([0])    # down
      train_x.append([[x] for x in feature.values])

   return np.array(train_x),np.array(train_y)

file_name = 'stock_yasukawa.csv'
#adj_end = pd.read_csv(file_name,usecols=[6])
adj_end = pd.read_csv(file_name)
adj_end = adj_end['5']
adj_end = convert_data(adj_end)
#print adj_end

X,Y = create_train_data(adj_end,length_of_sequences)

split_pos = int(len(X) * 0.8)
train_x = X[:split_pos]
train_y = Y[:split_pos]
test_x = X[:split_pos]
test_y = Y[:split_pos]


# keras/tensorflow で、LSTM
from keras.models import Sequential
from keras.layers import Dense, Activation, Dropout
from keras.layers.recurrent import LSTM

hidden_neurons = 128
in_out_neurons = 1

def create_model():
   model = Sequential()
   model.add(LSTM(hidden_neurons,batch_input_shape=(None,length_of_sequences,in_out_n
eurons)))
   model.add(Dropout(0.5))
   model.add(Dense(in_out_neurons))
   model.add(Activation("sigmoid"))

   return model

model = create_model()
model.compile(loss="binary_crossentropy",optimizer="adam",metrics=['accuracy'])
history = model.fit(train_x,train_y,batch_size=10,verbose=1)

# evaluation

score = model.evaluate(test_x,test_y,batch_size=10,verbose=1)
print("score: ",score)

preds = model.predict(test_x)
correct = 0
for i in range(len(preds)):
   p = 0 if preds[i][0] < 0.5 else 1
   t = test_y[i][0]
   if p == t:
      correct += 1

print("correct rate: ",correct/len(preds))




計算できたのだが、期待するような数字にはもちろんならない。世の中そんなに甘くないことを再び確認する。

◆ネタ元
LSTM で株価予測してみる


株価をスクレイピングして、グラフ表示する。 いろいろググって、やっと収まった。

プログラムを2つに分けて

(1)データ取得編
Yahoo financeから「安川電機:6506」の2016/4/1-2017/5/1 の株価をGet


(2)グラフ化編
CSVに落としたデータを成形してグラフ化


(1)データ取得編
stockDataGet.py
最初のSSL 2行は、Yahoo Webでの SSLでつまずいたので入れた。

# -*- coding: utf-8 -*-
import ssl
ssl._create_default_https_context = ssl._create_unverified_context

from bs4 import BeautifulSoup
import urllib2
import time
import numpy as np
import pandas as pd

page_num = 90 #適当に多めのページ数
stock_temp = []

# Code
# 6506.T : yasukawa 

for i in range(page_num):
   url = "https://info.finance.yahoo.co.jp/history/?code=6506.T&sy=2016&sm=4&sd=1&ey=
2017&em=5&ed=1&tm=d&p=" + str(i+1)

   html = urllib2.urlopen(url)
   soup = BeautifulSoup(html,"lxml")

   table = soup.find_all("table",{"class":"boardFin yjSt marB6"})[0]
   stock_extract = [value.get_text() for value in table.find_all("td")]
   stock_temp.extend(stock_extract)

   time.sleep(0.4)

stock_temp = np.array(stock_temp)

stock = stock_temp.reshape(int(len(stock_temp)/7), 7) #取得データは7項目
stock = pd.DataFrame(stock[:,1:8],index=stock[:,0])
#stock = pd.DataFrame(stock[:,1:8],columns=["date","start","high","low","end","volumn
s","adjusted"], index=stock[:,0])

# 取得した株価データのカンマ , を外す作業。ほんとに必要?
for i in range(6):
   stock.ix[:,i] = stock.ix[:,i].astype(str)
   stock.ix[:,i] = stock.ix[:,i].str.replace(",","").astype(float)

# Save to csv file.
stock.to_csv('stock_yasukawa.csv',encoding='UTF-8')



(2)グラフ化編
つぎに、入手した株価データをグラフ化する

stockAnalyse.py 

# -*- coding: utf-8 -*-
 
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.finance as mpf
from matplotlib.dates import date2num

fig = plt.figure()
ax = plt.subplot()

# indexとして使う日付を、matplotlib.finance.candlestick_ohlc関数で使えるようにtype変換する
# 日本語がなければ、pandas.read_csv の parse_dates で変換できたそうだ。
# Japanese date (string) -> Timestamp date
fmt='%Y年%m月%d日'
my_parser=lambda date: pd.datetime.strptime(date,fmt)
df=pd.read_csv('stock_yasukawa.csv',index_col=0,parse_dates=0,date_parser=my_parser)
df=df.sort_index(ascending=True)

# mpf.candlestick_ohlc use "datetime"
xdate = [x.date() for x in df.index]  # Timestamp -> datetime
ohlc = np.vstack((date2num(xdate),df.values.T)).T
mpf.candlestick_ohlc(ax,ohlc,width=0.7,colorup='g',colordown='r')

ax.grid()
ax.set_xlim(df.index[0].date(),df.index[-1].date())  # range of X-axis
fig.autofmt_xdate()

plt.show()


取り敢えずグラフ化できた。

★お世話になったページ
pandasで様々な日付フォーマットを取り扱う
Pythonでローソク足チャートの表示(matplotlib編)


Raspberry Pi2 で、物体追跡 の続き。

適当に動かしてると物理的にぶつかり、サーボが悲鳴を上げていた。このままだと壊れる。そのため、
そもそも思い通りにカメラを動かせるのかを試すために、サーボ SG90 の動かし方を調べる。

以下のpythonサンプルを読み解きながら進める。
https://github.com/adafruit/Adafruit_Python_PCA9685

以下の データシートの Example1 を見て、計算方法を理解できた。
PCA9685データシート



カメラマウントで、2つのサーボを使うのだが、実際に動かして

 pwm.set_pwm(channel, on, off)

の off を実測する。縦廻旋の許容範囲は、物理的に横廻旋サーボにぶつかるところや配線でぶつかるところを考慮して許容範囲を狭くすると、以下のようになった。
同じ SG90 だが基本の値が違っていたのは愛嬌か、何かミスってるか?

channel 0 横廻旋(左右) 104 - 296 - 491
channel 1 縦廻旋(上下) 116 - 309 - 500 ⇒ 165 - 309 - 447

この値(min - center - max)を使えば、壊れないだろう。(と信じる)


Installing ROS Indigo on the Raspberry Pi

 OS:Raspbian GNU/Linux 8 (jessie)
 ROS:indigo
 python:2.7.9



Ros-comm を入れる。(Desktopを入れればよかった。と後で反省)

基本は ROS のページどおりだが、いくつか地雷を踏んだ。(後で反省)

◆empy で落ちる
 $sudo apt-get install python-empy

◆cpp-comm あたりで落ちる
 $sudo apt-get install libconsole-bridge0.2 libconsole-bridge-dev

◆rospack で落ちる。Could NOT find TinyXML
 $sudo apt-get install libtinyxml-dev

◆roslz4 で落ちる。lz4 includes not found
 $sudo apt-get install liblz4-1 liblz4-dev

◆rosbag_storage で落ちる
gtest not found, C++ tests can not be built.
$sudo apt-get install libgtest-dev

Could NOT find BZip2
$sudo apt-get install libbz2-dev

◆roscpp

warning: unused parameter /// serializedLength

超超時間がかかった上にエラーが出てとまる。

make[1]: *** [CMakeFiles/roscpp.dir/all] Error 2
Makefile: 138: recipe for target 'all' failed
make: *** [all] Error 2
<== Failed to process package 'roscpp':

どうも swap が足りないらしい。swap増やす。defaut 100M から 1Gへ。

sudo vi /etc/dphys-swapfile

CONF_SWAPSIZEを1024MB(1GB)
CONF_SWAPSIZE=1024

$ sudo systemctl stop dphys-swapfile
$ sudo systemctl start dphys-swapfile
$ free -h  増えたのを確かめる。

インストールはできた。

$ roscore で落ちる。

$ sudo apt-get install python-netifaces

これでやっとインストールできた。振り返ると 3.2 Resolve Dependencies を飛ばしてた自分のせいでこんな面倒になっていただけだった。


で、なにか動かしてみたい。チュートリアルに出てくる tutlesim を動かす。

余計な環境変数を消して(unset)から
$ source /opt/ros/indigo/setup.bash
をしてから rosコマンドを実行する。

ROSのノードを理解する
ROSトピックの理解

いきなり、rosdep install turtlesim で、「turtlesim なんてパッケージない!」と。

$ rosinstall_generator turtlesim --rosdistro indigo --deps --wet-only --exclude roslisp --tar > indigo-custom_ros.rosinstall
$ wstool merge -t src indigo-custom_ros.rosinstall
$ wstool update -t src
$ rosdep install --from-paths src --ignore-src --rosdistro indigo -y -r --os=debian:jessie

ーーーーーーー
rpt がない。どうも ROS-comm を入れたあとに Desktop 部分を入れるのは苦行のような。
いちから入れなおそうか。

http://flow-developers.hatenablog.com/entry/2016/06/05/172041

$ sudo apt-get install libblkid-dev e2fslibs-dev libaudit-dev

いくつか本が出てた。悩まずにこのとおりにやれば早いかも


Raspberry Pi2 で、サーボを使って、物体追跡をする。このサイトを見て、

Raspberry Pi サーボモーターとOpen CVで物体追跡カメラ(Meanshift)
ArduinoとAdafruit 16-channel Servo Driverでサーボモーター多数制御

ブツを揃える、


これらを Amazon で Getした。この Qiita のサイトとは異なるブツだが、とりあえず安かったので買ってみた。
カメラは手持ちのヤツを使う。

◆サーボモータを動かす
i2c を有効にする

$ sudo apt-get install i2c-tools
$ sudo raspi-conifig

 > 5. interfacing options
     > P5 I2C

以下のように結線する。サーバを動かすために、Servo Driver基板中央に別途電源をつなぐ(6Vまでらしい)

   RaspPi2   16ch Servo Driver (PCA9685)
  -------------------------------------------------------------
    3.3V○   ○ GND
   I2C SDA○   ○
   I2C SCL○   ○ SCL
           ○   ○ SDA
     GND○   ○ VCC
        ○   ○


結線して、servo_test.py を動かすとちゃんと動いた。
# いかん! サーボモータが、動作電圧:4.8V(~5V) なので、乾電池は3つにしておこう。

PCA9685 16-channel 12bit PWM Fm + I2C-bus LED controller



◆カメラを動かす
$ sudo apt-get install libopencv-dev python-opencv


USBカメラと OpenCV で画像を表示させるサンプルプログラムはググると沢山でてくる。
ただ、最近のサンプルでないと遠回りする。
結局、
import cv2.cv  や CaptureFromCAM ではなく、以下に落ち着いた。(ターミナル上で、ctrl + c で終了)
これを camera.py に保存して、実行
import cv2

cv2.namedWindow("monitor")
vc=cv2.VideoCapture(0)

if vc.isOpened():
   rval, frame = vc.read()
else:
   rval = False

while rval:
   cv2.imshow("monitor", frame)
   rval, frame = vc.read()
   key = cv2.waitKey(10)
   if key == 27:
      break
cv2.destroyWindow("monitor")     

【追記 4/22】
自分の環境で、TightVNCで Raspiを動かしていた。 2行目の namedWindow で以下が出てこけた。

GdkGLExt-WARNING **: Window system doesn't support OpenGL.

で、ググったら、対処方法があったので試してみた。

$ sudo apt-get install libgl1-mesa-dri 
$ sudo reboot

で、これでもダメで、workaround(1) として

$ sudo apt-get install guvcview

をした。workaround(2) は、VNC をTightvnc をやめて、X11vnc 変えるとできるらしい。がやってない。
workaround(3) は、元に戻して、VNCやめて Raspi にモニター直結したら、もちろんOK。

Raspberry Pi3 で、NHKラジオ英語を定期的に録音して、ノートPC(Windows10)から聞く方法の備忘録。


RaspberryPi3

sambaをインストールして、とりあえず設定を。
printerは使わないので、[global] に printing =  bsd を入れる(?)
共有するフォルダ [/home/pi/REC] は、Windwos で [homes] で見える
$ sudo apt-get update
$ sudo apt-get upgrade
$ sudo apt-get install samba

$ cd /etc/samba
$ sudo cp smb.conf smb.conf_org

$ sudo vi smb.conf

[global]
  printing = bsd

[homes]
  browseable = yes
  path = /home/pi/REC
 read only = no
 create mask = 0664
 directory mask = 0775

$ sudo touch /etc/printcap
最後の touch は、/var/log/samba/log.smbd にエラー吐いたので。


piアカウントで、sambaにアクセスするために。
$ sudo smbpasswd -a pi
New SMB password:  ←RaspPi3の pi アカウントのパスワードを。
Retype new SMB password:
Added user pi.

cronに入れる シェルプログラムは以下からもらってきた。
らじるらじるをrtmpdumpで録音する(8放送局対応)おまけで
https://gist.github.com/riocampos/5656450

crontab 設定。いつも迷う。
項目は、左から「分」「時」「日」「月」「曜日」
$ crontab -e
   :
10 09 * * 1,2,3,4,5 ~/bin/rec_nhk.sh NHK2 5 ~/REC enjoy_english
30 16 * * 7 ~/bin/rec_nhk.sh NHK2 75 ~/REC conversation


ノートPC

エクスプローラ > PC で右クリック > ネットワークドライブの割り当て

 ドライブ():Z:
 フォルダー:\\RASPBERRYPI\homes

 □ 別の資格情報を使用して接続する  ←チェックする

これでノートPC側から Raspberrypi3側に保存してある mp3 ファイルが聞ける。

↑このページのトップヘ