예제 #1
0
    def calc_rel_properties(self):
        """Calculates the mass, relative/local center of mass, and
        relative/local inertia tensor (about the segment's center of mass).
        Also computes the center of mass of each constituent solid with
        respect to the segment's base in the segment's reference frame.

        """
        # mass
        self._mass = 0.0
        for s in self.solids:
            self._mass += s.mass
        # relative position of each solid w.r.t. segment orientation and
        # segment's origin
        solidpos = []
        # center of mass of each solid w.r.t. segment orientation and
        # segment's origin
        solidCOM = []
        z_unit_vector = np.array([[0, 0, 1]]).T
        if self._build_toward_positive_z:
            solidpos.append(np.zeros((3, 1)))
            for i in np.arange(self.nSolids):
                if i != 0:
                    solidpos.append( solidpos[i-1] +
                                          self.solids[i-1].height *
                                          z_unit_vector)
            solidCOM.append(self.solids[0].rel_center_of_mass)
            for i in np.arange(self.nSolids):
                if i != 0:
                    solidCOM.append( solidpos[i] +
                                          self.solids[i].rel_center_of_mass)
        else: # not self._build_toward_positive_z
            # solidpos
            last_pos = np.zeros((3, 1))
            for solid in self.solids:
                solidpos.append(last_pos - solid.height * z_unit_vector)
                last_pos = solidpos[-1]
            # solidCOM
            for i in np.arange(self.nSolids):
                solidCOM.append(solidpos[i] +
                        self.solids[i].rel_center_of_mass)
        # TODO above code could be substantially cleaned up.
        # relative center of mass
        relmoment = np.zeros((3, 1))
        for i in np.arange(self.nSolids):
            relmoment += self.solids[i].mass * solidCOM[i]
        self._rel_center_of_mass = relmoment / self.mass
        # relative Inertia
        self._rel_inertia = np.mat(np.zeros((3, 3)))
        for i in np.arange(self.nSolids):
            dist = solidCOM[i] - self.rel_center_of_mass
            self._rel_inertia += np.mat(inertia.parallel_axis(
                                      self.solids[i].rel_inertia,
                                      self.solids[i].mass,
                                      [dist[0, 0], dist[1, 0], dist[2, 0]]))
예제 #2
0
파일: segment.py 프로젝트: agcooke/yeadon
    def calc_properties(self):
        '''Calculates the segment's center of mass with respect to the
        fixed human frame origin (in the fixed human reference frame) and the
        segment's inertia in the fixed human frame but about the segment's
        center of mass.

        '''
        # center of mass
        self.COM = self.pos + self.RotMat * self.relCOM
        # inertia in frame f w.r.t. segment's COM
        self.Inertia = inertia.rotate3_inertia(self.RotMat, self.relInertia)
        # an alternative way of calculating absolute inertia tensor,
        # implemented for validation purposes.
        # inertia in frame f w.r.t. segment's COM
        self.Inertia2 = np.mat(np.zeros((3, 3)))
        for s in self.solids:
            dist = s.COM - self.COM
            self.Inertia2 += np.mat(inertia.parallel_axis(
                                    s.Inertia, s.Mass,
                                    [dist[0, 0], dist[1, 0], dist[2, 0]]))
