プロジェクトファイルの仕様

概要

Choreonoidでの作業内容は プロジェクト として管理され、「プロジェクトファイル」として保存することができます。プロジェクトファイルには通常 ".cnoid" という拡張子を付与します。

本節では、このプロジェクトファイルの形式について解説します。プロジェクトファイルは通常Choreonoidが自動的に読み書きするものであり、ユーザが直接編集する必要はありません。しかし、

  • プロジェクトの内容をテキストとして確認したい

  • 差分をバージョン管理システムで管理したい

  • スクリプト等でプロジェクトファイルを生成・加工したい

  • プラグインを開発してアイテムの保存内容を設計したい

といった場合には、その構造を把握しておくと役に立ちます。

基本構造

プロジェクトファイルはYAML形式のテキストファイルです。トップレベルには以下のようなキーが並びます。

items:
  ...
views:
  ...
toolbars:
  ...
Body:
  ...
view_areas:
  ...
toolbar_layout:
  ...

各キーの内容は以下の通りです。

キー

内容

items

プロジェクトを構成するアイテムの内容。プロジェクトファイルの中心となる部分です。

views

各ビューの状態。

toolbars

各ツールバーの状態。

view_areas

ビューの配置(レイアウト)。

toolbar_layout

ツールバーの配置。

その他

プラグイン名をキーとするセクション。上記以外の情報をプラグインが保存する場合に用いられます。

このうち view_areastoolbar_layoutレイアウトの保存 を有効にしている場合にのみ出力されます。また、内容が空となるセクションは出力されません。例えばビューやツールバーの状態が全てデフォルトのままであれば、viewstoolbars は現れません。

なお、プロジェクトの読み込みに特定のプラグインが必要となる場合には、ファイルの先頭に以下のような記述が置かれることがあります。

optional_plugins: [ PhysX, MuJoCo, Bullet, ODE, AGXDynamics ]

これは、対応するプラグインが利用可能であれば読み込むことを示すもので、該当するプラグインが存在しない環境でも、その部分を除いてプロジェクトを読み込むことができます。

アイテムの記述

items 以下には、ルートアイテムを起点とする アイテムツリー が格納されます。各アイテムは以下のような構造で記述されます。

items:
  id: 0
  name: "RootItem"
  plugin: Base
  class: RootItem
  is_builtin_item: true
  children:
    -
      id: 1
      name: "World"
      plugin: Body
      class: WorldItem
      is_checked: true
      data:
        collision_detection: false
        collision_detector: AISTCollisionDetector
      children:
        -
          id: 2
          name: "SR1"
          ...

各アイテムに共通するキーは以下の通りです。

キー

内容

id

プロジェクト内でアイテムを一意に識別する整数値。詳細は アイテムの識別子 を参照してください。

name

アイテム名。アイテムツリービューに表示される名前です。

plugin

このアイテム型を提供しているプラグインの名前。

class

アイテム型( アイテム型 )の名前。 plugin と組み合わせてアイテム型が特定されます。

data

アイテム型ごとに固有の保存内容。詳細は アイテム固有の内容 を参照してください。

children

子アイテムのリスト。アイテムツリーの構造がこれによって表現されます。

is_checked

アイテムのチェックボックスがオンになっている場合に記述されます。

is_builtin_item

ルートアイテムのように、システムが標準で用意しているアイテムの場合に記述されます。

pluginclass によってアイテム型が特定できない場合、つまり対応するプラグインが読み込まれていない場合には、そのアイテムは読み飛ばされます。この場合もプロジェクト全体の読み込みは継続され、その旨がメッセージビューに表示されます。プロジェクトファイルが多数のプラグインによって分担して記述される以上、一部が解釈できないことをもって全体を読み込めなくするのは現実的ではないためです。

アイテム固有の内容

data 以下の内容は、アイテム型ごとに自由に定義されます。これはアイテムクラスの store 関数と restore 関数によって読み書きされるもので、どのようなキーを用いるかは各アイテムクラスの実装に委ねられています。

例えばボディアイテムの場合、以下のような内容が格納されます。

data:
  schema: 2
  file: "${SHARE}/model/SR1/SR1.body"
  format: CHOREONOID-BODY
  current_base_link: "WAIST"
  root_translation: [ 0, 0, 0.7135 ]
  root_rotation: [ 1, 0, 0, 0 ]
  joint_displacements: [ 0, -2.1, 0, 4.5, ... ]
  fix_root: false

アイテム型ごとに自由とはいえ、いくつかのキーについては共通の慣習があります。

キー

内容

file

アイテムの内容をファイルから読み込む場合の、そのファイルのパス。

format

file のファイル形式を識別する文字列。同じ種類のデータが複数のファイル形式で読み込める場合に、どの形式で読み込むかを決めるために用いられます。

schema

data 以下の内容の仕様のバージョン。詳細は スキーマバージョン を参照してください。

