Double-Wishbone#

This notebook walks through loading a DARTS Model file of a Double Wishbone multibody. Additionally, it demonstrates how to enforce loop constraints in the graph multibody.

Requirements:

In this tutorial we will:

  • Import the multibody

  • Setup constraint kinematics

  • Setup the kdFlex Scene

  • Run the simulation with loop constraints

  • Run the simulation without constraints

  • Clean up the simulation

For a more in-depth descriptions of kdflex concepts see usage.

import atexit
import Karana.Core as kc
import Karana.Frame as kf
import Karana.Dynamics as kd
import Karana.Dynamics.SOADyn_types as kdt

Import the Multibody#

The double wishbone model has already been prepared as a SOADyn_types.SubGraphDS and saved to an H5 file. We can use this file to populate our Multibody. We then perform basic initialization and log the model to verify.

# load our DARTS model into a multibody
fc = kf.FrameContainer("root")
mbody_ds = kdt.SubGraphDS.fromFile("../resources/double_wishbone/rigid_double_wishbone.h5")
mb = mbody_ds.toMultibody(fc)
del mbody_ds

# finalize and verify our multibody
mb.ensureHealthy()
mb.resetData()
assert kc.allReady()

# dump tree structure
mb.dumpTree()
LEGEND: [<hinge type> <prescribed subhinges>] <body name>[/num embedded bodies > 0] <flex dofs > 0>
|-mb_MBVROOT_
   |-[LOCKED] chassis    [<--- lc/BALL <--- upper_control_arm],  [---> lc/BALL ---> tie_rod],  [---> lc/REVOLUTE ---> shock_absorber_upper]
      |-[REVOLUTE (P)] lower_control_arm
         |-[REVOLUTE] shock_absorber_lower
            |-[SLIDER] shock_absorber_upper    [<--- lc/REVOLUTE <--- chassis]
         |-[REVOLUTE] spindle
            |-[REVOLUTE] spindleAlt
               |-[UJOINT] tie_rod    [<--- lc/BALL <--- chassis]
               |-[REVOLUTE] wheel
            |-[BALL] upper_control_arm    [---> lc/BALL ---> chassis]
# show model details
mb.displayModel()
LEGEND: <body number> <body name> <parent body> <hinge type> [prescribed subhinges] <U dofs/offset> [flex dofs/offset]

      Body                   Parent                   Hinge    Dofs          
      ____                   ______                   _____    ____          
   1. chassis                mb_MBVROOT_              LOCKED   0/0           
   2. lower_control_arm      chassis                 REVOLUTE  1/0 P         
   3. shock_absorber_lower   lower_control_arm       REVOLUTE  1/1           
   4. shock_absorber_upper   shock_absorber_lower     SLIDER   1/2           
   5. spindle                lower_control_arm       REVOLUTE  1/3           
   6. spindleAlt             spindle                 REVOLUTE  1/4           
   7. tie_rod                spindleAlt               UJOINT   2/5           
   8. wheel                  spindleAlt              REVOLUTE  1/7           
   9. upper_control_arm      spindle                   BALL    3/8           
                                                               ____          
                                                               11            

 Nodes
 ---
 spindleAlt_ik_ref_node <Node>  body=spindleAlt
 WheelContactNode <Node>  body=wheel force=true
 chassis_shock_absorber_constraint_node <ConstraintNode>  body=chassis
 chassis_tie_rod_constraint_node <ConstraintNode>  body=chassis
 chassis_upper_control_arm_constraint_node <ConstraintNode>  body=chassis
 shock_absorber_chassis_constraint_node <ConstraintNode>  body=shock_absorber_upper
 tie_rod_chassis_constraint_node <ConstraintNode>  body=tie_rod
 upper_control_arm_chassis_constraint_node <ConstraintNode>  body=upper_control_arm

 Enabled cutjoint loop constraints <3, residuals=11, error=18>
 ---------------
 chassis_shock_absorber_constraint <REVOLUTE>  source=chassis/chassis_shock_absorber_constraint_node   target=shock_absorber_upper/shock_absorber_chassis_constraint_node
 chassis_tie_rod_constraint <BALL>  source=chassis/chassis_tie_rod_constraint_node   target=tie_rod/tie_rod_chassis_constraint_node
 chassis_upper_control_arm_constraint <BALL>  source=upper_control_arm/upper_control_arm_chassis_constraint_node   target=chassis/chassis_upper_control_arm_constraint_node

Setup Constraint Kinematics#

To use constraint kinematics, we use Karana.Dynamics.Multibody.cks() to get an instance of Karana.Dynamics.ConstraintKinematicsSolver. Then, Karana.Dynamics.ConstraintKinematicsSolver.solveQ() can solve for the coordinates to satisfy loop constraints and return the residual error.

solver = mb.cks()
error = solver.solveQ()
assert error < 1e-10

Setup the kdFlex Scene#

Next we setup kdflex’s graphics by calling the setupGraphics helper method on the multibody. This method takes care of setting up the graphics environment. We use Karana.Dynamics.Multibody.createStickParts() to add automatically add visual geometries.

See Visualization and Scene Layer for more information relating to this section.

# setup graphics and add visual stick parts
cleanup_graphics, web_scene = mb.setupGraphics(port=0, axes=0.5)
mb.createStickParts()

Run the Simulation With Loop Constraints#

We can now articulate through the lower control arm’s subhinge and demonstrate how loop constraints are enforced on the subhinge.

# grab the lower arm subhinge and enforce the loop constraints in it
lca = mb.getBody("lower_control_arm")
lca_shg = lca.parentHinge().subhinge(0)
mb.articulateSubhinge(lca_shg, 0)

Run the Simulation Without Constraints#

Compare this to when we articulate the bodies without enforcing loop constraints.

# this will not enforce the loop constraints
mb.articulateBodies()

Clean Up the Simulation#

Finally, we cleanup by deleting local variables, discarding our containers, multibodies, and cleaning up the scene.

# Cleanup
def cleanup():
    """Cleanup the simulation."""
    import gc

    global web_scene, solver, lca, lca_shg
    del solver, lca, lca_shg, web_scene
    gc.collect()
    cleanup_graphics()

    kc.discard(mb)
    kc.discard(fc)


atexit.register(cleanup)
<function __main__.cleanup()>

Summary#

By importing a model with built-in constraints, we can setup a ConstraintKinematicsSolver to enforce these constraints. Then, we compare the outputs from running the simulation with and without constraints.

Further Readings#

Load a mars 2020 rover urdf
Load a robotic arm urdf
Create and use constraints in a slider-crank model
Drive an ATRVjr rover