예제 #3
0
파일: segment.py 프로젝트: StefenYin/yeadon
    def calc_rel_properties(self):
        '''Calculates the mass, relative/local center of mass, and
        relative/local inertia tensor (about the segment's center of mass).
        Also computes the center of mass of each constituent solid with
        respect to the segment's base in the segment's reference frame.

        '''
        # mass
        self.Mass = 0.0
        for s in self.solids:
            self.Mass += s.Mass
        # relative position of each solid w.r.t. segment orientation and
        # segment's origin
        self.solidpos = []
        self.solidpos.append(np.zeros((3, 1)))
        for i in np.arange(self.nSolids):
            if i != 0:
                self.solidpos.append( self.solidpos[i-1] +
                                      self.solids[i-1].height *
                                      np.array([[0, 0, 1]]).T)
        # center of mass of each solid w.r.t. segment orientation and
        # segment's origin
        self.solidCOM = []
        self.solidCOM.append(self.solids[0].relCOM)
        for i in np.arange(self.nSolids):
            if i != 0:
                self.solidCOM.append( self.solidpos[i] +
                                      self.solids[i].relCOM)
        # relative center of mass
        relmoment = np.zeros((3, 1))
        for i in np.arange(self.nSolids):
            relmoment += self.solids[i].Mass * self.solidCOM[i]
        self.relCOM = relmoment / self.Mass
        # relative Inertia
        self.relInertia = np.mat(np.zeros((3, 3)))
        for i in np.arange(self.nSolids):
            dist = self.solidCOM[i] - self.relCOM
            self.relInertia += np.mat(inertia.parallel_axis(
                                      self.solids[i].relInertia,
                                      self.solids[i].Mass,
                                      [dist[0, 0], dist[1, 0], dist[2, 0]]))
예제 #4
0
    def steer_assembly_moment_of_inertia(self,
                                         handlebar=True,
                                         fork=True,
                                         wheel=True,
                                         aboutSteerAxis=False,
                                         nominal=False):
        """
        Returns the inertia tensor of the steer assembly with respect to a
        reference frame aligned with the steer axis.

        Parameters
        ----------
        handlebar : boolean, optional
            If true the handlebar will be included in the calculation.
        fork : boolean, optional
            If true the fork will be included in the calculation.
        wheel : boolean, optional
            If true then the wheel will be included in the calculation.
        aboutSteerAxis : boolean, optional
            If true the inertia tensor will be with respect to a point made
            from the projection of the center of mass onto the steer axis.
        nominal : boolean, optional
            If true the nominal values will be returned instead of a uarray.

        Returns
        -------
        iAss : float
            Inertia tensor of the specified steer assembly parts with respect
            to a reference frame aligned with the steer axis.

        Notes
        -----
        The 3 component is aligned with the steer axis (pointing downward), the
        1 component is perpendicular to the steer axis (pointing forward) and
        the 2 component is perpendicular to the steer axis (pointing to the
        right).

        This function does not currently take into account the flywheel, D, if
        it is defined, beware.

        """
        # load in the Benchmark parameter set
        par = self.parameters['Benchmark']

        if 'mD' in par.keys():
            print(
                "You have a flywheel defined. Beware that it is ignored in " +
                "the calculations and the results do not reflect that it is " +
                "there.")

        # there should always be either an H (handlebar/fork) and sometimes
        # there is a G (handlebar) and S (fork) if the fork and handlebar were
        # measured separately
        try:
            if fork and handlebar:
                # handlebar/fork
                I = inertia.part_inertia_tensor(par, 'H')
                m = par['mH']
                x = par['xH']
                z = par['zH']
            elif fork and not handlebar:
                # fork alone
                I = inertia.part_inertia_tensor(par, 'S')
                m = par['mS']
                x = par['xS']
                z = par['zS']
            elif handlebar and not fork:
                # handlebar alone
                I = inertia.part_inertia_tensor(par, 'G')
                m = par['mG']
                x = par['xG']
                z = par['zG']
            else:
                # if neither set to zero
                I = np.zeros((3, 3))
                m = 0.
                x = 0.
                z = 0.
        except KeyError:
            raise ValueError("The fork and handlebar were not measured " +
                             "separately for this bicycle." +
                             " Try making both the fork and handlebar either" +
                             " both True or both False.")

        if wheel:
            # list the mass and com of the handlebar/assembly and the front
            # wheel
            masses = np.array([m, par['mF']])

            coords = np.array([[x, par['w']], [0., 0.], [z, -par['rF']]])

            # mass and com of the entire assembly
            mAss, cAss = com.total_com(coords, masses)

            # front wheel inertia in the benchmark reference frame about the
            # com
            IF = inertia.part_inertia_tensor(par, 'F')

            # distance from the fork/handlebar assembly (without wheel) to the
            # new center of mass for the assembly with the wheel
            d = np.array([x - cAss[0], 0., z - cAss[2]])

            # distance from the front wheel center to the new center of mass
            # for the assembly with the wheel
            dF = np.array([par['w'] - cAss[0], 0., -par['rF'] - cAss[2]])

            # this is the inertia of the assembly about the com with reference
            # to the benchmark bicycle reference frame
            iAss = (inertia.parallel_axis(I, m, d) +
                    inertia.parallel_axis(IF, par['mF'], dF))

            # this is the inertia of the assembly about a reference frame aligned with
            # the steer axis and through the center of mass
            iAssRot = inertia.rotate_inertia_tensor(iAss, par['lam'])

        else:  # don't add the wheel
            mAss = m
            cAss = np.array([x, 0., z])
            iAssRot = inertia.rotate_inertia_tensor(I, par['lam'])

        if aboutSteerAxis:
            # this is the distance from the assembly com to the steer axis
            distance = geometry.distance_to_steer_axis(par['w'], par['c'],
                                                       par['lam'], cAss)
            print "handlebar cg distance", distance

            # now calculate the inertia about the steer axis of the rotated frame
            iAss = inertia.parallel_axis(iAssRot, mAss,
                                         np.array([distance, 0., 0.]))
        else:
            iAss = iAssRot

        if nominal:
            return unumpy.nominal_values(iAss)
        else:
            return iAss
