1

我想使用 Pymunk 生成一种遗传算法,其目的是向目标扔球。

我知道如何做大部分遗传算法的事情,但 Pymunk 对我来说是新的。这个想法是球将从一个固定的位置开始,比如离地1.5m,水平距离篮筐20m。假设球重1公斤。然后,球将以一定的角度以一定的力发射。适应度将是它在飞行过程中最接近目标中心的位置。

因此,例如,人口中的一个人可能看起来像[10.0, 5.0]10.0 表示水平应用 10N,而 5.0 表示垂直应用 5N。

我不希望它在我能看到的窗口中运行每个模拟,但我希望能够打开它,这样我就可以在一定数量的世代后或达到一定的适应度后为最适合的个体运行它等级。这是我的意思https://prnt.sc/pn1hoc的直观表示。

我已经尽力使用 pymunk 文档自己解决它,但我没有发现它很有帮助,所以我相当卡住了。

我所知道的是我需要启动一个空间

space = pymunk.Space()
space.gravity = (0.0, -900.0)  # not sure what this means?

然后在那个空间创造一个球

def add_ball(space):
    mass = 1
    radius = 14
    moment = pymunk.moment_for_circle(mass, 0, radius)
    body = pymunk.Body(mass, moment)
    body.position = 100, 150
    shape = pymunk.Circle(body, radius)
    space.add(body, shape)
    return shape

虽然这个球会开始下落,但不知道如何让它静态开始。

然后生成目标我应该做类似的事情

def add_target(space):
    body = pymunk.Body(body_type = pymunk.Body.STATIC)
    body.position = (500, 300)
    target = pymunk.Segment(body, (-150, 0), (-150, 50), 5)
    space.add(target)
    return target

我真的很感谢一些帮助,以便我可以从我的遗传算法中运行模拟,但也可以切换它们以在窗口中运行。

谢谢。

编辑:

我已经弄清楚了如何放置地板,这是我到目前为止的所有代码

import sys, random
import pygame
from pygame.locals import *
import pymunk
import pymunk.pygame_util


def add_ball(space):
    mass = 1
    radius = 14
    moment = pymunk.moment_for_circle(mass, 0, radius)
    body = pymunk.Body(mass, moment)
    body.position = (50, 150)
    shape = pymunk.Circle(body, radius)
    space.add(body, shape)
    return shape


def add_floor(space):
    body = pymunk.Body(body_type = pymunk.Body.STATIC)
    body.position = (0, 0)
    target = pymunk.Segment(body, (0, 5), (600, 5), 5)
    space.add(target)
    return target


def add_target(space):
    body = pymunk.Body(body_type = pymunk.Body.STATIC)
    body.position = (700, 300)
    target = pymunk.Segment(body, (-150, 0), (-150, 50), 5)
    space.add(target)
    return target


def main():
    pygame.init()
    screen = pygame.display.set_mode((600, 600))
    clock = pygame.time.Clock()

    space = pymunk.Space()
    space.gravity = (0.0, -900.0)

    target = add_target(space)
    floor = add_floor(space)
    balls = []
    draw_options = pymunk.pygame_util.DrawOptions(screen)

    ball_count = 0
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                sys.exit(0)
            elif event.type == KEYDOWN and event.key == K_ESCAPE:
                sys.exit(0)

        if ball_count < 1:
            ball_shape = add_ball(space)
            balls.append(ball_shape)
            ball_count += 1

        space.step(1/50.0)

        screen.fill((255,255,255))
        space.debug_draw(draw_options)

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

if __name__ == '__main__':
    main()
4

1 回答 1

1

我认为这个问题有几个部分。我的第一条建议是从尽可能小的事情开始,然后从那里扩展。当有几个部分令人困惑或您不知道如何解决时,这对于各种问题都非常有效。

这里有一些提示:

你需要弄清楚你想如何衡量健康。一些例子:

  1. 一定时间后测量球到目标的距离。这应该很容易实现,因为您只需要在模拟结束时测量一次距离。但是,可能很难决定应该是什么时间。

  2. 在一次“投掷”期间测量球到目标的最小距离。我想这比 1 好,但取决于你想训练什么。在这种情况下,您可以在每次调用 step 函数时测量距离,并跟踪其最小值。我想我会从这种测量健康的方法开始,因为它相当简单,但感觉还可以。

您可能希望将模拟与其显示分开。因此,您应该尝试编写执行一轮/“投掷”的函数并将其模拟到最后。

例如,这是一个简单的示例,我使用了一个方法 do_one_throw,该方法以投掷脉冲作为输入,并在 1000 个模拟步骤内返回到目标的最小距离:

def add_ball(space):
    mass = 1
    radius = 14
    moment = pymunk.moment_for_circle(mass, 0, radius)
    body = pymunk.Body(mass, moment)
    body.position = (50, 150)
    shape = pymunk.Circle(body, radius)
    space.add(body, shape)
    return shape


def add_floor(space):
    body = pymunk.Body(body_type = pymunk.Body.STATIC)
    body.position = (0, 0)
    target = pymunk.Segment(body, (0, 5), (600, 5), 5)
    space.add(target)
    return target


def add_target(space):
    body = pymunk.Body(body_type = pymunk.Body.STATIC)
    body.position = (700, 300)
    target = pymunk.Segment(body, (-150, 0), (-150, 50), 5)
    space.add(target)
    return target

def do_one_throw(impulse):
    space = pymunk.Space()
    space.gravity = (0.0, -900.0)

    ball_shape = add_ball(space)
    add_floor(space)
    target_shape = add_target(space)

    ball_shape.body.apply_impulse_at_local_point(impulse)

    min_distance_to_target = 1000000
    steps = 1000
    for _ in range(steps):
        space.step(1/50.0)
        distance_to_target = target_shape.body.position.get_distance(ball_shape.body.position)
        if distance_to_target < min_distance_to_target:
            min_distance_to_target = distance_to_target
    return min_distance_to_target

一旦你掌握了这些基础知识,你就可以添加更多花哨的功能

  • 例如,您可以通过使用 prints 打印出模拟中发生的事情来轻松开始。
  • 您在空间上设置的重力是重力矢量。请注意,pymunk 是独立于单元的,因此只要您传入的所有内容都是一致的,它就应该正确:http ://www.pymunk.org/en/latest/overview.html#mass-weight-and-units
  • 您可以优化我上面的快速而肮脏的代码,例如通过一些更智能的逻辑来决定何时结束模拟,这样如果球已经出局并且没有机会回来,您就不需要运行所有 1000 步。
  • 你可以添加pygame绘图代码,看起来你已经走了很长一段路
  • 改进对目标距离的测量,现在我通过测量物体之间的距离做出了最简单的解决方案,但您可能需要形状之间的距离,这样如果形状发生碰撞,距离为 0。
于 2019-10-25T09:18:13.767 回答