def plot_bicycle_geometry(self, show=True, pendulum=True, centerOfMass=True, inertiaEllipse=True): '''Returns a figure showing the basic bicycle geometry, the centers of mass and the moments of inertia. Notes ----- If the flywheel is defined, it's center of mass corresponds to the front wheel and is not depicted in the plot. ''' par = io.remove_uncertainties(self.parameters['Benchmark']) parts = get_parts_in_parameters(par) try: slopes = io.remove_uncertainties(self.extras['slopes']) intercepts = io.remove_uncertainties(self.extras['intercepts']) penInertias = io.remove_uncertainties( self.extras['pendulumInertias']) except AttributeError: pendulum = False fig = plt.figure() ax = plt.axes() # define some colors for the parts numColors = len(parts) cmap = plt.get_cmap('gist_rainbow') partColors = {} for i, part in enumerate(parts): partColors[part] = cmap(1. * i / numColors) if inertiaEllipse: # plot the principal moments of inertia for j, part in enumerate(parts): I = inertia.part_inertia_tensor(par, part) Ip, C = inertia.principal_axes(I) if part == 'R': center = np.array([0., par['rR']]) elif part in 'FD': center = np.array([par['w'], par['rF']]) else: center = np.array([par['x' + part], -par['z' + part]]) # which row in C is the y vector uy = np.array([0., 1., 0.]) for i, row in enumerate(C): if np.abs(np.sum(row - uy)) < 1E-10: yrow = i # remove the row for the y vector Ip2D = np.delete(Ip, yrow, 0) # remove the column and row associated with the y C2D = np.delete(np.delete(C, yrow, 0), 1, 1) # make an ellipse Imin = Ip2D[0] Imax = Ip2D[1] # get width and height of a ellipse with the major axis equal # to one unitWidth = 1. / 2. / np.sqrt(Imin) * np.sqrt(Imin) unitHeight = 1. / 2. / np.sqrt(Imax) * np.sqrt(Imin) # now scaled the width and height relative to the maximum # principal moment of inertia width = Imax * unitWidth height = Imax * unitHeight angle = -np.degrees(np.arccos(C2D[0, 0])) ellipse = Ellipse((center[0], center[1]), width, height, angle=angle, fill=False, color=partColors[part], alpha=0.25) ax.add_patch(ellipse) # plot the ground line x = np.array([-par['rR'], par['w'] + par['rF']]) plt.plot(x, np.zeros_like(x), 'k') # plot the rear wheel c = plt.Circle((0., par['rR']), radius=par['rR'], fill=False) ax.add_patch(c) # plot the front wheel c = plt.Circle((par['w'], par['rF']), radius=par['rF'], fill=False) ax.add_patch(c) # plot the fundamental bike deex, deez = geometry.fundamental_geometry_plot_data(par) plt.plot(deex, -deez, 'k') # plot the steer axis dx3 = deex[2] + deez[2] * (deex[2] - deex[1]) / (deez[1] - deez[2]) plt.plot([deex[2], dx3], [-deez[2], 0.], 'k--') # don't plot the pendulum lines if a rider has been added because the # inertia has changed if self.hasRider: pendulum = False if pendulum: # plot the pendulum axes for the measured parts for j, pair in enumerate(slopes.items()): part, slopeSet = pair xcom, zcom = par['x' + part], par['z' + part] for i, m in enumerate(slopeSet): b = intercepts[part][i] xPoint, zPoint = geometry.project_point_on_line( (m, b), (xcom, zcom)) comLineLength = penInertias[part][i] xPlus = comLineLength / 2. * np.cos(np.arctan(m)) x = np.array([xPoint - xPlus, xPoint + xPlus]) z = -m * x - b plt.plot(x, z, color=partColors[part]) # label the pendulum lines with a number plt.text(x[0], z[0], str(i + 1)) if centerOfMass: # plot the center of mass location def com_symbol(ax, center, radius, color='b'): '''Returns axis with center of mass symbol.''' c = plt.Circle(center, radius=radius, fill=False) w1 = Wedge(center, radius, 0., 90., color=color, ec=None, alpha=0.5) w2 = Wedge(center, radius, 180., 270., color=color, ec=None, alpha=0.5) ax.add_patch(w1) ax.add_patch(w2) ax.add_patch(c) return ax # radius of the CoM symbol sRad = 0.03 # front wheel CoM ax = com_symbol(ax, (par['w'], par['rF']), sRad, color=partColors['F']) plt.text(par['w'] + sRad, par['rF'] + sRad, 'F') # rear wheel CoM ax = com_symbol(ax, (0., par['rR']), sRad, color=partColors['R']) plt.text(0. + sRad, par['rR'] + sRad, 'R') for j, part in enumerate([x for x in parts if x not in 'RFD']): xcom = par['x' + part] zcom = par['z' + part] ax = com_symbol(ax, (xcom, -zcom), sRad, color=partColors[part]) plt.text(xcom + sRad, -zcom + sRad, part) if 'H' not in parts: ax = com_symbol(ax, (par['xH'], -par['zH']), sRad) plt.text(par['xH'] + sRad, -par['zH'] + sRad, 'H') plt.axis('equal') plt.ylim((0., 1.)) plt.title(self.bicycleName) # if there is a rider on the bike, make a simple stick figure if self.human: human = self.human mpar = self.parameters['Measured'] bpar = self.parameters['Benchmark'] # K2: lower leg, tip of foot to knee start = rider.yeadon_vec_to_bicycle_vec(human.K2.end_pos, mpar, bpar) end = rider.yeadon_vec_to_bicycle_vec(human.K2.pos, mpar, bpar) plt.plot([start[0, 0], end[0, 0]], [-start[2, 0], -end[2, 0]], 'k') # K1: upper leg, knee to hip start = rider.yeadon_vec_to_bicycle_vec(human.K2.pos, mpar, bpar) end = rider.yeadon_vec_to_bicycle_vec(human.K1.pos, mpar, bpar) plt.plot([start[0, 0], end[0, 0]], [-start[2, 0], -end[2, 0]], 'k') # torso start = rider.yeadon_vec_to_bicycle_vec(human.K1.pos, mpar, bpar) end = rider.yeadon_vec_to_bicycle_vec(human.B1.pos, mpar, bpar) plt.plot([start[0, 0], end[0, 0]], [-start[2, 0], -end[2, 0]], 'k') # B1: upper arm start = rider.yeadon_vec_to_bicycle_vec(human.B1.pos, mpar, bpar) end = rider.yeadon_vec_to_bicycle_vec(human.B2.pos, mpar, bpar) plt.plot([start[0, 0], end[0, 0]], [-start[2, 0], -end[2, 0]], 'k') # B2: lower arm, elbow to tip of fingers start = rider.yeadon_vec_to_bicycle_vec(human.B2.pos, mpar, bpar) end = rider.yeadon_vec_to_bicycle_vec(human.B2.end_pos, mpar, bpar) plt.plot([start[0, 0], end[0, 0]], [-start[2, 0], -end[2, 0]], 'k') # C: chest/head start = rider.yeadon_vec_to_bicycle_vec(human.B1.pos, mpar, bpar) end = rider.yeadon_vec_to_bicycle_vec(human.C.end_pos, mpar, bpar) plt.plot([start[0, 0], end[0, 0]], [-start[2, 0], -end[2, 0]], 'k') if show: fig.show() return fig
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
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
def plot_bicycle_geometry(self, show=True, pendulum=True, centerOfMass=True, inertiaEllipse=True): '''Returns a figure showing the basic bicycle geometry, the centers of mass and the moments of inertia. Notes ----- If the flywheel is defined, it's center of mass corresponds to the front wheel and is not depicted in the plot. ''' par = io.remove_uncertainties(self.parameters['Benchmark']) parts = get_parts_in_parameters(par) try: slopes = io.remove_uncertainties(self.extras['slopes']) intercepts = io.remove_uncertainties(self.extras['intercepts']) penInertias = io.remove_uncertainties(self.extras['pendulumInertias']) except AttributeError: pendulum = False fig = plt.figure() ax = plt.axes() # define some colors for the parts numColors = len(parts) cmap = plt.get_cmap('gist_rainbow') partColors = {} for i, part in enumerate(parts): partColors[part] = cmap(1. * i / numColors) if inertiaEllipse: # plot the principal moments of inertia for j, part in enumerate(parts): I = inertia.part_inertia_tensor(par, part) Ip, C = inertia.principal_axes(I) if part == 'R': center = np.array([0., par['rR']]) elif part in 'FD': center = np.array([par['w'], par['rF']]) else: center = np.array([par['x' + part], -par['z' + part]]) # which row in C is the y vector uy = np.array([0., 1., 0.]) for i, row in enumerate(C): if np.abs(np.sum(row - uy)) < 1E-10: yrow = i # remove the row for the y vector Ip2D = np.delete(Ip, yrow, 0) # remove the column and row associated with the y C2D = np.delete(np.delete(C, yrow, 0), 1, 1) # make an ellipse Imin = Ip2D[0] Imax = Ip2D[1] # get width and height of a ellipse with the major axis equal # to one unitWidth = 1. / 2. / np.sqrt(Imin) * np.sqrt(Imin) unitHeight = 1. / 2. / np.sqrt(Imax) * np.sqrt(Imin) # now scaled the width and height relative to the maximum # principal moment of inertia width = Imax * unitWidth height = Imax * unitHeight angle = -np.degrees(np.arccos(C2D[0, 0])) ellipse = Ellipse((center[0], center[1]), width, height, angle=angle, fill=False, color=partColors[part], alpha=0.25) ax.add_patch(ellipse) # plot the ground line x = np.array([-par['rR'], par['w'] + par['rF']]) plt.plot(x, np.zeros_like(x), 'k') # plot the rear wheel c = plt.Circle((0., par['rR']), radius=par['rR'], fill=False) ax.add_patch(c) # plot the front wheel c = plt.Circle((par['w'], par['rF']), radius=par['rF'], fill=False) ax.add_patch(c) # plot the fundamental bike deex, deez = geometry.fundamental_geometry_plot_data(par) plt.plot(deex, -deez, 'k') # plot the steer axis dx3 = deex[2] + deez[2] * (deex[2] - deex[1]) / (deez[1] - deez[2]) plt.plot([deex[2], dx3], [-deez[2], 0.], 'k--') # don't plot the pendulum lines if a rider has been added because the # inertia has changed if self.hasRider: pendulum = False if pendulum: # plot the pendulum axes for the measured parts for j, pair in enumerate(slopes.items()): part, slopeSet = pair xcom, zcom = par['x' + part], par['z' + part] for i, m in enumerate(slopeSet): b = intercepts[part][i] xPoint, zPoint = geometry.project_point_on_line((m, b), (xcom, zcom)) comLineLength = penInertias[part][i] xPlus = comLineLength / 2. * np.cos(np.arctan(m)) x = np.array([xPoint - xPlus, xPoint + xPlus]) z = -m * x - b plt.plot(x, z, color=partColors[part]) # label the pendulum lines with a number plt.text(x[0], z[0], str(i + 1)) if centerOfMass: # plot the center of mass location def com_symbol(ax, center, radius, color='b'): '''Returns axis with center of mass symbol.''' c = plt.Circle(center, radius=radius, fill=False) w1 = Wedge(center, radius, 0., 90., color=color, ec=None, alpha=0.5) w2 = Wedge(center, radius, 180., 270., color=color, ec=None, alpha=0.5) ax.add_patch(w1) ax.add_patch(w2) ax.add_patch(c) return ax # radius of the CoM symbol sRad = 0.03 # front wheel CoM ax = com_symbol(ax, (par['w'], par['rF']), sRad, color=partColors['F']) plt.text(par['w'] + sRad, par['rF'] + sRad, 'F') # rear wheel CoM ax = com_symbol(ax, (0., par['rR']), sRad, color=partColors['R']) plt.text(0. + sRad, par['rR'] + sRad, 'R') for j, part in enumerate([x for x in parts if x not in 'RFD']): xcom = par['x' + part] zcom = par['z' + part] ax = com_symbol(ax, (xcom, -zcom), sRad, color=partColors[part]) plt.text(xcom + sRad, -zcom + sRad, part) if 'H' not in parts: ax = com_symbol(ax, (par['xH'], -par['zH']), sRad) plt.text(par['xH'] + sRad, -par['zH'] + sRad, 'H') plt.axis('equal') plt.ylim((0., 1.)) plt.title(self.bicycleName) # if there is a rider on the bike, make a simple stick figure if self.human: human = self.human # K2: lower leg plt.plot([human.k[7].pos[0, 0], human.K2.pos[0, 0]], [-human.k[7].endpos[2, 0], -human.K2.pos[2, 0]], 'k') # K1: upper leg plt.plot([human.K2.pos[0, 0], human.K1.pos[0, 0]], [-human.K2.pos[2, 0], -human.K1.pos[2, 0]], 'k') # torso plt.plot([human.K1.pos[0, 0], human.B1.pos[0, 0]], [-human.K1.pos[2, 0], -human.B1.pos[2, 0]], 'k') # B1: upper arm plt.plot([human.B1.pos[0, 0], human.B2.pos[0, 0]], [-human.B1.pos[2, 0], -human.B2.pos[2, 0]], 'k') # B2: lower arm plt.plot([human.B2.pos[0, 0], human.b[6].pos[0, 0]], [-human.B2.pos[2, 0], -human.b[6].endpos[2, 0]], 'k') # C: chest/head plt.plot([human.B1.pos[0, 0], human.C.endpos[0, 0]], [-human.B1.pos[2, 0], -human.C.endpos[2, 0]], 'k') if show: fig.show() return fig
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
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