Androidアプリのレイアウトを動的に変化

やりたいこと

Androidアプリの画面(Activity)のレイアウトの一部を実行時に動的に変化させたい

やりかた

Activityのレイアウトで、動的に変化させたい部分のLayout要素に名前(id)を付けておく。ここでは hoge とする。

    <LinearLayout
        android:id="@+id/hoge"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:orientation="horizontal">
    </LinearLayout>

この部分に動的に挿入する内容のレイアウトを別ファイルで作る。ここでは piyo.xml というファイル名とする。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/piyo"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >
    (ここに内容を入れる)
</LinearLayout>

実行時に下記のコードでhogeの部分をpiyo.xmlの内容に変化させる。

    // hogeのLayoutを取得
    LinearLayout layout = (LinearLayout)findViewById(R.id.hoge);
    // hogeの内容を全て消去
    layout.removeAllViews();
    // piyo.xmlの内容を挿入する
    getLayoutInflater().inflate(R.layout.piyo, layout);

注意点

上記のコードのinflateメソッドを実行したすぐ後で、piyo.xmlの内容のViewの高さや幅を取得してもゼロが返ってくることがある。これは、その時点ではまだinflateの処理が完了していないためである。そこで、そのViewないしその親Viewを監視するViewTreeObserverを取得して、レイアウト変化の完了をonGlobalLayoutイベントで知らせてもらう。ただし、このイベントは頻繁に発生する場合があるので、用がすんだらイベントリスナーは削除しておくこと。

// piyoViewというViewを監視するViewTreeObserverを取得
ViewTreeObserver vto = piyoView.getViewTreeObserver();
// レイアウト変化時のイベントリスナを登録
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
    @Override
    public void onGlobalLayout() {
        // ここにレイアウト変化時の処理を記述
        ;
        // 用がすんだらこのイベントリスナを削除
        vto.removeOnGlobalLayoutListener(this);
    }
});

メモ:adbでプリファレンスを読み出す方法

Androidアプリで設定データを保存するにはプリファレンス(SharedPreferences)を用いるのが簡単で一般的だが、その実体はXML形式のファイルで保存されており、以下の手順で内容を確認できる。

たとえば、net.lipoyang.hoge というパッケージ名のアプリの、Piyo という名前(getSharedPreferencesの第一引数)のプリファレンスの内容を確認する場合、

  1. コマンドラインで adb shell
  2. run-as net.lipoyang.hoge
  3. cd shared_prefs
  4. cat Piyo.xml

3月のまとめ

進捗

イベント等

技術書典 応援祭に「GR-ROSEでいこう!」を出品しました。
techbookfest.org

動画公開


所感

コロナの影響でイベントが軒並み中止となる中、デジもく会ではオンラインでのもくもく会開催を試み、良い手ごたえと知見が得られました。当分の間はコロナの脅威が続くと思われますが、健康管理に留意しつつも平常心を保って開発を続けていきたい。

Madgwickフィルタで姿勢角推定

Madgwickフィルタとは?

  • マドウィックフィルタと読む
  • 3次元の姿勢角推定に用いる
  • 加速度、ジャイロ、(地磁気) でセンサフュージョンする
  • 相補フィルタより精度が良く、カルマンフィルタより計算が軽い
  • 計算にはクォータニオンを用いる

Arduinoで使えるMadgwickフィルタライブラリ

下記のライブラリが利用できる。
github.com

  • 入力: 6軸センサ(加速度+ジャイロ) または 9軸センサ(加速度+ジャイロ+地磁気)
  • 出力: 姿勢角(ロール、ピッチ、ヨーのオイラー角表現)

f:id:licheng:20200326200938p:plain

地磁気を入力しない場合、鉛直軸の回転はジャイロのドリフトを補正できない。重力加速度による角度推定が鉛直軸回転には利用できないためである。これは相補フィルタやカルマンフィルタも同様である。

使い方

【宣言】

