Contents
ONVIF仕様に準拠したIPカメラの制御
1.目的
ONVIF規格に対応したIO DATA社製のネットワークカメラ TS-NS310WをNode-REDで制御します。また、画像差分の表示を行うフローを作成します。
TS-NS310Wは以下の様な外観をしており、パン動作(左右)、チルト動作(上下)方向に動かすことができます。また、ONVIF-SとONVIF-Tに対応しています。
このカメラを用いて、Node-REDのONVIF対応Nodeで画像取得とパン、チルト動作制御確認を行います。
2.ONVIFとは
ONVIF(Open Network Video Interface Forum)とは、ネットワークカメラ(IPカメラ)製品のインターフェースに互換性をもたせるために設立された標準化フォーラムのことです。2008年にAXIS(キヤノングループ)、BOSCH、SONYの3社で設立されました。
ネットワークカメラ情報の設定や取得、カメラの光学制御やPTZ (パン・チルト・ズーム) 制御、イベントハンドリング、Video Analytics, ストリーミング、セキュリティなど、ネットワークカメラの利用に必要なインターフェイスを定義しています。
ONVIF準拠製品は少なくとも1つのONVIFプロファイルに準拠している必要があります。 ONVIFプロファイルによって、準拠しているクライアント (ビデオ管理ソフトウェアなど) とデバイス (ネットワークカメラやビデオエンコーダーなど) が相互運用することができます。
プロファイル |
概要 |
S |
IPネットワークに接続された映像機器の通信・制御プロトコルの標準化 |
C |
サイト情報、ドアアクセス制御、イベントおよびアラーム管理 |
G |
IPネットワークに接続された録画機器の通信/制御プロトコルの標準化 |
Q |
IPネットワーク内のセキュリティ製品の検出と設定の提供 |
A |
アクセス制御、従業員の認証情報の付与と取り消し、スケジュールの作成と更新、アクセスルールの割り当て |
T |
H.264およびH.265エンコーディング方式、画像の設定、また動体検知やいたずら検知などのアラームイベントの使用などの高度なストリーミング機能をサポート |
3.専用ソフトQwatchを使ったIPカメラの動作確認
TS-NS310Wは、取扱説明書に従って、PC用のソフトをインストールすることでネットワーク越しに動画を見ることができます。また、この画面でパン・チルト動作を行うこともできます。
4.Node-REDのNodeを使った動作確認
Node-REDのnodeとして、“node-red-contrib-onvif-nodes”を使用します。パレット管理を使って、インストールする必要があります。
このノードは、ONVIF関連の7つのノードが含まれています。
ここでは、1) ONVIF準拠のデバイスをネットワーク上で検索するonvif-discoveryノード、2) 静止画像を取得する“onvif-media”と3)パン・チルト・ズーム動作を行う”onvif-ptz”を使用します。
その他の機能の詳細は、次のHPを参照ください。
4.1.ONVIF準拠デバイスの検索
ネットワーク上に設置したIPカメラを検索して、情報を取得できるか確認します。
■ ノードの説明:
このノードが(入力メッセージによって)トリガされると、ネットワーク上のすべてのデバイスに ONVIF ブロードキャストを送信します。ONVIF 準拠デバイスはすべてこれに応答し、出力メッセージ msg.payload にリストされます。
取得情報の例:
{“endpointReference”:{“address”:“urn:uuid:862613a5-eced-a2cd-9ae8-5041b923cc8a”}, “types”:[“dn:NetworkVideoTransmitter”,“tds:Device”], “scopes”:[“onvif://www.onvif.org/type/video_encoder”, “onvif://www.onvif.org/type/audio_encoder”, “onvif://www.onvif.org/type/ptz”, “onvif://www.onvif.org/hardware/TS-NS310W”, “onvif://www.onvif.org/name/IODATATS-NS310W”, “onvif://www.onvif.org/Profile/T”, “onvif://www.onvif.org/Profile/Streaming”, “onvif://www.onvif.org/location/”], “XAddrs”:[“http://192.168.20.4:43334/onvif/device_service”, “http://169.254.126.101:43334/onvif/device_service”], "metadataVersion":1}
上の取得城例では、hardwareとしてTS-NS310W、nameとしてIODATATS-NS310Wというのが見て取れます。また、XAddrsとして、IPアドレスとポート番号が 確認できます。IPアドレスとポート番号は次から接続のための設定に使用します。
4.2.静止画像の取得確認
onvif mediaノードを使って、静止画像をNode-REDのダッシュボードに表示させます。
■ ノードの説明:
getSnapshotUriアクションは、カメラからスナップショット画像を取得するために使用できるURLを返します。このURLはHttpRequestノードに渡してスナップショット画像を取得できます。URLは、”msg.payload.uri”に格納されて返ってきます。
次のフローは、injectノードのボタンを押すと、onvif mediaノードのgetSnapshotUriアクションを使ってURLを取得します。そして、そのURLをhttp requestノードに渡して画像を表示しています。上段は、base64ノードを使ってテキストに変換した後、ダッシュボードのテンプレートノードを使ってダッシュボードに表示します。下段は、取得した画像をimage-outputノードを使ってフローエディタの画面に表示します。次に、画像が表示されたフローとダッシュボードの画像を示します。
以下、①~④のノードの設定に関して説明します。
① onvif mediaノードのプロパティ設定
onvif mediaノードのプロパティ設定を説明します。
1) Deviceを新規に作成します。Device欄の「新規にonvif-configを追加」を選ぶとonvif-configノードの編集画面が開きます。
IPアドレス、ポート番号、User名、パスワードを設定して「更新」を押します。
2) Actionは“Get snapshot URI”を選びます。
3) ProfileにはQwatchで設定したProfileを記述します。空欄でも動作します。
② changeノードのプロパティ設定
“set msg.url”と言う名称のchangeノードは、onvif-mediaノードから送られた”msg.payload.url”を”msg.url”に変換して、http requestノードに送ります。
③ http requestノードのプロパティ設定
onvif-mediaノードから送られたIPアドレスに、http requestを送信します。そのため、http requestノードを使用します。
http requestノードのプロパティ編集画面です。
1)メソッドを“GET”にして、画像を取得します。
2)URLは、前段からの”msg.url”で入力しますので空欄にします。
3)「認証を使用」にチェックを入れます。ここがポイントです。
・ 認証は、“Digest認証”を選びます。
・ユーザ名とパスワードを入力します。
4)出力形式は、バイナリバッファを選びます。
④ templateノードのプロパティ設定
ダッシュボードのtemplateノードは、http reqestノードで取得したバイナリのjpeg画像データをbase64ノードでテキストに変換した後に、ダッシュボードで表示します。
そのため、HTMLコードの欄に、以下の記述を入れます。
<img alt="現在画像" src="data:image/jpg;base64,{{msg.payload}}" />
4.3.パン・チルト・ズーム動作の確認
onvif ptzノードを使って、カメラをパン・チルト・ズーム動作させます。ただし、今回使用したIPカメラTS-NS310Wはズームに対応していないため、パンとチルト動作の確認になります。
■ ノードの説明:
このノードはOnVifデバイスをPTZ制御(パン/チルト/ズーム)するだけでなく、ホームポジションと複数のプリセットポジションを提供します。もちろん、これはカメラのハードウェアがPTZをサポートしている場合にのみ可能です。
1) パン・チルト動作
PTZカメラは、遠隔操作で方向(垂直、水平)とズーム(イン、アウト)を変更できます:
パン:カメラを左(値は-1.0~0.0)または右(値は0.0~1.0)に移動します。
チルト: カメラを下方向(-1.0 ~ 0.0)または上方向(0.0 ~ 1.0)に動かします。
具体的には、msg.pan_speed, msg.tilt_speedに-1.0~1.0の値を設定し、msg.action = continuousMoveと一緒にonvif ptzノードへ送ります。
2) ホームポジション
PTZカメラにはホームポジションがあります。PTZコントロールでカメラを特定の位置に移動し、ホームポジションを「設定」します。ホームポジションを設定した後、再びPTZコントロールでカメラを移動できます。しかし、’goto home position’を押すと、カメラは自動的に以前に設定したホームポジションに戻ります。
具体的には、msg.action = gotoHomePositionを設定して、onvif ptzノードへ送ります。
次のフローは、パン・チルト動作(-0.5, 0.5)とホームポジションへ帰る動作をIPカメラにさせます。ボタンはダッシュボードに設置します。
以下、①~④のノードの設定に関して説明します。
① templateノードのプロパティ設定
templateノードのプロパティ設定は、onvif medeiaノードで設定したのと同じonvif-configを選択するだけです。Actionは、前段から送ります。
② buttonノードのプロパティ設定
buttonノードのプロパティ設定は、payloadに移動スピードを設定します。ここでは左なので負の数字-0.1を入れています。右なら0.1、上なら0.1、下なら-0.1です。
③ changeノードのプロパティ設定(pan, tilt)
changeノードの設定です。2つあります。
1つ目は、msg.pan_speedにmsg.payloadを代入します。②のbuttonノードでmsg.payload=-0.1を設定しましたので、ここで、msg.pan_speed = -0.1 となります。上下の場合は、msg.tilt_speedに変えてください。
2つ目は,msg.actionに continuousMove を設定します。これで、後段のonvif ptzノードにmsg.payload = -0.1 と msg.action = continuousMove が送られ、左向きに0.1のスピードで継続動作します。継続動作とは、今の位置からさらに左に動くということです。
④ changeノードのプロパティ設定(Home position)
Home position用のchangeノードのプロパティ設定です。
msg.actionに、getHomePosionを設定します。結果、msg.action = getHomePosition がonvif ptzに送られます。
前段のbuttonノードは、起動用でしかありません。
◆ 動作確認結果
上下、左右に一度ずつ動かした場合の画像です。パンとチルト、ホームポジションへの移動は問題なく動きます。
5.Node-REDのNodeを使った応用例
5.Node-REDのNodeを使った応用例
4.2と4.3で作ったNode-REDのフローを組み合わせて、パン・チルト動作をさせながら、画像を確認するアプリケーションを作成しました。
SHUTTERボタンを押すと、現在の画像を取得します。
また、UP, DOWN, LEFT, RIGHT を押すと上下左右に移動して、画像を取得します。下記フローのdelayノードは、カメラが移動していくのに応じて、画像を取得するために追加しています。HOME POSITIONを押すとホームに戻り、画像を更新します。
Appendix2.にフローファイルを載せておきますので、動作確認を行ってみてください。
ここで紹介したのは、機能の一部です。
Appendix1. もう一つのonvifノードの動作確認
パレット検索で“onvif”検索を行うともう一つ、ノードが出てきます。node-red-contrib-onvifです。こちらの動作を確認したのですが、上手く動かせませんでした。
onvifノードの出力が“null”で画像データが読み出せない。
Appendix2. Node-REDのNodeを使った応用例のサンプルフロー
[{"id":"821399bfa42516a9","type":"tab","label":"IP Camera","disabled":false,"info":"","env":[]},{"id":"e4b0b26622603cd6","type":"ui_button","z":"821399bfa42516a9","name":"Left","group":"5dcdd9123d7e0714","order":7,"width":3,"height":1,"passthru":false,"label":"Left","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"-0.1","payloadType":"num","topic":"topic","topicType":"msg","x":190,"y":320,"wires":[["ba59e9c3f83263eb"]]},{"id":"ba59e9c3f83263eb","type":"change","z":"821399bfa42516a9","name":"continuousMove","rules":[{"t":"set","p":"pan_speed","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"action","pt":"msg","to":"continuousMove","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":340,"wires":[["287e56ad3b2146a1","e92df425e06c96fe"]]},{"id":"012cfc807b7df344","type":"ui_button","z":"821399bfa42516a9","name":"Right","group":"5dcdd9123d7e0714","order":9,"width":3,"height":1,"passthru":false,"label":"Right","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"0.1","payloadType":"num","topic":"topic","topicType":"msg","x":190,"y":360,"wires":[["ba59e9c3f83263eb"]]},{"id":"287e56ad3b2146a1","type":"onvif-ptz","z":"821399bfa42516a9","name":"","deviceConfig":"c3dd806e6fa19e2d","profileName":"","action":"","panSpeed":0,"tiltSpeed":0,"zoomSpeed":0,"panPosition":0,"tiltPosition":0,"zoomPosition":0,"panTranslation":0,"tiltTranslation":0,"zoomTranslation":0,"time":1,"preset":"","presetName":"","stopPanTilt":true,"stopZoom":true,"configurationToken":"","x":588,"y":438,"wires":[["703bfea6b16d711c"]]},{"id":"e92df425e06c96fe","type":"link out","z":"821399bfa42516a9","name":"link out 2","mode":"link","links":["4cff44982fa43ad7"],"x":515,"y":380,"wires":[]},{"id":"3aa760db38a164bf","type":"change","z":"821399bfa42516a9","name":"continuousMove","rules":[{"t":"set","p":"tilt_speed","pt":"msg","to":"payload","tot":"msg"},{"t":"set","p":"action","pt":"msg","to":"continuousMove","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":340,"y":420,"wires":[["287e56ad3b2146a1","e92df425e06c96fe"]]},{"id":"a5c1ef703bb52e11","type":"change","z":"821399bfa42516a9","name":"","rules":[{"t":"set","p":"action","pt":"msg","to":"gotoHomePosition","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":338,"y":498,"wires":[["287e56ad3b2146a1","4e8fa9666581edef","b4fd66b78c2e981e"]]},{"id":"703bfea6b16d711c","type":"debug","z":"821399bfa42516a9","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","x":748,"y":438,"wires":[]},{"id":"5b218a7afc59392b","type":"ui_button","z":"821399bfa42516a9","name":"Up","group":"5dcdd9123d7e0714","order":5,"width":3,"height":1,"passthru":false,"label":"Up","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"0.1","payloadType":"num","topic":"topic","topicType":"msg","x":190,"y":400,"wires":[["3aa760db38a164bf"]]},{"id":"4f92f57c6ab96a0a","type":"ui_button","z":"821399bfa42516a9","name":"Down","group":"5dcdd9123d7e0714","order":11,"width":3,"height":1,"passthru":false,"label":"Down","tooltip":"","color":"","bgcolor":"","className":"","icon":"","payload":"-0.1","payloadType":"num","topic":"topic","topicType":"msg","x":188,"y":438,"wires":[["3aa760db38a164bf"]]},{"id":"b1ad8075128a7b67","type":"ui_button","z":"821399bfa42516a9","name":"Home Position","group":"5dcdd9123d7e0714","order":8,"width":3,"height":1,"passthru":false,"label":"Home Position","tooltip":"","color":"","bgcolor":"Green","className":"","icon":"","payload":"","payloadType":"date","topic":"topic","topicType":"msg","x":158,"y":498,"wires":[["a5c1ef703bb52e11"]]},{"id":"4e8fa9666581edef","type":"link out","z":"821399bfa42516a9","name":"link out 1","mode":"link","links":["4cff44982fa43ad7"],"x":753,"y":498,"wires":[]},{"id":"b4fd66b78c2e981e","type":"delay","z":"821399bfa42516a9","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":518,"y":538,"wires":[["4e8fa9666581edef","ce8e1b8cc117a4a5"]]},{"id":"ce8e1b8cc117a4a5","type":"delay","z":"821399bfa42516a9","name":"","pauseType":"delay","timeout":"1","timeoutUnits":"seconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":658,"y":558,"wires":[["4e8fa9666581edef"]]},{"id":"296e9e8472bbddbb","type":"comment","z":"821399bfa42516a9","name":"Pan-Tilt","info":"","x":128,"y":278,"wires":[]},{"id":"4cff44982fa43ad7","type":"link in","z":"821399bfa42516a9","name":"link in 5","links":["4e8fa9666581edef","e92df425e06c96fe"],"x":213,"y":218,"wires":[["4be107eb9d78e4c4"]]},{"id":"4be107eb9d78e4c4","type":"delay","z":"821399bfa42516a9","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":328,"y":218,"wires":[["66ca0a39d8b49b3f","a32abaf3478ef4d2"]]},{"id":"a32abaf3478ef4d2","type":"delay","z":"821399bfa42516a9","name":"","pauseType":"delay","timeout":"500","timeoutUnits":"milliseconds","rate":"1","nbRateUnits":"1","rateUnits":"second","randomFirst":"1","randomLast":"5","randomUnits":"seconds","drop":false,"allowrate":false,"outputs":1,"x":508,"y":218,"wires":[["66ca0a39d8b49b3f"]]},{"id":"5227faaad89843db","type":"inject","z":"821399bfa42516a9","name":"Get snapshot","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"","payloadType":"date","x":148,"y":138,"wires":[["66ca0a39d8b49b3f"]]},{"id":"e3abe5e07925c89d","type":"ui_button","z":"821399bfa42516a9","name":"Shutter","group":"5dcdd9123d7e0714","order":2,"width":3,"height":1,"passthru":false,"label":"Shutter","tooltip":"","color":"","bgcolor":"red","className":"","icon":"","payload":"","payloadType":"date","topic":"topic","topicType":"msg","x":158,"y":98,"wires":[["66ca0a39d8b49b3f"]]},{"id":"66ca0a39d8b49b3f","type":"onvif-media","z":"821399bfa42516a9","name":"","deviceConfig":"c3dd806e6fa19e2d","profileToken":"","profileName":"OnvifProfile2","videoEncoderConfigToken":"","videoEncoderConfigName":"","videoEncoderConfigEncoding":"","action":"getSnapshotUri","protocol":"","stream":"","x":328,"y":138,"wires":[["51f301d110db38c6"]]},{"id":"51f301d110db38c6","type":"change","z":"821399bfa42516a9","name":"","rules":[{"t":"set","p":"url","pt":"msg","to":"payload.uri","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":488,"y":138,"wires":[["421ae672061b2795"]]},{"id":"421ae672061b2795","type":"http request","z":"821399bfa42516a9","name":"","method":"GET","ret":"bin","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","insecureHTTPParser":false,"authType":"digest","senderr":false,"headers":[],"x":648,"y":138,"wires":[["893975461dc141c7","ee424675e0f504a7"]]},{"id":"ee424675e0f504a7","type":"image","z":"821399bfa42516a9","name":"","width":200,"data":"payload","dataType":"msg","thumbnail":false,"active":true,"pass":false,"outputs":0,"x":838,"y":178,"wires":[]},{"id":"893975461dc141c7","type":"base64","z":"821399bfa42516a9","name":"","action":"","property":"payload","x":818,"y":138,"wires":[["29f0e6ad337287fb"]]},{"id":"29f0e6ad337287fb","type":"ui_template","z":"821399bfa42516a9","group":"5dcdd9123d7e0714","name":"Display image","order":1,"width":9,"height":6,"format":"\n<img alt=\"HTTP Snap\" src=\"data:image/jpg;base64,{{msg.payload}}\" />\n","storeOutMessages":true,"fwdInMessages":true,"resendOnRefresh":false,"templateScope":"local","className":"","x":978.3645515441895,"y":138.6041603088379,"wires":[[]]},{"id":"5add915703d47918","type":"comment","z":"821399bfa42516a9","name":"Imaging node(snap shot-1)","info":"","x":148,"y":58,"wires":[]},{"id":"5dcdd9123d7e0714","type":"ui_group","name":"OnVIF Node","tab":"71b325fdded2da13","order":5,"disp":true,"width":"9","collapse":false,"className":""},{"id":"c3dd806e6fa19e2d","type":"onvif-config","xaddress":"192.168.20.4","port":"43334","timeout":"3","checkConnectionInterval":"5","name":"ip-camera"},{"id":"71b325fdded2da13","type":"ui_tab","name":"IP Camera","icon":"dashboard","disabled":false,"hidden":false}]