ファイルに対応するアイテムでは、アイテムの内容そのものはプロジェクトファイルには格納されず、fileformat による参照のかたちで保存されます。例えばボディアイテムであれば、モデルの形状や関節構成は モデルファイル の側にあり、プロジェクトファイルにはそのパスと、現在の関節角などの状態のみが記録されます。したがって、プロジェクト全体を保存・移動する際には、参照されているファイルもあわせて管理する必要があります。

file のパスに含まれる ${SHARE} のような記述は、Choreonoidのディレクトリを表す変数で、読み込み時に実際のパスに展開されます。これにより、インストール先が異なる環境でも同じプロジェクトファイルを利用することができます。プロジェクトファイルからの相対パスが記述されることもあります。

アイテムの識別子

各アイテムに付与される id は、プロジェクトファイル内でアイテムを参照するための識別子です。プロジェクトの保存時にアイテムツリーを走査しながら順に番号が振られるもので、アイテム自体が恒久的に保持する値ではありません。同じプロジェクトでも、保存のたびに異なる値が振られることがあります。

この識別子は、アイテム名では参照が一意に定まらないために用意されています。アイテム名は重複が許されており、また名前を変更しても参照が壊れないようにする必要があるためです。

id による参照は、主に以下のような箇所で用いられます。

まず、アイテムが他のアイテムを参照する場合です。この場合、data 以下に相手のアイテムの id が記述されます。

data:
  item1: 3
  item2: 5

次に、ビューが対象とするアイテムを記録する場合です。例えばアイテムツリービューでは、ツリー上で展開されているアイテムを id のリストとして記録します。

views:
  -
    id: 1
    plugin: Base
    class: ItemTreeView
    mounted: true
    state:
      expanded: [ 1, 2, 5 ]

また、プラグインが保存するセクションからアイテムを参照する場合にも用いられます。

Body:
  "OperableSceneBody":
    scene_bodies:
      -
        body_item: 1
        show_cm: false

なお、参照先が 複合アイテム を構成するサブアイテムである場合には、単一の整数ではなく、親アイテムの id とそこからのサブアイテム名を並べたリストとして記述されます。

data:
  target_item: [ 2, "SubItemName" ]

読み込み時には、これらの値から実際のアイテムが解決されます。ただし、参照先のアイテムがまだ生成されていない段階では解決できないため、アイテムツリー全体の構築が完了した後に参照を解決するしくみが用意されています。プラグインの開発においてアイテム間の参照を保存する場合は、この点に注意する必要があります。

ビューの状態

views 以下には、各ビューの状態がリストとして格納されます。

views:
  -
    id: 0
    plugin: Base
    class: ItemPropertyView
    mounted: true
  -
    id: 4
    plugin: Base
    class: SceneView
    mounted: true
    state:
      operation_mode: view
      floor_grid: true
      background_color: [ 0.1, 0.1, 0.3 ]
      cameras:
        -
          camera: Perspective
          is_current: true
          eye: [ 3, 1.5, 1.2 ]
          direction: [ -0.880451, -0.440225, -0.17609 ]
          up: [ -0.1575, -0.0787499, 0.984374 ]
      ...

各ビューのキーは以下の通りです。

キー

内容

id

プロジェクトファイル内でビューを識別する整数値。view_areas からビューを参照する際に用いられます。アイテムの id とは別の体系です。

plugin

このビュー型を提供しているプラグインの名前。

class

ビュー型(ビュークラス)の名前。

name

同じビュー型のビューを複数生成できる場合に、それらを区別するための名前。

mounted

ビューがメインウィンドウ上に配置されている場合に記述されます。

state

ビュー型ごとに固有の状態。内容は各ビュークラスの実装によります。

state の内容はビュー型ごとに自由に定義されます。例えばシーンビューであれば、カメラの位置姿勢、描画モード、グリッドや光源の設定などが格納されます。

ツールバーの状態

toolbars 以下には、各ツールバーの状態がツールバー名をキーとして格納されます。

toolbars:
  "TimeBar":
    current_time: 0
    min_time: 0
    max_time: 30
    frame_rate: 1000
    playback_frame_rate: 60
    playback_speed_ratio: 1
  "KinematicsBar":
    forward_kinematics: true
    inverse_kinematics: true
    preset_kinematics: true

ビューと異なり、ツールバーは名前で識別されます。内容は各ツールバーの実装によって自由に定義されます。

プラグイン固有のセクション

アイテムやビューに属さない情報をプラグインが保存する場合、プラグイン名をキーとするセクションが作られます。

Body:
  "KinematicFaultChecker":
    checkJointPositions: true
    angleMargin: 0
  "OperableSceneBody":
    scene_bodies:
      -
        body_item: 1
        show_cm: false

この例ではBodyプラグインが、干渉チェッカのダイアログの設定や、シーン上でのボディの表示状態を保存しています。セクション内の構造はプラグインが自由に決めることができます。

