PICO-PI-IMX8 環境構築メモ
PICO-PI 環境構築メモ
目的:
組み込みLinux Driver 開発勉強のため、PICO-PI-IMX8 環境構築を行う。
環境構築後、driver 改造、新規 driver 作成が可能となる。
基本環境:
Windows 上で動く VirtualBox(Ubuntu16.04) を host OS とする
必要なもの
Ether ケーブル
PICO-PI-IMX8
virtual box 導入
参考:https://qiita.com/ykawakami/items/4bae371932110b2e25e3
割当メモリ:2048MB
ハードディスク容量 120GB
以降の作業はVirtual Box 上での作業
i.MX8M ARM Toolchain DL
gcc-linaro-6.4.1-2017.11-x86_64_aarch64-linux-gnu.tar.xz を DL
参考:https://www.technexion.com/support/knowledgebase/preparing-a-toolchain-for-building-arm-binaries-on-x86-hosts/
DL後、
sudo tar xvf gcc-linaro-6.4.1-2017.11-x86_64_aarch64-linux-gnu.tar.xz -C /opt/
toolchain 環境変数設定
以下を ~/.bashrc に追記、source ~/.bashrc を実行
export ARCH=arm64
export CROSS_COMPILE=/opt/[toolchain directory]/bin/aarch64-linux-gnu-
export CC=/opt/[toolchain directory]/bin/aarch64-linux-gnu-
hello world 作成
hello world を行う test.cを作成
$ {CC}gcc test.c -o test
$ file test
ARM64 形式であることを確認
kernel source 取得
任意ディレクトリで
$ git clone https://github.com/TechNexion/linux.git
kernel build
$ cd ~/path/to/work/linux
$ make defconfig
$ make all
drivers/media/platform/mxc/capture/mx6s_capture.c
probe() 先頭に pr_info("20200831 test"); を追記
PICO-PI 起動時に以下の通りログが現れるため、ログを保存、検索をかける
参考:PICO-PI 起動時のログ
[ 1.787511] input: 30370000.snvs:snvs-powerkey as /devices/platform/30370000.snvs/30370000.snvs:snvs-powerkey/input/input0
[ 1.801515] snvs_rtc 30370000.snvs:snvs-rtc-lp: rtc core: registered 30370000.snvs:snvs- as rtc0
[ 1.811097] i2c /dev entries driver
[ 1.817608] 20200831 test
[ 1.820700] 20200831 test
[ 1.825344] mxc-mipi-csi2_yav 30a70000.mipi_csi1: mipi_csi2_probe
[ 1.831571] CSI: Registered sensor subdevice: mxc-mipi-csi2.0
[ 1.837429] mxc-mipi-csi2_yav 30a70000.mipi_csi: Remote device a
rootfs 取得
Ubuntu 20.04 file system を取得する。
参考:https://www.digikey.com/eewiki/display/linuxonarm/MCIMX8M-EVK#MCIMX8M-EVK-Ubuntu20.04LTS
以下を実行。
$ wget -c https://rcn-ee.com/rootfs/eewiki/minfs/ubuntu-20.04-minimal-armhf-2020-05-10.tar.xz
取得した tar.xz を /public/nfsroot で展開
$ ls /public/nfsroot
bin dev home media opt root sbin sys usr
boot etc lib mnt proc run srv tmp var
nfs server 構築
参考:https://qiita.com/salty-vanilla/items/5bc95ad47af6bf051bf7
$ sudo apt-get install nfs-kernel-server
/etc/exports に以下を追記
/public/nfsroot/ 192.168.4.200(rw,sync,no_subtree_check)
tftp server 構築
$ sudo apt install tftpd-hpa tftp-hpa
$ sudo systemctl enable tftpd-hpa
$ sudo systemctl restart tftpd-hpa
$ mkdir /tftpboot
/etc/default/tftpd-hpa に以下を追記
TFTP_USERNAME="tftp"
TFTP_DIRECTORY="/tftpboot"
TFTP_ADDRESS=":69"
TFTP_OPTIONS="--ipv4 --secure --create"
tftpserver を restart
$ sudo service tftpd-hpa restart
起動Image の配置
kernel build 成果物である Image, dtb を tftpserver に配置する
$ cd ~/path/to/work/linux
$ sudo cp arch/arm64/boot/Image /tftpboot
$ sudo cp arch/arm64/boot/dts/freescale/pico-8m.dtb /tftpboot
PICO-PI-IMX8 起動
typeB USB, USB type C をPC と接続。type C 接続でボード電源が入る。
teraterm を起動、Serial 接続で port 3,4 (環境による)いずれかを選択してボードのリセットボタンを押下
起動ログが流れる port で、ボーレートを115200 に設定。再起動。
文字化けのない起動ログが現れたら成功。
User name: root
password: root
でログイン
工場出荷状態では eMMC boot 設定となっているため、uboot 環境変数を編集して NFS boot に切り替える必要アリ
virtual box ネットワーク設定
Virtual Box を終了
Virtual Box 起動画面 -> 設定
設定 -> ネットワーク ->
割り当て:ブリッジアダプター
名前:Realtek PCIe FE Familiy Controller
とする
名前の部分は環境によって変えること。今回はEther ポートに直接接続。
Ubuntu 起動
Ubuntu 側で network アドレスを 192.168.4.10 に設定
PICO-PI を起動、teraterm 上で ifconfig eth0 192.168.4.200 を実行
Ubuntu から ping 192.168.4.200 が通ればOK
uboot 環境変数に Ether boot 設定を追加
デフォルトで NFS boot が走るように uboot 環境変数を変更する
変更前に printenv で工場出荷状態の環境変数を確認、保存しておくこと
環境変数のバックアップ後、以下を実行。
setenv ipaddr 192.168.4.200
setenv serverip 192.168.4.10
setenv nfsroot /public/nfsroot
setenv fdt_addr 0x43000000
setenv loadaddr 0x40480000
setenv fdt_file pico-8m.dtb
setenv bootcmd 'run netboot'
setenv netargs 'setenv bootargs root=/dev/nfs rw ip=${ipaddr}:${serverip}:${serverip}:255.255.255.0:hostname::off nfsroot=${serverip}:${nfsroot},nolock,vers=4,tcp'
ステレオカメラとIRステレオカメラによるヘビの運動量算出【3】
2つのIR カメラをrasberry pi に繋ぐ。
1. 準備
- rasberry pi用マルチカメラアダプタ
- IR カメラ x 2
2. 接続
写真のようにフレキケーブルを接続する。GPIOピンの位置はこの通りにしないと壊れると思う。
マルチカメラアダプタはABCDの4つ口があり、最大4段まで拡張が可能で、1段積み用の設定、2段積み用・・と設定がある。
1段しか使わない場合は、DIPスイッチの1, 5のみONにする。
3. 動作確認
このマルチカメラアダプタにはライブラリはない(たぶん)ので泥臭くコードを書く必要がある。
1. コード
#!/bin/python import RPi.GPIO as gp import os import time gp.setwarnings(False) gp.setmode(gp.BOARD) # Setup the stack layer 1 board gp.setup(7, gp.OUT) gp.setup(11, gp.OUT) gp.setup(12, gp.OUT) def main(): gp.output(7, False) gp.output(11, False) gp.output(12, True) capture(1) gp.output(7, False) gp.output(11, True) gp.output(12, False) capture(3) def capture(cam): cmd = "raspistill -o capture_%d.jpg" % cam os.system(cmd) if __name__ == "__main__": time.sleep(5) main() gp.output(7, False) gp.output(11, False) gp.output(12, True)
●コードの解説
gp.output(7, False) gp.output(11, False) gp.output(12, True) capture(1) gp.output(7, False) gp.output(11, True) gp.output(12, False) capture(3)
CAM A をキャプチャする場合、ピン出力を以下にする。
ピン番号 | 値 |
---|---|
7 | OFF |
11 | OFF |
12 | ON |
CAM C をキャプチャする場合、ピン出力を以下にする。
ピン番号 | 値 |
---|---|
7 | OFF |
11 | ON |
12 | OFF |
その他、マルチカメラアダプタの詳細設定は以下を参照。
http://www.arducam.com/downloads/RaspCAM/RaspberryPi_Multi_Camera_Adapter_Module_UG.pdf
2. 赤外線ライトの強度調整
添付画像の赤枠箇所をマイナスドライバーで回すと、赤外線ライトの強度を変更できる。
3. カメラピント調整
図の赤枠箇所を回すと、ピント調整できる。
4. 結果
左右でこんな画が撮れた。
ステレオカメラとIRステレオカメラによるヘビの運動量算出【2】
ステレオIR カメラを作成する。
1. 準備
- 木材
- IR カメラ x 2
- 電動ドリル
- ノコギリ
2. 作成
電動ドリルでネジ穴を空ける。
カメラを固定する
余分な部分をノコギリで切って、やすりで角を削ったら完成。
ステレオカメラとIRステレオカメラによるヘビの運動量算出【1】
過去に挑戦した、ペットのヘビを監視するシステムを進化させることにした。
参考:ヘビの安否確認システム - <!--親の顔より見た光景-->
前回のシステムは、通常のUSBカメラを用いた撮影なので
夜間の行動を見ることができなかった(夜行性なのに。
そこで今回は、IRカメラを導入 & 複眼カメラにして1日の運動量を計測することにした。
今回は準備編。
---------------------------
●背景
ペットのヘビを監視するシステムを構築し、1日あたりの運動量を測りたい。
●原理
通常のステレオカメラとIRステレオカメラを作成し、被写体の深度を測る。
昼間は通常のカメラ、夜間はIRカメラを使用する。
深度画像と物体検出アルゴリズムを用いることで、ヘビの運動量を計算することが可能なはず。
こんなイメージで観察したい。
●実験準備
ざっとコレだけ必要
●マイコン周り
モノ | 値段 | 参考リンク |
Rasberry pi 3 B+ | ¥5100 | Amazon |
SDカード 32GB | ¥1350 | Amazon |
ACアダプタ 3.0A | ¥1580 | Amazon |
通常カメラ(CSI インターフェース) | ¥1699 | Amazon |
赤外線カメラ(CSI インターフェース) | ¥2899 | Amazon |
Raspberry Pi用マルチカメラアダプタ(CSI 拡張基盤) | ¥6171 | スイッチサイエンス |
●機構周り(ホームセンターで全て購入)
モノ | 値段 | 参考リンク |
アームライト(電球別売り) | ¥2894 | Amazon |
充電式電動ドリルドライバー | ¥3218 | ビバホームオンラインショップ |
精密ドライバー | ¥321 | ホームセンター |
カメラプレート用木材 | ¥116 | ホームセンター |
マイコン土台用木材 | ¥354 | ホームセンター |
のこぎり | ¥1002 | ホームセンター |
紙やすり #240 | ¥84 | ホームセンター |
M3 スペーサ * 4 | ¥578 | ホームセンター |
M3 ねじ+ナット+ワッシャー | ¥159 | ホームセンター |
M2 ねじ+ナット+ワッシャー | ¥159 | ホームセンター |
定規 | ¥0 | どこでも |
電動ドリルが意外と安かったので、
カメラのプレートと、マイコン用の土台を木材で作ることにした。
あと、のこぎりがカッコいい
●ソフトウェア
モノ | 値段 | 参考リンク |
fusion 360(CAD ソフト)無償体験版 | ¥0 | ホームページ |
図4のような感じで設計し、IR ステレオカメラの完成イメージを掴む。
(手間はかかるが、カメラ位置微調整用のネジ穴数を増やしたほうが良いと気づけた。)
●合計
¥27684
(めっちゃかかった……)
【C++】離散フーリエ級数展開
理解するためにすべて手書きで書いた。
乱数で作ったグラフを、
三角関数の足し合わせのみでキレイに再現できている。
(あと一息で離散フーリエ変換ができそう)
#include "pch.h" #include <iostream> #include <fstream> #include <sstream> #include <vector> #define M_PI 3.14159265358979323846 int main(void) { std::ifstream ifs( "./source.csv" ); if ( ifs.fail() ) { std::cout << "file read error..." << std::endl; return -1; } std::string str; std::vector<double> f; while ( getline( ifs, str ) ) { f.push_back(std::stod( str )); } double a0 = 0; std::vector<double> an; std::vector<double> bn; double sum = 0; int N = f.size(); for ( int x = 0; x < N; x++ ) { sum += f[x]; } a0 = sum / N; for ( int n = 1; n < N; n++ ) { double a_sum = 0; double b_sum = 0; for ( int x = 0; x < N; x++ ) { a_sum += f[x] * cos( 2 * M_PI * n * x / N ); b_sum += f[x] * sin( 2 * M_PI * n * x / N ); } if ( n == ( N / 2 - 1 ) ) { an.push_back( a_sum / N ); bn.push_back( b_sum / N ); } else { an.push_back( 2 * a_sum / N ); bn.push_back( 2 * b_sum / N ); } } std::vector<double> fx_fourier; for (int x = 0; x < N; x++) { double a_sum = 0; double b_sum = 0; for ( int n = 1; n < N; n++ ) { a_sum += an[n - 1] * cos( 2 * M_PI * n * x / N ); b_sum += bn[n - 1] * sin( 2 * M_PI * n * x / N ); } fx_fourier.push_back( a0 + a_sum + b_sum ); } std::ofstream dist; dist.open("dist.csv", std::ios::out); for ( int i = 0; i < fx_fourier.size(); i++ ) { dist << fx_fourier[i] << std::endl; } return 0; }
【画像処理】2次微分フィルタ
二次微分フィルタを自作した。
def edge_filter_2(file): image = cv2.imread(file, cv2.IMREAD_GRAYSCALE) (height, width) = image.shape[:2] edge_width = image.copy() edge_height = image.copy() edge_lap = image.copy() for i in range(1, height-1): for j in range(1, width-1): left = image[i, j-1] right = image[i, j+1] top = image[i-1, j] bottom = image[i+1, j] center = -image[i, j] * 2 edge_width[i, j] = abs(left + right + center) edge_height[i, j] = abs(top + bottom + center) edge_lap[i, j] = abs(top + bottom + left + right + center*2) cv2.imwrite('gray.png', image) cv2.imwrite('edge2_width.png', edge_width) cv2.imwrite('edge2_height.png', edge_height) cv2.imwrite('edge2_lap.png', edge_lap)
参考は以下。
画像処理フィルタの1次微分と2次微分の違い | ぱーくん plus idea
適用フィルタは以下。
横方向
0 | 0 | 0 |
1 | -2 | 1 |
0 | 0 | 0 |
縦方向
0 | 1 | 0 |
0 | -2 | 0 |
0 | 1 | 0 |
4近傍
0 | 1 | 0 |
1 | -4 | 1 |
0 | 1 | 0 |
●横方向二次微分
●縦方向二次微分
●4近傍(ラプラシアンフィルタ)
★なぜ1次微分より2次微分のほうがエッジが目立つ?
左から、元画像・1次微分(横方向)・2次微分(横方向)
横方向微分の1次微分と2次微分の出力差を見てみる。
仮に、顔の輪郭にフィルタを適用するとして、
フィルタ適用前画素列を、以下とする
32 | 32 | 64 | 64 | 64 | 64 | 255 | 255 |
★★1次微分(横方向)
-1/2 | 0 | 1/2 |
このフィルタを適用すると(左端・右端は元画像のままとする)、
32 | 32 | 64 | 64 | 64 | 64 | 255 | 255 |
↓↓ 1次微分適用
32 | 16 | 16 | 0 | 0 | 95 | 95 | 255 |
★★2次微分(横方向)
1 | -2 | 1 |
このフィルタを適用すると(左端・右端は元画像のままとする)、
※絶対値を画素値として採用
32 | 32 | 64 | 64 | 64 | 64 | 255 | 255 |
↓↓ 2次微分適用
32 | 32 | -32 | 0 | 0 | 191 | -191 | 255 |
1次微分より白成分が2倍になっている。