【簡単】Arduinoで角度を取得しよう!



はじめに

今回はArduinoと、加速度、ジャイロ(角速度)を取得できるセンサーであるMPU6050を使用して、角度を取得してみます。

専用のライブラリを作成したので、とても簡単に使えるようになると思います!

用意するもの

・Arduino UNO
【永久保証付き】Arduino Uno

・MPU6050
MPU-6050 使用 3軸ジャイロスコープ・3軸加速度センサー モジュール

・ジャンパケーブル
ジャンパーワイヤ(メス-オス)(20cm)40本

・ブレットボード
サンハヤト SAD-101 ニューブレッドボード

回路図

 

Arduino MPU6050
5V VCC
GND GND
SCL A5(SCL)
SDA A4(SDA)

 

Arduino UNOにはAREFの上にSCL、SDAがありますが、これらはA5、A4とそれぞれ繋がっているのでどちらに接続しても問題ありません。


ライブラリをインクルード

加速度、ジャイロを元に1から正しい角度を取得するプログラムを書こうとするとけっこう大変です。

そこで、今回は簡単に各情報を取得できるライブラリを作成したので、それを使用していこうと思います。

まずはライブラリをダウンロードします。

以下のURLにアクセスし、

https://github.com/Tockn/MPU6050_tockn

右側にある緑色のボタン「Clone or download」をクリック、そして「Download ZIP」をクリックしてください。

 

ZIPファイルがダウンロードされると思います。これをインクルードします。

Arduino IDEを開き、上部の「スケッチ」→「ライブラリをインクルード」→「.ZIP形式のライブラリをインクルード…」をクリックし、先ほどダウンロードしたZIPファイルを選択してください。

これでライブラリのインクルードが完了しました。


プログラム

ライブラリの使用例も用意したので、それを使ってみましょう!

「ファイル」→「スケッチの例」から、下矢印にカーソルを合わせて下へスクロール

「MPU6050_tockn」が現れるのでクリック→「GetAngle」をクリック!

すると角度を取得できるスケッチが開かれると思うので、それをArduinoへ書き込みましょう。

書き込み後、シリアルモニターを開きましょう!

シリアルモニタのボーレートは9600bpsに設定してください。(右下)

最初、できるだけ正確なジャイロセンサーの値を取得するために、キャリブレーションという処理を行っています。この時はMPU6050に触れたりせず、動かさないようにしてください!

キャリブレーションが終わると、各値が出力されると思います。

 

上手く行けばある程度正確な角度が表示されると思います!

試しにグリグリ動かしてみてください。

さいごに

上手く動いたでしょうか?ライブラリを使用したので凄く簡単にできたと思います。

使用したスケッチを書き換えれば、様々なことに応用できると思います。

ちなみに今回使用したプログラムでは、相補フィルターというフィルターを使用することでより良い値が出力されるよう計算しています。

詳しい仕組みについては私が以前書いた

ESP32 (ESP-WROOM-32)でMPU6050を使おう!(i2C) (Arduino IDE)

この記事を参考にしてください!

質問、ご指摘はコメントでどうぞ!

それでは!!


PHPでSqlite使おうとしたら500errorが出た

 

PHPで書いたCGIを実行してみたら

恐怖の500エラーが。。。

エラーログを覗いてみると

PHP Fatal error: Class 'SQLite3' not found

SQLite3を理解しないだと。。。!?

しかし、困ったことに、コマンド上でそのPHPファイルを実行してみると、エラーを吐かずに正常に動作していました。

念のためphp5-sqliteを更新してみる

sudo apt-get install php5-sqlite

結果は

php5-sqlite is already the newest version.

最新の状態らしい。

Class ‘SQLite3’ not found の対処法

 

いろいろ調べてみると、

cd /etc/php5/apache2/conf.d
vim 20-sqlite3.ini

でsqlite3.iniを開いて、

extension=sqlite3.so

があることを確認。その後

cd /etc/php5/apache2/
vim php.ini

でphp.iniを開いて、940行目あたりにある [sqlite3] の項目に

extension=sqlite3.so

を追加!その後Apacheを再起動。

これで動きました。

Arduinoでドローンを制御! 1からドローンを自作しよう – プロローグ

今回はプロローグ回です。

すべてのはじまり

僕はArduinoが大好きだ。

ライブラリが豊富で、簡単に環境を構築できて、作りたいものをすぐに作れる。

今日は何作ろうかなぁ、と、考えていたら、思いついてしまった。

ドローンを作ろう!!!!!!!!

そして、早速製作に取り掛かったのであった。。。。。。。。。。



これがなんと、1年半程前の話である。開発には想像以上に時間を要した。なぜなら、Arduinoの既存のOSS(MultiWiiなど)を利用してドローンを作る、要は殆ど組み立てるだけの記事はあっても、ドローンの制御部分から作る記事は見当たらなかったのだ!

