仮想ディスプレイによるウィンドウを表示しないGUI実行¶
概要¶
コマンドラインからのスクリプト実行 の ウィンドウを表示しないヘッドレス実行 では、 --no-window オプションによってウィンドウを表示せずにChoreonoidを実行する方法を紹介しています。このモードは手軽にヘッドレス実行を行えるものですが、シーンビュー等のGUI上の描画は行われないという制限があります。
一方で、GUIを非表示のまま自動実行しつつ、GUIの動作そのものは必要となる場合もあります。例えば以下のような場合です。
スクリプトによる自動実行において、シーンビューの描画結果を画像として取得したい
GUIの動作を含めた一連の処理の確認やテストを自動化したい
このような場合は、「仮想ディスプレイ」を使用することで、実際の画面には何も表示せずに、通常のウィンドウシステムがあるのと同じ状態でChoreonoidを実行することができます。ウィンドウは仮想ディスプレイ上に生成されて描画も通常どおり行われるため、GUIの機能をすべて利用でき、なおかつユーザの画面には何も表示されません。
本節では、仮想ディスプレイの代表的な実装であるXvfbを使う方法を中心に解説します。なお、本節の内容はLinuxを対象としています。
Xvfbとは¶
Xvfb (X virtual framebuffer)は、X Window Systemの仮想ディスプレイサーバです。実際のディスプレイやグラフィックスハードウェアを使用せず、メモリ上のフレームバッファに対して描画を行います。
Ubuntuでは以下のコマンドでインストールできます。
sudo apt install xvfb
基本的な使い方¶
まずXvfbを起動して仮想ディスプレイを作成します。
Xvfb :99 -screen 0 1920x1080x24 &
":99" はディスプレイ番号で、使用中の番号と重複しなければ任意の番号を使用できます。 "-screen 0 1920x1080x24" は画面番号0の解像度と色深度の指定です。
次に、環境変数 DISPLAY でこの仮想ディスプレイを指定して、Choreonoidを起動します。
DISPLAY=:99 choreonoid project.cnoid script.py
するとChoreonoidのウィンドウは全て仮想ディスプレイ上に生成され、実際の画面には何も表示されません。GUIは通常どおり動作しており、シーンビューの描画も行われています。
ただし、デスクトップがWaylandセッションで動作している環境では、この指定だけではウィンドウが実際の画面に表示されてしまいます。その場合は Waylandセッションでの利用 で述べる方法で起動してください。
使い終わったら、起動したXvfbのプロセスを終了(kill)しておきます。
また、 xvfb-run コマンドを使うと、Xvfbの起動・終了と DISPLAY の設定を自動で行ってくれます。
xvfb-run -a -s "-screen 0 1920x1080x24" choreonoid project.cnoid script.py
"-a" は空いているディスプレイ番号を自動で選択するオプションです。コマンドの終了とともにXvfbも終了するので、バッチ実行ではこちらの方が手軽です。
Waylandセッションでの利用¶
近年のLinuxディストリビューションでは、デスクトップのセッションがX11ではなくWaylandで動作していることが多くなっています。現在のセッションタイプは以下のコマンドで確認できます。
echo $XDG_SESSION_TYPE
これが "wayland" と表示される環境では、ChoreonoidのGUIを構成するQtは通常Waylandプラットフォームプラグインを使用して動作します。この場合、ウィンドウの表示先の決定に環境変数 DISPLAY は使用されないため、 DISPLAY に仮想ディスプレイを指定しても、ウィンドウは実際の画面(Waylandコンポジタ)上に表示されてしまいます。
XvfbはX Window Systemのサーバであるため、これを利用するにはQtをX11(xcb)プラットフォームで動作させる必要があります。これは環境変数 QT_QPA_PLATFORM で指定できます。Waylandセッションでは以下のようにしてChoreonoidを起動してください。
env -u WAYLAND_DISPLAY QT_QPA_PLATFORM=xcb DISPLAY=:99 choreonoid project.cnoid script.py
ここでは QT_QPA_PLATFORM=xcb によってQtのプラットフォームをX11(xcb)に固定しています。また、Waylandの利用を前提とする処理が実行されることのないよう、 env -u WAYLAND_DISPLAY によって環境変数 WAYLAND_DISPLAY を解除しています。これにより、ウィンドウは仮想ディスプレイ上にのみ生成されるようになります。
xvfb-run を使用する場合も同様に指定します。
env -u WAYLAND_DISPLAY QT_QPA_PLATFORM=xcb xvfb-run -a -s "-screen 0 1920x1080x24" choreonoid project.cnoid script.py
なお、X11セッションではQtはデフォルトでxcbプラットフォームを使用するため、これらの指定を付けても動作は変わりません。セッションタイプが不明な場合や、様々な環境で使用するスクリプトでは、常にこの指定を付けておくと確実です。
シーンビューの描画結果の取得¶
仮想ディスプレイ上ではシーンビューの描画が通常どおり行われているので、その結果を画像として取得することができます。
ひとつの方法は、Pythonスクリプトからシーンビューの画像を保存することです。以下のようにして、シーンビューの描画内容を画像ファイルに保存できます。
from cnoid.Base import *
SceneView.instance.sceneWidget.saveImage("scene.png")
プロジェクトの読み込み直後など、まだ描画が行われていない状態では正しい画像が得られない場合があるので、必要に応じてタイマー等を用いて描画後のタイミングで実行するようにしてください。
もうひとつの方法は、仮想ディスプレイの画面全体のスクリーンショットを外部ツールで取得することです。例えばImageMagick(Ubuntuでは sudo apt install imagemagick でインストールできます)の import コマンドを使用して以下のようにします。
DISPLAY=:99 import -window root screenshot.png
この場合は、メインウィンドウ全体を含む画面全体の画像が得られます。
PythonのPillowライブラリがインストールされている環境であれば、以下のようにして同様のスクリーンショットを取得することもできます。
from PIL import ImageGrab
ImageGrab.grab(xdisplay=':99').save('screenshot.png')
留意点¶
Xvfb上での描画にはGPUは使用されず、ソフトウェアレンダリング(Mesaのllvmpipe等)となります。描画内容の確認には十分ですが、描画速度は実際のGPUよりも遅くなるため、描画性能の測定等には適しません。
視覚センサのシミュレーション(GLVisionSimulator)だけが目的であれば、仮想ディスプレイを使う必要はありません。 ウィンドウを表示しないヘッドレス実行 で述べているように、ウィンドウシステムの無い環境ではEGLによるレンダリングが使用され、こちらはGPUによるハードウェアアクセラレーションも有効となります。仮想ディスプレイはあくまで「GUI自体を動かす必要がある」場合の手段です。
自動化にあたっては、エラー発生時にChoreonoidを終了させる
--test-modeオプションの併用も便利です。
その他の方法¶
仮想ディスプレイとしてはXvfb以外にも以下のようなものがあり、目的に応じて使い分けることができます。
Xvnc(VNCサーバ)
TigerVNC 等が提供するXvncも、Xvfbと同様に仮想ディスプレイとして機能します。こちらはVNCクライアントから仮想ディスプレイの画面を随時見ることができるので、普段はウィンドウを表示せずに実行しつつ、動作の様子をときどき確認したいような場合に便利です。
xpra
xpra は「スクリーンレスモード」で仮想ディスプレイとして動作し、実行中のアプリケーションの画面に後からアタッチ(表示)・デタッチ(非表示)することができます。Xvncと同様に、必要なときだけ画面を確認する使い方ができます。
Qtのoffscreenプラットフォーム
Qtアプリケーションであるchoreonoidは、環境変数
QT_QPA_PLATFORM=offscreenを設定することでウィンドウシステム無しで起動することもできます。ただしこの場合、シーンビューの描画に使用しているQOpenGLWidgetがサポートされないため、シーンビューの描画は行われません。Choreonoidにおいては、ウィンドウシステムの無い環境で--no-windowモードが自動適用される際に内部的に使用されているものと同等であり、本節の目的(GUIの描画を伴う実行)には使用できません。
なお、仮想ディスプレイ上でもGPUによるレンダリングを行いたい場合は、 VirtualGL を組み合わせる方法があります。設定はやや高度になりますが、描画速度が必要な場合は検討してみてください。