Drake添加足部地面转动关节时提示形成闭环机器人报错排查
LQR腿部平衡控制足部地面连接报错排查
通过销接关节约束足部到地面的实现思路触发Drake闭环报错,导入两个URDF后得到的机器人拓扑结构如下:
报错核心原因
- Drake的
Joint(底层对应Mobilizer)仅支持构建无环开式运动树:每个刚体(Body)有且仅有一个入端(inboard)mobilizer连接到运动树的上游节点,最终所有节点都追溯到唯一根节点——世界坐标系。 - 你当前的场景里已经存在两条独立的开链:
- 加载
ground.urdf时,ground刚体默认通过固定关节锚定到世界坐标系,形成静态固定链路:世界 → (固定关节) ground - 加载腿部URDF时,由于没有手动指定腿部和世界的连接,Drake自动给顶层的
base_link添加了6自由度浮动关节,形成第二条开链:世界 → (6DoF浮动关节) base_link → 各运动连杆 → Link_foot,这也是你误以为base_link未连接世界的认知偏差来源。
- 加载
- 你尝试添加的
RevoluteJoint连接Link_foot和ground后,两条开链首尾闭合形成环:世界 → ground → foot → 各连杆 → base_link → 世界;且ground刚体本身已经存在一个入端固定关节,新增的关节相当于给它加了第二个入端mobilizer,直接触发闭环报错。拓扑图里的箭头方向就是mobilizer从父(上游)指向子(下游)的方向,和URDF定义的父/子连杆逻辑一致,本身不存在问题,你写的base_link为父连杆的prismatic关节定义也没有错误。
正确实现方案
结合LQR控制对模型线性化的需求,优先选择无约束的开链建模方式(避免微分代数方程影响线性化精度),如果不想调整现有URDF加载逻辑,也可以通过运动约束实现:
方案1:开链建模(推荐,适配LQR)
不需要单独导入地面URDF,直接将足部通过销接旋转关节连接到世界帧,把整个腿部构建为以足部铰接点为根的无环开链,从根源避免闭环问题。
builder = DiagramBuilder() plant, scene_graph = AddMultibodyPlantSceneGraph(builder, time_step=0.0) # 仅加载腿部模型,地面后续通过注册几何体实现 leg_instance = Parser(plant).AddModelFromFile("HoppingLeg/leg_v2/urdf/LEG_002_Foot_Hinge.urdf") # 移除Drake自动给base_link添加的6自由度浮动关节 float_joint = plant.GetJointByName("base_link") plant.RemoveJoint(float_joint) # 将足部通过绕x轴的旋转关节直接连接到世界帧(地面固定点) frame_foot = plant.GetFrameByName("Link_foot") joint_ground = RevoluteJoint( name="foot_ground", frame_on_parent=plant.world_frame(), frame_on_child=frame_foot, axis=np.array([1., 0., 0.]), damping=0.0 ) plant.AddJoint(joint_ground) # 注册地面碰撞与可视化几何体(替代单独的ground URDF) plant.RegisterCollisionGeometry( plant.world_body(), RigidTransform(), HalfSpace(), "ground_collision", CoulombFriction(1.0, 1.0) ) plant.Finalize()
方案2:闭环约束实现(保留原有URDF加载逻辑)
如果需要保留机器人、地面两个URDF分开导入的逻辑,不能使用Joint连接两个已接入运动树的刚体,改用RevoluteConstraint添加闭环运动限制,该约束会锁死足部和地面之间除绕x轴旋转外的所有相对自由度,实现销接效果。
builder = DiagramBuilder() plant, scene_graph = AddMultibodyPlantSceneGraph(builder, time_step=0.0) leg_instance = Parser(plant).AddModelFromFile("HoppingLeg/leg_v2/urdf/LEG_002_Foot_Hinge.urdf") ground_instance = Parser(plant).AddModelFromFile("HoppingLeg/leg_v2/urdf/ground.urdf") frame_foot = plant.GetFrameByName("Link_foot") frame_ground = plant.GetFrameByName("ground") # 添加旋转约束实现销接,而非新增Joint plant.AddConstraint( RevoluteConstraint( frame_on_body1=frame_ground, axis_on_body1=np.array([1.,0.,0.]), frame_on_body2=frame_foot, axis_on_body2=np.array([1.,0.,0.]), threshold=1e-6 ) ) plant.Finalize()
注意:方案2引入了闭环约束,系统模型为微分代数方程(DAE),做LQR线性化时需要确认线性化接口支持对应约束的线性化,控制场景下优先使用方案1的纯开链建模。
内容的提问来源于stack exchange,提问作者Noah Wechtler
相关产品推荐
相关产品推荐