そもそも僕は情報系の学部生である(当時1年生)。機械系ですらない。ましてや制御のことなんて全然わからない!かなり無謀な挑戦だったのだ。

そんな僕でも、海外のサイトをサーフィンしまくり、わからないなりに情報を集めて、ある程度形にすることが出来た。

制御に詳しくないからこそできる、ドローンの作り方を紹介する記事を、これから書いていこうと思う。

(それはつまり、間違った知識を書いてしまうこともあるということです。この記事をあまり鵜呑みにしないでください。間違い等を見つけましたら、是非コメントしてください。僕も勉強していきたいです)

1からって、どこから?

まずはそれを説明します。今回は、ドローンのフレーム、モーター、プロペラ、バッテリー、プロポは、市販のものを使います。

あくまでメインはArduinoでフライトコントローラーを自作する」ということです。0から作ると書くとフレームから作る羽目になるので、1からとはそういうことです!

前提知識は?

今回は、Arduinoで簡単なものを作れる状態」の人をターゲットにしようと思っています。簡単なものって何?と聞かれると難しいですが、Lチカができて、はんだができれば十分だと思います。

要はArduinoのインストール!からは始めない、ということです!そもそも僕自身全然知識がなく、ごまかしながら使っているので大丈夫です!イェイ!!

どんなことを書くの?

一応予定なので、今後変更があるとは思いますが、

1, ジャイロ、加速度センサを使おう

2, モーターを制御しよう

3, プロポを使おう

4, ドローンを組み立てよう

5, 姿勢制御をしよう

という大まかな流れで行こうと思ってます。

さいごに

と、いうわけで、こんな調子でこれからバンバン記事書いていこうと思います!全て書き終わるのにどれだけ時間がかかるかわかりませんが、どうぞお付き合いください!

はっぴーあるでゅいーのどろーんライフを送りましょう!!



Twenty Seventeenの記事一覧を記事タイトルのみにする



こんにちは。

ついにドメインを取得しました!

これからはtockn.comとしてやっていきます。よろしくお願いします。

初めてWordPressを使ったのですが、デフォルトのテーマ(Twenty Seventeen)、めちゃくちゃ良いですね。ハイパー好みです。

しかし幾つか気に入らないところが…。

今回はその中の1つ、記事一覧で本文全文も表示されちゃうところを、記事タイトルのみ表示されるよう変更していこうと思います。

これから紹介することを真似するのは、自己責任でお願いします。何かあっても僕は一切の責任を負いません…。万が一のために、必ずバックアップを取っておいてください。



themeフォルダを覗いてみよう

どうすればいじれるのか模索していると、/wp-content/themesというディレクトリを見つけました。中を覗くと、案の定今インストールされているテーマの情報が入っているディレクトリが見つかりました。

今回はTwenty Seventeenをいじるのでそれを覗くと、ヘッダーやフッターなど、それぞれのパーツを出力しそうなファイルがありました。

記事を出力するPHPを特定

その中のindex.phpを開いてみると、トップページを出力してそうなコードが!その65行目くらいから…


while( have_posts() ) : the_post();

........省略........

get_template_part('template-parts/post/content', get_post_format() );

........省略........

endwhile;

いかにも記事を出力しそうなループ文ですね。そしてその中の
get_template_part(‘template-parts/post/content’, get_post_format() );
これで実際に記事を出力するプログラムを呼び出しているようだ。どうやらtwentyseventeen/template-parts/post/にある、content.phpを呼び出しているようですね。早速覗いてみましょう。

content.phpの54行目から


<div class="entry-content">
<?php
/* translators: %s: Name of current post */
the_content( sprintf( __( 'Continue reading<span class="screen-reader-text"> "%s"</ span>', 'twentyseventeen' ),
get_the_title() ) );

wp_link_pages( array( 'before' => '<div class="page-links">' . __( 'Pages:', ' twentyseventeen' ), 'after' => '</div>', 'link_before' => '<spanclass="page-number">', 'link_after' => '</span>', ) );
?>
</div><!-- .entry-content -->

 

ここで本文全文を出力しているようです。

かといってここをコメントアウトしてしまうと、記事のページに飛んだ時も本文が表示されなくなってしまいます。どうしましょう…

やるべきことはこれ!

そこで、考えました

まずcontent.phpと同じディレクトリに、content-top.phpというファイルを作成して、中身をcontent.phpからコピペしましょう。

そして、content-top.phpから、先程載せた部分である54行目から69行目を削除(もしくはコメントアウト)しましょう。

次に、wp-content/themes/twentyseventeenに戻って、index.phpを開き、50行目あたりにある


get_template_part( 'template-parts/post/content', get_post_format() );

を、


get_template_part( 'template-parts/post/content', 'top' );

に書き換えましょう。

