ENGLISH

Actian Zen と産業用ラズパイ「 netPI 」で設備の状態監視を実装する

*目次*

  1. センサーデータを活用した設備の状態監視システム
  2. エッジ側システム環境
  3. 分析側システム環境
  4. 結果
 

1.センサーデータを活用した設備の状態監視システム

IIoT(Industrial IoT)分野では、機械設備から発生する大量のセンサーデータを収集して、設備の状態管理や予知保全などに活かす取り組みが行われています。収集したビッグデータをクラウド上で処理・解析し、見える化するソリューションが多くのベンダーから提供されています。

 

このソリューションを「データ」に着目して見てみると、ここで使われる大量のデータは時系列の連続データであり、あくまでも傾向を捉えるための要素に過ぎず、1つ1つのデータにそれほどの意味はありません。多少の欠損があっても大勢に影響なく、その中の特定のデータを抽出して参照または更新するようなこともありません。つまり「使い捨て」のデータです。

 

日本企業はセキュリティに対する意識が高く、特に製造業の現場ではインターネットから隔絶しているケースも多く見られます。大量のデータ処理にはクラウドのスケーラブルなリソース調達は確かに有用なのですが、データをアップロードするための通信回線に関して、セキュリティを担保し、常時一定の帯域を占有する形態は、扱うデータが「使い捨て」であることを考えると、やや無駄なようにも感じます。このソリューションをもっと効率的に実現することはできないのでしょうか?

 

弊社の扱うデータベース製品「Actian Zen」ファミリには、IoT/エッジコンピューティング分野向けにリリースされたエディション「Edge」と「Core」があります。高速 NoSQL と SQL のデュアルアクセス機構はそのままに、低消費電力の ARM 系チップを使った小型デバイスに搭載することを想定した小インストールサイズのエディションです。

 

このうち「Edge」は、昨今産業用途での活用が進む Raspberry Pi での動作を公式に保証しています。小リソースデバイス向けデータベース「Edge」と、産業用に耐久性を強化した「産業用ラズパイ」を使って、設備の状態監視システムを実装してみました。

 

Actian Zen Edge エディション

・インストールサイズ 30 MB の軽量データベース
・ARM 系 CPU デバイスに対応
・Raspberry Pi OS を正式サポート
・Btireve API による高速逐次処理および SQL による柔軟なクエリプログラミングが可能
・AutoTimeStamp 型による時刻データ自動取得で時系列データ処理が容易

 

参考:Actian Zen ファミリ   https://www.agtech.co.jp/actian/features/#a03

 

産業用ラズパイ「netPI」

「netPI」は IIoTと産業用通信インターフェース部品製造の専門企業、ドイツのヒルシャー社が開発・製造する産業用途向けの小型デバイスです。日本では、現地法人であるヒルシャー・ジャパン株式会社が販売しています。

 

参考:産業用ラズパイ「netPI」   https://www.hilscher.com/ja/netpi/

 

netPI のハードウェアは、産業用途向けに最適設計した基板を金属ハウジングに収めることで耐久・堅牢化し、かつアプリケーションプラットフォームとして Docker 環境を標準装備することで、システムの展開・導入の効率性を高めています。また、IIoT 分野でよく使用される Node-RED や CODESYS などのツールも Docker Hub 上にコンテナイメージとして用意し、netPI にダウンロードして使用できるようにしています。

 

今回はこの「netPI」を使ってセンサーからのデータを取り込み、値の推移を可視化します。

 

システムイメージ

 

【実装概要】

  1. ① 機械に取り付けた加速度センサーから発生するデータを3台の netPI 経由で Actian Zen Edge に生データとして取り込む。(テーブルA) ※この生データは毎日 0:00 に全削除する。
  2. ② 取り込んだ生データから状態監視用データとして1分間隔の平均値、最大値、最小値を抽出し、構内サーバの Actian Zen Cloud SV に転送する。(テーブルB) ※この状態監視用データは永久保存する。
  3. ③ JavaScript の UI フレームワーク「React」でグラフ化し、ブラウザからモニタする。

センサーから生成されるデータを netPI 側で一次処理することで、ネットワークトラフィックを削減し、またデータボリュームが小さくなることでサーバ側の処理も効率化されます。

 

