Simulasi ruang sederhana menggunakan Python dan Box2D

Halo Habr.

Artikel ini terinspirasi oleh publikasi Modeling the Universe baru-baru ini , di mana penulis menunjukkan simulasi yang sangat menarik dari berbagai fenomena kosmik. Namun, kode yang disajikan di sana tidak mudah bagi pemula. Saya akan menunjukkan kepada Anda bagaimana melakukan simulasi fisik menggunakan mesin Box2D dengan menulis hanya beberapa baris kode.

Saya akan mengambil kesempatan untuk membuat kesalahan, tetapi ini adalah deskripsi pertama Box2D untuk Python di Habré, kami mengisi celah ini.



Bagi mereka yang tertarik dengan cara kerjanya, detailnya ada di bawah potongan.

Box2D adalah perpustakaan lintas platform gratis yang dibuat oleh Blizzard Erin Catto. Perpustakaan diperkenalkan pada tahun 2007, dan hari ini telah diangkut ke hampir semua platform. Ada port untuk Python juga, deskripsinya agak membingungkan, tetapi saya berharap bahwa dengan bantuan artikel ini semuanya akan menjadi lebih jelas.

pengantar


Pustaka pybox2d terdiri dari dua komponen - Box2D sendiri, pustaka lintas platform untuk pemodelan fisik, dan modul render terpisah yang disebut Kerangka. Rendering diperlukan jika kita ingin melihat objek yang dibuat di layar, yang cukup nyaman untuk simulasi kita. Kelas Framework dapat menggunakan berbagai metode output (lebih lanjut di sini ), kita akan menggunakan pygame. Jika perpustakaan pygame diinstal, itu "diambil" secara otomatis, dan tidak ada lagi yang perlu dilakukan. Untuk menginstal, cukup masukkan perintah pip install Box2D pygame .

Program berjalan terkecil menggunakan Box2D ditunjukkan di bawah ini. Kode ini adalah lintas platform, dan akan bekerja di mana saja, baik di Linux maupun Windows.

from Box2D.examples.framework import Framework
from Box2D import *

class Simulation(Framework):
    def __init__(self):
        super(Simulation, self).__init__()

        # Ground body
        self.world.CreateBody(shapes=b2LoopShape(vertices=[(20, 0), (20, 40), (-20, 40), (-20, 0)]))
        # Dynamic body
        circle = b2FixtureDef(shape=b2CircleShape(radius=2), density=1, friction=1.0, restitution=0.5)
        self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0,30), fixtures=circle, linearVelocity=(5, 0))

    def Step(self, settings):
        super(Simulation, self).Step(settings)

if __name__ == "__main__":
    Simulation().run()

Seperti yang Anda lihat, kami membuat kelas Simulasi yang mewarisi dari Kerangka yang sudah disebutkan. Selanjutnya, kita membuat dua objek dengan memanggil metode CreateBody . Yang pertama adalah objek statis yang mendefinisikan batas-batas dunia kita. Objek kedua adalah tipe b2_dynamicBody, parameter yang tersisa (bentuk, ukuran, kepadatan, koefisien gesekan, kecepatan awal) jelas dari kode. Fungsi Langkah dipanggil setiap kali selama simulasi, kami akan menggunakan ini di masa depan. Jika UI tidak diperlukan, misalnya, kami melakukan backend untuk server, maka tentu saja, kelas Kerangka dapat dihilangkan, tetapi bagi kami itu cukup nyaman.

Itu saja, jalankan program dan lihat simulasi yang sudah selesai:



Seperti yang Anda lihat, kami baru saja membuat dua objek dan menentukan parameternya. Semuanya bekerja "di luar kotak" - gravitasi, gesekan, elastisitas, interaksi benda, dll. Berdasarkan ini, kita dapat melanjutkan dengan simulasi "ruang" kami.

Kami meluncurkan "satelit"


Sayangnya, tidak ada dukungan bawaan untuk gravitasi Newtonian di Box2D, Anda harus menambahkannya sendiri dengan menambahkan fungsi Langkah. Untuk pengujian pertama, kita akan membuat dua benda - sebuah planet, dan sebuah satelit berputar di sekitarnya.

Kode sumber secara keseluruhan:

