python-venvノード開発記

# はじめに
　[Node-RED Advent Calendar 2024](https://qiita.com/advent-calendar/2024/node-red)の22日目です。

　English Version:

https://qiita.com/background/items/3244fc1b70cc454befef

　去年は[Node-RED MCUのノードの作成](https://qiita.com/background/items/9b820251aa9dda5a3167)について書かせて頂きました。

　今年も新しいノードをいくつか開発していたのですが、その中でも特にpython-venvノードの開発に力を入れていました。詳細は後述しますが、簡単に言うとPythonの仮想環境でPythonを実行するノードになっています。

　開発はしたものの、私自身は当初あまりPythonを使う機会がありませんでした。今ではpython-venvノードでPythonを使いつつ、海外の方々と交流しつつ、改良しながら開発を進めています。
　この記事ではノードの概要と開発の変遷、ノードの活用事例について書きます。

▼GitHubのリポジトリはこちら

https://github.com/404background/node-red-contrib-python-venv

▼Node-REDのフローライブラリでのページはこちら

https://flows.nodered.org/node/@background404/node-red-contrib-python-venv

# ノードの仕組み
## 概要
　Pythonの仮想環境を作成し、その中にパッケージをインストールして、Pythonのコードを実行できるノードになっています。
　node-red-contrib-python-venvをインストールすると、以下のvenvノード、pipノード、venv-execノードが追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/6604390b-3b82-fd47-ac85-6ce308ae4607.png)
 
　以下のように、venvノードにPythonのコードを記述して実行できます。Node-REDでJavaScriptを実行できるfunctionノードの、Python版のようなものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/033d3ec9-6225-476d-e05f-9a00d72678a4.png)
 
　以下の画像では仮想環境のpipとシステムのpipのパッケージが異なっています。ちょっと特殊な独立性を持ったノードになっています。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/4248fba0-6966-75b3-631c-3969d5955612.png)
　pipノードとしてパッケージのインストールのプロセスを残すことができます。フローを共有するだけで、Pythonの実行環境を再構築することが可能です。

　ノード内部の処理では以下の3つの仕組みを主に利用しています。
- Pythonの仮想環境内のパスを指定することで、仮想環境を利用できる
- 実行するコードの絶対パスを取得することができる
- npm installでインストールするときにpreinstallのスクリプトが先に実行される

### Pythonの仮想環境について

　個人的なデスクトップアプリをElectronで開発していたときに、Pythonの仮想環境内の実行ファイルであるactivateを実行しなくても、仮想環境を利用したいことがありました。
　仮想環境の中身や公式ドキュメントを見て試しているうちに、パスを指定して実行すればいいことに気づきました。その仕組みを利用しています。
関連記事：[Pythonの仮想環境を作成する（venv、Windows）](https://404background.com/program/python-venv/)

### 絶対パスについて

　JavaScriptでは__dirname、Pythonでは__file__などのファイルの絶対パスを取得する仕組みがあります。作成した仮想環境のパスは.jsonファイルに保存しておき、JavaScriptとPythonの両方で読み込むことができるようにしています。
関連記事：[Node-REDのノードを作成してみる　その１（python-venv）](https://404background.com/program/node-create-python-venv/)

### package.jsonのscriptsについて

　npmのscriptsに関する仕組みを利用して、仮想環境の作成に関するスクリプトを実行しています。
　package.jsonのscriptsフィールドで、preinstallにコマンドを記述すると、npm installが実行される前にそのコマンドが実行されます。
公式ドキュメントでの記述：https://docs.npmjs.com/cli/v8/using-npm/scripts

# 開発の変遷

　ノードの開発はGitHub上で公開しながら行っています。全部英語で書いていたおかげなのか、海外の方々と交流する機会がありました。

　一番最初のバージョンをリリースしたのは今年の3月頃です。春休みに作ったのを覚えています。その頃の実装については以下の記事に書いています。
　[Node-REDのノードを作成してみる　その１（python-venv）](https://404background.com/program/node-create-python-venv/)

　Node.jsのchild_processモジュールの、execSyncを利用してコマンドを実行するものになっていました。

　6月頃に海外の開発者の方からプルリクエストが来て、Node-REDのmsgオブジェクトへアクセスできるようになりました。どう実装するかは私も考えあぐねていたので、ありがたかったです。

 https://github.com/404background/node-red-contrib-python-venv/pull/6

 以下のようにmsg.payloadやmsg.testをPythonで取得することができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/cf594495-0950-4589-c97a-3495b1ef56e4.png)

　私もコンフィグレーションノードを追加して、複数の仮想環境を作成できるようにしました。

 https://github.com/404background/node-red-contrib-python-venv/pull/15

　ノードで仮想環境を選択して、その仮想環境にパッケージをインストールしたりコードを実行したりできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/37506440-8fd2-8d38-a0f8-6a5a729c15a8.png)

　npmコマンドでインストールしたものは実行ディレクトリのnode_modulesフォルダに追加されるのですが、絶対パスを指定して他のディレクトリに仮想環境を作成することもできます。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/0b6add29-bccb-8ed0-d7d8-e06e91285f27.png)

　午前中にマルチラインテキストエディタの機能を追加したら、午後にはPython用になってさらにエディタ画面が拡張できるように改善されたプルリクエストが来たこともありました。一気に開発が進みました。