2.エッジ側システム環境

 

センサーからデータを取り込むプログラムは、ヒルシャー社が提供する Docker イメージ(hilschernetpi/netpi-raspbian)を開発機(市販のラズパイを使用)上にコンテナ展開して環境作成し、本番機(netPI)用のイメージを作成します。

 

【開発機とセンサーの接続】

今回使用するセンサーは、アナログ・デバイセズ社製のデジタル3軸センサー「ADXL345」です。

 

開発機と加速度センサーは、以下のように接続します。

・GPIO の1番ピン → センサーの 3.3V
・GPIO の3番ピン → センサーの SDA
・GPIO の5番ピン → センサーの SCL
・GPIO の7番ピン → センサーの GND

 

【ソフトウェアのインストール】

開発機(市販ラズパイ)に以下をインストールします。

・Docker コンテナ(netpi-raspbian) https://hub.docker.com/r/hilschernetpi/netpi-raspbian
・Actian Zen Edge   https://www.agtech.co.jp/actian/zen/v14/edge/trial90/
・Btrieve 2 API SDK   https://www.agtech.co.jp/actian/support/reference/sdk/access_methods/btrieve/
・Btrieve 2 API Python 用ライブラリ   Btrieve2API_Python.zip

※Btrieve 2 API Python 用ライブラリは、Python から Btrieve API を使う場合に必要なライブラリです。
     言語および Btrieve のバージョンが異なると動作しない場合があります。
     当該ライブラリは、Python 3.7.3、Actian Zen Edge v14 SP2 で動作します。

 

【センサーデータ取り込みプログラムの開発】

センサーからのデータは市販ラズパイの GPIO ピン経由で I2C 規格によって取り込みます。開発言語は、統計処理用のライブラリが豊富で IoT 分野と相性の良い Python を使用しました。

 

今回実装したプログラムの構成は以下のようになっています。


 

Actian Zen はローレベルで高速にデータアクセスする「Btrieve API」と、Btrieve よりも速度は劣りますが、複雑な条件のクエリを柔軟に作成できる「SQL」の2つのアクセスメソッドを備えています。これらの特徴を考慮し、大量の時系列データをそのまま登録するデータ取り込みプログラムでは逐次的処理が高速な Btrieve API を、必要なデータを抽出して転送するプログラムでは SQL を使用します。

 

また、分析サーバ側に実装する動的なグラフ表示には JavaScript を利用した Web アプリケーションが適していますが、この場合、Actian Zen では「Cloud SV」エディションを使用します。

   

Sensor-collect.py の作成

まず、エッジ側データベースのスキーマを定義します。ここで作成するテーブルは、SQL アクセスを可能にするとともにそのまま Btrieve のデータファイルとして使用されます。

 

センサーデータを格納するテーブル(data1テーブル)を作成。

 
CODE
CREATE TABLE "data1" IN DICTIONARY USING 'data1.mkd' PCOMPRESS PAGESIZE=16384 (
 "rno" BIGIDENTITY DEFAULT '0' CONSTRAINT "UK_rno" UNIQUE USING 1,
 "instime" AUTOTIMESTAMP DEFAULT '0' CONSTRAINT "DK_instime" NOT UNIQUE USING 0,
 "sno" INTEGER NOT NULL,
 "xdata" INTEGER NOT NULL,
 "ydata" INTEGER NOT NULL,
 "zdata" INTEGER NOT NULL )
;
			
 

続いて I2C センサーモジュールとの通信を行うライブラリをインストールします。

 
CODE
sudo apt-get install python-smbus i2c-tools

# インストール後、再起動。
sudo reboot

# I2C接続の確認。
sudo i2cdetect –y 1
			
 

接続されていればデバイス番号(0x53)が表示されます。(センサーと通信する際に必要ですので控えておきます)

 

 

Sensor-collect.py ファイルを作成して、センサー情報の取得及び、データベース(Zen Edge)へ登録します。データベースへのアクセスは Btrieve 2 API を使用します。

 
CODE

import btrievePython as btrv
import smbus
 
# センサー通信の初期化
i2c = smbus.SMBus(1)
addr = 0x53 
i2c.write_byte_data(addr,0x2c,0x0f)
i2c.write_byte_data(addr,0x31,0x00)
i2c.write_byte_data(addr,0x38,0x00)
i2c.write_byte_data(addr,0x2d,0x08)
 