#include <MadgwickAHRS.h>
Madgwick madgwick;

【初期化】

  madgwick.begin(100); //100Hz (10msec周期)

【計算】

    // 6軸センサの場合 (ジャイロは[deg/s]、加速度は生値でも[G]でも[m/s^2]でも可)
    madgwick.updateIMU(gx, gy, gz, ax, ay, az);
    // 9軸センサの場合 (地磁気は生値でも[uT]でも可)
    madgwick.update(gx, gy, gz, ax, ay, az, mx, my, mz);

【姿勢角(オイラー角表現)の取得】

    // [deg]で取得
    float roll  = madgwick.getRoll();
    float pitch = madgwick.getPitch();
    float yaw   = madgwick.getYaw();
    // [rad]で取得
    float roll  = madgwick.getRollRadians();
    float pitch = madgwick.getPitchRadians();
    float yaw   = madgwick.getYawRadians();
注意事項

このライブラリでは、内部計算に用いているクォータニオンがprivate変数になっている。しかし、オイラー角への変換には逆三角関数を用いるので計算量が多いし、その後の計算にもクォータニオンのまま扱ったほうが有利なことも多い。そこで、クォータニオンをpublic変数に変更しておく。具体的には MadgwickAHRS.h の当該箇所を下記のように修正する。

public:
    float q0;
    float q1;
    float q2;
    float q3;

6軸センサBMI160

BMI160はBOSCH社製の6軸センサである。詳しくは下記の記事を参照。
lipoyang.hatenablog.com

Arduino用ライブラリも公開されている。 (非公式)
github.com

実験プログラム

Arduino系ボードのGR-CITRUSに6軸センサBMI160をSPI接続してMadgwickフィルタによる姿勢推定の実験プログラムを作成した。

f:id:licheng:20200326205542j:plain:w480

結線
GR-CITRUS BMI160
GND GND
3.3V 3V3
13 SPI CK SCL/SCx
12 SPI MI SAO/SDO
11 SPI MO SDA/SDx
10 SPI SS CS
スケッチ

こちらからダウンロードできます。

メモ:クォータニオンと回転行列

回転とクォータニオン

3次元空間での回転をクォータニオンで表すことを考える。

回転軸のベクトルを  {\bf n}、回転角を  \theta とすると

回転を表すクォータニオン {\bf q} = \cos \frac{\theta}{2} + {\bf n} \sin \frac{\theta}{2}

 {\bf n} = \left(\begin{array}{c} x \\ y \\ z \\ \end{array} \right) なら  {\bf q} =
 \left(\begin{array}{c} q_{0} \\ q_{1} \\ q_{2} \\ q_{3} \\ \end{array} \right) = 
\left(\begin{array}{c}
\cos \frac{\theta}{2} \\
x \sin \frac{\theta}{2} \\
y \sin \frac{\theta}{2} \\
z \sin \frac{\theta}{2} \\
\end{array} \right)