예제 #5
0
def calculate_benchmark_from_measured(mp):
    '''Returns the benchmark (Meijaard 2007) parameter set based on the
    measured data.

    Parameters
    ----------
    mp : dictionary
        Complete set of measured data.

    Returns
    -------
    par : dictionary
        Benchmark bicycle parameter set.

    '''

    forkIsSplit = is_fork_split(mp)

    par = {}

    # calculate the wheelbase, steer axis tilt and trail
    par = geometry.calculate_benchmark_geometry(mp, par)

    # masses
    par['mB'] = mp['mB']
    par['mF'] = mp['mF']
    par['mR'] = mp['mR']
    try:
        # we measured the mass of the flywheel plus the mass of the front
        # wheel, mp['mD'], so to get the actual mass of the flywheel, subtract
        # the mass of the front wheel
        par['mD'] = mp['mD'] - mp['mF']
    except KeyError:
        pass
    if forkIsSplit:
        par['mS'] = mp['mS']
        par['mG'] = mp['mG']
    else:
        par['mH'] = mp['mH']

    # get the slopes, intercepts and betas for each part
    slopes, intercepts, betas = com.part_com_lines(mp, par, forkIsSplit)

    # calculate the centers of mass
    for part in slopes.keys():
        par['x' + part], par['z' + part] = com.center_of_mass(
            slopes[part], intercepts[part])

    # find the center of mass of the handlebar/fork assembly if the fork was
    # split
    if forkIsSplit:
        coordinates = np.array([[par['xS'], par['xG']], [0., 0.],
                                [par['zS'], par['zG']]])
        masses = np.array([par['mS'], par['mG']])
        mH, cH = inertia.total_com(coordinates, masses)
        par['mH'] = mH
        par['xH'] = cH[0]
        par['zH'] = cH[2]

    # local accelation due to gravity
    par['g'] = mp['g']

    # calculate the wheel y inertias
    par['IFyy'] = inertia.compound_pendulum_inertia(mp['mF'], mp['g'],
                                                    mp['lF'], mp['TcF1'])
    par['IRyy'] = inertia.compound_pendulum_inertia(mp['mR'], mp['g'],
                                                    mp['lR'], mp['TcR1'])
    try:
        # we measured the inertia of the front wheel with the flywheel inside
        iFlywheelPlusFwheel = inertia.compound_pendulum_inertia(
            mp['mD'], mp['g'], mp['lF'], mp['TcD1'])
        par['IDyy'] = iFlywheelPlusFwheel - par['IFyy']
    except KeyError:
        pass

    # calculate the y inertias for the frame and fork
    lB = (par['xB']**2 + (par['zB'] + par['rR'])**2)**(0.5)
    par['IByy'] = inertia.compound_pendulum_inertia(mp['mB'], mp['g'], lB,
                                                    mp['TcB1'])

    if forkIsSplit:
        # fork
        lS = ((par['xS'] - par['w'])**2 + (par['zS'] + par['rF'])**2)**(0.5)
        par['ISyy'] = inertia.compound_pendulum_inertia(
            mp['mS'], mp['g'], lS, mp['TcS1'])
        # handlebar
        l1, l2 = geometry.calculate_l1_l2(mp['h6'], mp['h7'], mp['d5'],
                                          mp['d6'], mp['l'])
        u1, u2 = geometry.fwheel_to_handlebar_ref(par['lam'], l1, l2)
        lG = ((par['xG'] - par['w'] + u1)**2 +
              (par['zG'] + par['rF'] + u2)**2)**(.5)
        par['IGyy'] = inertia.compound_pendulum_inertia(
            mp['mG'], mp['g'], lG, mp['TcG1'])
    else:
        lH = ((par['xH'] - par['w'])**2 + (par['zH'] + par['rF'])**2)**(0.5)
        par['IHyy'] = inertia.compound_pendulum_inertia(
            mp['mH'], mp['g'], lH, mp['TcH1'])

    # calculate the stiffness of the torsional pendulum
    IPxx, IPyy, IPzz = inertia.tube_inertia(mp['lP'], mp['mP'], mp['dP'] / 2.,
                                            0.)
    torStiff = inertia.torsional_pendulum_stiffness(IPyy, mp['TtP1'])
    #print "Torsional pendulum stiffness:", torStiff

    # calculate the wheel x/z inertias
    par['IFxx'] = inertia.tor_inertia(torStiff, mp['TtF1'])
    par['IRxx'] = inertia.tor_inertia(torStiff, mp['TtR1'])
    try:
        par['IDxx'] = inertia.tor_inertia(torStiff, mp['TtD1']) - par['IFxx']
    except KeyError:
        pass

    pendulumInertias = {}

    # calculate the in plane moments of inertia
    for part, slopeSet in slopes.items():
        # the number of orientations for this part
        numOrien = len(slopeSet)
        # intialize arrays to store the inertia values and orientation angles
        penInertia = np.zeros(numOrien, dtype=object)
        beta = np.array(betas[part])
        # fill arrays of the inertias
        for i in range(numOrien):
            penInertia[i] = inertia.tor_inertia(torStiff,
                                                mp['Tt' + part + str(i + 1)])
        # store these inertias
        pendulumInertias[part] = list(penInertia)
        inert = inertia.inertia_components(penInertia, beta)
        for i, axis in enumerate(['xx', 'xz', 'zz']):
            par['I' + part + axis] = inert[i]

    if forkIsSplit:
        # combine the moments of inertia to find the total handlebar/fork MoI
        IG = inertia.part_inertia_tensor(par, 'G')
        IS = inertia.part_inertia_tensor(par, 'S')
        # columns are parts, rows = x, y, z
        coordinates = np.array([[par['xG'], par['xS']], [0., 0.],
                                [par['zG'], par['zS']]])
        masses = np.array([par['mG'], par['mS']])
        par['mH'], cH = com.total_com(coordinates, masses)
        par['xH'] = cH[0]
        par['zH'] = cH[2]
        dG = np.array([par['xG'] - par['xH'], 0., par['zG'] - par['zH']])
        dS = np.array([par['xS'] - par['xH'], 0., par['zS'] - par['zH']])
        IH = (inertia.parallel_axis(IG, par['mG'], dG) +
              inertia.parallel_axis(IS, par['mS'], dS))
        par['IHxx'] = IH[0, 0]
        par['IHxz'] = IH[0, 2]
        par['IHyy'] = IH[1, 1]
        par['IHzz'] = IH[2, 2]

    # package the extra information that is useful outside this function
    extras = {
        'slopes': slopes,
        'intercepts': intercepts,
        'betas': betas,
        'pendulumInertias': pendulumInertias
    }

    return par, extras
