Example #1
0
    def _warmstart(self):
        """Calculate qacc_warmstart. Initializing qacc_warmstart in the
           simulator will result in a more rapid computation of qacc when
           other fields (e.g. qpos, qvel, ctrl) are only slightly changed."""
        # TODO: This is largely undocumented in MuJoCo; I'm following the lead
        # of derivative.cpp. Does this have any implications for correctness,
        # or just performance?

        curr_state = np.concatenate((self.get_state(), self.sim.data.ctrl))
        if np.array_equal(self.warmstart_for, curr_state):
            # self.qacc_warmstart has already been computed for this x.
            return

        self.sim.forward()  # run full computation
        for i in range(self.NWARMUP):
            # iterate several times to get a good qacc_warmstart
            mjfunc.mj_forwardSkip(self.env.sim.model, self.env.sim.data,
                                  SkipStages.VEL.value, 1)
        self.warmstart[:] = self.sim.data.qacc_warmstart
        self.warmstart_for = curr_state
Example #2
0
def _finite_diff(sim, qacc_warmstart, vary, eps):
    # Select the variable to perform finite differences over
    variables = {
        VaryValue.POS: sim.data.qpos,
        VaryValue.VEL: sim.data.qvel,
        VaryValue.CTRL: sim.data.ctrl,
    }
    variable = variables[vary]

    # Select what parts of the forward dynamics computation can be skipped.
    # Everything depends on position, so perform full computation in that case.
    # When varying velocity, can skip position-dependent computations.
    # When varying control, can skip acceleration/force-dependent computations.
    skip_stages = {
        VaryValue.POS: SkipStages.NONE,
        VaryValue.VEL: SkipStages.POS,
        VaryValue.CTRL: SkipStages.VEL,
    }
    skip_stage = skip_stages[vary]

    # Make a copy of variable, to restore after each perturbation
    original = np.copy(variable)
    # Run forward simulation and copy the qacc output. We will take finite
    # differences w.r.t. this value later.
    sim.data.qacc_warmstart[:] = qacc_warmstart
    sim.forward()  # TODO: this computation is probably often redundant
    center = np.copy(sim.data.qacc)

    if vary in [VaryValue.VEL, VaryValue.CTRL]:
        nin = len(variable)
        nout = len(center)
        jacobian = np.zeros((nout, nin), dtype=np.float64)
        for j in range(nin):
            # perturb
            variable[j] += eps
            # compute finite-difference
            sim.data.qacc_warmstart[:] = qacc_warmstart
            mjfunc.mj_forwardSkip(sim.model, sim.data, skip_stage.value, 1)
            jacobian[:, j] = (sim.data.qacc - center) / eps
            # unperturb
            variable[j] = original[j]
    elif vary == VaryValue.POS:
        # When varying qpos, positions corresponding to ball and free joints
        # are specified in quaternions. The derivative w.r.t. a quaternion
        # is only defined in the tangent space to the configuration manifold.
        # Accordingly, we do not vary the quaternion directly, but instead
        # use mju_quatIntegrate to perturb with a certain angular velocity.
        # Note that the Jacobian is always square with size sim.model.nv,
        # which in the presence of quaternions is smaller than sim.model.nq.
        nv = sim.model.nv
        jacobian = np.zeros((nv, nv), dtype=np.float64)

        # If qvel[j] belongs to a quaternion, then quatadr[j] gives the address
        # of that quaternion and  dofpos[j] gives the corresponding degree of
        # freedom within the quaternion.
        #
        # If qvel[j] is not part of a quaternion, then quatadr[j] is -1
        # and ids[j] gives the index in qpos to that joint. If there are no
        # quaternions, ids will just be a sequence of consecutive numbers.
        # But in the presence of a quaternion, it will skip a number.

        # Common variables
        joint_ids = sim.model.dof_jntid
        jnt_types = sim.model.jnt_type[joint_ids]
        jnt_qposadr = sim.model.jnt_qposadr[joint_ids]
        dofadrs = sim.model.jnt_dofadr[joint_ids]

        # Compute ids
        js = np.arange(nv)
        ids = jnt_qposadr + js - dofadrs

        # Compute quatadr and dofpos
        quatadr = np.tile(-1, nv)
        dofpos = np.zeros(nv)

        # A ball joint is always a quaternion.
        ball_joints = (jnt_types == JointTypes.BALL.value)
        quatadr[ball_joints] = jnt_qposadr[ball_joints]
        dofpos[ball_joints] = js[ball_joints] - dofadrs[ball_joints]

        # A free joint consists of a Cartesian (x,y,z) and a quaternion.
        free_joints = (jnt_types == JointTypes.FREE.value) & (js > dofadrs + 3)
        quatadr[free_joints] = jnt_qposadr[free_joints] + 3
        dofpos[free_joints] = js[free_joints] - dofadrs[ball_joints] - 3

        for j in js:
            # perturb
            qa = quatadr[j]
            if qa >= 0:  # quaternion perturbation
                angvel = np.array([0, 0, 0])
                angvel[dofpos[j]] = eps
                # side-effect: perturbs variable[qa:qa+4]
                mjfunc.mju_quatIntegrate(variable[qa], angvel, 1)
            else:  # simple perturbation
                variable[ids[j]] += eps

            # compute finite-difference
            sim.data.qacc_warmstart[:] = qacc_warmstart
            mjfunc.mj_forwardSkip(sim.model, sim.data, skip_stage.value, 1)
            jacobian[:, j] = (sim.data.qacc - center) / eps

            # unperturb
            if qa >= 0:  # quaternion perturbation
                variable[qa:qa + 4] = original[qa:qa + 4]
            else:
                variable[ids[j]] = original[ids[j]]
    else:
        assert False

    return jacobian