2D physics simulation with Box2d and wxPython

Reinforcement learning is also showing signs that it will be hot next year with the environment of gym and DeepMind, but after all, do you often want to solve your own short (robot control) problem?

In The environment of the line tracer was made OpenAI I / F -Qiita, the line tracer ignoring the physics rule was moving around, but in the future, In order to tackle the problem of tire grip and gravitational acceleration, we have to incorporate physics into the simulation.

There are various 3D robot simulators such as Gazebo and Webot on the market, but 2D is lighter for PoC and the speed to try conceptually is faster, so I would like to recommend this first for PoC. I personally think.

That's why for a while, physics simulation with Box2D, which was popular, and animation with the classic wxPython (for me).

2016-12-18.png

Introducing Box2d

I still use the bad environment of Windows, so I had to prepare Swig first ⇒ [SWIG introduced to Windows -Volo di notte](http://chachay.hatenablog.com/entry/2016/12/ 18/090010)

Now, clone and install pybox2d!

python setup.py build
python setup.py install

Windows people may need a VC ++ build tool or something. check.

Introducing wxPython

I recently switched to Python3, so make a note of the wxPython installation. From Index of /Phoenix/snapshot \ -builds, "https://www.wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix- 3.0.3.dev2700 + c524ed1-cp35-cp35m-win_amd64.whl "Pick up the link of the wheel package that applies to you,

pip install https://www.wxpython.org/Phoenix/snapshot-builds/wxPython_Phoenix-3.0.3.dev2700+c524ed1-cp35-cp35m-win_amd64.whl

The ease of building an environment around here is the best in python.

Let's box2D

First determine the scale factor and time of the simulation

PPM = 20.0  # pixels per meter
TimeStep = 20 # ms

This is the only package to use

# -*- coding: utf-8 -*-
import Box2D 
from Box2D.b2 import (world, polygonShape, staticBody, dynamicBody)
import wx

wxPython initialization

It is the same as the previous Chachay series, but we will make it with the following configuration based on the wx.App class.

class APPWINDOW(wx.Frame):
    def __init__(self, parent=None, id=-1, title=None):
        # 1.Initialization of drawing window, timer initialization
        # 2.CloseWindow bind
        # 3.Physics engine initialization+Physical object(The ground or objects)Definition of
        # 4.Start timer loop
    def CloseWindow(self, event):
        #Simple termination process
    def OnTimer(self, event):
        # 1.Clear window clean
        # 2.Drawing physics objects
        # 3.Physical simulation for 1 step

Physics engine initialization

First, define gravity in the world, create the ground (Create Static Body), and then place the box in the air (Create Dynamic Body).

        # pybox2d world
        self.phys_world = world(gravity=(0, -10), doSleep=True)
        self.ground_body = self.phys_world.CreateStaticBody(
            position=(0, 1),
            shapes=polygonShape(box=(50, 5)),
        )
        self.dynamic_body = self.phys_world.CreateDynamicBody(position=(10, 15), angle=15)
        self.box = self.dynamic_body.CreatePolygonFixture(box=(2, 1), density=1, friction=0.3)

Timer loop processing

Simulation is super easy if you define it so far. Just call the Step function. After that, I devised only the place to draw a picture. You can use kivy or whatever you like as an animation.

This is a simulation of a box falling from the air and landing on the ground.

    def OnTimer(self, event):
        # 1.Clear window clean
        self.bdc = wx.BufferedDC(self.cdc, self.bmp)
        self.gcdc = wx.GCDC(self.bdc)
        self.gcdc.Clear()
        
        self.gcdc.SetPen(wx.Pen('white'))
        self.gcdc.SetBrush(wx.Brush('white'))
        self.gcdc.DrawRectangle(0,0,640,640)
        
        # 2.Drawing physics objects
        for body in (self.ground_body, self.dynamic_body):  # or: world.bodies
            for fixture in body.fixtures:
                shape = fixture.shape

                vertices = [(body.transform * v) * PPM for v in shape.vertices]
                vertices = [(int(v[0]), int(480 - v[1])) for v in vertices]
                
                self.gcdc.SetPen(wx.Pen(wx.Colour(50,50,50)))
                self.gcdc.SetBrush(wx.Brush(wx.Colour(colors[body.type])))
                self.gcdc.DrawPolygon(vertices)
        
        # 3.Physical simulation for 1 step
        self.phys_world.Step(TimeStep/1000, 10, 10)

The whole source

# -*- coding: utf-8 -*-
import Box2D 
from Box2D.b2 import (world, polygonShape, staticBody, dynamicBody)
import wx

PPM = 20.0  # pixels per meter
TimeStep = 20 # ms

colors = {
    staticBody: (150,150,150),
    dynamicBody: (112,146,190),
}

class APPWINDOW(wx.Frame):
    def __init__(self, parent=None, id=-1, title=None):
        # 1.Initialization of drawing window, timer initialization
        wx.Frame.__init__(self, parent, id, title)
        self.MainPanel = wx.Panel(self, size=(640, 480))
        self.MainPanel.SetBackgroundColour('WHITE')
        
        self.panel = wx.Panel(self.MainPanel, size = (640,480))
        self.panel.SetBackgroundColour('WHITE')

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        mainSizer.Add(self.panel)

        self.SetSizer(mainSizer)
        self.Fit()
        
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnTimer)
        
        self.cdc = wx.ClientDC(self.panel)
        w, h = self.panel.GetSize()
        self.bmp = wx.Bitmap(w,h)

        # 2.CloseWindow bind
        self.Bind(wx.EVT_CLOSE, self.CloseWindow)
        
        # 3.Physics engine initialization+Physical object(The ground or objects)Definition of
        self.phys_world = world(gravity=(0, -10), doSleep=True)
        self.ground_body = self.phys_world.CreateStaticBody(
            position=(0, 1),
            shapes=polygonShape(box=(50, 5)),
        )
        self.dynamic_body = self.phys_world.CreateDynamicBody(position=(10, 15), angle=15)
        self.box = self.dynamic_body.CreatePolygonFixture(box=(2, 1), density=1, friction=0.3)
        
        # 4.Start timer loop
        self.timer.Start(TimeStep)

    def CloseWindow(self, event):
        #Simple termination process
        wx.Exit()

    def OnTimer(self, event):
        # 1.Clear window clean
        self.bdc = wx.BufferedDC(self.cdc, self.bmp)
        self.gcdc = wx.GCDC(self.bdc)
        self.gcdc.Clear()
        
        self.gcdc.SetPen(wx.Pen('white'))
        self.gcdc.SetBrush(wx.Brush('white'))
        self.gcdc.DrawRectangle(0,0,640,640)
        
        # 2.Drawing physics objects
        for body in (self.ground_body, self.dynamic_body):  # or: world.bodies
            for fixture in body.fixtures:
                shape = fixture.shape

                vertices = [(body.transform * v) * PPM for v in shape.vertices]
                vertices = [(int(v[0]), int(480 - v[1])) for v in vertices]
                
                self.gcdc.SetPen(wx.Pen(wx.Colour(50,50,50)))
                self.gcdc.SetBrush(wx.Brush(wx.Colour(colors[body.type])))
                self.gcdc.DrawPolygon(vertices)
        
        # 3.Physical simulation for 1 step
        self.phys_world.Step(TimeStep/1000, 10, 10)

if __name__ == '__main__':
    app = wx.App()
    w = APPWINDOW(title='Box2D Test')
    w.Center()
    w.Show()
    app.MainLoop()

Recommended Posts

2D physics simulation with Box2d and wxPython
AtCoder ARC080 D simulation solved in Ruby and Python
Try to operate DB with Python and visualize with d3
Output 2D thermal diffusion simulation results with Python VTK
Create a 3D model viewer with PyQt5 and PyQtGraph
3D display with plotly
3D plot with matplotlib
3D or D with Py
Solving with Ruby and Python AtCoder ABC178 D Dynamic programming
Solving with Ruby and Python AtCoder ABC151 D Breadth-first search
Solve with Ruby and Python AtCoder ABC133 D Cumulative sum
Solving with Ruby and Python AtCoder AISING2020 D Iterative Squares
Try MD simulation with ANN potential using aenet and ASE
Make a 2D RPG with Ren'Py (3) -Items and Tool Shop
Solving with Ruby and Python AtCoder ABC138 D Adjacency list
Performance comparison between 2D matrix calculation and for with numpy