예제 #6
0
    def steer_assembly_moment_of_inertia(self, handlebar=True, fork=True,
            wheel=True, aboutSteerAxis=False, nominal=False):
        """
        Returns the inertia tensor of the steer assembly with respect to a
        reference frame aligned with the steer axis.

        Parameters
        ----------
        handlebar : boolean, optional
            If true the handlebar will be included in the calculation.
        fork : boolean, optional
            If true the fork will be included in the calculation.
        wheel : boolean, optional
            If true then the wheel will be included in the calculation.
        aboutSteerAxis : boolean, optional
            If true the inertia tensor will be with respect to a point made
            from the projection of the center of mass onto the steer axis.
        nominal : boolean, optional
            If true the nominal values will be returned instead of a uarray.

        Returns
        -------
        iAss : float
            Inertia tensor of the specified steer assembly parts with respect
            to a reference frame aligned with the steer axis.

        Notes
        -----
        The 3 component is aligned with the steer axis (pointing downward), the
        1 component is perpendicular to the steer axis (pointing forward) and
        the 2 component is perpendicular to the steer axis (pointing to the
        right).

        This function does not currently take into account the flywheel, D, if
        it is defined, beware.

        """
        # load in the Benchmark parameter set
        par = self.parameters['Benchmark']

        if 'mD' in par.keys():
            print("You have a flywheel defined. Beware that it is ignored in "
                + "the calculations and the results do not reflect that it is "
                + "there.")

        # there should always be either an H (handlebar/fork) and sometimes
        # there is a G (handlebar) and S (fork) if the fork and handlebar were
        # measured separately
        try:
            if fork and handlebar:
                # handlebar/fork
                I = inertia.part_inertia_tensor(par, 'H')
                m = par['mH']
                x = par['xH']
                z = par['zH']
            elif fork and not handlebar:
                # fork alone
                I = inertia.part_inertia_tensor(par, 'S')
                m = par['mS']
                x = par['xS']
                z = par['zS']
            elif handlebar and not fork:
                # handlebar alone
                I = inertia.part_inertia_tensor(par, 'G')
                m = par['mG']
                x = par['xG']
                z = par['zG']
            else:
                # if neither set to zero
                I = np.zeros((3, 3))
                m = 0.
                x = 0.
                z = 0.
        except KeyError:
            raise ValueError("The fork and handlebar were not measured " +
                             "separately for this bicycle." +
                             " Try making both the fork and handlebar either" +
                             " both True or both False.")

        if wheel:
            # list the mass and com of the handlebar/assembly and the front
            # wheel
            masses = np.array([m, par['mF']])

            coords = np.array([[x, par['w']],
                               [0., 0.],
                               [z, -par['rF']]])

            # mass and com of the entire assembly
            mAss, cAss = com.total_com(coords, masses)

            # front wheel inertia in the benchmark reference frame about the
            # com
            IF = inertia.part_inertia_tensor(par, 'F')

            # distance from the fork/handlebar assembly (without wheel) to the
            # new center of mass for the assembly with the wheel
            d = np.array([x - cAss[0], 0., z - cAss[2]])

            # distance from the front wheel center to the new center of mass
            # for the assembly with the wheel
            dF = np.array([par['w'] - cAss[0],
                           0.,
                           -par['rF'] - cAss[2]])

            # this is the inertia of the assembly about the com with reference
            # to the benchmark bicycle reference frame
            iAss = (inertia.parallel_axis(I, m, d) +
                    inertia.parallel_axis(IF, par['mF'], dF))

            # this is the inertia of the assembly about a reference frame aligned with
            # the steer axis and through the center of mass
            iAssRot = inertia.rotate_inertia_tensor(iAss, par['lam'])

        else: # don't add the wheel
            mAss = m
            cAss = np.array([x, 0., z])
            iAssRot = inertia.rotate_inertia_tensor(I, par['lam'])

        if aboutSteerAxis:
            # this is the distance from the assembly com to the steer axis
            distance = geometry.distance_to_steer_axis(par['w'], par['c'],
                    par['lam'], cAss)
            print "handlebar cg distance", distance

            # now calculate the inertia about the steer axis of the rotated frame
            iAss = inertia.parallel_axis(iAssRot, mAss, np.array([distance, 0., 0.]))
        else:
            iAss = iAssRot

        if nominal:
            return unumpy.nominal_values(iAss)
        else:
            return iAss