(近くに

get_template_part( ‘template-parts/post/content’, ‘none’ );

がありますが、これではありません。書き換えるのは

get_template_part( ‘template-parts/post/content’, get_post_format() );

です。)

これでトップページの記事一覧が、記事タイトルのみとなるはずです

さいごに

タイトルのみ表示されるように変更しましたが、少し寂しくなってしまいました。しかし、カスタマイズしていけば、どんどん良くなっていきそうです。

本文の一部を表示とかもできるんだろうな、もっといじってみよう!

それでは!



Raspberry PiでGitサーバ、Windowsからclone! 後編

こんにちは。前回に続き、今回は実際にRaspberry PiDebian 8.0)でGitサーバ(リモートリポジトリ)を構築し、Windows PCからcloneしてみます。

Git超初心者がRaspberry PiでGitサーバを構築して、Windowsでcloneするまで! 前編 Gitとは?



Raspberry Piにリモートリポジトリを作成

まずはRaspberry PiにGitをインストールします。

(Raspberry Pi)
apt-get install git

次に、Git専用のユーザーを作成しましょう。今回はgitというユーザーを追加します。

(Raspberry Pi)
useradd -m git
passwd パスワード

そして、リモートリポジトリを作成したいディレクトリ上で(今回は/home/git)

(Raspberry Pi)
cd /home/git
git init --bare --shared hoge.git

こうすることで/home/gitにhoge.gitという名前でリポジトリフォルダが作成されます。
–bareは作成するリポジトリをベアリポジトリとして、–sharedはみんな書き込みできるよ~というオプションです。
ベアリポジトリはフォルダ名に.gitを付けるのが慣例のようです。

ベアリポジトリについては前回の記事を参考に!

ls -a

で、”.git”というフォルダが作成されていることが確認できると思います。

 

一旦これでRaspberry Piでの作業は置いておきます。
次にWindowsでの作業に移ります。


Windows PCにローカルリポジトリを作成

Gitのインストール

まずはWindowsにGitをインストールしましょう。
こちらのページよりダウンロードできます。

git-for-windows.github.io

インストール後、私はCygwinからGitを使用していきますが、コマンドプロンプトでもほぼ同じ内容で作業ができると思います。

鍵の生成、登録

早速clone!と行きたいところですが、まだやらなければいけないことがあります。
Windowsから先ほど作成したRaspberry Piのgitユーザーでssh接続をできるようにします。
まずはWindows PCのわかりやすい場所に.sshというフォルダを作成し、そこに公開鍵とそれに対応する秘密鍵を作成しましょう。

 

Cygwin

