PinJoint causes perpetual motion

18 views Asked by At

I'm trying to simulate a chain with pymunk and pygame. To do this i have the objects Chain, ChainLink, and ChainEstremity. They are respectively: the container for all the chainlinks, a capsule shaped object which consists in two pymunk Circle objects, and a static box where the initial or final chain links are attached to. Chainlinks have 4 costraints, 3 PinJoints to keep their shape intact (remember they consist in 2 shapes), and 1 RotaryLimitJoint to avoid a rotation bigger than 90 degrees. Multiple chain links can be linked together with the link_to function which adds a SlideJoint. And finally a chain link and a chain estremity are attached with a PinJoint. My code can be found on github here.

My problem is that the chain keeps shaking randomly and doesn't stop with time, making the simulation unrealistic. I tried adjusting the mass of the bodies and adding a manual damping to the angular velocities of the bodies but none of these measures have any effect at all.

Here's what the chain looks like: the chain

Here is how i init the classes and the link_to function:

class ChainEstremity:
    def __init__(self, center):
        half_side = ChainEstremity.half_side
        self.body = pymunk.Body(body_type=pymunk.Body.STATIC)
        self.shape = pymunk.Poly(self.body,
                                 [(-half_side,-half_side), (half_side,-half_side), (half_side,half_side), (-half_side, half_side)]
                                 )
        self.shape.mass = ChainEstremity.mass
        self.body.position = center
        space.add(self.body, self.shape)



    
class Chain:
    def __init__(self, point_a, point_b, chain_links):
        Chain.estremities.append(ChainEstremity(point_a))
        Chain.estremities.append(ChainEstremity(point_b))

        estremities_dist = Chain.estremities[0].body.position.get_distance(Chain.estremities[1].body.position)
        links_length = estremities_dist // chain_links
        first_link = ChainLink(links_length)
        Chain.all_links.append(first_link)
        first_link.setup(Chain.estremities[0].body.position)
        first_link_constraint = pymunk.constraints.PinJoint(Chain.estremities[0].body, first_link.shape1.body)
        first_link_constraint.collide_bodies = False
        space.add(first_link_constraint)
        
        for i in range(1, chain_links):
            new_link = ChainLink(links_length)
            Chain.all_links.append(new_link)
            new_link.setup(Chain.estremities[0].body.position + Vec2d(links_length * i, 0))
            new_link.link_to(Chain.all_links[i-1])
            
            if i == chain_links - 1:
                last_link_costraint = pymunk.constraints.PinJoint(Chain.estremities[1].body, new_link.shape2.body)
                last_link_costraint.collide_bodies = False
                space.add(last_link_costraint)



class ChainLink:
    def __init__(self, length):
        self.length = length
        self.shape1 = pymunk.Circle(pymunk.Body(), ChainLink.radius)
        self.shape2 = pymunk.Circle(pymunk.Body(), ChainLink.radius)
        self.shape1.mass = ChainLink.mass
        self.shape2.mass = ChainLink.mass
        self.rotary_limit = pymunk.constraints.RotaryLimitJoint(self.shape1.body, self.shape2.body, 0, 0)
        space.add(self.shape1, self.shape1.body, self.shape2, self.shape2.body, self.rotary_limit)


    def link_to(self, other):
        #NOTE: in the chain this object links to the previous one added
        slide_constraint = pymunk.constraints.SlideJoint(self.shape1.body, other.shape2.body,
                                                 Vec2d(0, 0), Vec2d(-ChainLink.radius, 0),
                                                 other.get_full_length() // 2, (other.get_full_length() + ChainLink.radius) // 2)
        rotary_constraint = pymunk.constraints.RotaryLimitJoint(self.shape1.body, other.shape2.body, -math.pi // 2, math.pi // 2)
        slide_constraint.collide_bodies = False
        rotary_constraint.collide_bodies = False
        space.add(slide_constraint, rotary_constraint)
1

There are 1 answers

0
viblo On

I have a couple of suggestions you can try:

  1. Make the ChainLink a single object. You can to that by attaching the two circle shapes to the same body with an offset. This way you simplify the setup quite a bit since you wont need constraints to keep it together.

  2. You can experiment with different values for max_bias and/or max_force on the constraints.

  3. You can try setting space.damping to a value slightly lower than 1.