예제 #7
0
def calculate_benchmark_from_measured(mp):
    '''Returns the benchmark (Meijaard 2007) parameter set based on the
    measured data.

    Parameters
    ----------
    mp : dictionary
        Complete set of measured data.

    Returns
    -------
    par : dictionary
        Benchmark bicycle parameter set.

    '''

    forkIsSplit = is_fork_split(mp)

    par = {}

    # calculate the wheelbase, steer axis tilt and trail
    par = geometry.calculate_benchmark_geometry(mp, par)

    # masses
    par['mB'] = mp['mB']
    par['mF'] = mp['mF']
    par['mR'] = mp['mR']
    try:
        # we measured the mass of the flywheel plus the mass of the front
        # wheel, mp['mD'], so to get the actual mass of the flywheel, subtract
        # the mass of the front wheel
        par['mD'] = mp['mD'] - mp['mF']
    except KeyError:
        pass
    if forkIsSplit:
        par['mS'] = mp['mS']
        par['mG'] = mp['mG']
    else:
        par['mH'] = mp['mH']

    # get the slopes, intercepts and betas for each part
    slopes, intercepts, betas = com.part_com_lines(mp, par, forkIsSplit)

    # calculate the centers of mass
    for part in slopes.keys():
        par['x' + part], par['z' + part] = com.center_of_mass(slopes[part],
            intercepts[part])

    # find the center of mass of the handlebar/fork assembly if the fork was
    # split
    if forkIsSplit:
        coordinates = np.array([[par['xS'], par['xG']],
                                [0., 0.],
                                [par['zS'], par['zG']]])
        masses = np.array([par['mS'], par['mG']])
        mH, cH = inertia.total_com(coordinates, masses)
        par['mH'] = mH
        par['xH'] = cH[0]
        par['zH'] = cH[2]

    # local accelation due to gravity
    par['g'] = mp['g']

    # calculate the wheel y inertias
    par['IFyy'] = inertia.compound_pendulum_inertia(mp['mF'], mp['g'],
                                            mp['lF'], mp['TcF1'])
    par['IRyy'] = inertia.compound_pendulum_inertia(mp['mR'], mp['g'],
                                            mp['lR'], mp['TcR1'])
    try:
        # we measured the inertia of the front wheel with the flywheel inside
        iFlywheelPlusFwheel = inertia.compound_pendulum_inertia(mp['mD'], mp['g'], mp['lF'], mp['TcD1'])
        par['IDyy'] = iFlywheelPlusFwheel - par['IFyy']
    except KeyError:
        pass

    # calculate the y inertias for the frame and fork
    lB = (par['xB']**2 + (par['zB'] + par['rR'])**2)**(0.5)
    par['IByy'] = inertia.compound_pendulum_inertia(mp['mB'], mp['g'], lB,
                                                    mp['TcB1'])

    if forkIsSplit:
        # fork
        lS = ((par['xS'] - par['w'])**2 +
              (par['zS'] + par['rF'])**2)**(0.5)
        par['ISyy'] = inertia.compound_pendulum_inertia(mp['mS'], mp['g'],
                                                lS, mp['TcS1'])
        # handlebar
        l1, l2 = geometry.calculate_l1_l2(mp['h6'], mp['h7'],
                                 mp['d5'], mp['d6'], mp['l'])
        u1, u2 = geometry.fwheel_to_handlebar_ref(par['lam'], l1, l2)
        lG = ((par['xG'] - par['w'] + u1)**2 +
              (par['zG'] + par['rF'] + u2)**2)**(.5)
        par['IGyy'] = inertia.compound_pendulum_inertia(mp['mG'], mp['g'],
                                                lG, mp['TcG1'])
    else:
        lH = ((par['xH'] - par['w'])**2 +
              (par['zH'] + par['rF'])**2)**(0.5)
        par['IHyy'] = inertia.compound_pendulum_inertia(mp['mH'], mp['g'],
                                                lH, mp['TcH1'])

    # calculate the stiffness of the torsional pendulum
    IPxx, IPyy, IPzz = inertia.tube_inertia(mp['lP'], mp['mP'],
                                            mp['dP'] / 2., 0.)
    torStiff = inertia.torsional_pendulum_stiffness(IPyy, mp['TtP1'])
    #print "Torsional pendulum stiffness:", torStiff

    # calculate the wheel x/z inertias
    par['IFxx'] = inertia.tor_inertia(torStiff, mp['TtF1'])
    par['IRxx'] = inertia.tor_inertia(torStiff, mp['TtR1'])
    try:
        par['IDxx'] =  inertia.tor_inertia(torStiff, mp['TtD1']) - par['IFxx']
    except KeyError:
        pass

    pendulumInertias = {}

    # calculate the in plane moments of inertia
    for part, slopeSet in slopes.items():
        # the number of orientations for this part
        numOrien = len(slopeSet)
        # intialize arrays to store the inertia values and orientation angles
        penInertia = np.zeros(numOrien, dtype=object)
        beta = np.array(betas[part])
        # fill arrays of the inertias
        for i in range(numOrien):
            penInertia[i] = inertia.tor_inertia(torStiff, mp['Tt' + part + str(i + 1)])
        # store these inertias
        pendulumInertias[part] = list(penInertia)
        inert = inertia.inertia_components(penInertia, beta)
        for i, axis in enumerate(['xx', 'xz', 'zz']):
            par['I' + part + axis] = inert[i]

    if forkIsSplit:
        # combine the moments of inertia to find the total handlebar/fork MoI
        IG = inertia.part_inertia_tensor(par, 'G')
        IS = inertia.part_inertia_tensor(par, 'S')
        # columns are parts, rows = x, y, z
        coordinates = np.array([[par['xG'], par['xS']],
                                [0., 0.],
                                [par['zG'], par['zS']]])
        masses = np.array([par['mG'], par['mS']])
        par['mH'], cH = com.total_com(coordinates, masses)
        par['xH'] = cH[0]
        par['zH'] = cH[2]
        dG = np.array([par['xG'] - par['xH'], 0., par['zG'] - par['zH']])
        dS = np.array([par['xS'] - par['xH'], 0., par['zS'] - par['zH']])
        IH = (inertia.parallel_axis(IG, par['mG'], dG) +
              inertia.parallel_axis(IS, par['mS'], dS))
        par['IHxx'] = IH[0, 0]
        par['IHxz'] = IH[0, 2]
        par['IHyy'] = IH[1, 1]
        par['IHzz'] = IH[2, 2]

    # package the extra information that is useful outside this function
    extras = {'slopes' : slopes,
              'intercepts' : intercepts,
              'betas' : betas,
              'pendulumInertias' : pendulumInertias}

    return par, extras