(Windows)
mkdir .ssh
ssh-keygen -t rsa -C "コメント"
Generating public/private rsa key pair.
Enter file in which to save the key (//.ssh/id_rsa): ここに.sshのパスを入れる

すると、指定した.ssh内に「id_rsa」「id_rsa.pub」が生成されることが確認できると思います。この.sshのパスは覚えておいてください。

 

Raspberry Piに戻り、/home/git(ユーザー名)に「.sshディレクトリを作成し、authorized_keysというファイルを作成しましょう。
そして、「id_rsa.pub」の中身をauthorized_keysへコピーしましょう。

(Raspberry Pi)
mkdir /home/git/.ssh
cd /home/git/.ssh
vim authorized_keys
(id_rsa.pubの中身をコピーして保存)

これでsshの為の鍵の登録は完了しました。
Windowsに戻ります。

 

次に、clone等をする際に行うsshで使用する秘密鍵の場所を設定してあげます。

(僕はこれを忘れていて、
The authenticity of host ” can’t be established.
Host key verification failed.
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
こんなエラーにしばらく悩まされましたとさ…)

(windows)
vim ~/.ssh/config

で設定ファイルを開き、

Host 任意のホスト名
Hostname ssh://(さっき作ったユーザー名):(パスワード)@(IPアドレス)(リモートリポジトリのパス)
User git
IdentityFile "(さっき鍵を作ったパス)"
Port 22

このように書き込みます。
IPアドレスの欄には、内部ネットワークから接続する場合はRaspberry PiのプライベートIPアドレスを、外部ネットワークから接続する場合はグローバルIPアドレスを入れましょう。
例えば

Host hoge
Hostname ssh://git:password@192.168.0.50/home/git/hoge.git
User git
IdentityFile "c:/Users/$user_profile/.ssh/id_rsa"
Port 22

こんな感じになります。


いざclone!

これでようやくcloneの準備が整いました。
ローカルリポジトリを作成したいディレクトリへ移動し、以下のコマンドを実行します

git clone ssh://(さっき作ったユーザー名):(パスワード)@(さっき設定した任意のホスト名)(リモートリポジトリのパス) (ローカルリポジトリフォルダ名)

例えば私の場合は

git clone ssh://git:password@hoge/home/git/hoge.git hoge

になります。

 

これでようやくcloneができましたね!(おそらく空のリポジトリをクローンしたよ!平気?という警告メッセージが出るかと思いますが、無視してください。)

ls -a

で、”.git”というフォルダが作成されていることが確認できると思います。

あとはこっちのモンだ

早速commit、pushしていきましょう!

ワーキングツリーで適当にファイルを作って…(ここではtest.txtを作りました)

(Windows)
git status

これで、インデックスに追加されていない変更内容を確認できます。

On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
test.txt
nothing added to commit but untracked files present (use "git add" to track)

test.txtが変更されてるけど追加されてないよ~って言ってますね

addでインデックスへ追加してあげましょう。

(Windows)
git add test.txt

そして

(Windows)
git status
On branch master
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file:   test.txt

これで準備完了!commitしましょう。

git commit -m "test.txtを追加"

-mオプションを付けて、スペース空けて後ろにコメントを書きます。主にどのような更新をしたのかを記しましょう。

最後にpush!
初回のpushでは、-uオプションを付けましょう。

git push -u ssh://(さっき作ったユーザー名):(パスワード)@(IPアドレス)(リモートリポジトリのパス) master

無事pushできましたか?

 

ここで、毎回毎回pushの度にssh://~~を入力していたら大変ですよね。それを省略する機能を使いましょう。

git remote add origin ssh://(さっき作ったユーザー名):(パスワード)@(IPアドレス)(リモートリポジトリのパス)

このように、ホストをoriginという名前に対応させましょう。すると

git push

このように省略が可能になりました。

ちなみに、こう書けるのは対応した名前が”origin”の時のみで、別名を付けた場合は

git push 付けた名前

としましょう。

pullしたい時は、同じように

git pull

もしくは

git pull 付けた名前

するだけです。

終わった…

これで一通り環境が整ったのではないでしょうか!(書き忘れていることがあるような気がしますが…)

長かった…疲れた…後半はテキトーに書いてました。

ここが違うよ!できないよ!といった意見がございましたら、どしどしコメントをください!

 

それでは!良いGitライフを!!



Raspberry PiでGitサーバ、Windowsからclone! 前編

こんにちは。久しぶりの更新です。

今まで何かアプリケーションを開発する時、日付.zipやバージョン名.zipでクソ管理してきた僕ですが、ついにGitを使うときがやってきました。

訳あってGithub等の外部サービスにリモートリポジトリを置けない状況だったので、自宅にあるRaspberry Pi3でリモートリポジトリを作成し、手元のWindows PCにもGitを入れてそこからclone、pushできる環境を作りました。

Gitってなんだ?という状態から行ったこと、躓いたことを備忘録として書いていこうと思います。

 



Gitってなんだ!

Gitはプログラムのバージョン管理を楽に行っちゃおうっていうツールです。

例えば1人でアプリを開発する場合。新たに機能を追加したいけど、それで何か不具合が起きたら困るし…と、既存のプログラムをoldVersionなんて言う名前にしてバックアップを取り、新たな機能を開発する…。そんな事を繰り返していると、フォルダの中はoldVersion1, oldVersion2, oldVersion2 – コピーと、意味分からないファイルでいっぱい!

または複数人で開発する場合。うまく連携が取れず、気がついたら同じプログラムを複数人で編集してしまい、競合が発生!

こんな経験、ありませんか?
これを全て解決してくれるのが、Gitなのです。

Gitは主に「リモートリポジトリ」「ローカルリポジトリ」「インデックス」「ワーキングツリー」から構成されます。
リポジトリは、ファイルの変更履歴を保存する場所を指します。)

 

・ローカルリポジトリ
個人の変更履歴を保存する場所です。

・ワーキングツリー
ローカルリポジトリにあります。要は作業場です。ここにあるファイル(フォルダ)を編集していきます。

・インデックス
ローカルリポジトリへ変更内容を記録したいワーキングツリーのファイルを、まずはここに格納します。ここに格納されていないファイルは、変更内容がローカルリポジトリへ記録されません。

・リモートリポジトリ
サーバ等へ置き、みんなで共有するリポジトリ。みんなの変更履歴がどんどん追加されていく。基本的にリモートリポジトリはワーキングツリーを持たない。これをベアリポジトリという。

gitでは主なコマンドとして、

clone
add
commit
push
pull

があります。

・clone
リモートリポジトリを、個人(ローカル)のディレクトリへコピーする(クローンを生成する)コマンドです。それがローカルリポジトリとなります。

・add
指定したワーキングツリーのファイル(フォルダ)をインデックスへ格納するコマンドです。

・commit
インデックスにあるファイル(フォルダ)の前回commitからの更新内容をローカルリポジトリへ保存するコマンドです。普通はメッセージを付け、何をどう変更したのかを明白にします。

