こんにちはイチケンです。
Rapberry Pi 4でIntelのLiDARセンサーL515を起動できたので備忘録としてまとめておきます。既存の記事はどれもD435とUbuntuの組み合わせでしたが、私は慣れているRaspberry Pi OS(旧Raspbian)が使いたいのでそちらでチャレンジしてみました。今の所公式ではRaspberry Pi OS対応とは書かれていませんね。
Supported Platforms
Ubuntu 16.04/18.04/20.04 LTS (1) (Linux Kernels 4.4, 4.8 ,4.10, 4.13, 4.15, 4.16(4) , 4.18, 5.0, 5.3, 5.4)
Windows 10 (Build 15063 or later)
Windows 8.1 (2)
Windows 7 (3)
Mac OS (High Sierra 10.13.2)
Android 7, 8
インテルのページはこちら。
Intel® RealSense™ LiDAR Camera L515結論から言うと、Raspberry Pi OSでもPythonライブラリpyrealsense2を使ってL515でストリーミングできます。しかし一筋縄ではいかないので成功したやり方をまとめておきます。早くWindowsみたいにサクっとできるようになってほしいですね。
pyrealsense2を使うとRGB画像と距離データをそれぞれ三次元と二次元のndarrayとして取得できます。従来の画像では精度がでなかった検査なども距離情報を使うことにより大幅に精度向上できる可能性があります。しかもオープンソースライブラリが準備されておりPythonで動かせるのでラズパイやAIとの相性もバッチリ。
この記事でわかること
Raspberry Pi OS上でPythonライブラリpyrealsense2を使ってRealSense
L515でストリーミングする方法がわかります。サンプルコードあり。インストールには全部で4時間くらい必要ですので、余裕を持ってトライしてください。
AttributeError: module 'pyrealsense2' has no attribute
'pipeline'というimportできたのにpipelineが無いと言われてしまうエラーに対する処置方法もわかります。
前提条件
ラズパイ自体は以下の記事で使ってきたものです。OpenCV導入済みのラズパイ4 8GBになります。
【IoT×キャンプ|OpenCVインストール編】意地でもラズパイとキャンプへ!らずキャン△プロジェクト第10回前提条件となる私の環境はこちら。
- Raspberry Pi 4 Model B 8GB
- Raspberry Pi OS 32bit (Linux arm 5.10.17-v7l+, Raspbian 8.3.0-6+rpi1)
- Visual Studio Code ver 1.55.2
- Python 3.7.3
- OpenCV 4.5.1
- Cmake 3.16.3
- protobuf 3.5.1
- RealSense L515
- RealSense SDK 2.44.0
インストール
ではさっそくインストールを進めていきましょう。基本的にはRealSenseのGit内の手順Raspbian(RaspberryPi3) Installationに従いますが、そのままではうまく行きません。特にprotobufはGitのProtocol Buffers Google's data interchange formatの「C++ Installation - Unix」に記載の依存ツールを先に入れる必要があります。あと私Bash派なので環境パス周りがちょっと違ったりなど。以下の手順で進めます。- パッケージのアップデート
- Cmakeインストール
- スワップ追加
- 依存パッケージインストール
- udev ruleアップデート
- パスの設定
- protobufインストール
- TBBインストール
- OpenCVは既にインストール済みのためスキップ
- RealSense SDKインストール
- pyrealsense2インストール
- OpenGLを有効化
パッケージのアップデート
まずは恒例のアップデート。パッケージを最新版にします。LXTerminalで以下のコマンドを実行します。
sudo apt update
sudo apt upgrade
大量のインストールにより後戻りできない感がすごいので念の為バックアップしておいたほうが良いです。手順はこちらが参考になります。
ラズパイ SDカードのバックアップ作成、最小イメージ作成~大容量カードにリストアCmakeインストール
Cmakeが無い場合はインストールします。
sudo apt install cmake
スワップ追加
標準100MBのスワップでは不足するようなので2GBに変更します。Gitでは編集にvimを使用していますが、個人的に使い勝手がいまいちなので標準で入っているnanoで編集します。
sudo nano /etc/dphys-swapfile
CONF_SWAPSIZE=100を2048に変更して保存します。「Ctrl+O」→「Enter」で保存。「Ctrl+X」で終了。
その後次のコードを実行。
sudo /etc/init.d/dphys-swapfile restart swapon -s
依存パッケージインストール
指定の依存パッケージをインストールします。所要時間は約3分。
sudo apt-get install -y libdrm-amdgpu1 libdrm-amdgpu1-dbgsym libdrm-dev libdrm-exynos1 libdrm-exynos1-dbgsym libdrm-freedreno1 libdrm-freedreno1-dbgsym libdrm-nouveau2 libdrm-nouveau2-dbgsym libdrm-omap1 libdrm-omap1-dbgsym libdrm-radeon1 libdrm-radeon1-dbgsym libdrm-tegra0 libdrm-tegra0-dbgsym libdrm2 libdrm2-dbgsym
sudo apt-get install -y libglu1-mesa libglu1-mesa-dev glusterfs-common libglu1-mesa libglu1-mesa-dev libglui-dev libglui2c2
sudo apt-get install -y libglu1-mesa libglu1-mesa-dev mesa-utils mesa-utils-extra xorg-dev libgtk-3-dev libusb-1.0-0-dev
udev ruleアップデート
udevのルールを更新します。最後の一行はrootで実行する必要がありましたのでGitの手順に少し手を加えます。所要時間は約3分。
cd ~
git clone https://github.com/IntelRealSense/librealsense.git
cd librealsense
sudo cp config/99-realsense-libusb.rules /etc/udev/rules.d/
sudo su
udevadm control --reload-rules && udevadm trigger
exit
パスの設定
パスを通すために.bashrcをテキストエディタで開いてコマンドを追記します。所要時間約1分。
nano ~/.bashrc
以下のコマンドを追加して保存。
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
再度LXTerminalから次のコマンドを実行して有効化。
source ~/.bashrc
protobufインストール
さて一個目のハードル、protobufです。所要時間約50分。protobufはビルドするための依存ツールが複数あるためRealSenseのGit手順だけだとうまく行きません。まずは本家protobufのGit記載の依存ツールをインストールします。
https://github.com/protocolbuffers/protobuf/blob/master/src/README.mdsudo apt-get install autoconf automake libtool curl make g++ unzip
あとは順番にLXTerminalで実行していくのみですが、makeに40分くらい必要です。
cd ~
git clone --depth=1 -b v3.5.1 https://github.com/google/protobuf.git
cd protobuf
./autogen.sh
./configure
make -j1
sudo make install
cd python
export LD_LIBRARY_PATH=../src/.libs
python3 setup.py build --cpp_implementation
python3 setup.py test --cpp_implementation
sudo python3 setup.py install --cpp_implementation
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=cpp
export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION_VERSION=3
sudo ldconfig
protoc --version
うまく行けばバージョン情報が確認できると思います。
TBBインストール
Intel Threading Building Blocksをインストールします。所要時間約2分。ラズパイにTBBを導入するためのdebパッケージを使います。
cd ~
wget https://github.com/PINTO0309/TBBonARMv7/raw/master/libtbb-dev_2018U2_armhf.deb
sudo dpkg -i ~/libtbb-dev_2018U2_armhf.deb
sudo ldconfig
rm libtbb-dev_2018U2_armhf.deb
OpenCVインストール
すでにOpenCVはインストール済みのためスキップします。インストール手順はこちら。
【IoT×キャンプ|OpenCVインストール編】意地でもラズパイとキャンプへ!らずキャン△プロジェクト第10回RealSense SDKインストール
いよいよRealSense関連のツールをインストールしていきます。所要時間は約1時間50分です。コンパイルに死ぬほど時間がかかります。
cd ~/librealsense
mkdir build && cd build
cmake .. -DBUILD_EXAMPLES=true -DCMAKE_BUILD_TYPE=Release -DFORCE_LIBUVC=true
make -j1
sudo make install
pyrealsense2インストール
OpenCVでPython3を使いますのでPython3用をインストールします。所要時間は約45分。
cd ~/librealsense/build
cmake .. -DBUILD_PYTHON_BINDINGS=bool:true -DPYTHON_EXECUTABLE=$(which python3)
make -j1
sudo make install
インストールしたライブラリにパスを通します。
nano ~/.bashrc
次のコードを追記して保存。
export PYTHONPATH=$PYTHONPATH:/usr/local/lib
有効化します。
source ~/.bashrc
OpenGLを有効化
最後にラズパイのコンフィグでOpenGLを有効にします。Gitとリストの位置が違いましたが項目名自体は同じでした。所要時間約5分。
sudo apt-get install python-opengl
sudo -H pip3 install pyopengl
sudo -H pip3 install pyopengl_accelerate
sudo raspi-config
"6.Advanced Options" - "A2 GL Driver" - "G2 GL (Fake KMS)"
最後に再起動したら全てのインストール作業は完了です!
動作確認
それではちゃんとインストールできているか確認しましょう。
ビューワーで動作確認
まずはLiDARのデータが取り込めているかサクッとビューワーで確認します。いきなりPythonに行くとエラー時に何がおかしいのか切り分けにくくなっちゃいますしね。
USBにLiDARを接続しLXTerminalで次のコマンドを実行。
realsense-viewer
イケてる雰囲気のビューワーが立ち上がるのでLiDARをONにしてみましょう。能力不足感は否めませんが、3Dイメージが表示されればSDKまではひとまずインストール成功です。
Pythonライブラリの動作確認
ビューワーでLiDARデータ取得の確認ができたら、次にPythonライブラリのpyrealsense2の動作確認をしましょう。初回トライ時はパスがちゃんと通っておらずimport時にモジュールが見つからないエラーが出ましたが、ブログ用に確認した今回の手順では問題ありませんでした。
では、確認のためLXTerminalでPython3を立ち上げます。
python3
次のコードを試します。執筆時点ではimportは問題なしでメソッドで以下のようなエラーになりました。
AttributeError: module 'pyrealsense2' has no attribute 'pipeline'
モジュールのミスだと思いますけど、よくよく見てみるとpyrealsense2が二重の構成になっていました。そのためimportで二回pyrealsense2を繰り返しています。これはそのうち解消されると思いますので、バージョンによりけりかと。
import pyrealsense2.pyrealsense2 as rs
pipeline = rs.pipeline()
エラー無しで実行できればライブラリ導入成功です。
これで全ての準備は完了ですのでいよいよPythonでプログラミングしていきましょう。
Pythonでストリーミングテスト
サンプルコードは下のとおりです。
LOG_PATH = 'Realsense/Log/'
の部分をご自身の環境に合わせて変更してください。ログを保存する場所の指定です。
サンプルとして実装した機能はこちら
- カラーデータと深度データの取得
- 表示用にリサイズ
- リサイズしたカラーデータと深度データの表示
- ログ機能
- キー入力cでndarray次元情報等保存
このサンプルを応用するとOpenCVで画像処理がそのままできます。ただし配列のデータ型に注意してください。深度データはuint16です。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
###############################################
## RealSense and OpenCV integration
###############################################
from logging import getLogger, StreamHandler, DEBUG, INFO, FileHandler, Formatter
import datetime
import os
import numpy as np
import cv2
import pyrealsense2.pyrealsense2 as rs
def main():
logger.info("Start")
# Configure depth and color streams
pipeline = rs.pipeline()
config = rs.config()
# Set config
config = set_rs_config(pipeline, config)
# Start streaming
pipeline.start(config)
try:
while True:
# Wait for a coherent pair of frames: depth and color
frames = pipeline.wait_for_frames()
depth_image = get_depth_image(frames)
color_image = get_color_image(frames)
if np.all(depth_image == 0) or np.all(color_image == 0):
continue
# Apply colormap on depth image (image must be converted to 8-bit per pixel first)
depth_colormap = cv2.applyColorMap(cv2.convertScaleAbs(depth_image, alpha=0.03), cv2.COLORMAP_JET)
depth_colormap_dim = depth_colormap.shape
color_colormap_dim = color_image.shape
# If depth and color resolutions are different, resize color image to match depth image for display
if depth_colormap_dim != color_colormap_dim:
resized_color_image = cv2.resize(color_image, dsize=(depth_colormap_dim[1], depth_colormap_dim[0]), interpolation=cv2.INTER_AREA)
images = np.hstack((resized_color_image, depth_colormap))
else:
images = np.hstack((color_image, depth_colormap))
# Show images
cv2.namedWindow('RealSense', cv2.WINDOW_AUTOSIZE)
cv2.imshow('RealSense', images)
# Select mode by key
k = cv2.waitKey(1) & 0xFF
if k == ord('c'):
check_dimension(depth_image, 'depth_image')
check_dimension(color_image, 'color_image')
if k == 27: # ESC
logger.debug('Main: ESC')
break
except Exception as e:
logger.error(f"Main:{e}")
finally:
# Stop streaming
pipeline.stop()
cv2.destroyAllWindows()
logger.info("End")
def check_dimension(ndarray, name):
logger.info(f'ndarray: Name={name}, ndarray={ndarray}')
logger.info(f'ndim: Name={name}, ndim={ndarray.ndim}')
logger.info(f'shape: Name={name}, shape={ndarray.shape}')
def set_rs_config(pipeline, config):
# Get device product line for setting a supporting resolution
pipeline_wrapper = rs.pipeline_wrapper(pipeline)
pipeline_profile = config.resolve(pipeline_wrapper)
device = pipeline_profile.get_device()
device_product_line = str(device.get_info(rs.camera_info.product_line))
# Set data format
config.enable_stream(rs.stream.depth, 640, 480, rs.format.z16, 30)
config.enable_stream(rs.stream.color, 1280, 720, rs.format.bgr8, 30)
return config
def get_depth_image(frames):
try:
depth_frame = frames.get_depth_frame()
depth_image = np.asanyarray(depth_frame.get_data())
return depth_image
except Exception as e:
logger.error(f'get_depth_image:{e}')
finally:
return depth_image
def get_color_image(frames):
try:
color_frame = frames.get_color_frame()
color_image = np.asanyarray(color_frame.get_data())
except Exception as e:
logger.error(f'get_color_image:{e}')
finally:
return color_image
def set_log_config(logger):
dt_now = datetime.datetime.now()
# Output destination
logfolder = LOG_PATH + dt_now.strftime("%Y%m") + "/"
os.makedirs(logfolder, exist_ok=True)
# handler1 for terminal
handler1 = StreamHandler()
handler1.setLevel(DEBUG)
handler1.setFormatter(Formatter("%(asctime)s %(levelname)8s %(message)s"))
# handler2 for log file
logpath = logfolder+dt_now.strftime("%Y%m%d_%H%M%S")+".log"
handler2 = FileHandler(filename = logpath)
handler2.setLevel(INFO)
handler2.setFormatter(Formatter("%(asctime)s %(levelname)8s %(message)s"))
logger.info(f'set_log_config: logpath = {logpath}')
# Set handler to logger
logger.setLevel(DEBUG)
logger.addHandler(handler1)
logger.addHandler(handler2)
logger.propagate = False
return logger
#### Global variables ####
# 個人環境に合わせて変更してください
LOG_PATH = 'Realsense/Log/'
dt_now = datetime.datetime.now()
## End of Global variables
#### Log Config ####
# loggingとloggerがややこしいのでloggingが候補に出てこないようimport指定
logger = getLogger(__name__)
logger = set_log_config(logger)
## End of Log config
#### Start ####
if __name__ == '__main__':
main()
最後に
いかがでしたか?
流行りのLiDARをラズパイから制御できるってなんだか胸熱。
追記:残念ながらIntelはLiDAR開発から撤退しました。結構良いセンサーだと思ってたんですけどねー。
ラズパイPythonと三菱PLCをネットワーク経由で連携させる方法 ラズパイのCPUクロックをPythonでPID制御しCPU温度コントロール
0 件のコメント:
コメントを投稿