この回転でベクトル  {\bf r} = \left(\begin{array}{c} r_{1} \\ r_{2} \\ r_{3} \\ \end{array} \right) {\bf r'}写像されるとすると

ベクトル  {\bf r}クォータニオン  {\bf r} = \left(\begin{array}{c} 0 \\ r_{1} \\ r_{2} \\ r_{3} \\ \end{array} \right) と読み替えて

  {\bf r'} = {\bf q r q^{*}}

ただし、  {\bf q^{*}}  {\bf q} の共役クォータニオンで、 {\bf q^{*}} =
 \left(\begin{array}{r} q_{0} \\ -q_{1} \\ -q_{2} \\ -q_{3} \\ \end{array} \right)

回転行列

同じ回転を表す行列を  R とすると

  {\bf r'} = R {\bf r}

この行列  Rクォータニオン  {\bf q} の間には次式が成り立つ。

 R = 
\left(\begin{array}{ccc}
q_{0}^{2} + q_{1}^{2} - q_{2}^{2} - q_{3}^{2}  &  2(q_{1}q_{2} - q_{0}q_{3})  &  2(q_{1}q_{3} + q_{0}q_{2})\\
2(q_{1}q_{2} + q_{0}q_{3})  &  q_{0}^{2} - q_{1}^{2} + q_{2}^{2} - q_{3}^{2}  &  2(q_{2}q_{3} - q_{0}q_{1})\\
2(q_{1}q_{3} - q_{0}q_{2})  &  2(q_{2}q_{3} + q_{0}q_{1})  &  q_{0}^{2} - q_{1}^{2} - q_{2}^{2} + q_{3}^{2}\\
\end{array} \right)

これは、基底ベクトル
\left(\begin{array}{c} 1 \\ 0 \\ 0 \\ \end{array} \right)
\left(\begin{array}{c} 0 \\ 1 \\ 0 \\ \end{array} \right)
\left(\begin{array}{c} 0 \\ 0 \\ 1 \\ \end{array} \right)
の回転による写像を並べたものである。

回転行列は方向余弦行列(DCM)とも呼ばれる。

回転を表現する方法の比較

方法 パラメータ数 ジンバルロック 計算量
オイラー 3 ×(有) ×(大)
回転ベクトル(軸角度表現) 3(4) 〇(無) △(中)
回転行列 9 〇(無) 〇(小)
クォータニオン 4 〇(無) 〇(小)

オイラー角は人間にとっては直感的(せやろか?)と言われるが、ジンバルロックがあるし、三角関数を多用し計算量が多いのでコンピュータでの内部表現にはあまり向かない。回転ベクトル(軸角度表現)も三角関数を使うがオイラー角よりは少ない。回転行列はもっとも単純明解な数式で回転を記述できるがパラメータ数が多い。クォータニオンはパラメータ数も少なく、計算量も少ない。(しらんけど)

メモ:ESP32でmDNS

mDNSとは?

ESP32でmDNS

Arduinoスケッチの場合、まず ESPmDNS.h をインクルードする。

#include <WiFi.h>
#include <ESPmDNS.h>

STAモードなりAPモードなりでWiFiを開始してから、ホスト名を指定してmDNSを開始する。

    MDNS.begin("esp32"); // ホスト名 esp32.local

これだけで、とりあえずOK。超簡単。

DNS-SD?

サンプルコードでは下記のようなAPIも使われている。DNS-SD (DNS Service Discovery)へのサービス追加らしいがよく分からない。

    // ポート80のTCPでHTTPやってることを周知させる?
    MDNS.addService("http", "tcp", 80);

MPLAB X IDEのコンパイラバージョン指定

MPLAB X IDEで特定のバージョンのコンパイラを指定する方法まとめ。

コンパイラのインストール

下記ページの「Language Tool Archives」にてXC8、XC16、XC32の各バージョンをダウンロードできるので、所望のバージョンをダウンロードしてインストールする。
www.microchip.com

デフォルトのコンパイラバージョン指定

MPLAB X IDEのメニューの「Tools」→「Options」で、「Embedded」→「Build Tools」タブを開く。「Toolchain」にインストール済みのコンパイラがリスト表示されるので、所望のバージョンを選んで「Default」ボタンを押すとそのバージョンがデフォルトで指定されるようになり、リストの表示が太字になる。

f:id:licheng:20200318162121p:plain:w640

プロジェクトごとのコンパイラバージョン指定

プロジェクト作成時にコンパイラのバージョンを指定できる。

f:id:licheng:20200320094600p:plain:w640

この指定はあとから変更できる。MPLAB X IDEでプロジェクトのプロパティを開き(Dashboardでプロジェクト名をダブルクリック)、「Conf: [default]」を選択すると「Compiler Toolchain」にインストール済みのコンパイラが列挙されるので、所望のバージョンを選択する。

f:id:licheng:20200318163258p:plain:w640