ステップ6: カメラ画像のシミュレーションと取得

Tankモデルはカメラも搭載しています。ステップ6ではこのカメラをシミュレーションできるようにし、コントローラ側からカメラ画像を取得する方法を学びます。

ステップ5で作成したプロジェクトは視覚的に様々な要素が入っていますので、本ステップはこのプロジェクトで設定した状態から進めていきましょう。まずはプロジェクトを"Step6.cnoid"という名前であらためて保存し、本ステップの作業を行っていくことにします。

カメラデバイス

ステップ5で扱ったライトと同様に、カメラもChoreonoid上では「デバイス」のひとつとして定義されています。Tankモデルが搭載しているカメラは"Camera"という名前がつけられており、この名前でデバイスを識別することができます。コントローラからのアクセス方法はライトを含む他のデバイスと同様です。

モデルファイル上でカメラが実際にどのように定義されているかは、 Tankモデルの作成 における カメラの記述 を参照してください。

シーンビューにおけるカメラの変更

シーンビューは、ロボットモデルに搭載されたカメラからの視点で表示することも可能となっています。この機能を用いて、まずはTankモデルに搭載されているカメラからどのような画像が得られるかを確認してみることにしましょう。

カメラの切り替えは、以下のシーンバーの「カメラの選択」コンボボックスで行うことができます。

../../_images/scenebar-camera.png

ここをクリックすると、システムで定義されたカメラやシーンに表示されているモデルに含まれるカメラが一覧で表示されます。現在のプロジェクトの状態においては、以下が選択肢として表示されるかと思います。

  • Perspective - System
  • Orthographic - System
  • Camera - Tank

"Perspective - System" と "Orthographic - System" はシーンビューが標準で備えているカメラで、ユーザのマウス操作により視点を変更できるものです。それぞれ透視投影による描画、正射影による描画を行うカメラとなっていて、デフォルトはPerspectiveが選択されています。

"Camere - Tank"とあるのは、Tankモデルに搭載されている"Camera"という名前のカメラです。今回このカメラの視点でシーンを表示させたいので、これを選択しましょう。すると、Tankモデルが向いている方向やシーンビューの描画設定にもよりますが、以下のような画像がシーンビュー上に表示されるかと思います。

../../_images/sceneview-tankcameraview.png

この状態でシミュレーションを開始し、ゲームパッドでTankモデルを操作してみてください。するとTankの移動や砲塔の動きと連動して画像が動いていくかと思います。これがTankモデル搭載カメラからの映像になります。

なお、シミュレーションにおいては、このようなカメラ画像と、元々のシーンビューの表示である俯瞰的な画像を、両方同時に表示したいこともあります。Choreonoidではシーンビューを追加表示することによりこれが可能ですので、試してみましょう。

メインメニューの「表示」-「ビューの生成」から、「シーン」を選択します。すると「SceneViewの生成」というダイアログが表示されます。ここでは追加するビューの名前を設定できますが、特に指定したい名前がなければデフォルトのものでもかまいません。ここで「生成」ボタンを押すと、追加のシーンビューが生成され、メインウィンドウ上に配置されます。

ビューが生成される場所はビューの種類ごとに決まっており、今回は恐らく元々シーンビューが表示されていた領域に覆いかぶさるように追加のシーンビューが表示されるかと思います。これについてはタブを操作することで、元のビューに切り替えることができますが、これでは同時表示にはなりません。同時表示するためには、 ビューの移動 操作を行って、追加のビューをメインウィンドウ上の別の領域に移動させます。

ビューの移動ができたら、描画に使用するカメラも切り替えておきましょう。各シーンビューのカメラはそれぞれ独立に切り替えることができます。この場合、シーンバーの操作は最後にフォーカスの入ったシーンビューに対して適用されますので、まずは元からあるシーンビューの領域をマウスでクリックするなどしてフォーカスを入れた後、シーンバーのカメラ選択コンボで "Perspective - System" を選択します。次に追加したシーンビューに対して同様にフォーカスを入れ、カメラ選択コンボで "Camera - Tank" を選択します。それぞれのビューの位置やサイズについても調整して見やすくしましょう。このような操作により、以下のような表示を行うことができます。

../../_images/multisceneviews.png

ここでは、追加したシーンビューを左側に配置し、そこにはTankモデルのカメラ画像を表示し、右側のシーンビューにはデフォルトのカメラによる俯瞰表示を行っています。

カメラ画像のシミュレーション

シーンビューを用いてカメラ画像を表示することができました。ただしこれはGUI上での描画を行うものであり、シミュレーションとは独立した機能となっています。実際シミュレーションを開始していなくても、カメラ画像の描画は行えているのが分かるかと思います。シミュレーションにおいてコントローラからカメラ画像を取得するためには、デバイスとしてのカメラ画像のシミュレーションも行う必要があります。

