Microduck 开源不到一周,社区就完成了一次相当彻底的逆向整理:开发者 fanhao375 从官方 MJCF 仿真模型和 47 个 STL 网格文件出发,还原出这台 25 厘米双足鸭子机器人的装配关系、爆炸图与 CAD 装配体,并整理了一份精确到 0.1 毫米的零件对照表。项目 README 给出的判断很直接:整机可以复刻。
这件事的价值不只在于“又多了一套 CAD 文件”。它展示了一条值得复用的机器人开源工作流:把面向仿真的模型资产,转化为可检查、可装配、可制造的工程资料。
MJCF 提供关系,STL 提供形状
MJCF 常用于描述机器人在仿真环境中的结构与动力学信息。一个典型模型会包含刚体层级、关节、坐标变换、网格引用,以及质量或碰撞相关参数。STL 则主要保存三角网格几何,本身通常不表达“哪个零件连接哪个零件”。
因此,从 MJCF 和 STL 恢复 CAD 装配体,并不是简单地批量导入 47 个文件。真正需要处理的是三类信息:
- 零件身份:MJCF 中的 mesh 名称究竟对应哪个 STL 文件。
- 装配层级:机身、髋部、腿部、足部等刚体如何形成父子关系。
- 坐标变换:每个零件相对于父级的位置、姿态和旋转基准是什么。
可以把 MJCF 看作装配关系的“骨架”,把 STL 看作附着在骨架上的“外形”。只有同时解释两者,爆炸图和 CAD 装配体才不会沦为一堆摆放得大致相似的网格。
这也是零件对照表重要的原因。精确到 0.1 毫米,不只是为了让表格显得完整,而是为了发现单位换算、缩放比例、左右件镜像和装配间隙等问题。某个尺寸如果整体偏差 10 倍或 1000 倍,通常意味着毫米与米的单位没有统一;如果只有一侧偏差,则可能是镜像或坐标轴方向处理错误。
从“仿真能跑”到“实物能装”还有距离
仿真模型和制造模型服务于不同目标。仿真关注运动链、碰撞、质量和计算效率,制造则需要孔位、公差、紧固方式、材料厚度、线束空间和加工可行性。
STL 是三角网格,不等于带参数特征的 CAD 原生零件。将 STL 放入装配体,可以验证外形和相对位置,却未必能直接修改孔径、壁厚或倒角。要得到适合生产的模型,通常还需要重新建模关键机械特征,或者至少检查网格是否封闭、法向是否一致,以及是否存在自相交和破面。
“整机可复刻”也不应理解为下载文件后即可无条件量产。复刻者仍需核实:
- 舵机、电机、轴承和紧固件的真实规格;
- 关节零位、限位与左右腿对称关系;
- 打印或加工后的收缩、间隙和强度;
- 控制器、供电、线束与散热空间;
- 仿真惯性参数是否与实际材料和器件一致。
换句话说,CAD 装配体解决了“零件如何组合”的核心问题,但物料采购、加工公差和机电联调仍然决定实物能否稳定行走。
实践:先自动核对 MJCF 与 STL 清单
在进入 CAD 软件之前,可以先用脚本检查 MJCF 引用了哪些网格、文件是否存在,以及 STL 的包围盒尺寸是否明显异常。下面的脚本只依赖 Python 标准库,支持二进制和 ASCII STL。
运行前,把 MODEL_XML 和 MESH_DIR 改成仓库中的实际路径。如果 MJCF 使用了编译器级别的 mesh 目录,还需要让 MESH_DIR 指向解析后的真实目录。
#!/usr/bin/env python3
import struct
import xml.etree.ElementTree as ET
from pathlib import Path
MODEL_XML = Path("model/microduck.xml")
MESH_DIR = Path("meshes")
def stl_vertices(path: Path):
data = path.read_bytes()
# Binary STL: 80-byte header, uint32 triangle count, 50 bytes per triangle.
if len(data) >= 84:
count = struct.unpack_from("<I", data, 80)[0]
if 84 + count * 50 == len(data):
for index in range(count):
offset = 84 + index * 50 + 12
for vertex in range(3):
yield struct.unpack_from("<fff", data, offset + vertex * 12)
return
# Fall back to ASCII STL.
for line in data.decode("utf-8", errors="ignore").splitlines():
fields = line.strip().split()
if len(fields) == 4 and fields[0].lower() == "vertex":
yield tuple(map(float, fields[1:]))
def bounds(path: Path):
vertices = list(stl_vertices(path))
if not vertices:
raise ValueError("no vertices found")
minimum = [min(v[axis] for v in vertices) for axis in range(3)]
maximum = [max(v[axis] for v in vertices) for axis in range(3)]
return [maximum[axis] - minimum[axis] for axis in range(3)]
tree = ET.parse(MODEL_XML)
assets = {}
for mesh in tree.findall(".//asset/mesh"):
name = mesh.get("name")
filename = mesh.get("file")
if name and filename:
assets[name] = filename
used_meshes = {
geom.get("mesh")
for geom in tree.findall(".//geom[@mesh]")
if geom.get("mesh")
}
print(f"Declared meshes: {len(assets)}")
print(f"Referenced meshes: {len(used_meshes)}")
for name in sorted(used_meshes):
filename = assets.get(name)
if not filename:
print(f"MISSING DECLARATION {name}")
continue
path = MESH_DIR / filename
if not path.exists():
print(f"MISSING FILE {name}: {path}")
continue
try:
size = bounds(path)
print(
f"OK {name:28} "
f"{size[0]:9.1f} x {size[1]:9.1f} x {size[2]:9.1f} {path}"
)
except ValueError as error:
print(f"INVALID STL {name}: {error}")
unused = sorted(set(assets) - used_meshes)
for name in unused:
print(f"UNUSED ASSET {name}: {assets[name]}")
执行方式如下:
python3 inspect_microduck_assets.py
输出尺寸使用 STL 文件自身的单位。如果仓库约定 STL 以毫米建模,可以直接按毫米阅读;如果 MJCF 通过 scale 属性缩放网格,还必须将缩放值计入最终尺寸。这个脚本适合发现缺失文件、未使用资产和数量级异常,但不能替代 CAD 中的干涉检查。
还可以用命令快速确认仓库里的 STL 数量:
find . -type f -iname '*.stl' -print | sort | tee /tmp/microduck-stl.txt
wc -l /tmp/microduck-stl.txt
如果数量与预期的 47 个不一致,应先检查是否存在重复导出、不同版本目录或遗漏文件,再开始建立装配体。
建立可维护的 CAD 装配体
实际导入 CAD 时,可以按 MJCF 的 body 层级组织组件,而不是把所有 STL 放在同一层。一个可操作的流程是:
- 解析 MJCF 中的 body、joint、geom 和 mesh 引用,生成零件映射表。
- 统一米、毫米以及网格
scale,记录所有隐式换算。 - 先建立躯干到髋关节的主链,再分别装配左右腿。
- 用关节轴和零位验证旋转方向,不只依靠外观对齐。
- 在几个典型姿态下检查零件干涉和运动范围。
- 为电机、轴承、螺钉等标准件补充型号与采购信息。
- 对需要加工的关键零件重建参数化特征,并标注公差。
爆炸图适合检查是否漏件,完整装配体适合检查空间关系,而零件对照表负责把仿真命名、文件名、CAD 名称和制造编号连接起来。三者配合,才能让后来者理解并修改模型,而不是只能重复作者的导入步骤。
采用这套成果前应检查什么
Microduck 的社区复刻说明,一套公开的仿真资产已经可能包含足够多的机械结构信息。MJCF 保存运动链和坐标关系,STL 保存外形,细致的映射和测量则将两者重新组织成工程装配资料。
准备动手复刻时,建议把工作分成三个验收阶段:先验证文件完整性和单位,再验证关节、零位及干涉,最后才制作实体样件。第一轮加工优先选择少量关键连接件和单腿子装配,确认孔位、公差与执行器匹配后再扩展到整机。这样既能利用现有 CAD 成果,也能避免把仿真模型中的简化假设直接带到实体机器人上。