・push
ローカルリポジトリの中身をリモートリポジトリへ追加するコマンドです。

・pull
誰かが更新し、リモートリポジトリへ追加した内容を、自分のローカルリポジトリへ反映させるコマンドです。

 

他にもbranch等、大事な大事な機能がありますが、今はとにかくRaspberry Piでリモートリポジトリを作ってwinPCでcloneするのが目的なので、今回は省略!

もう文章だらけで飽きましたよね。

百聞は一見に如かず、次回はいよいよRaspberry Piでの作業に移ります!



後編はこちらから!

Raspberry PiでGitサーバ、Windowsからclone! 後編

ESP32 (ESP-WROOM-32)でSDカードへ読み書きする。(Arduino IDE)

 

 

ESP32弄り第三弾は、SDカードへの読み書きです。

 



 

SDカードはSPIを使って通信します。

今回はこちらのSDカードモジュールを使用しました。

ピン配置は、

 

f:id:tocknblog:20170312021909p:plain

 

このようになっています。

ESP32への配線は以下の通り行いました。

  • DAT2 -> 無し
  • CD/DAT3 -> GPIO5
  • CMD -> GPIO23
  • VDD -> +3.3V
  • CLK -> GPIO18
  • VSS -> GND
  • DAT0 -> GPIO19
  • 以下接続なし

ちなみに、どうにかすれば接続するピンを変えられるみたいです。多分。

 

また、先に言っておきますが、絶対にいらないSDカードを使用してください。中身が飛ぶことがよくあります。
そして配線は慎重に行ってください。3.3V以上の電圧をかけるとSDカードがぶっ壊れると聞いたことがあります。
そして、SDカードの抜き差しは電源が入っていない状態で行ってください。SDカードを読み込まなくなったりします。
何か起きても私は責任トリマセン…

 

スケッチは、公式のExampleをそのまま使いました。ここにあります。

これを使えばSDカードの読み書きに関する大抵のことはできると思います。

もしライブラリを追加する方法がわからない~とかでしたら、コメントください。

上手く行けば実行結果はこんな感じになると思います。

SD Card Type: SDSC
SD Card Size: 1875MB
Listing directory: /
DIR : /SYSTEM~1
FILE: /YEAH.TXT  SIZE: 8
FILE: /TEST.TXT  SIZE: 1048576
FILE: /FOO.TXT  SIZE: 13
DIR : /MYDIR
DIR : /TESTDIR
Creating Dir: /mydir
Dir created
Listing directory: /
DIR : /SYSTEM~1
FILE: /YEAH.TXT  SIZE: 8
FILE: /TEST.TXT  SIZE: 1048576
FILE: /FOO.TXT  SIZE: 13
DIR : /MYDIR
DIR : /TESTDIR
Removing Dir: /mydir
rmdir failed
Listing directory: /
DIR : /SYSTEM~1
Listing directory: /SYSTEM~1
FILE: /SYSTEM~1/WPSETT~1.DAT  SIZE: 12
FILE: /SYSTEM~1/INDEXE~1  SIZE: 76
FILE: /YEAH.TXT  SIZE: 8
FILE: /TEST.TXT  SIZE: 1048576
FILE: /FOO.TXT  SIZE: 13
DIR : /MYDIR
Listing directory: /MYDIR
DIR : /MYDIR/TEEEEE
Listing directory: /MYDIR/TEEEEE
DIR : /MYDIR/TEEEEE/SDF
DIR : /TESTDIR
Listing directory: /TESTDIR
Writing file: /hello.txt
File written
Appending to file: /hello.txt
Message appended
Reading file: /hello.txt
Read from file: Hello World!
Deleting file: /foo.txt
File deleted
Renaming file /hello.txt to /foo.txt
File renamed
Reading file: /foo.txt
Read from file: Hello World!
1048576 bytes read for 3219 ms
1048576 bytes written for 5442 ms

SDカードのルートディレクトリにFOO.TXT、TEST.TXTというファイルが生成され、FOO.TXTの中にHello World!と書いてあれば成功だと思います。

以上!



ESP32 (ESP-WROOM-32)でMPU6050を使おう!(i2C) (Arduino IDE)



ESP32弄り日記第二弾は、安くて有名なInvenSense製6軸センサ、MPU6050を使おう!です。

 

MPU6050には3軸加速度センサー、3軸ジャイロセンサーが積んであります。3+3=6軸センサーというわけです。

ESP32とMPU6050を用いて、角度計を作ってみましょう。
今回もArduino IDEを使って開発していきます。

私が使用したのは以下のモジュールです。

 

 

これでOKです。(めちゃくちゃ安い。ありがたい。)

まず配線ですが、ESP32は以前書いたように、SCLピンをGPIO25、SDAピンをGPIO26にしました。

するとブレッドボードはこんな具合になります。