これを行うためには、「GLビジョンシミュレータアイテム」をプロジェクトに導入する必要があります。メインメニューの「ファイル」-「新規」-「GLビジョンシミュレータ」によってこのアイテムを生成し、シミュレータアイテムの子アイテムとして配置してください。これにより、アイテムの構成は以下のようになります。

../../_images/visionsimulatoritem.png

この状態でシミュレーションを実行すると、シミュレータの内部でカメラ画像がシミュレートされるようになり、コントローラからの取得も可能となります。

GLビジョンシミュレータアイテムの詳細は 視覚センサのシミュレーション で解説していますので、そちらも参考にしてください。

コントローラのソースコード

GLビジョンシミュレータアイテムの導入によりカメラ画像をシミュレートできるようになりましたが、それを確認するにはカメラデバイスから画像を取得するためのコントローラが必要です。ここではそのためのコントローラとして、取得した画像をファイルに出力するというコントローラを作成します。以下にそのソースコードを示します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#include <cnoid/SimpleController>
#include <cnoid/Camera>
#include <cnoid/Joystick>

using namespace cnoid;

class CameraController : public SimpleController
{
    Camera* camera;
    Joystick joystick;
    bool prevButtonState;
    std::ostream* os;

public:
    virtual bool initialize(SimpleControllerIO* io) override
    {
        camera = io->body()->findDevice<Camera>("Camera");
        io->enableInput(camera);
        prevButtonState = false;
        os = &io->os();
        return true;
    }

    virtual bool control() override
    {
        joystick.readCurrentState();

        bool currentState = joystick.getButtonState(1);
        if(currentState && !prevButtonState){
            const Image& image = camera->constImage();
            if(!image.empty()){
                std::string filename = camera->name() + ".png";
                camera->constImage().save(filename);
                (*os) << "The image of " << camera->name()
                      << " has been saved to \"" << filename << "\"."
                      << std::endl;
            }
        }
        prevButtonState = currentState;

        return true;
    }
};

CNOID_IMPLEMENT_SIMPLE_CONTROLLER_FACTORY(CameraController)

これまでと同様に、上記ソースコードを "CameraController.cpp" というファイル名でプロジェクトディレクトリに保存します。

CMakeLists.txt に

choreonoid_add_simple_controller(TankTutorial_CameraController CameraController.cpp)

を追加して、コンパイルを行って下さい。

コントローラの導入

これまでと同様に、作成したコントローラをシンプルコントローラアイテムを用いてプロジェクトに導入し、TurretControllerの子アイテムとして配置します。これにより、アイテムツリービューは以下のようになります。

../../_images/cameracontrolleritem.png

カメラ画像の取得と表示

ではシミュレーションを実行しましょう。

Cameraコントローラの機能として、ゲームパッドもしくは仮想ジョイスティックビューのBボタン(プレイステーション用ゲームパッドの場合は○ボタン)を押すと、現在のカメラ画像がファイルに保存されます。この際メッセージビュー上に

The image of Camera has been saved to "Camera.png".

と表示されます。ファイルの保存先はカレントディレクトリで、ファイル名は "Camera.png" となります。

ボタンを押してみて、保存された画像ファイルを適当な画像ビューアで表示してみてください。Ubuntu上では標準の"eog"という画像ビューアがあり、コマンドライン上で

eog Camera.png

などとすることにより、取得したカメラ画像を表示できます。

eogには、読み込んだ画像ファイルが更新されるとそれに伴って表示も更新する機能があるようです。これにより、eogを表示したままにしておけば、新たな画像を取得する度に、取得した画像が更新されているのを確認できます。

実装内容の解説

ステップ5のライトと同様に、initialize関数の

camera = io->body()->findDevice<Camera>("Camera");

によってCameraに対応するデバイスオブジェクトを取得し、これをcamera変数に格納しています。

また、

io->enableInput(camera);

によって、このデバイスからの入力を有効化しています。デバイスからの入力を行う場合は、この記述が必要となります。

注釈

デバイスからの出力については、Step5で解説したように、デバイスに対して "notifyStateChange()" を実行することで行います。このため、enableOutput() のような関数はデバイスに対しては用意されていませんので、ご注意ください。

control関数ではBボタンの状態をチェックし、Bボタンが押されたらその時のCameraデバイスの画像をファイルに保存するという処理をしています。この処理では、まず

const Image& image = camera->constImage();

によってCameraデバイスが有するImage型の画像データを取得しています。これが空でなければ、

camera->constImage().save(filename);

によって、画像をそのままファイルに保存しています。

実際のコントローラでは、この画像データに対して、画像認識の処理を行ったり、取得した画像を遠隔操作端末に送ったりといった処理を行うことになるかと思います。