レイアウト

view_areas にはビューの配置が、toolbar_layout にはツールバーの配置が格納されます。これらは レイアウトの保存 を有効にしている場合にのみ出力されます。

view_areas は、画面を分割する「スプリッタ」と、ビューを格納する「ペイン」の入れ子構造で表現されます。

view_areas:
  -
    type: embedded
    tabs: true
    contents:
      type: splitter
      orientation: horizontal
      sizes: [ 969, 307 ]
      children:
        -
          type: pane
          views: [ 1 ]
        -
          type: pane
          views: [ 0, 8 ]
          current: 0

splitter では orientation で分割方向を、sizes で分割比率を指定します。paneviews には、そのペインに配置されるビューが views セクションの id によって列挙されます。ひとつのペインに複数のビューが配置される場合はタブで切り替えられ、current が現在選択されているビューを示します。

toolbar_layout では、ツールバーの行ごとの並びが記述されます。

toolbar_layout:
  rows:
    -
      - { name: "FileBar", x: 0, priority: 0 }
      - { name: "SimulationBar", x: 0, priority: 2 }
    -
      - { name: "BodyBar", x: 0, priority: 0 }
      - { name: "KinematicsBar", x: 0, priority: 1 }

スキーマバージョン

アイテムの保存内容の仕様は、Choreonoidの開発に伴って変更されることがあります。この際、既存のプロジェクトファイルが正しく読み込めなくなることを避けるため、data 以下に schema というキーを記述して、保存内容の仕様のバージョンを示すことができます。

data:
  schema: 2
  ...

schema は、アイテム固有の内容の解釈方法を決めるものであるため、data 内の先頭に記述します。

このキーの値の意味は、アイテム型ごとに独立して定義されます 。あるアイテム型の schema が2であることと、別のアイテム型の schema が2であることの間には、何の関係もありません。値の形式についても各アイテム型が自由に決めることができ、単純な整数のバージョン番号としてもよいですし、必要に応じて文字列やリストを用いることも可能です。

このように仕様のバージョンをアイテム型ごとに管理するのは、プロジェクトファイルが本体と多数のプラグインによって分担して記述される複合的な文書だからです。プロジェクト全体でひとつのバージョン番号を持つ方式では、外部で開発されたプラグインが独自に仕様を変更する場合に対応できません。保存内容を記述する主体と同じ単位でバージョンを管理することで、各アイテム型が独立して仕様を発展させることができます。

schema を扱う際の規約は以下の通りです。

  • schema が記述されていない場合は、そのアイテム型の最初の仕様(バージョン1に相当)とみなします。これにより、このしくみが導入される前に保存されたプロジェクトファイルも、従来通り読み込むことができます。

  • 保存内容の解釈が変わるような変更を行う場合にバージョンを上げます。キーの追加のように、古い実装が無視しても問題のない変更では上げる必要はありません。

  • 読み込み側では、バージョンの判定を「以上」の比較で行うようにします。こうすることで、将来さらにバージョンが上がった場合にも、それ以前の変更点が引き継がれます。

  • 読み込み側が想定していない新しいバージョンに遭遇した場合は、警告を出力した上で、解釈できる範囲で読み込みを続行します。プロジェクトは多数のアイテムから構成されるため、一部が解釈できないことをもって全体の読み込みを中止するよりも、可能な範囲で復元する方が実用的です。

なお、このしくみによって対応できるのは、新しい実装が古いプロジェクトファイルを読み込む場合です。逆に、古い実装が新しいプロジェクトファイルを読み込む場合には、schema の存在自体が認識されないため、内容が正しく解釈されない可能性があります。

ボディアイテムのスキーマ

参考として、ボディアイテムにおける schema の履歴を以下に示します。

内容

1

最初の仕様。schema キーは記述されません。関節変位を格納する joint_displacementsinitial_joint_displacements において、直動関節の値も回転関節と同様に度単位に換算して記述されていました。

2

直動関節の関節変位をメートル単位でそのまま記述するようにしました。

バージョン1では、直動関節の値についても読み込み時と書き込み時の両方で同じ換算が行われていたため、プロジェクトの状態自体は正しく復元されていましたが、ファイル中の数値は物理的な意味を持たないものとなっていました。バージョン2ではこれを修正しています。

サブプロジェクト

プロジェクトの一部を別のプロジェクトファイルとして分離し、それを読み込んで利用することもできます。これを「サブプロジェクト」と呼び、サブプロジェクトアイテムによって扱います。

サブプロジェクトのファイル自体も、通常のプロジェクトファイルと同じ形式です。ただし、サブプロジェクトとして保存される場合には、view_areastoolbar_layout によるレイアウトの情報は含まれません。レイアウトはプロジェクト全体に対してひとつ定まるものであり、その一部であるサブプロジェクトが独自に持つべきものではないためです。