f:id:tocknblog:20170311164837j:plain

 

いやぁ、、、わちゃわちゃしてるなぁ、、、

スケッチはこんな感じです。
(一応断っておきますが、私は素人です…こんな書き方はヤメロぉ!とかありましたらご指摘ください…)

#include <Wire.h>
#define MPU6050_ADDR         0x68 // MPU-6050 device address
#define MPU6050_SMPLRT_DIV   0x19 // MPU-6050 register address
#define MPU6050_CONFIG       0x1a
#define MPU6050_GYRO_CONFIG  0x1b
#define MPU6050_ACCEL_CONFIG 0x1c
#define MPU6050_WHO_AM_I     0x75
#define MPU6050_PWR_MGMT_1   0x6b
double offsetX = 0, offsetY = 0, offsetZ = 0;
double gyro_angle_x = 0, gyro_angle_y = 0, gyro_angle_z = 0;
float angleX, angleY, angleZ;
float interval, preInterval;
float acc_x, acc_y, acc_z, acc_angle_x, acc_angle_y;
float gx, gy, gz, dpsX, dpsY, dpsZ;
void culcRotation();
//I2c書き込み
void writeMPU6050(byte reg, byte data) {
Wire.beginTransmission(MPU6050_ADDR);
Wire.write(reg);
Wire.write(data);
Wire.endTransmission();
}
//i2C読み込み
byte readMPU6050(byte reg) {
Wire.beginTransmission(MPU6050_ADDR);
Wire.write(reg);
Wire.endTransmission(true);
Wire.requestFrom(MPU6050_ADDR, 1/*length*/);
byte data =  Wire.read();
return data;
}
void setup() {
Wire.begin(26, 25);
Wire.beginTransmission(MPU6050_ADDR);
Wire.write(0x6B); // PWR_MGMT_1 register
Wire.write(0); // set to zero (wakes up the MPU-6050)
Wire.endTransmission(true);
Serial.begin(9600);
delay(100);
//正常に接続されているかの確認
if (readMPU6050(MPU6050_WHO_AM_I) != 0x68) {
Serial.println("\nWHO_AM_I error.");
while (true) ;
}
//設定を書き込む
writeMPU6050(MPU6050_SMPLRT_DIV, 0x00);   // sample rate: 8kHz/(7+1) = 1kHz
writeMPU6050(MPU6050_CONFIG, 0x00);       // disable DLPF, gyro output rate = 8kHz
writeMPU6050(MPU6050_GYRO_CONFIG, 0x08);  // gyro range: ±500dps
writeMPU6050(MPU6050_ACCEL_CONFIG, 0x00); // accel range: ±2g
writeMPU6050(MPU6050_PWR_MGMT_1, 0x01);   // disable sleep mode, PLL with X gyro
//キャリブレーション
Serial.print("Calculate Calibration");
for(int i = 0; i < 3000; i++){
int16_t raw_acc_x, raw_acc_y, raw_acc_z, raw_t, raw_gyro_x, raw_gyro_y, raw_gyro_z ;
Wire.beginTransmission(MPU6050_ADDR);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU6050_ADDR, 14, true);
raw_acc_x = Wire.read() << 8 | Wire.read();
raw_acc_y = Wire.read() << 8 | Wire.read();
raw_acc_z = Wire.read() << 8 | Wire.read();
raw_t = Wire.read() << 8 | Wire.read();
raw_gyro_x = Wire.read() << 8 | Wire.read();
raw_gyro_y = Wire.read() << 8 | Wire.read();
raw_gyro_z = Wire.read() << 8 | Wire.read();
dpsX = ((float)raw_gyro_x) / 65.5;
dpsY = ((float)raw_gyro_y) / 65.5;
dpsZ = ((float)raw_gyro_z) / 65.5;
offsetX += dpsX;
offsetY += dpsY;
offsetZ += dpsZ;
if(i % 1000 == 0){
Serial.print(".");
}
}
Serial.println();
offsetX /= 3000;
offsetY /= 3000;
offsetZ /= 3000;
Serial.print("offsetX : ");
Serial.println(offsetX);
Serial.print("offsetY : ");
Serial.println(offsetY);
Serial.print("offsetZ : ");
Serial.println(offsetZ);
}
void loop() {
calcRotation();
Serial.print("angleX : ");
Serial.print(angleX);
Serial.print("    angleY : ");
Serial.print(angleY);
Serial.print("    angleZ : ");
Serial.println(angleZ);
}
//加速度、ジャイロから角度を計算
void calcRotation(){
int16_t raw_acc_x, raw_acc_y, raw_acc_z, raw_t, raw_gyro_x, raw_gyro_y, raw_gyro_z ;
//レジスタアドレス0x3Bから、計14バイト分のデータを出力するようMPU6050へ指示
Wire.beginTransmission(MPU6050_ADDR);
Wire.write(0x3B);
Wire.endTransmission(false);
Wire.requestFrom(MPU6050_ADDR, 14, true);
//出力されたデータを読み込み、ビットシフト演算
raw_acc_x = Wire.read() << 8 | Wire.read();
raw_acc_y = Wire.read() << 8 | Wire.read();
raw_acc_z = Wire.read() << 8 | Wire.read();
raw_t = Wire.read() << 8 | Wire.read();
raw_gyro_x = Wire.read() << 8 | Wire.read();
raw_gyro_y = Wire.read() << 8 | Wire.read();
raw_gyro_z = Wire.read() << 8 | Wire.read();
//単位Gへ変換
acc_x = ((float)raw_acc_x) / 16384.0;
acc_y = ((float)raw_acc_y) / 16384.0;
acc_z = ((float)raw_acc_z) / 16384.0;
//加速度センサーから角度を算出
acc_angle_y = atan2(acc_x, acc_z + abs(acc_y)) * 360 / -2.0 / PI;
acc_angle_x = atan2(acc_y, acc_z + abs(acc_x)) * 360 / 2.0 / PI;
dpsX = ((float)raw_gyro_x) / 65.5; // LSB sensitivity: 65.5 LSB/dps @ ±500dps
dpsY = ((float)raw_gyro_y) / 65.5;
dpsZ = ((float)raw_gyro_z) / 65.5;
//前回計算した時から今までの経過時間を算出
interval = millis() - preInterval;
preInterval = millis();
//数値積分
gyro_angle_x += (dpsX - offsetX) * (interval * 0.001);
gyro_angle_y += (dpsY - offsetY) * (interval * 0.001);
gyro_angle_z += (dpsZ - offsetZ) * (interval * 0.001);
//相補フィルター
angleX = (0.996 * gyro_angle_x) + (0.004 * acc_angle_x);
angleY = (0.996 * gyro_angle_y) + (0.004 * acc_angle_y);
angleZ = gyro_angle_z;
gyro_angle_x = angleX;
gyro_angle_y = angleY;
gyro_angle_z = angleZ;
}

 




