Conveyor belt kinematics Python

559 Views Asked by At

I'm trying to create a simulation for a conveyor belt with objects on it, to see how the behaviour would be. I am playing around but struggling with the functionality of the conveyor belt. Right now, I try to add impulse or velocity to the objects whenever they have a collision with the belt, but I have yet to get a good result. Here is just an example of how I tried to give impulse to the objects, however, whenever I add a new object (with mouse event), the new impulse only applies to the newest object.

import pygame
import pymunk
import pymunk.pygame_util
import math
import sys

pygame.init()
space = pymunk.Space()
WIDTH, HEIGHT = 1920,800
mu = 2

window = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Kinematics approximation ")

def calculate_distance(p1, p2):
    return math.sqrt((p2[1] - p1[1])**2 + (p2[0] - p1[0])**2)

def calculate_angle(p1, p2):
    return math.atan2(p2[1] - p1[1], p2[0] - p1[0])

def draw(space, window,draw_options):
    window.fill("white")
    space.debug_draw(draw_options) 
    pygame.display.update()
    



def create_belt(space):
    belts = [
        [(0,500), (600,500), 5],
        [(600,500), (800,400), 6],
        [(800,400), (WIDTH,400), 6]
    ]

    for pos_1, pos_2, width in belts:
        shape = pymunk.Segment(space.static_body,pos_1, pos_2, width) 
        shape.body.position = 0,0
        shape.friction = mu
        space.add(shape)





def create_object(space, mass, pos):
    body = pymunk.Body(body_type = pymunk.Body.DYNAMIC)
    body.position = pos
    shape = pymunk.Poly.create_box(body, size = (20,10))
    shape.mass = mass
    shape.color = (255,0,0, 100)
    shape.body.friction = mu
    #shape.body.velocity = (80,0)
    space.add(body,shape)
    return shape





def run(window, width, height):
    run = True
    clock = pygame.time.Clock()
    fps = 240
    dt = 1/fps

    space = pymunk.Space()
    space.gravity = (0,981)

 
    create_belt(space)





    def coll_begin(arbiter, space, data):
        print(arbiter)
        #angle = calculate_angle(*)
        #force = calculate_distance(*line) * 50
        #fx = math.cos(angle) * force
        #fy = math.sin(angle) * force

        return True

    def coll_pre(arbiter, space, data):
        for object in objects:
               object.body.apply_impulse_at_local_point((12, 0),(0,0))
        return True

    def coll_post(arbiter, space, data):
        #print(velocity_at_local_point)
        pass

    def coll_separate(arbiter, space, data):
        pass

    handler = space.add_default_collision_handler()
    handler.begin = coll_begin
    handler.pre_solve = coll_pre
    handler.post_solve = coll_post
    handler.separate = coll_separate
    

    draw_options = pymunk.pygame_util.DrawOptions(window)

    pressed_pos = None

    while run:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit()
                sys.exit()    
        
            if event.type == pygame.MOUSEBUTTONDOWN:
                pressed_pos = pygame.mouse.get_pos()
                objects = []
                objects.append(create_object(space,10, pressed_pos))
                

        draw(space,window, draw_options)
        space.step(dt)
        clock.tick(fps)
        

    pygame.quit()
    sys.exit()



if __name__ == "__main__":
    run(window, WIDTH, HEIGHT)
1

There are 1 best solutions below

2
The_spider On

There are two problems in your code:

First, you are resetting the objects list every time a new object is created. This means that when you apply impulse to all objects in the objects list, only impulse is applied to the last object created. This can be easily fixed by moving the objects = [] line out of the game loop.

After fixing this, you might have noticed that all objects are getting impulse, whether they touch the belt or not. This is caused by the following: whenever any object is colliding with the conveyor belt, you apply an impulse to all objects. This causes all objects to move forward, not only the one that collides with the conveyor belt.

You can fix the two above errors using the Arbiter.shapes tuple that is provided as an argument to the coll_pre function. This tuple contains the two objects colliding with eachother, and thus also the object you want to move. The second object is the new object you want to move. This change also makes the objects list useless, as you don't use it anymore to apply the impulse.

Here's the code:

def coll_pre(arbiter, space, data):
    print(arbiter)
    arbiter.shapes[1].body.apply_impulse_at_local_point((12, 0),(0,0))
    return True

I would also recommend increasing the impulse given, as now the objects travel to slowly to resist to gravity on the inclined part of the belt. A value of 40 worked for me.