btrieveFileName = "/usr/local/actianzen/data/VSENSOR/DATA1.MKD"

# Btrieveセッションの作成:
btrieveClient = btrv.BtrieveClient(0x4232, 0) 
btrieveFile = btrv.BtrieveFile()
 
# ファイルを開く
rc = btrieveClient.FileOpen(btrieveFile, btrieveFileName, None, btrv.Btrieve.OPEN_MODE_NORMAL)
if (rc == btrv.Btrieve.STATUS_CODE_NO_ERROR):
     print('ファイルを正常に開きました。\n')
else:
     print('ファイルを開くのに失敗しました。ステータス: ', rc, '\n')
import struct
recordFormat = "<qqiiii"
 
while True:
     # X値の取得
     datax = i2c.read_i2c_block_data(addr, 0x32, 2)
     add_x = 256 * datax[0] + datax[1]   
     # Y値の取得
     datay = i2c.read_i2c_block_data(addr, 0x34, 2)
     add_y = datay[0]+datay[1]
     # Z値の取得
     dataz = i2c.read_i2c_block_data(addr, 0x36, 2)
     add_z = dataz[0]+dataz[1]
 
# Pythonの値をCの構造体データに変換をします。
record = struct.pack(recordFormat,0,0,sno,add_x,add_y,add_z)

# 上記で変換した record オブジェクトを格納
rc = btrieveFile.RecordCreate(record)
if (rc == btrv.Btrieve.STATUS_CODE_NO_ERROR):
  print(' レコードの挿入は正常に実行されました。')
else:
  print(' レコードの挿入に失敗しました。ステータス: ', rc)
			
 

※センサーデータ取得プログラムの作成にあたり、以下の記事を参考にしています。
     https://www.ne.senshu-u.ac.jp/~iida/pc/?p=1629  

Sensor-extract.py の作成

分析サーバ側のデータベースを定義します。スキーマ情報は以下になります。

 

data1average: 統計データ用のテーブル

data1manage: 稼働中のセンサーを管理するテーブル

 
CODE

CREATE TABLE "data1average" IN DICTIONARY USING 'data1average.mkd' PAGESIZE=4096 (
 "instime" TIMESTAMP,
 "sno" INTEGER NOT NULL,
 "xavg" DOUBLE NOT NULL, 
 "yavg" DOUBLE NOT NULL,
 "zavg" DOUBLE NOT NULL,
 "xmax" INTEGER NOT NULL,
 "ymax" INTEGER NOT NULL,
 "zmax" INTEGER NOT NULL,
 "xmin" INTEGER NOT NULL,
 "ymin" INTEGER NOT NULL,
 "zmin" INTEGER NOT NULL );

CREATE UNIQUE INDEX "AVGidx1" USING 0 IN DICTIONARY  ON "data1average" ( "instime", "sno" );

CREATE TABLE "data1manage" IN DICTIONARY USING 'data1manage.mkd' PAGESIZE=4096 (
 "sno" INTEGER,
"title" CHAR(50) );
			
 

次のライブラリをインストールします。

 
CODE

# ODBC経由でデータベースに接続するためのライブラリ
pip3 install pyodbc 

#統計情報を計算するためのためのライブラリ
pip3 install pandas  
			
 

Sensor-extract.py ファイルを作成して、統計情報(1分ごとの平均値、最大値、最小値)を取得するロジックを作成します。ポイントとなる箇所のみピックアックして説明します。

 

データベースの接続。(接続先の情報はお客様の環境にあわせてください)

 
CODE

import pyodbc
conn_str = 'Driver={Pervasive ODBC Interface};server=172.31.x.xx:1583;DBQ=vsensor'
db = pyodbc.connect(conn_str)

			
 

統計データを取得。こちらは DB 操作に SQL を使用します。

 

pandas の to_datetime() 関数を使い、instime のカラムを datetime 型に変換(新たに作成したデータは inplace=True でそのまま instime カラムに上書き)し、その後、pandas の resample() 関数で時系列データをグルーピングし直し、その結果を元に平均値、最大値、最小値を求めていきます。

 
CODE