まず最初のsetup()内のfor文ですが、これでジャイロセンサーのドリフト誤差を抑える為の計算をしています。

ドリフト誤差とはなんぞ?という方のために…
ジャイロセンサとは、つまりは角速度センサなのであります。角速度から角度を得るには…そう、積分ですよね!
プログラム内では、毎回毎回ジャイロセンサからの出力値と時間(秒)を掛け合わせたものを足していき、それが出力する角度としています。

理想的なジャイロセンサならば静止状態の時の出力値はゼロなのですが、世の中そうは行きません。普通は、静止状態でも常にゼロ以外の小さな値を出力し続けてしまいます。
放っておくと、どうなるでしょうか。
エス、どんどん角度が増えて(減って)いってしまう!!!
これをドリフト誤差といいます。

それを防ぐために、静止状態で出力し続ける値を1000回位読み取って平均値を取り、その値を積分時に生の値から引いているのです。

 

さて次は、一番下の関数calcRotation()に注目してください。

その一部分

raw_acc_x = Wire.read() << 8 | Wire.read();
raw_acc_y = Wire.read() << 8 | Wire.read();
raw_acc_z = Wire.read() << 8 | Wire.read();
raw_t = Wire.read() << 8 | Wire.read();
raw_gyro_x = Wire.read() << 8 | Wire.read();
raw_gyro_y = Wire.read() << 8 | Wire.read();
raw_gyro_z = Wire.read() << 8 | Wire.read();

ここ、何だこれって思いませんか?私は思います。

MPU6050は、各センサの出力値を

x軸加速度、y軸加速度、z軸加速度、温度、x軸角速度、y軸角速度、z軸角速度

の順番に出力しますが、さらにその各データは上位8bit→下位8bitの順に出力されています。つまり計14個のデータですね。
そこで、上記の部分ではビットシフト演算を行い、正しい値へ変換しているのです。

 

そしてその下、「単位Gへ変換」という部分ですが、MPU6050は出力した加速度センサのデータを、16384で割ることで単位Gへ変換することが出来ます。
ただしこれは設定で変更することが出来ます。
ちゃっかりその設定を、最初のsetup()内で行っていました。

  //設定を書き込む
writeMPU6050(MPU6050_SMPLRT_DIV, 0x00);   // sample rate: 8kHz/(7+1) = 1kHz
writeMPU6050(MPU6050_CONFIG, 0x00);       // disable DLPF, gyro output rate = 8kHz
writeMPU6050(MPU6050_GYRO_CONFIG, 0x08);  // gyro range: ±500dps
writeMPU6050(MPU6050_ACCEL_CONFIG, 0x00); // accel range: ±2g
writeMPU6050(MPU6050_PWR_MGMT_1, 0x01);   // disable sleep mode, PLL with X gyro

