ラインセンサーの開発がなんとなく終わった話
以前,Elecrowで基板を発注した話をしました.
ryuga13tk.hatenablog.com
この時に作った基板が,カラーセンサーver1で,今回,いくつかの改良を加えたカラーセンサーver2を発注して,動作確認までしたので,それについて書いていきます.
動作確認は,ProcessingでGUIで確認できるようにしてあります.
2019/03/31時点ですでにフットプリントの大きさを手直しした,2.1を用意しています.
ちなみにこんな感じ.
では始めます.
経緯
ロボコン現役中に,自動ロボットの自己位置推定に,カラーセンサーがあれば楽なんだろうなぁ...って言う気持ちから開発をしていた基板です(自己位置推定のプログラム書くのが難しそうだが).
現役を引退しても完成していなかったので,ロボコンで使う予定はないけど,無念なので開発を続行していました.
現役のころから,すべて実費で開発を行ってきたので,開発費は,基板の発注費と部品代で,だいたい8,000円ぐらいです.
ただ,この基板ではひとつ350円のカラーセンサーを5個使っているので,はんだ付けを失敗するわけにはいかず(1回失敗しましたが),HAKKOのはんだごてを新しく買いました.
今までは,gootのこてライザーを使っていました.
さよならコテライザー...
スペック
詳しく書いても仕方がないので,ざっくり書くと,
S11059をI2Cのマルチプレクサで動かしてるだけです.I2Cアドレス同じって面倒ですね.せめてプログラマブルであればいいんですけどね.
動作確認
表面実装のはんだ付けの練習が出来て楽しかったです.
デバッガー
デバッガーについてですが,Processingで色出してるだけです.
サンプルプログラムです.
import processing.serial.*; Serial myPort; String str_get_data = null; String buf[]; void setup() { size(500, 500); myPort = new Serial(this, "COM43", 115200); frameRate(1000); } int j = 0; void draw() { float r = 0;// / 6553.50; float g = 0;// / 6553.50; float b = 0;// / 6553.50; str_get_data = myPort.readStringUntil('\n'); //myPort.clear(); println(str_get_data); if (str_get_data != null){ str_get_data = trim(str_get_data); //改行コード取り除き buf = split(str_get_data,"\t"); try{ r = float(buf[0]);// / 6553.50; g = float(buf[1]);// / 6553.50; b = float(buf[2]);// / 6553.50; } catch(ArrayIndexOutOfBoundsException e){ print("erro"); } background(r, g, b ); ++j; if(j > 10){ myPort.clear(); j = 0; } //色を設定 //fill(int./(buf[0])*10, int(buf[1])*10, int(buf[2])*10); //rect(100 ,100, 300, 300); } //delay(50); }
エンコーダのチェック回路を作った話
どうも皆さんこんにちは.
今日はエンコーダのチェック回路(動作確認用)を作った話をします.
ではいきます.
エンコーダのチェック回路
これは私がロボットの足回りの制御をしているときに入れていた回路なのですが,エンコーダのパルスに合わせてLEDをON,OFFさせることでエンコーダのチェックができる回路です.
これをPull-Upでやるか,マイコンのDigitalIOでやるかで,意味合いが変わってきます.
私はマイコンのDigitalIOでやっていました.
前者の場合であれば,マイコンの動作は置いておいて,とりあえずエンコーダは生きている状態だよって言うのがわかります.
後者の場合であれば,マイコンがしっかりと動作している状態であればエンコーダのパルスに合わせてON,OFFするので,主にマイコンのチェックが可能になります.ついで程度にエンコーダ.
今回私が作ったのはエンコーダのパルスに合わせてPull-Upで光らせる回路です.
理由はピカピカ光るものが好きだからです.あと,マイコン使ってIOって,それ波形読んでるからわざわざLED光らせる必要ないよね.っていうことだからです.
エンコーダのチェックが主な目的でした.
動機
エンコーダの動作チェックに毎回オシロスコープを使っていたので、もう少し楽にしたいなと思ったので回路を作成しました.
回路
回路としては,私の使っているエンコーダがNPNオープンコレクタなので,それに合わせてPull-Up抵抗を入れて,そこにLEDを入れているだけです.
回路図です.
これがElecrowに頼んだ結果やってきた基板です.
動作
ちゃんとエンコーダのパルスに合わせてピカピカしてくれてて,私としては,すごくいい感じでした.
個人的には,モジュール化するんじゃなくて,オドメトリ用の基板に埋め込むように使うものと思っています.
モジュール化したのは,ただ光るものが好きだからです.
MbedStudioでLチカまでやってみた byねころこ
どうも皆さんこんにちは,ねころこです.
先日Mbedの公式からオフラインで動作するIDEがリリースされましたね.
私はずっとLinuxでMbedのオフラインコンパイラを使っていたので,正直恩恵は微妙なんですが,デバッグができるという事,Gitが標準サポートであることなので,使ってみることにしました.
今回Lチカまでの道のりを書いていきます.
ちなみにエラーはたくさん起こりましたが,今回は割愛します.
結局皆さんが知りたいのは「こうしたらエラーが出た.」ではなく,「こうしたらできた」なのだと思っているので.
それでは始めます.
MbedStudioインストール
まずはとにもかくにもインストールです.
下記リンクより,MbedStudioをダウンロードしてインストールしてください.そこそこ時間がかかります.
os.mbed.com
MbedStudio LEDチカチカ(Lチカ)
インストールが終わればLチカです.
今回使用したボードは,LPC1768です.
まずは一番最初にこの画面になります.
初期状態は真っ暗だと思います.
私はLightテーマにしています.ダークは苦手なので(知らんがな)
次に,[Create Program]->[New Program...]をクリックします.
これが出てくるので,次に[Select]->[empty Mbed OS program]
を指定して,名前をsample(何でもよい)にします.
[Add Program]を選択すると,左側にsampleが追加されると思います.
なぜ,empty Mbed OS programにしたかというと,ほかのやつだと,MbedOSが入っていないからです.MbedOSが入っていないと,自分で必要なバージョンのものを引っ張って来る必要があります.なので,Mbed OSが入っているものを選びます.
また,MbedOSが入っていないと,そもそも接続したボードを認識しないなどがあります.MbedOS絶対必須.
そもそも初期状態で入ってないものがあるっていうのも,おいおいって感じがしますが,MbedOSが最初から入ったものがあるので,まあいいでしょう.
次はボードを繋ぎます.
ボードを繋ぐと,このようになります.
ちなみに,以下のようになる場合は,MbedOSが入っていない状態なので,OSを入れてください.
[Mbed OSが入っていない状態]
ここでマークの意味を説明すると,
トンカチマーク:プロジェクトをビルド(基本1回)
再生マーク:プログラムの実行
虫マーク:デバッグ
プログラムは以下のものをmain.cppに書きます.
#include "mbed.h" DigitalOut led1(LED1); Serial pc(USBTX, USBRX, 115200); Timer t; // main() runs in its own thread in the OS int main() { t.start(); int count = 0; float tim = 0; while (true) { // Blink LED and wait 0.5 seconds led1 = !led1; pc.printf("%f\r\n", t.read_ms() - tim); ++count; tim = t.read_ms(); wait_ms(20); } }
え?wait_ms(2)は速すぎるですって?
実は,私の環境だと,中の数字をnとすると,10*n[ms]waitしちゃうんですよね.
なので,2です.
ほかの人は速すぎたら20にしてください.
そしたらビルドしてください.
終われば,[Outout]に[IMAGE:BUILD/LPC1768/ARMC6/sample.bin]が表示されるので,それをMbedにコピーします.
MbedStudioでは,自動的に書き込むって言う風に書いてあったのですが,書き込んでくれないので自分で書き込みます.
再起動すれば一回だけ自動で書き込んでくれました.が,それ以降はだめでした.
sample.binを右クリックして,[Open Terminal]を押します.
私の環境だと,MbedはEドライブとして扱われているので,ターミナルで
cp sample.bin E:/
を実行すれば一発でコピーできます.
そしたら,Lチカができます.
シリアル通信を見てみると,200って表示されます.200msでループしてる….
PCを再起動したら,20msでループしたのですが,なぜか書き込んだ直後は200msでループします.
デバッグについてはまだ何もわかっていないです.
ブレーキングポイントを設置してコンティニューボタンを押せばできるよって書いてあったのですが,コンティニューボタン…?
そもそもデバッグに必要な機器って何?っていう状態です...
おそらくドキュメント読んでる限り,
LPC1768は単体でデバッグができそうであること.
デバッグにはデバッグボタンを押して,ブレイクポイントを設置すれば動作するはずであること.がわかりました.
まあ,バグもいくつか見つかっているようなので,今後に期待です.
以下,known issueです.
Known issues - Arm Mbed Studio | Mbed Studio Documentation
When you start a debug session, breakpoints are ignored until they are disabled and then re-enabled.
wwww
STM導入-HALでUARTまで-
前回の導入記事の続き
www.nekorokomemordm.info
今回はHALを使ったUARTについてです.
UART
STM32CubeMXを確認してみると,UART2(俗にいうPCSerial的な)が既に宣言されているのがわかる.なので,UART2を使ってPCと通信をする.
while文を下記のように書き換える.
while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ ButtonStatus = HAL_GPIO_ReadPin(B1_Blue_GPIO_Port, B1_Blue_Pin); HAL_GPIO_WritePin(GreenLED_GPIO_Port, GreenLED_Pin, !ButtonStatus); char transmitData[16]; sprintf(transmitData, "data is %d\r\n", !ButtonStatus); HAL_UART_Transmit(&huart2, (uint8_t *)transmitData, sizeof(transmitData), 0x10); } /* USER CODE END 3 */
次にTeratermを起動して,接続をする.
BaudrateはCubeMXのConfigrationの中で,UART2をダブルクリックすると,下記画面が出てくるので確認をする.もしくは,uart.cの中のvoid MX_USART2_UART_Init(void)でも宣言されている.
これで,Build実行する.
UARTもこれで実行が確認できる.
受信完了割り込み
UARTでは受信完了割り込みや送信完了割り込みも用意されているのでそれを書いてく.
まず,STM32CubeMXでUART2を以下のようにUSART2 global interruptにチェックを入れる.
プログラムを以下のように変更する.
main関数の外に,割り込み完了用の関数を再定義しておく.この関数は,weakで既に宣言してあるので,mainで宣言する必要がある.
int ButtonStatus; char RxData; int warikomi_led; void HAL_UART_RxCpltCallback(UART_HandleTypeDef *UartHandle) { warikomi_led = !warikomi_led; HAL_UART_Receive_IT(&huart2, &RxData, 1); //-->割り込みの設定 HAL_GPIO_WritePin(GreenLED_GPIO_Port, GreenLED_Pin, warikomi_led); }
次にmain関数で
warikomi_led = 0; while (1) { HAL_UART_Receive_IT(&huart2, &RxData, 1); //-->割り込みの設定 /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ char transmitData[16]; sprintf(transmitData, "data is %c\r\n", RxData); HAL_UART_Transmit(&huart2, (uint8_t *)transmitData, sizeof(transmitData), 0x10); //-->受信データを返している }
これで,Teretermでキーボードを押すと,押した後にLEDが点滅して,押した英字が返ってくる.
送信完了割り込みは,ほとんど同じなので割愛._ITを付ければ,使える.
次回はベーシックタイマーペリフェラルの話.
TensorFlow-勾配法でもはや機械学習(1)-
どうも皆さんこんにちは.今日はもはや機械学習です.大事なことなので二回言います機械学習です.
今回から,matplotlibというものを使っていきます.あとnumpyとkeras.もし,入っていない場合は,コマンドラインで
pip install matplotlib pip install numpy pip install keras
をすればインストールできるので,よろしくお願いします.
TensorFlow使ったことない人は,
www.nekorokomemordm.info
そもそも導入していない人は,
www.nekorokomemordm.info
以上,よろしくお願いします.
勾配法って?
ググれ.
冗談です.
そもそも機械学習って?
機械学習とは,予測するためのパラメータの調整をすることが目的なんですよね.
イメージとしては,スーパーの駐車場に自転車が100台あるよりも,車が100台あるほうが店内には人が多そうじゃないですか.
これは,自転車だとおひとり様が多くて,車だと,ひとり以上乗れますから,自然とバイアスがかかるんですよね.
この場合だと,自転車の台数≒人数と考えられますが,車だと,乗っている人数を実数kとすると,k×車の台数≒人数っていう風に推定できるからです.
今回の場合,自転車には原則ひとりで乗ってもらって,,,自動車に乗っている人は1~5人としましょう.そうすると,自転車の数を,自動車の数をとして,実際に店内にいる人をとして,車に乗っている人を実数人と表現とすると,その関係は,
となります.
これはただの1次関数ですが,このkを予測するのに,機械学習を使います.実際はもっと複雑なモデルに使いますが,イメージのための簡略化です(あんまりにも分かりにくいと判断したらそのうちかえます).
んで勾配法って?
じゃあ勾配法っていうのは何かっていう説明をするのに,実は1次関数じゃ無理なので2次関数で考えていきます.
勾配法とは,その関数において,最小もしくは最大となるxを探すためのアルゴリズムになっています.
について,TensorFlowで書いていきます.
%matplotlib inline import numpy as np import tensorflow as tf import matplotlib.pyplot as plt x = tf.Variable(2., name = 'x') func = ((x - 1) ** 2) #以下4行グラフの描写のためのデータ.勾配法には無関係. train_x = [] y = [] func_x = np.linspace(-0, 1.2, 200) func_y = (func_x - 1) ** 2 # 勾配法のパラメータ更新担当.learning_rateの大きさで更新値が変わる. optimizer = tf.train.GradientDescentOptimizer( learning_rate = 0.1 ) # パラメータを実際にずらしていく担当.sess.run(train_step)で実際にずらす train_step = optimizer.minimize(func) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) for i in range(50): #更新. sess.run(train_step) #実行ごとに,更新された値を取得. train_x.append(sess.run(x)) #更新したときのyの値を取得. y.append((train_x[i] - 1) ** 2) print(train_x) #更新の様子を描画. plt.scatter(train_x, y) #最小値を推定するグラフの描画. plt.plot(func_x, func_y) plt.show()
ちなみにこれの実行結果は,
という感じ.
見事最小値を見つけ出しています.
ちなみに勾配法は実装しません,TensorFlowが用意してくれてるので.素晴らしい.
x = tf.Variable(2., name = 'x')
これに関しては,「2.」となっているのは,勾配法は初期値を決めてそこから最小値を推定するアルゴリズムなので,初期値が必要.
今回の場合はパッと見ただけで,最小値がわかるのでありがたみを感じないが,これが例えば,とかだと勾配法が強い.ただし,初期値を間違えると無限大に大きく(小さく)なる可能性がある...
勾配法のアルゴリズム
ここで,勾配法のアルゴリズムをちょろっと紹介.
勾配法(今回使用したのは最急降下法)は,最適化手法です.ニュートン法と似てる.
プログラム中の登場したlearning_rateをと定義.初期値を推定する関数を,初期値を,更新後の値をと置くと,更新後のは,
(ちなみにニュートン法だと)
と,なる.
なので,となる地点に近づくほど,更新値()が小さくなって行くのがわかる.
つまり,傾きが大きい時ほど大きく更新して,小さくなると,少しずつ更新する.しかもこれを学習計数である程度いじれるという.
部屋の価格が機械学習で予測される.
Kerasのデータセットに,犯罪率とか部屋の数とかもろもろのデータと住宅価格があるけれど,これ以上は長くなるので,今回はここまでにします.
機械学習は次回からです.
機械学習で住宅価格を推定するモデルの学習ですが,更新したら下にリンク貼ります.
TensorFlowでセッションとSaverについて
この記事から少しずつ機械学習要素がぽつぽつと出てきます.
今日はSaverとTensorBoardについてです.
前回の記事で,tf.session()を使って計算の実行を行いました.
TensorFlowの使い方をまだ知らないって人は,前回の記事をご覧ください.
それでは始めます.
\二本立て/
Saverって?
Saverは,複数のセッションで計算結果を使いまわすときに使います.
セッションが変わると,変数は元の値になってしまうので,せっかく計算した値が別のセッションでは使われないということが発生してしまいます.
それのためにSaverを使います.
機械学習で,モデルの学習時にも使います.
一度,下記コードを実行してみます.
import tensorflow as tf a = tf.Variable(1, name = 'a') b = tf.assign(a, a + 1) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) print("1:", sess.run(b)) with tf.Session() as sess: sess.run(tf.global_variables_initializer()) print("2:", sess.run(b))
これの実行結果は.
1: 2
2: 2
ということになります.
では,セッションが変わったときに,どうするかというと,さっきのSaverです.Saver込みのプログラムを示しておきます.
import tensorflow as tf a = tf.Variable(1, name = 'a') b = tf.assign(a, a + 1) saver = tf.train.Saver() with tf.Session() as sess: sess.run(tf.global_variables_initializer()) print("1:", sess.run(b)) saver.save(sess, 'model/model.ckpt') with tf.Session() as sess: sess.run(tf.global_variables_initializer()) saver.restore(sess, save_path = 'model/model.ckpt') print("2:", sess.run(b))
これを実行すると,
1: 2
INFO:tensorflow:Restoring parameters from model/model.ckpt
2: 3
となります.
セッションが変わっても,値が初期化されずに済みました.
厳密にいうと,初期化されてますが,saver.saveで,モデルを保存して,saver.restoreで,モデルの読み込みをしています.
グラフの可視化
ここで言うグラフとは,データフローグラフのグラフのことです.
グラフの可視化には,TensorBoardを使います.初期装備です,追加のインストールはありません.
流れとしては,実行→サマリの書き出し→グラフの可視化
といった感じです.
早速プログラムです.
import tensorflow as tf LOG_DIR = './logs' a = tf.constant(1, name = 'a') b = tf.constant(1, name = 'b') c = a + b graph = tf.get_default_graph() with tf.summary.FileWriter(LOG_DIR) as write: write.add_graph(graph)
\こんなの覚えられないよ/
^^
これを実行すると,該当のディレクトリに"events.out....."みたいなファイルが保存されているはずです.
次にコマンドプロンプトを開いて,該当のディレクトリのひとつ前まで移動します.
Anacondaユーザーは[Open terminal]で新しく開きましょう.
そしたら,コマンドプロンプトで
>tensorboard --logdir=logs
を実行してください.
そうしたら,URLが出てきたと思うので,それにアクセスしてください.
ちなみに自分の場合は,出てきたURLだと,マシンによってはなぜかアクセスできませんでした.なので,http;//localhost:(出てきた番号)でアクセスしましょう.
こんな感じで出てきます.
前回の記事にも登場しているのですが,aとbを足すんだなぁ・・・って感じのグラフですね.それ以外に言いようがありません.間違いない(確信).
現場で使える! TensorFlow開発入門 Kerasによる深層学習モデル構築手法 (AI & TECHNOLOGY)
- 作者: 太田満久,須藤広大,黒澤匠雅,小田大輔
- 出版社/メーカー: 翔泳社
- 発売日: 2018/04/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る
TensorFlowの基礎的な使い方
今日もTensorFlowのお話です.
TensorFlow入ってないよって人は,
過去の記事を見てください.
www.nekorokomemordm.info
それではいきましょう.
TensorFlowの基礎って?
TensorFlowとは,データフローグラフという概念を取り入れています.データフローグラフが分かっている方は,飛ばしてもらって構いません.
下のソースコードを見てください.
import tensorflow as tf a = tf.constant(1, name = 'a') b = tf.constant(1, name = 'b') c = a + b with tf.Session() as sess: print(sess.run(c))
一目見るだけでも,1+1の計算をしてそうだなぁ...と思う人が多いと思いますが,実際に1+1の計算をしているのは,どの部分だと思いますか?
聞き方で大体察しが付くとは思いますが,
sess.run(c)
が,実際に計算を行っている部分です.
じゃあ,c=a+bは何なのか,気になると思いますが,これがデータフローグラフであります.
データフローグラフとは,「構築」と「実行」が別々に行われた処理のことです.
先に構築をしておくことで,並列・分散処理が可能になっています.
では,a=...b=...c=...の処理は何なんだと思う方がいると思います.下の図を見てください.
見ましたね?
この図のようなグラフを定義することが,a=...b=...c=...の処理です.
そして,このグラフを定義した後一気にsess.run(c)で,additionを実行します.
このように,「構築」と「実行」を分けています.
TensorFlowでの処理
「TensorFlowの基礎って?」という部分では,tf.constantを使いました.これは定数になっていまして,これからは変数も混ぜたコードになっていきます.
サクサク行きます.説明もないので.
変数とその代入
import tensorflow as tf a = tf.Variable(1, name = 'a') #変数の宣言 b = tf.constant(1, name = 'b') #定数の宣言 c = tf.assign(a, a +b) #aにa+bの値を代入して,その値を返すと定義 with tf.Session() as sess: sess.run(tf.global_variables_initializer()) #すべての変数の初期化 print("1回目:[c, a] = ", sess.run([c, a])) print("2回目:[c, a] = ", sess.run([c, a]))
上記コードを実行すると,
1回目:[c, a] = [2, 2]
2回目:[c, a] = [3, 3]
という結果が出るはずです.
これは,コメントでも説明しているままです.
ちなみに,a = a + bは無理なのか?という質問があるかなと思いますが,出来ますが,実行結果が2になり,その結果がaに代入されません.なので,更新するためにassignを使います.
また,tf.global_variables_initializer()は,すべての変数を初期化するための関数になっています.
変数の初期化にはほかにもtf.initialize_variablesがありますが,一括で初期化できる前者のほうが便利です.
プレースホルダー
プレースホルダーは,実行時に値を入れてあげることができる便利な箱です.
定数だと実行時に変更するのはできないし,変数もできません.なので,プレースホルダーを使います.
import tensorflow as tf a = tf.placeholder(tf.int32, name = 'a') b = tf.constant(1, name = 'b') c = a + b with tf.Session() as sess: print("a + b = ", sess.run(c, feed_dict = {a : 1}))
実行結果は,
a + b = 2
のようになるはずです.
以上が,スカラの計算です.
もちろん,機械学習のライブラリなので,名前の通り行列・・・いや,テンソル(多次元配列)の計算もできます.
ためしに・・・
import tensorflow as tf a = tf.constant([1, 6], name = 'a') b = tf.constant([2, 7], name = 'b') c = a + b with tf.Session() as sess: print(sess.run(c))
を実行してみればわかると思います.
結果は,[ 3 13]です.
これは,ベクトルの計算ですが.
行列の計算だと・・・
import tensorflow as tf a = tf.constant([[1, 6], [2, 3]], name = 'a') b = tf.constant([[2, 7], [4, 5]], name = 'b') c = a + b with tf.Session() as sess: print(sess.run(c))
結果:[[ 3 13]
[ 6 8]]
指定の仕方を変えれば,placeholderでも...ほら,簡単に.
import tensorflow as tf a = tf.placeholder((2, 2), tf.int32, name = 'a') b = tf.constant([[2, 7], [4, 5]], name = 'b') c = a + b with tf.Session() as sess: print(sess.run(c, feed_dict = {a : [[1, 6], [2, 3]]}))
結果:[[ 3 13]
[ 6 8]]
以上です.
次回は,実行した結果を別のセッションでも扱うためのSaverを扱います.
現場で使える! TensorFlow開発入門 Kerasによる深層学習モデル構築手法 (AI & TECHNOLOGY)
- 作者: 太田満久,須藤広大,黒澤匠雅,小田大輔
- 出版社/メーカー: 翔泳社
- 発売日: 2018/04/19
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る