notes
Robotics

Inventor から iLogic で URDF を出す

公式 URDF エクスポータが無い Inventor で、iLogic で AssemblyJoint と MassProperties を JSON に出し、Python で URDF に変換する 2 段構成。link・joint・frame・単位の対応と、STL を部品座標系で出す必要性。

要点: Inventor には保守されている URDF エクスポータが無い。iLogic で「上位 occurrence = link、AssemblyJoint = joint、MassProperties = inertial」を JSON に出し、Python で URDF に変換する。STL は各部品を自分の座標系で書き出せば URDF 側の visual origin は 0 で済む。

対応表 (Inventor API → URDF)#

URDF 要素Inventor API備考
<link>AssemblyComponentDefinition.Occurrences の各要素上位 occurrence 1 個 = 剛体 1 個。サブアセンブリ内部は 1 剛体扱い
<inertial>ComponentOccurrence.MassPropertiesMass, CenterOfMass, XYZMomentsOfInertia
<joint>AssemblyComponentDefinition.Joints (AssemblyJoint)Constraint ではなく Joint で組むこと
typeAssemblyJointDefinition.JointTypeRotational → revolute/continuous、Slider → prismatic、Rigid → fixed
parent / childOccurrenceTwo / OccurrenceOneInventor は OccurrenceOne が動く側
axis / originjoint を微小駆動して parent / child の Transformation の差から算出OriginOne の geometry から取る軸は符号が保証されない (下記)
<limit>AngularPositionStartLimit / EndLimit、LinearPosition*LimitHasAngularPositionLimits で有無を確認
rest poseAngularPosition / LinearPositionCAD 保存姿勢が joint 中間位置のときは、この値を差し引いて URDF の 0 を機械零点に合わせる
<visual> / <collision>STL translator で部品ごとに書き出し下記「STL の座標系」

Cylindrical / Planar / Ball は URDF に直接の対応が無い。raw 型名を JSON に残し、変換側で revolute に落とすか捨てるかを決める。

質量特性は表示ボディから取る#

マルチボディの .ipt から「コンポーネントを作成」で切り出した派生部品は、自分以外のボディが非表示のまま残ることがある。この状態で ComponentOccurrence.MassProperties を読むと、非表示ボディも含んだ値 (= 元のマルチボディ全体) が返る。実測: 5 部品全てが同じ質量 2.58 kg、同じ部品座標系重心 (0, 270, 58) mm を返し、STL (表示ボディのみ) の体積合計 2.64 L × 既定密度 1 g/cm³ と一致した。

対処: ComponentOccurrence.SurfaceBodies を回して Visible = True のボディだけ SurfaceBody.MassProperties を取り、平行軸の定理で合成する。STL 書き出しも表示ボディだけなので整合する。bodies_total > bodies_visible なら非表示ボディが残っている。

検算: STL の体積 (四面体分割で計算) と質量から密度を出す。1000 kg/m³ 前後なら材料未設定。

表示ボディを平行軸の定理で合成した慣性テンソルは、STL から四面体積分で計算したテンソルと相対誤差 0.05% 以内で一致した (5 link、4〜22 ボディ)。ボディ単位の XYZMomentsOfInertia は各ボディの重心まわり・部品座標系。

単位換算#

Inventor の内部データベース単位は cm / kg / rad。

項目Inventor 内部URDF係数
長さ (CoM、joint 原点、Slider 限界)cmm× 0.01
質量kgkg× 1
慣性モーメント (XYZMomentsOfInertia)kg·cm²kg·m²× 1e-4
角度 (限界、AngularPosition)radrad× 1

g·cm² (係数 1e-7) と書かれた解説記事があるが誤り。質量 kg オーダーの部品を 1 個出して桁を照合すれば判定できる。

