Как исправить несбалансированную кинематику падения в Pymunk

Почему физика неверна в следующем примере Pymunk?

from __future__ import print_function
import sys
from math import pi

import pygame
from pygame.locals import USEREVENT, QUIT, KEYDOWN, KEYUP, K_s, K_r, K_q, K_ESCAPE, K_UP, K_DOWN, K_LEFT, K_RIGHT
from pygame.color import THECOLORS

import pymunk
from pymunk import Vec2d
import pymunk.pygame_util

LEG_GROUP = 1

class Simulator(object):

    def __init__(self):
        self.display_flags = 0
        self.display_size = (600, 600)

        self.space = pymunk.Space()
        self.space.gravity = (0.0, -1900.0)
        self.space.damping = 0.999 # to prevent it from blowing up.

        # Pymunk physics coordinates start from the lower right-hand corner of the screen.
        self.ground_y = 100
        ground = pymunk.Segment(self.space.static_body, (5, self.ground_y), (595, self.ground_y), 1.0)
        ground.friction = 1.0
        self.space.add(ground)

        self.screen = None

        self.draw_options = None

    def reset_bodies(self):
        for body in self.space.bodies:
            if not hasattr(body, 'start_position'):
                continue
            body.position = Vec2d(body.start_position)
            body.force = 0, 0
            body.torque = 0
            body.velocity = 0, 0
            body.angular_velocity = 0
            body.angle = body.start_angle

    def draw(self):
        ### Clear the screen
        self.screen.fill(THECOLORS["white"])

        ### Draw space
        self.space.debug_draw(self.draw_options)

        ### All done, lets flip the display
        pygame.display.flip()

    def main(self):

        pygame.init()
        self.screen = pygame.display.set_mode(self.display_size, self.display_flags)
        width, height = self.screen.get_size()
        self.draw_options = pymunk.pygame_util.DrawOptions(self.screen)

        def to_pygame(p):
            """Small hack to convert pymunk to pygame coordinates"""
            return int(p.x), int(-p.y+height)
        def from_pygame(p):
            return to_pygame(p)

        clock = pygame.time.Clock()
        running = True
        font = pygame.font.Font(None, 16)

        # Create the torso box.
        box_width = 50
        box_height = 100
        # leg_length = 100
        leg_length = 125
        leg_thickness = 2

        leg_shape_filter = pymunk.ShapeFilter(group=LEG_GROUP)

        # Create torso.
        mass = 200
        points = [(-box_width/2, -box_height/2), (-box_width/2, box_height/2), (box_width/2, box_height/2), (box_width/2, -box_height/2)]
        moment = pymunk.moment_for_poly(mass, points)
        body1 = pymunk.Body(mass, moment)
        body1.position = (self.display_size[0]/2, self.ground_y+box_height/2+leg_length)
        body1.start_position = Vec2d(body1.position)
        body1.start_angle = body1.angle
        shape1 = pymunk.Poly(body1, points)
        shape1.filter = leg_shape_filter
        shape1.friction = 0.8
        shape1.elasticity = 0.0
        self.space.add(body1, shape1)

        # Create leg extending from the right to the origin.
        mass = 10
        points = [
            (leg_thickness/2, -leg_length/2),
            (-leg_thickness/2, -leg_length/2),
            (-leg_thickness/2, leg_length/2),
            (leg_thickness/2, leg_length/2)
        ]
        moment = pymunk.moment_for_poly(mass, points)
        body2 = pymunk.Body(mass, moment)
        body2.position = (self.display_size[0]/2-box_width/2+leg_thickness/2, self.ground_y+leg_length/2)
        body2.start_position = Vec2d(body2.position)
        body2.start_angle = body2.angle
        shape2 = pymunk.Poly(body2, points)
        shape2.filter = leg_shape_filter
        shape2.friction = 0.8
        shape2.elasticity = 0.0
        self.space.add(body2, shape2)

        # Link bars together at end.
        pj = pymunk.PivotJoint(body1, body2, (self.display_size[0]/2-box_width/2, self.ground_y+leg_length))
        self.space.add(pj)

        # Attach the foot to the ground in a fixed position.
        # We raise it above by the thickness of the leg to simulate a ball-foot. Otherwise, the default box foot creates discontinuities.
        pj = pymunk.PivotJoint(self.space.static_body, body2, (self.display_size[0]/2-box_width/2, self.ground_y+leg_thickness))
        self.space.add(pj)

        # Actuate the bars via a motor.
        motor_joint = pymunk.SimpleMotor(body1, body2, 0)
        motor_joint.max_force = 1e10 # mimicks default infinity
        # motor_joint.max_force = 1e9
        # motor_joint.max_force = 1e7 # too weak, almost no movement
        self.space.add(motor_joint)

        # Add hard stops to leg pivot so the leg can't rotate through the torso.
        hip_limit_joint = pymunk.RotaryLimitJoint(body1, body2, -pi/4., pi/4.) # -45deg:+45deg
        self.space.add(hip_limit_joint)

        last_body1_pos = None
        last_body1_vel = None
        simulate = False
        while running:
            # print('angles:', body1.angle, body2.angle)
            # print('torso force:', body1.force)
            print('body1.position: %.02f %.02f' % (body1.position.x, body1.position.y))

            current_body1_vel = None
            if last_body1_pos:
                current_body1_vel = body1.position - last_body1_pos
                print('current_body1_vel: %.02f %.02f' % (current_body1_vel.x, current_body1_vel.y))

            current_body1_accel = None
            if last_body1_vel:
                current_body1_accel = current_body1_vel - last_body1_vel
                print('current_body1_accel: %.02f %.02f' % (current_body1_accel.x, current_body1_accel.y))

            servo_angle = (body1.angle - body2.angle) * 180/pi # 0 degrees means leg is angled straight down
            servo_cw_enabled = servo_angle > -45
            servo_ccw_enabled = servo_angle < 45

            for event in pygame.event.get():
                if event.type == QUIT or (event.type == KEYDOWN and event.key in (K_q, K_ESCAPE)):
                    sys.exit(0)
                elif event.type == KEYDOWN and event.key == K_s:
                    # Start/stop simulation.
                    simulate = not simulate

            last_body1_pos = Vec2d(body1.position)
            if current_body1_vel:
                last_body1_vel = Vec2d(current_body1_vel)

            self.draw()

            ### Update physics
            fps = 50
            iterations = 25
            dt = 1.0/float(fps)/float(iterations)
            if simulate:
                for x in range(iterations): # 10 iterations to get a more stable simulation
                    self.space.step(dt)

            pygame.display.flip()
            clock.tick(fps)

