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.MassProperties | Mass, CenterOfMass, XYZMomentsOfInertia |
<joint> | AssemblyComponentDefinition.Joints (AssemblyJoint) | Constraint ではなく Joint で組むこと |
type | AssemblyJointDefinition.JointType | Rotational → revolute/continuous、Slider → prismatic、Rigid → fixed |
parent / child | OccurrenceTwo / OccurrenceOne | Inventor は OccurrenceOne が動く側 |
axis / origin | joint を微小駆動して parent / child の Transformation の差から算出 | OriginOne の geometry から取る軸は符号が保証されない (下記) |
<limit> | AngularPositionStartLimit / EndLimit、LinearPosition*Limit | HasAngularPositionLimits で有無を確認 |
| rest pose | AngularPosition / LinearPosition | CAD 保存姿勢が 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 限界) | cm | m | × 0.01 |
| 質量 | kg | kg | × 1 |
慣性モーメント (XYZMomentsOfInertia) | kg·cm² | kg·m² | × 1e-4 |
| 角度 (限界、AngularPosition) | rad | rad | × 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 を実際に動かして測ること。
jd.AngularPosition.Value(Slider はLinearPosition.Value) を読む → vA- vA + 0.1 rad (Slider は +1 cm) を代入し
Document.Update()。parent / child のTransformationを記録 - 元の値へ戻して
Update() - 変換側で 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。部品でもサブアセンブリでも、その文書自身の座標系で出る - 主なオプション:
OutputFileType0 = binary、Resolution0 High / 1 Medium / 2 Low、ExportFileStructure0 = 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 側の前提
- 動く単位ごとに上位 occurrence を 1 個にする (ボルト等は部品側でサブアセンブリに含める)
- base にする部品を Ground する
- 関節は Joint (Rotational / Slider / Rigid) で組む。Constraint で組んだ既存モデルは
Convert to Jointで置き換える - Joint の Limits を入れておくと
<limit>がそのまま出る - 並列リンクは 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で文字列化する。日本語環境でも小数点は.になるが、明示しておく
実装例
- ForteFibre/ilogic_ws:
ExportUrdfJson.iLogicVb(JSON dump) +json_to_urdf.py(URDF 生成)
https://github.com/ForteFibre/ilogic_ws
関連
- Claude に URDF を生成させる
- Inventor Joint の親子と位置合わせ
- Inventor Constraint と Joint の関係
- CAD から URDF への変換 (Fusion 360 / Inventor)
- URDF の visual メッシュを Inventor の DAE で差し替える
- URDF 並列リンクと mimic
Inventor Constraint と Joint の関係 (URDF 用)
Constraint は幾何拘束、Joint は運動学的拘束。URDF joint 変換には Joint 主体で組むか、Convert to Joint / iLogic script で半自動化する
Inventor Joint の親子と位置合わせ
Inventor の Joint は対称な関係で URDF のような親子は無い。API の OccurrenceOne/Two は選択順。joint 原点の合わせ方 (原点・アライメント・反転・値 0) と URDF の parent/child・rest pose への対応。