XYZMomentsOfInertia の Ixy / Iyz / Ixz は慣性テンソルの成分 (URDF と同じ符号、−∫xy dm) で、積 (+∫xy dm) ではない。根拠: STL から四面体積分で計算したテンソルの非対角成分と符号・値が一致 (Iyz = −9.06e-5 kg m² で両者一致)。URDF にはそのまま書ける。

回転軸は geometry からでなく駆動して測る#

OriginOne の geometry (cylinder face の AxisVector 等) から取った軸方向は、Inventor の joint 正方向と一致する保証が無い。AngularPosition は「OriginOne の origin direction まわり」の角度で、face の軸ベクトルとは独立に決まる。符号が逆だと limit が鏡像になる (例: Inventor で [1.4, 3.0] rad の joint が URDF で [-3.0, -1.4] 側に動く)。

Slider ではさらに悪く、OriginOne の geometry (平面) から取れるのは面法線 = joint の Z で、滑り方向は joint の X (位置合わせで決まる) なので 90 度ずれる。実測 (Slider 3 本) で全て直交していた。geometry 由来の軸は Rotational の回転軸にしか使えない。

確実な方法は joint を実際に動かして測ること。

  1. jd.AngularPosition.Value (Slider は LinearPosition.Value) を読む → vA
  2. vA + 0.1 rad (Slider は +1 cm) を代入し Document.Update()。parent / child の Transformation を記録
  3. 元の値へ戻して Update()
  4. 変換側で D = (Tp⁻¹Tc)_B (Tp⁻¹Tc)_A⁻¹ を作り、回転角と軸を取り出す。軸の符号は「値の増加方向 = 正」に合わせる。軸上の点は t = (I − R)p を解く

これで axis・正方向・pivot・機械零点 (値 0 の姿勢) が全部決まり、limit をそのまま写せる。limit がある joint は範囲内に収まる側へ動かす。

Inventor は駆動中でない joint の値を固定したまま解く。そのため他の joint と連動する joint (閉ループ) は単独では動かず、パラメータを書き換えても Transformation が変わらない。対処は、他の可動 joint を AssemblyJoint.Suppressed = True で一時的に外してから駆動し、終わったら戻す。それでも動かない joint は Ground や Mate 拘束で別途固定されている。

URDF の joint 原点は回転軸上に置く#

URDF は child link 原点を通る axis まわりに回す。child link 座標系を部品座標系のままにすると、部品原点がピンから外れている分だけ回転中心がずれる (実測例: 55 mm)。child link 座標系は「回転軸上で部品原点に最も近い点 + 値 0 での部品の向き」に取り直し、<visual> / <inertial> の origin に部品座標系とのオフセットを入れる。

座標系 (frame) の取り方#

  • base link と fixed joint の child link の frame = その occurrence の部品座標系 (.ipt / サブ .iam 自身の原点)
  • 可動 joint の child link の frame = 上記の joint 座標系 (部品座標系との差は並進のみ)
  • ComponentOccurrence.Transformation が assembly → link の 4x4 行列。JSON に一緒に出す
  • CoM と慣性テンソルは MassProperties が assembly 座標系で返す値を、Transformation の逆行列で link 座標系へ移す
  • joint の origin / axis も assembly 座標系で取り、parent link の Transformation 逆行列で parent frame へ移す

STL は 各部品を単体で開いて自分の座標系で書き出す。base と fixed の link は <visual><origin xyz="0 0 0">、可動 link は上記オフセットを origin に入れる。assembly 全体から選択書き出しすると assembly 座標系になり、ずれる。

STL の書き出し (iLogic)#

  • occurrence ごとに oOcc.Definition.Document を STL translator ({533E9A98-FC3B-11D4-8E7E-0010B541CD80}) で SaveCopyAs。部品でもサブアセンブリでも、その文書自身の座標系で出る
  • 主なオプション: OutputFileType 0 = binary、Resolution 0 High / 1 Medium / 2 Low、ExportFileStructure 0 = 1 ファイル
  • ExportUnits の数値の意味は資料が無い。指定せず、書き出した STL のバウンディングボックスと ComponentDefinition.RangeBox (cm) の対角長を比べて「STL 単位 → m」の係数を実測するほうが確実。binary STL は 80 byte ヘッダ + uint32 三角形数 + 50 byte × N なので数十行で読める