if __name__ == '__main__':
    sim = Simulator()
    sim.main()

Это визуализирует коробку, расположенную поверх тонкой ножки. Нога соединена с коробкой шарнирным соединением, а с землей - другим шарнирным соединением. Однако нога прикреплена к коробке не по центру влево, поэтому центр тяжести не уравновешен. В реальном мире такая установка привела бы к падению коробки вправо. Однако, когда вы запускаете этот код (и нажимаете «s», чтобы начать), он показывает, что поле опрокидывается влево. Почему это?

Я пытался отрегулировать массу (большая масса ящика, малая масса ноги), центр тяжести ящика и настроить точки крепления суставов, но, похоже, ничего не меняет результат. Что я делаю не так?

Я хочу использовать это для моделирования явлений реального мира, но пока я не смогу воспроизвести явления реального мира, я застрял.


person Cerin    schedule 01.04.2018    source источник
comment
Вы вставили правильный код? Текущий, кажется, не вызывает какую-либо функцию рисования?   -  person viblo    schedule 02.04.2018
comment
@viblo Извините, я обновил его.   -  person Cerin    schedule 03.04.2018


Ответы (1)


Кажется, это потому, что форма ноги сталкивается с формой основания.

Самый простой способ сделать так, чтобы они не сталкивались, — это немного раздвинуть их. Например, сделайте ногу немного короче, чтобы она не касалась земли.

Другое решение - сделать то же, что и в другом вопросе, игнорировать столкновения между ногой и землей. Для этого вы можете настроить фильтр формы, но поскольку вы, вероятно, хотите, чтобы коробка не сталкивалась с ногой, и в то же время подсчитывали столкновения между коробкой и землей, я думаю, вам нужно использовать категории/маски формы фильтровать, как описано здесь: http://www.pymunk.org/en/latest/pymunk.html#pymunk.ShapeFilter

person viblo    schedule 03.04.2018