import pandas as pd
query = "SELECT * from data1 where sno = {0} and instime between \'{1}\' and \'{2}\'".format(sno,st_date,end_date)
df = pd.read_sql(sql = query,con =  db)
 
df['instime'] = pd.to_datetime(df['instime'])
df.set_index('instime', inplace=True)
 
data_range = "{0}T".format(key)
df.resample(data_range).mean()
average_data = df.resample(data_range).mean()
max_data = df.resample(data_range).max()
min_data = df.resample(data_range).min()

			
 

動作確認

センサーデータ収集プログラム(sensor-collect.py)は、センサー番号を引数にして実行します。

 

python3 sensor-collect-DB.py 1111

			
 

統計データ作成プログラム(sensor-extract.py)は、センサー番号と任意のセンサー名を引数に実行します。

 

python3 sensor-extract-DB.py 1111 sensor1

			
 

※3機の工作機械に取り付けたセンサーの番号を「1111」~「3333」、センサー名を「sensor1」~「sensor3」としています。

 

SQL 文から1分ごとに統計情報の計算を行っているのが分かります。

 

 

netPI セットアップ

開発機では GPIO ピン経由でセンサーからのデータを取り込みましたが、netPI では金属ハウジングを開けずにスライドインで装着できる拡張モジュールを介してデータを取り込みます。(I2C 通信用には、「I2C/SPI用拡張モジュール」が用意されています)

 

【センサーと netPI + I2C 拡張モジュールの接続】

センサーと I2C/SPI 用拡張モジュールは以下のように接続します。

・I2C 拡張モジュールの2番ピン → センサーの GND
・I2C 拡張モジュールの3番ピン → センサーの SDA
・I2C 拡張モジュールの4番ピン → センサーの SCL
・I2C 拡張モジュールの5番ピン → センサーの 3.3V

 

※実際には卓上扇風機を振動する工作機械に見立ててセンサーを取り付けています。

 

開発機で作成したプログラムをコンテナごとイメージファイルにして netPI に展開する

開発機から netPI へプログラムを移行する方法は幾つかありますがコンテナイメージの圧縮ファイルを作り、netPI の管理画面からインポートする方法が便利です。

 

以下のコマンドにてコンテナ情報をコミットし、save します。

 
CODE

# コンテナ情報のコミット( 引数は、 コンテナ名 Dockerイメージ名)
docker commit f38d14df0f6a netpidemo1027

# tarファイル作成 (引数は、 tarファイル名 Dockerイメージ名)
docker save -o netpidemo.tar netpidemo1027
			
 

取得した tar ファイルを netPI 管理画面からインポートします。

 

 

これで netPI でセンサーデータを取得して、分析サーバへデータを送る環境が整いました。

 

3.分析側システム環境

netPI 側から転送された1分ごとの分析値(平均、最大、最小)をグラフィック表示し、ブラウザからモニタできるようにします。

 

今回実装した分析サーバー側のプログラムの構成は以下のようになっています。

 

 

【モニタリングプログラムの開発】

グラフィック表示には JavaScript のライブラリである「React」を使用します。また、Web アプリ用インターフェースとして FastAPI を使用します。こちらのプログラム言語は Python です。

 

DB のライセンスについて、Web システムの場合、クラサバ型のシステムと違って DB への接続が1つの API に集約されて DB 側が接続クライアント数を捕捉できないことから、同時接続数ベースのライセンスは使用できません。よって、ファイル容量ベースのライセンスである「Zen Cloud SV」を使用します。(「Cloud SV」というネーミングですが、使用場所の制限はなく、オンプレミスでも使用可能です)

 

Main.py の作成

サーバー側のプログラムは以下のような構成です。

  • api/
    • db.py
    • main.py
    • routers
      • read.py

Web フレームワーク「FastAPI」のセットアップ。(FastAPI のサーバ起動は、Uvicorn ライブラリを利用します)

 

$ pip install fastapi uvicorn

			
 

プロジェクトルートに、以下のように api/main.py を追加し FastAPI のコードを記述します。

 

<Main.py>

CODE

from fastapi import FastAPI
 
from api.routers import read
from fastapi.middleware.cors import CORSMiddleware
 
app = FastAPI()
			
 

プロジェクトルートに、以下のように api/db.py を追加し、DB 接続クラスを作成します。

 