from Box2D import *
from Box2D.examples.framework import Framework


class Simulation(Framework):
    def __init__(self):
        super(Simulation, self).__init__()

        # Default gravity disable
        self.world.gravity = (0.0, 0.0)
        # Gravity constant
        self.G = 100

        # Planet
        circle = b2FixtureDef(shape=b2CircleShape(radius=5), density=1, friction=0.5, restitution=0.5)
        self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0,0), fixtures=circle)

        # Satellite
        circle_small = b2FixtureDef(shape=b2CircleShape(radius=0.2), density=1, friction=0.5, restitution=0.2)
        self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0, 10), fixtures=circle_small, linearVelocity=(20, 0))

    def Step(self, settings):
        super(Simulation, self).Step(settings)

        # Simulate the Newton's gravity
        for bi in self.world.bodies:
            for bk in self.world.bodies:
                if bi == bk:
                    continue

                pi, pk = bi.worldCenter, bk.worldCenter
                mi, mk = bi.mass, bk.mass
                delta = pk - pi
                r = delta.length
                if abs(r) < 1.0:
                    r = 1.0

                force = self.G * mi * mk / (r * r)
                delta.Normalize()
                bi.ApplyForce(force * delta, pi, True)

if __name__ == "__main__":
    Simulation().run()

Seperti yang Anda lihat, kami "mematikan" gravitasi standar dengan mengatur parameter self.world.gravity ke 0. Kami juga menambahkan parameter G, ini adalah "konstanta gravitasi" dari dunia virtual kami, yang digunakan dalam perhitungan metode Langkah. Kami juga menciptakan dua objek - satelit dan planet. Penting untuk mencatat parameter kepadatan dan jari-jari. Menurut parameter ini, pustaka Box2D sendiri menghitung massa yang digunakan dalam perhitungan. Untuk menghitung gaya interaksi, rumus "sekolah" yang biasa dari hukum gravitasi Newton digunakan :



Sekarang kita mulai simulasi. Kami tidak mencapai kecepatan kosmik pertama, dan meskipun satelit masih mengelilingi seluruh planet, sulit untuk menyebutnya "penerbangan": Kami



meningkatkan kecepatan dengan mengubah garis kode ke linearVelocity = (28, 0):



"Satelit" kami telah berhasil memasuki orbit di sekitar "planet"! Jika kecepatan semakin meningkat, orbitnya akan menjadi elips:



Akhirnya, kami akan menggambarkan sesuatu yang lebih mirip dengan "tata surya" kami, menambahkan tiga planet dengan ukuran berbeda dalam orbit berbeda:

circle_small = b2FixtureDef(shape=b2CircleShape(radius=0.2), density=1, friction=0.5, restitution=0.2)
circle_medium = b2FixtureDef(shape=b2CircleShape(radius=0.3), density=1, friction=1.0, restitution=0.5)
self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0, 6), fixtures=circle_small, linearVelocity=(37, 0))
self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0, 10), fixtures=circle_small, linearVelocity=(28, 0))
self.world.CreateBody(type=b2_dynamicBody, position=b2Vec2(0, 15), fixtures=circle_medium, linearVelocity=(22, 0))

Hasil:



Kita melihat bahwa semakin jauh planet ini berasal dari "matahari", semakin lama periode revolusinya (hukum ke-3 Kepler). Sayangnya, mesin Box2D tidak memungkinkan menggambar trek gerak di layar, sehingga sulit untuk "melihat" hukum 1 dan 2 Keppler, tetapi Anda dapat memastikan bahwa itu juga diterapkan.

Kesimpulan


Seperti yang Anda lihat, dengan Box2D, simulasi sederhana dapat dilakukan dengan sedikit usaha. Tentu saja, mesin ini masih merupakan mesin permainan, bukan yang ilmiah, jadi Anda tidak boleh mengharapkan darinya simulasi yang benar dari tabrakan galaksi atau perluasan materi selama Big Bang. Tetapi beberapa pola cukup menarik untuk ditonton.

Semua dikandung dalam satu potong tidak cocok. Jika estimasi positif, pada bagian kedua dimungkinkan untuk mempertimbangkan lebih banyak contoh non-sepele.

All Articles