https://github.com/404background/node-red-contrib-python-venv/pull/11

　さらに7月頃にはpython-shellノードを参考にした、Node.jsのchild_processモジュールのspawnを利用した実装方法が提案されました。execSyncではできなかった実行の停止や順次出力、実行状態の表示ができるようになりました。

https://github.com/404background/node-red-contrib-python-venv/pull/20

　例えばwhile文の処理を順次出力することができます。injectノードでも指定した時間間隔で出力できますが、venvノードでもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/464bcac9-8595-7901-5620-4b7af9983186.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/e5ce6509-2f81-ea5a-77c5-75438555dc86.png)

　少し期間が空いて10月頃に新しいノードを追加しました。venv-execノードです。

https://github.com/404background/node-red-contrib-python-venv/pull/32

　例えばWhisperやgTTSをpipでインストールするとコマンドとして実行できるようになります。このコマンドをノードでも実行できるようにしたのがvenv-execノードです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/70d82e36-1177-6801-be46-25f2c1443178.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/86c0f1f5-c9e6-4276-8006-e210cec93705.png)

　まだ実行を途中で止めたり、順次出力したりはできないので、改善点の残るノードではあります。

# ノードの活用例

## Pythonのパッケージの利用
　Pythonを実行できるということは、Pythonで利用できる便利で強力なパッケージを利用することができるということでもあります。

　例えばマイコンを使った簡易的なロボットを音声で操作するフローを作成してみました。

<iframe width="560" height="315" src="https://www.youtube.com/embed/EH8wNS8ryJ4" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

　全体のフローは以下のようになっています。
関連記事：[音声でロボットを操作してみる（Node-RED、Gemma2、Faster Whisper、XIAO ESP32C3）](https://404background.com/program/voice-operate/)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/5ecf2173-c1e3-4cfb-6774-b7f07f36847e.png)

　マイクで音声を録音し、その音声をテキスト化するのにvenvノードを使っています。さらにローカルLLMを利用するのにollamaノードを使ってみました。
関連記事：[Ollamaを使ってみる　その１（Gemma2、Node-RED）](https://404background.com/program/ollama/)

　ローカルLLMに送信するテキストをtemplateノードで作成しています。AIが条件判定を行うようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/8e40964f-92fd-e75d-0881-a048dc0d8558.png)

　文字起こしに2秒、ローカルLLMでの処理に2秒ぐらいかかるので、リアルタイム性が求められる処理には向いていないという印象です。ロボットアームを指定の位置に動かす場合などであれば使えると思います。

## ChatGPTでコード生成

　生成AIでコードも生成できる時代になりましたが、コードを実行できるvenvノードはAIとの相性がいいです。

　以下のフローでは論文のPDFからテキストを抽出して、翻訳し、ローカルLLMで要約するというフローになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616003/5186051e-70f4-b20e-cdbc-493709f7f8e3.png)

　文章量が多い論文だとvenvノードの出力を入力として受け取ることができなかったので、一度ファイルに保存してからそれぞれのノードで順次処理しています。
　ChatGPTですべての処理を一つのコードとして作成することもできるのですが、ノードに分割することで修正しやすく、管理しやすくなります。


## Raspberry Piでの実行

　具体例は無いのですが、ChatGPTにRaspberry Piでの開発の相談をしていたらPythonのコードを提示されることがありました。
　python-venvノードはRaspberry Piでも動作します。Raspberry PiのPythonに関するリソースを利用できます。
　ただしOSの種類によってはエラーが起きるかもしれません。UbuntuでデフォルトのPythonのバージョンが2系だったり、venvやpipがインストールされていなかった場合は対応が必要でした。

## Unreal Engine 5との連携

　[Node-RED Advent Calendar 2024](https://qiita.com/advent-calendar/2024/node-red)の21日目で書いた記事にも載せていたものですが、Unreal Engine 5(UE5)と連携させることができます。
　venvノードを連続実行モードにしてYOLOの検出結果を逐次出力するようにして、HTTP Requestノードで送信することができます。二視点の二次元座標から三次元座標に変換して、UE5内のオブジェクトを操作しています。

<iframe width="560" height="315" src="https://www.youtube.com/embed/3VK54l226DA" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>

　UE5の画面をPixel Streamingを利用してストリーミングし、venvノードでその画面を取得してYOLOを実行することもできました。YOLOの学習はコマンドでも実行できるのですが、venv-execノードを使えば実行できます。
関連記事：[YOLOで物体検出　その２（Python、Node-RED）](https://404background.com/program/yolo-2/)
[Unreal Engine 5を使ってみる　その11（Pixel Streaming ）](https://404background.com/ue5/unreal-engine-5-11/)
<iframe width="560" height="315" src="https://www.youtube.com/embed/lJGo7YjbGmk" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" loading="lazy" allowfullscreen></iframe>


# 最後に

　python-venvノードを利用することで、システムのPythonの環境に影響を与えることなくNode-REDでもPythonを実行できるようになりました。
　私が実装できなかった機能は、海外の方々の協力もあって実装することができました。今後も少しずつ改良していこうと思っています。

　最後に、Contributorの方々に感謝申し上げます。Thank you contributors!
　不具合や改善案、質問等があれば[GitHubのリポジトリ](https://github.com/404background/node-red-contrib-python-venv)へお気軽にご連絡ください！