<db.py>

CODE

import pyodbc 

# 分析サーバーと接続するためのDSNを作成しておく。
cnxn = pyodbc.connect("dsn=xxxxx")
cursor = cnxn.cursor()

			
 

api/routers/read.py を追加し、DB に接続する Read 処理と、API に繋げて動作を確認します。

 

<Read.py>

CODE

from fastapi import APIRouter, Depends
import json
from api.db import cursor
import time
 
router = APIRouter()
 
@router.get("/sno/{item_id}")
def get_data(item_id:int):
    cursor.execute('SELECT * from data1average where sno ={0} and instime = (select max(instime) from data1average where sno ={0})'.format(item_id,item_id))
    row = cursor.fetchall()
    users = []
    for doc in row:
        user = {
            "rno": doc[1],
            "instime": str(doc[0]),
            "sno": doc[2],
            "xdata": doc[3],
            "ydata": doc[4],
            "zdata": doc[5],
        }
        users.append(user)
    
    return user

			
 

以下のコマンドで API を立ち上げます。

 

Python run uvicorn –host 0.0.0.0 api.main:app

			
 

 

この状態で、ブラウザで以下の URL にアクセスします。

http://localhost:8000/docs

 

するとこのように、GET /sno/{item_id} というエンドポイントが現れます。これでサーバーが立ち上がりました。

 

 

Dashboard.js の作成

今回は create-react-app の雛形を用いました。

 

フロント側は以下のような構成になっています。(主要ファイルのみ掲載)

  • frontend/src/
    • node_modules/
    • components/
      • RealtimeLineChart.js
      • xxxxxx.js
    • pages/
      • Dashboard.js
      • Settings.js
    • ・・・・・
    • ・・・・・

dashboard.js の以下の部分でサーバー側とのやり取りを行なっています。

※http://0.0.0.0:8000/ は、上記で構築した API サーバーになります。

 

<dashboard.js>

   
 const getSensorData =   (data,id) => {
        return new Promise(async(resolve, reject)=>{
          try {       
              const sensor_data =  await axios.get(`http://0.0.0.0:8000/sno/${id}`).then((res) => {

			
 

グラフの描画は React Apex-chart を利用しています。

 

<RealtimeLineChart.js>


import Chart from "react-apexcharts"; 

			
 

4.結果

フロントエンド/バックエンド用にそれぞれターミナルを立ち上げて以下のコマンドを実行します。

 

<サーバー側>


Python run uvicorn –host 0.0.0.0 api.main:app

python server.py

			
 

<フロント側>


npm run start

			
 

ブラウザから localhost:3000 にアクセスすることでアプリを利用できます。(npm run start で自動で立ち上がります)

 

 

3台の設備から発する振動データの1分ごと平均値の推移を見ることができるようになりました。このグラフから突出した値や異常な値の推移を視覚的に認識することができます。

※1分ごとのデータ推移を早送りで表示しています。

※最大値、最小値の表示は割愛しています。

 

産業用途向けに耐久性を強化した netPI と IoT 向け小型デバイスに搭載可能な軽量データベース Actian Zen を使ったシステムの一例を示しました。

 

製造現場の IT 化や IP ネットワーク対応のエッジデバイスの活用は今後も拡大していくと思われます。小リソースで軽量・軽快な処理を実現する NoSQL データベース・Actian Zen をぜひご活用ください。

 

※当記事に掲載しているコードは、本アプリケーションプログラムから一部を抜粋したものです。全ソースコードは以下「sample.zip」からダウンロードできます。(補足説明を Readme.txt に記載しています)
ご不明な点がありましたらお気軽にお問い合わせください。

Readme.txt
sample.zip

 

▼ Actian Zen の詳細についてはこちら

https://www.agtech.co.jp/actian/product/

 

▼ ヒルシャー・ジャパン株式会社「netPI」についてはこちら

https://www.hilscher.com/ja/netpi/

一覧に戻る

Contactお問い合わせ

お気軽にお問い合わせください。

お問い合わせ

    必須会社名

    個人のお客様は「個人」と入力してください。

    必須お名前
    必須メールアドレス
    必須メールアドレス(確認)
    必須ライセンス ありなし
    ダウンロード目的