<% # Tricycle with spherical wheels and front steering # Consists of triangular frame with 1 steerable # and 2 non-steerable wheels # SI units (length in meters) # Geometry wheel_radius = 0.15 wheelbase = 6 * wheel_radius half_track = wheelbase * Math.tan(30 * Math::PI/180) caster_angle = 10 * Math::PI / 180.0 fork_length = 1.5 * wheel_radius * Math.sqrt(2) headtube_length = 0.5 * fork_length front_steer_L = headtube_length + fork_length front_steer_dx = front_steer_L * Math.sin(caster_angle) front_steer_dz = front_steer_L * Math.cos(caster_angle) rear_steer_dx = wheelbase - front_steer_dx rear_steer_L = Math.sqrt(rear_steer_dx**2 + front_steer_dz**2) frame_pitch = Math.atan2(front_steer_dz, rear_steer_dx) frame_dx = rear_steer_L frame_dy = half_track frame_dz = 0.4*wheel_radius frame_z0 = wheel_radius + front_steer_dz / 2.0 frame_angle = 20 * Math::PI/180 fork_dx = 0.5 * wheel_radius fork_dy = 2*fork_length fork_dz = front_steer_L fork_offset = fork_length - 0.5*front_steer_L wheel_x0 = rear_steer_dx wheel_y0 = half_track steer_limit = 55 * Math::PI / 180.0 rear_wheel_locations = { "rear_left" => {:x0 => -wheel_x0, :y0 => wheel_y0 }, "rear_right" => {:x0 => -wheel_x0, :y0 => -wheel_y0 }, } # inertia frame_mass = 10 frame_ixx = frame_mass/12.0 * (frame_dy**2 + frame_dz**2) frame_iyy = frame_mass/12.0 * (frame_dz**2 + frame_dx**2) frame_izz = frame_mass/12.0 * (frame_dx**2 + frame_dy**2) # frame c.g. offset from center of box frame_cgx = frame_dx*0.0 frame_cgy = 0 frame_cgz = 0 frame_mass = 10 fork_mass = frame_mass / 3 fork_ixx = fork_mass/12.0 * (fork_dy**2 + fork_dz**2) fork_iyy = fork_mass/12.0 * (fork_dz**2 + fork_dx**2) fork_izz = fork_mass/12.0 * (fork_dx**2 + fork_dy**2) wheel_mass = 0.5 wheel_ixx = 0.4 * wheel_mass * wheel_radius**2 wheel_iyy = wheel_ixx wheel_izz = wheel_ixx # wheel surface properties wheel_kp = 1e8 wheel_kd = 1 wheel_min_depth = 0.0005 %> <%= -rear_steer_dx / 2 %> 0 <%= frame_z0 %> 0 <%= -frame_pitch %> 0 <%= frame_cgx %> <%= frame_cgy %> <%= frame_cgz %> 0 0 0 <%= frame_mass %> <%= frame_ixx %> <%= frame_iyy %> <%= frame_izz %> 0 0 0 <% ["collision", "visual"].each do |elem| %> <<%= elem %> name="axle_<%= elem %>"> <%= -0.5*frame_dx %> 0 0 <%= Math::PI/2 %> 0 0 <%= 2 * half_track %> <%= 0.2 * wheel_radius %> <% if elem == "visual" %> <% end %> > <% end %> <% ["collision", "visual"].each do |elem| %> <<%= elem %> name="frame_left_<%= elem %>"> 0 <%= 0.5*frame_dx*Math.tan(frame_angle) %> 0 0 <%= Math::PI/2 %> <%= -frame_angle %> <%= frame_dx / Math.cos(frame_angle) %> <%= 0.2 * wheel_radius %> <% if elem == "visual" %> <% end %> > <% end %> <% ["collision", "visual"].each do |elem| %> <<%= elem %> name="frame_right_<%= elem %>"> 0 <%= -0.5*frame_dx*Math.tan(frame_angle) %> 0 0 <%= Math::PI/2 %> <%= frame_angle %> <%= frame_dx / Math.cos(frame_angle) %> <%= 0.2 * wheel_radius %> <% if elem == "visual" %> <% end %> > <% end %> <%= front_steer_dx / 2 %> 0 <%= frame_z0 %> 0 <%= -caster_angle %> 0 <%= fork_mass %> <%= fork_ixx %> <%= fork_iyy %> <%= fork_izz %> 0 0 0 <% ["collision", "visual"].each do |elem| %> <<%= elem %> name="handlebars_<%= elem %>"> 0 0 <%= 2*headtube_length + fork_offset %> <%= Math::PI/2 %> 0 0 <%= 2*fork_length %> <%= 0.5*fork_dx %> <% if elem == "visual" %> <% end %> > <% end %> <% ["collision", "visual"].each do |elem| %> <<%= elem %> name="headtube_<%= elem %>"> 0 0 <%= headtube_length + fork_offset %> 0 0 0 <%= 2*headtube_length %> <%= 0.5*fork_dx %> <% if elem == "visual" %> <% end %> > <% end %> <% ["collision", "visual"].each do |elem| %> <<%= elem %> name="spindle_<%= elem %>"> 0 0 <%= -fork_length + fork_offset %> <%= Math::PI/2 %> 0 0 <%= 2 * fork_length %> <%= 0.1 * wheel_radius %> <% if elem == "visual" %> <% end %> > <% end %> <% ["collision", "visual"].each do |elem| %> <<%= elem %> name="fork_left_<%= elem %>"> 0 <%= fork_length / 2 %> <%= -fork_length/2 + fork_offset %> <%= Math::PI/4 %> 0 0 <%= fork_length * Math.sqrt(2) %> <%= 0.5*fork_dx %> <% if elem == "visual" %> <% end %> > <% end %> <% ["collision", "visual"].each do |elem| %> <<%= elem %> name="fork_right_<%= elem %>"> 0 <%= -fork_length / 2 %> <%= -fork_length/2 + fork_offset %> <%= -Math::PI/4 %> 0 0 <%= fork_length * Math.sqrt(2) %> <%= 0.5*fork_dx %> <% if elem == "visual" %> <% end %> > <% end %> <%= front_steer_dx %> 0 <%= wheel_radius %> 0 0 0 <%= wheel_mass %> <%= wheel_ixx %> <%= wheel_iyy %> <%= wheel_izz %> 0 0 0 <%= wheel_radius %> <%= wheel_kp %> <%= wheel_kd %> <%= wheel_min_depth %> <%= wheel_radius %> frame fork 0 0 1 <%= -steer_limit %> <%= steer_limit %> fork wheel_front 0 1 0 <% rear_wheel_locations.keys.each do |k| x0 = rear_wheel_locations[k][:x0] y0 = rear_wheel_locations[k][:y0] %> <%= x0 %> <%= y0 %> <%= wheel_radius %> 0 0 0 <%= wheel_mass %> <%= wheel_ixx %> <%= wheel_iyy %> <%= wheel_izz %> 0 0 0 <%= wheel_radius %> <%= wheel_kp %> <%= wheel_kd %> <%= wheel_min_depth %> <%= wheel_radius %> frame wheel_<%= k %> 0 1 0 <% end %>