class InvertedPendulum::Cart
Attributes
ipwin[RW]
pix_meters[RW]
Public Class Methods
new(ipwin: nil, scale: 0.50, ang: 90.1, xpos: 500.0, ypos: 845.0, cartmass: 200.0, polemass: 100.10, bang: 10.0, thrust_decay: 2.0, window_pix_width: 1280, update_interval: 16.666666, naked: false)
click to toggle source
@param ipwin – the windowed canvalss this will be shown. @param scale – visual scale on the canvas. @param ang – initial angle in degrees, 0 being the
positive side of the x axis.
@param xpos – initial x position of the center of cart, in pixels. @param cartmass – in kg. included are the mass of the wheels.
We will not deal with the angular momentum of the wheels, because that's beyond the scope of what this is supposed to accomplish.
@param polemass – in kg. The mass of the pole is assumed to all
reside at a point at the knobby end.
@param bang – per mouse event, how much bang (acceleration in m/s) to
deliver to the cart
# File neater/rnlib/inverted_pendulum.rb, line 117 def initialize(ipwin: nil, scale: 0.50, ang: 90.1, xpos: 500.0, ypos: 845.0, cartmass: 200.0, polemass: 100.10, bang: 10.0, # acceleration on a bang event thrust_decay: 2.0, # thrust decay percentage per second window_pix_width: 1280, update_interval: 16.666666, naked: false) @t = 0 @bang = bang @thrust = 0 # accumulated bang @thrust_decay = thrust_decay @scale = scale @cart_length = 5.0 # meters @pix_meters = 640.0 * scale / @cart_length @ipwin = ipwin @pix_width = @ipwin.nil? ? window_pix_width : @ipwin.pix_width @update_interval = ipwin.nil? ? update_interval : @ipwin.update_interval @cart = { pos: Vector[xpos / @pix_meters, ypos / @pix_meters, 0], vel: Vector[0.0, 0, 0], #speed in meters per second acc: Vector[0.0, 0, 0], #acceleration in meters per second squared scale: 0.2 * scale, length: nil, # in meters, calculated from the scaled pixel length. height: nil, # in meters, calculated from the scaled pixel height basis: { horiz: Vector[1.0, 0.0, 0.0], # The cart is resticted to movement on the x axis vert: Vector[0.0, 1.0, 0.0]}, # so these basis vectors will never change. force: {}, mass: cartmass } # Pole is relative to cart, and in the image is laying horizontal. @pole = { z: 0, xoff: 0.0, yoff: 0.5, ang: ang, # angle is in degrees dang: 0.0, # degrees per second ddang: nil, # angular acceleration, degree / second ** 2 cdang: 0.0, # degrees per second, contribution from cart cddang: nil, # angular acceleration, degree / second ** 2, contribution from cart scale: 0.2 * scale, force: {}, # shall hold the 2 force vectors :shaft and :radial basis: {}, # basis (unit) vectors cacc: Vector[0,0,0], # acceleration from cart length: nil, # in meters, calculated from the scaled pixel length. mass: polemass } # Wheels is relative to cart @wheels = [ { ang: 0, dang: 100, #FIXME: delete this for this will be overwritten anyway xoff: -0.7, # percentage from center yoff: 0.4, # percentage from center scale: 0.2 * scale }, { ang: 0, dang: 12.33, #FIXME: delete this for this will be overwritten anyway xoff: 0.7, yoff: 0.4, scale: 0.2 * scale } ] end
Public Instance Methods
big_bang(bb = 0)
click to toggle source
Provide a bang for the cart. Return a thrust (acc) vector (really just x) based on thrust
@param bb – bang factor, normally -1, 0, or 1, but could be
other values based on being a multiplier of the actual bang to be applied.
# File neater/rnlib/inverted_pendulum.rb, line 212 def big_bang(bb = 0) # FIXME: this is temporary. Eventually this will call a callback in the Neater script. v = Vector[@thrust, 0, 0] @thrust += @bang * bb @thrust -= @thrust * @thrust_decay * @dt v end
draw()
click to toggle source
# File neater/rnlib/inverted_pendulum.rb, line 328 def draw @pole[:image].draw_rot( @pole[:_x] % @pix_width, @pole[:_y], @pole[:z], -@pole[:ang], # negative because y is inverted on the canvas @pole[:xoff], @pole[:yoff], @pole[:scale], @pole[:scale]) @cart[:image].draw(@cart[:_x] % @pix_width, @cart[:_y], 0, @cart[:scale], @cart[:scale]) @wheels.each do |wh| wh[:image].draw_rot(wh[:_x] % @pix_width, wh[:_y], 0, wh[:ang], 0.5, 0.5, wh[:scale], wh[:scale]) end end
ipwin=(ipwin)
click to toggle source
# File neater/rnlib/inverted_pendulum.rb, line 190 def ipwin=(ipwin) @ipwin = ipwin @cart[:image] = Gosu::Image.new(ipwin, 'public/cart.png', true) @pole[:image] = Gosu::Image.new(ipwin, 'public/pole.png', true) @wheels.each {|w| w[:image] = Gosu::Image.new(ipwin, 'public/wheel.png', true) # radius of wheel in meters, need this for rotational velocity calculation w[:radius] = w[:image].width * w[:scale] / @pix_meters / 2.0 w[:circumference] = w[:radius] * 2.0 * PI } @cart[:length] = @cart[:image].width * @cart[:scale] / @pix_meters @cart[:height] = @cart[:image].height * @cart[:scale] / @pix_meters @pole[:length] = @pole[:image].width * @pole[:scale] / @pix_meters end
update()
click to toggle source
# File neater/rnlib/inverted_pendulum.rb, line 235 def update ## Physics updates @dt = @update_interval / 1000.0 @t += @dt ## Pole (Pendulum) forces, accelerations, etc. # basis vectors ang = @pole[:ang] * TORAD @pole[:time] = @t @pole[:basis][:shaft] = iShaft = Vector[cos(ang), sin(ang), 0] @pole[:basis][:radial] = iRadial = Vector[sin(ang), -cos(ang), 0] @pole[:r] = r = iShaft * @pole[:length] @pole[:force][:shaft] = iShaft.basis(GV * @pole[:mass]) @pole[:force][:radial] = radial = iRadial.basis(GV * @pole[:mass]) # the magnitude of the radial vector goes to the torque on @pole[:force][:torque] = torque = r.cross_product(radial) @pole[:alpha] = alpha = torque / (@pole[:mass] * (@pole[:length] ** 2.0)) @pole[:ddang] = -alpha[Z] / TORAD # the pseudo vector component Z is the signed magnitude @pole[:dang] += @pole[:ddang] * @dt # angular velocity @pole[:vel] = @pole[:basis][:radial] * (@pole[:dang] * TORAD * PI2 * @pole[:length]) #linear velocity at the pole mass @pole[:ang] += @pole[:dang] * @dt ## Cart forces from pole [:force][:shaft] and centifugal @cart[:time] = @t @pole[:force][:centrifugal] = centrifugal = (@pole[:basis][:shaft] * @pole[:vel].magnitude ** 2) * @pole[:mass] @cart[:force][:shaft] = shaft = @pole[:force][:shaft] # + centrifugal #FIXME cemtrifugal calculations wonky! @cart[:force][:horiz] = horiz = @cart[:basis][:horiz].basis shaft @cart[:force][:vert] = @cart[:basis][:vert].basis shaft # not that we use the vert # Now the horz force induces an acceleration on the cart. # Any additions to the acceleration vector must be done after # this point. @cart[:acc] = horiz / (@cart[:mass] + @pole[:mass] * cos(ang).abs) @cart[:acc] += big_bang ## Cart acceleration also affects angular torque # FIXME: Note that recalculations are being done here, which # FIXME: are not DRY. Redo this properly later. @pole[:cacc] = cacc = @cart[:acc] * -1.0 @pole[:force][:cradial] = radial = iRadial.basis(cacc * @pole[:mass]) # the magnitude of the radial vector goes to the torque on @pole[:force][:ctorque] = torque = r.cross_product(radial) @pole[:calpha] = alpha = torque / (@pole[:mass] * (@pole[:length] ** 2.0)) @pole[:cddang] = -alpha[Z] / TORAD # the pseudo vector component Z is the signed magnitude @pole[:cdang] += @pole[:cddang] * @dt @pole[:ang] += @pole[:cdang] * @dt ## Actual cart physics @cart[:vel][X] += @cart[:acc][X] * @dt @cart[:pos][X] += @cart[:vel][X] * @dt @cart[:x] = @cart[:pos][X] * @pix_meters @cart[:y] = @cart[:pos][Y] * @pix_meters #puts '=' * 80 #pp({pole: @pole, cart: @cart}) # wheels physics -- angular velocity of each wheel based # on the linear velocity of the cart. @wheels.each do |w| w[:dang] = 360.0 * @cart[:vel][X] / w[:circumference] w[:ang] += w[:dang] * @dt end # model update self.update_cart end
update_cart()
click to toggle source
update the positions of all the compnents of the cart in terms of the relative positional relationships
# File neater/rnlib/inverted_pendulum.rb, line 306 def update_cart # center of cart is taken as (x, y), actual is (_x, _y) pw = @cart[:image].width * @cart[:scale] ph = @cart[:image].height * @cart[:scale] @cart[:_x] = @cart[:x] - pw / 2.0 @cart[:_y] = @cart[:y] - ph / 2.0 # Wheels in their respective places @wheels.each do |wl| #ww = wl[:image].width * wl[:scale] #wh = wl[:image].height * wl[:scale] wl[:_x] = @cart[:x] + wl[:xoff] * pw / 2.0 wl[:_y] = @cart[:y] + wl[:yoff] * ph / 2.0 end # Pendulum polew = @pole[:image].width * @pole[:scale] poleh = @pole[:image].height * @pole[:scale] @pole[:_x] = @cart[:x] @pole[:_y] = @cart[:y] end