ここです。(上から3,4行目)
今回は加速度センサは±2G、ジャイロセンサは±500dps(degree per second)まで計測できる設定にしています。
ここの設定によって、取得した生のデータから変換するための計算式が変わってくるのです。
詳しくはデータシートを見てネ!

その下

//加速度センサーから角度を算出
acc_angle_y = atan2(acc_x, acc_z + abs(acc_y)) * 360 / -2.0 / PI;
acc_angle_x = atan2(acc_y, acc_z + abs(acc_x)) * 360 / 2.0 / PI;

こちらは書いてある通り、逆三角関数を用いて角度を計算しています。
選ぶ軸と正負には気をつけてね!!

dpsX = ((float)raw_gyro_x) / 65.5; // LSB sensitivity: 65.5 LSB/dps @ ±500dps
dpsY = ((float)raw_gyro_y) / 65.5;
dpsZ = ((float)raw_gyro_z) / 65.5;

ジャイロセンサーから取得した生のデータを、dps (degree per second)へ変換しています。加速度のときと同じですね。

//前回計算した時から今までの経過時間を算出
interval = millis() - preInterval;
preInterval = millis();
gyro_angle_x += (dpsX - offsetX) * (interval * 0.001);
gyro_angle_y += (dpsY - offsetY) * (interval * 0.001);
gyro_angle_z += (dpsZ - offsetZ) * (interval * 0.001);

ここでは数値積分を行っています。最初に計算したoffset達がここで役立つわけなんです!

millis()は、ESP32の起動から今までの経過時間を単位ミリ秒で取得できます。interval * 0.001は、ミリ秒から秒へ変換しているわけです。

そして最後の

  //相補フィルター
angleX = (0.996 * gyro_angle_x) + (0.004 * acc_angle_x);
angleY = (0.996 * gyro_angle_y) + (0.004 * acc_angle_y);
angleZ = gyro_angle_z;
gyro_angle_x = angleX;
gyro_angle_y = angleY;
gyro_angle_z = angleZ;

ここでは相補フィルターというものを使い、加速度センサーから求めた角度と、ジャイロセンサーから求めた角度を組み合わせています。
なぜそんなことをする必要があるのか、それは、互いの欠点を打ち消し合い、長所を活かしているのです。

まずジャイロセンサーには前述の通り、ドリフト誤差という欠点があります。一応このプログラムではそれを出来る限り抑えるようにしていますが、それでもやはりドリフトしてしまいます。

そして加速度センサーは、角度を加速度で求めているというのが欠点になります。なぜなら、静止状態なら良いのですが、対象が動く物体だったらどうでしょう。激しく動けば動くほど、あらゆる軸に加速度がかかり、角度がブレてしまうのです。

まとめると、

  • 加速度センサーは長時間運用に強いが、瞬間的なもの(外力)には弱い

そこで

 angleX = (0.996 * gyro_angle_x) + (0.004 * acc_angle_x);
angleY = (0.996 * gyro_angle_y) + (0.004 * acc_angle_y);

この式なのです。
瞬間的にはジャイロセンサーの値を適用するが、ほぼ静止するとじわじわと加速度センサーの値へ近づいていくのがわかると思います。
これが相補フィルターなのです。(多分)

こういったセンサーを組み合わせることを、センサフュージョンというらしいです。かっこいい!

 

説明は大体こんな感じでしょうか。

 

f:id:tocknblog:20170311175829p:plain

 

ちゃんと書き込めれば、こんな感じになると思います。

何か質問、ご指摘等ありましたら、気軽にコメントしてください!

以上!



 

ESP32 (ESP-WROOM-32)でi2C通信



2017/03/12追記:実際に使ってみました

 

先日、Arduinoの環境で開発できるということで、ESP32を購入しました。

 

私はATmega328(Arduino UNO)ではi2Cを頻繁に使用していたので、早速購入したESP32でもi2Cを使ってみました。

しかし、躓いた点があったので報告します。

Arduinoでi2C通信する際に欠かせないWireライブラリですが、最初に

Wire.begin();

を実行しますよね。

ATmegaではi2Cで使うSCL, SDAピンは固定されていますが、ESP32では対応している複数のピンの中から2つを選んで使うことができます。そのため、まず最初に使用するピンを指定しなければなりませんでした。

ESP32でWireライブラリを使う場合、SCL, SDAピンの指定は

int SDA = 25;
int SCL = 26;
Wire.begin(SDA, SCL);

のように、beginの引数にSDA、SCLの順にピンの番号を入れることで出来ます。

i2Cで使えるセンサーを使う時、データシートをみながらプログラムをコツコツ書かずに、誰かが作ったライブラリを用いることがあると思います。その場合にも、ライブラリ内のbegin()の部分を書き換える必要がありそうです。

以上!