STL の注意#

  • Inventor の STL translator は書き出し単位を選べる。mm で出したら URDF 側で <mesh scale="0.001 0.001 0.001">、m で出せば scale 不要
  • ファイル名を link 名と一致させる。変換スクリプトが <link 名>.stl を参照する前提にできる
  • collision は同じ STL で始めて、重ければ後で凸包や単純形状に差し替える

CAD 側の前提#

  1. 動く単位ごとに上位 occurrence を 1 個にする (ボルト等は部品側でサブアセンブリに含める)
  2. base にする部品を Ground する
  3. 関節は Joint (Rotational / Slider / Rigid) で組む。Constraint で組んだ既存モデルは Convert to Joint で置き換える
  4. Joint の Limits を入れておくと <limit> がそのまま出る
  5. 並列リンクは URDF の木構造に入らない。閉ループの 1 箇所は変換時に落とし、mimic で補う

iLogic 実装上の落とし穴#

  • OriginOne は GeometryIntent。直接 .AxisVector を呼ぶと "Public member 'AxisVector' on type 'GeometryIntent' not found"
  • jd.AngularPosition は Slider joint でも例外を出さず 0 を返すことがある。joint 種別で AngularPosition / LinearPosition を分岐する
  • HasLinearPositionLimits は存在しない。Slider は HasLinearPositionStartLimit / HasLinearPositionEndLimit が別々。Try で握ると limit が黙って消える
  • joint のロックは AssemblyJoint.Locked (Definition 側ではない)。ロック中は位置パラメータを書いても動かない
  • Mate / Flush / Tangent 拘束が Joint と同じ部品に付いていると、joint の位置パラメータを書き換えても動かない (no motion)。拘束を省略 (Suppress) すれば動く
  • XML コメントに -- を含めると不正な XML になる (CLI オプション名を書くときに起きる)
  • rule の Sub / Function の外に Const や Dim を置けない (「名前空間では有効でないステートメントです」)。設定値は値を返す Function にする。さらに Sub Main より前には Imports / AddReference 以外を置けない (Function も不可)。補助 Function は全部 Sub Main の後ろに書く
  • 遅延バインド (Dim o As Object 経由) で XYZMomentsOfInertia(a, b, c, ...) を呼ぶと ByRef 引数が書き戻されず全部 0 のまま。Type.InvokeMember に ParameterModifier (全引数 True) を付けて IDispatch 経由で呼び、args 配列から取り出す
  • SurfaceBody.MassProperties を早期バインド (Dim mp As MassProperties = b.MassProperties) で受けると E_NOINTERFACE (IID {2FA1D3CF-...})。Object のまま遅延バインドで .Mass / .CenterOfMass を読むと通る。返る座標は部品座標系で、ContainingOccurrence.Transformation で assembly 座標系へ移す必要がある
  • d1、d2、d3 のような Inventor の自動パラメータ名と同じ識別子を変数名にすると、iLogic がモデルパラメータへの参照とみなして「'd1' は宣言されていません」になる。d + 数字の変数名を避ける
  • VB は大文字小文字を区別しない。引数 M と局所変数 m は同名扱いで「既に宣言されています」になる
  • VB のキーワードは変数名にできない。ctype (CType) で「キーワードは、識別子として有効ではありません」。type、name、step 等も避ける
  • Imports / AddReference は rule 途中に置けない。System.Text.StringBuilder 等は full-qualified で書く
  • MessageBox ではなく VB 組込の MsgBox() を使うと参照追加が要らない
  • 数値は CultureInfo.InvariantCulture で文字列化する。日本語環境でも小数点は . になるが、明示しておく

実装例#

関連#

最終更新:

On this page