Super Kawaii Cute Cat Kaoani PyBullet 물리엔진 튜토리얼

연구/컴퓨터 그래픽스

PyBullet 물리엔진 튜토리얼

치킨고양이짱아 2022. 2. 28. 20:45
728x90
728x90

 

1. Bullet이란?

불릿 물리 라이브러리(Bullet Physics Library, 간단히 불릿)는 충돌 감지, 강체/연체 물리 시뮬레이션을 위한 전문 물리 라이브러리이다.

불릿은 하복 물리엔진에서 일했던 개발자가 만들었으며, 딱딱한 블록같은 소재의 시뮬레이션 뿐 아니라, 연체 소재의 시뮬레이션을 연속적으로 가능하게 하는 기능을 가지고 있다

 

2. 설치 방법

pip3 install pybullet

이 입력어를 통해 아주 간단하게 설치된다

 

3. 기능 및 사용방법

먼저 pybullet을 사용하기 위해 import 해주자.

import pybullet as p

 

그리고 PyBullet에서 제공하는 유용한 object들이 담겨있는 pybullet_data를 사용하기 위해 pybullet_data

import 해주어야한다.

import pybullet_data

 

Pybullet의 loadURDF 함수를 사용하면 URDF file에서 로봇과 같은 물체의 kinematic descriptions을 load할 수 있는데

추가하고 싶은 object를 찾기 위한 path를 생성하기 위해서는 os를 import 해줘야한다.

 

 

그리고 pybullet.connect(pybullet.GUI)를 실행해 시뮬레이션 월드를 만들 수 있다. 인자로 준 건 연결 방식인데

연결방식에는 p.GUIp.DIRECT 가 있다. 여기서는 화면을 바로 볼 수 있는 GUI를 선택했다.

 

 

pybullet.stepSimulation()를 실행할때마다 simulation이 한 step씩 진행된다.

 

예시 코드는 다음과 같다.

import os
import pybullet as p
import pybullet_data

p.connect(p.GUI)
pandaUid = p.loadURDF(os.path.join(pybullet_data.getDataPath(), "franka_panda/panda.urdf"),useFixedBase=True)

while True:
    p.stepSimulation()

위의 코드에서는 pybullet_data에 담겨있는 object들 중에서 panda model을 사용하였다.

위의 코드를 실행하면 다음과 같은 화면이 나온다.

조금 더 복잡한 문제!

심화로 table 위에 robot을 올려놓고 robot이 tray 안의 bin을 picking하는 problem을 다뤄보자.

먼저 robot과 tray를 올려놓을 table urdf file을 load해야한다.

tableUid = p.loadURDF(os.path.join(urdfRootPath, "table/table.urdf"),basePosition=[0.5,0,-0.65])

 

그리고 bin이 담길 tray를 load한다.

trayUid = p.loadURDF(os.path.join(urdfRootPath, "tray/traybox.urdf"),basePosition=[0.65,0,0])

 

pybullet_data에 사용가능한 object들이 많다고 했었는데, 여기에는 grasping에 사용할 수 있는 random object들도 많다.

그 중 하나를 tray 위에 추가해보자.

p.setGravity(0,0,-10)
objectUid = p.loadURDF(os.path.join(urdfRootPath, "random_urdfs/000/000.urdf"), basePosition=[0.7,0,0.1])

 

근데 base Position 세팅한 값을 보면 random object가 tray보다 높이 있다.

이 random object가 tray에 떨어질 수 있게 위의 코드처럼 gravity를 setting 해주었다.

카메라는 다음과 같이 세팅 할 수 있다.

p.resetDebugVisualizerCamera(cameraDistance=1.5, cameraYaw=0, cameraPitch=-40, cameraTargetPosition=[0.55,-0.35,0.2])


이제 우리는 robot을 움직여 물체를 grasp 할 준비가 되었다!

pybullet.setJointMotorControl2([objectUid],[jointIndex],[controller],[targetPosition])

를 사용하면 robot의 joint를 control하고 움직일 수 있다.

 

joint index는 description file(ex: urdf) 을 기준으로 한다.

예를 들어, 우리가 다루고 있는 이 로봇의 두 손가락(grid fingers)은 각각 joint 9, joint 10으로 정의되어 있으며

개별적으로 컨트롤 할 수 있다.

 

 

그리고 아래의 코드는 robot을 물체의 상단에 위치하게 한다.

p.setJointMotorControl2(pandaUid, 0, p.POSITION_CONTROL,0)
p.setJointMotorControl2(pandaUid, 1, p.POSITION_CONTROL,math.pi/4.)
p.setJointMotorControl2(pandaUid, 2, p.POSITION_CONTROL,0)
p.setJointMotorControl2(pandaUid, 3, p.POSITION_CONTROL,-math.pi/2.)
p.setJointMotorControl2(pandaUid, 4, p.POSITION_CONTROL,0)
p.setJointMotorControl2(pandaUid, 5, p.POSITION_CONTROL,3*math.pi/4)
p.setJointMotorControl2(pandaUid, 6, p.POSITION_CONTROL,-math.pi/4.)
p.setJointMotorControl2(pandaUid, 9, p.POSITION_CONTROL, 0.04)
p.setJointMotorControl2(pandaUid, 10, p.POSITION_CONTROL, 0.04)

위의 코드를 while loop에서 robot을 물체의 상단에 위치시키기 전에 집어넣어야한다. (무슨 역할을 하는지는 잘 모르겠다..)

p.configureDebugVisualizer(p.COV_ENABLE_SINGLE_STEP_RENDERING)


이제 우리는 object를 pick up할 준비가 되었다!

grasping process를 4가지의 서로 다른 states로 구현하게 되는데

  • 첫번째 state(current_state == 0)는 robot이 물체 위로 이동한다음 fingers를 벌린 상태이다.
  • 두번째 state(current_state == 1) object level까지 robot의 팔이 내려간 상태이다.
  • 세번째 state(current state == 2)는 fingers를 close한 상태이다.
  • 네번째 state(current state == 3)는 물체를 들어올린 상태이다.

위의 각 상태는 1 unit duration인 것으로 가정하며, 위의 단계들을 계속해서 반복하게 된다. pybullet.setTimestep을 사용해 각 step이 control_dt = 1.240동안 지속되게 할 것이다.

 

 

pybullet.setTimestep 함수를 사용하면 물리 시뮬레이션에서 한번 업데이트할 때 몇 초 업데이트 할 지를 설정할 수 있다.

타임스텝을 1/200 으로 설정하면 200번 업데이트해야 그 시뮬레이션 세계에서 1초가 흐른다.(초당 200프레임).

숫자가 작을수록 정밀하게 시뮬레이션되지만, 같은 시간만큼 시뮬레이션하기 위해 더 많은 step이 필요하다.


완성된 코드는 다음과 같다.

import os
import pybullet as p
import pybullet_data
import math

p.connect(p.GUI)
urdfRootPath=pybullet_data.getDataPath()
pandaUid = p.loadURDF(os.path.join(urdfRootPath, "franka_panda/panda.urdf"),useFixedBase=True)

tableUid = p.loadURDF(os.path.join(urdfRootPath, "table/table.urdf"),basePosition=[0.5,0,-0.65])

trayUid = p.loadURDF(os.path.join(urdfRootPath, "tray/traybox.urdf"),basePosition=[0.65,0,0])

p.setGravity(0,0,-10)
objectUid = p.loadURDF(os.path.join(urdfRootPath, "random_urdfs/000/000.urdf"), basePosition=[0.7,0,0.1])

p.resetDebugVisualizerCamera(cameraDistance=1.5, cameraYaw=0, cameraPitch=-40, cameraTargetPosition=[0.55,-0.35,0.2])

state_durations = [1,1,1,1]
control_dt = 1./240.
p.setTimestep = control_dt
state_t = 0.
current_state = 0

while True:
    state_t += control_dt
    p.configureDebugVisualizer(p.COV_ENABLE_SINGLE_STEP_RENDERING) 
    if current_state == 0:
        p.setJointMotorControl2(pandaUid, 0, 
                        p.POSITION_CONTROL,0)
        p.setJointMotorControl2(pandaUid, 1, 
                        p.POSITION_CONTROL,math.pi/4.)
        p.setJointMotorControl2(pandaUid, 2, 
                        p.POSITION_CONTROL,0)
        p.setJointMotorControl2(pandaUid, 3, 
                        p.POSITION_CONTROL,-math.pi/2.)
        p.setJointMotorControl2(pandaUid, 4, 
                        p.POSITION_CONTROL,0)
        p.setJointMotorControl2(pandaUid, 5, 
                        p.POSITION_CONTROL,3*math.pi/4)
        p.setJointMotorControl2(pandaUid, 6, 
                        p.POSITION_CONTROL,-math.pi/4.)
        p.setJointMotorControl2(pandaUid, 9, 
                        p.POSITION_CONTROL, 0.08)
        p.setJointMotorControl2(pandaUid, 10, 
                        p.POSITION_CONTROL, 0.08)
    if current_state == 1:
        p.setJointMotorControl2(pandaUid, 1, 
                        p.POSITION_CONTROL,math.pi/4.+.15)
        p.setJointMotorControl2(pandaUid, 3, 
                        p.POSITION_CONTROL,-math.pi/2.+.15)
    if current_state == 2:
        p.setJointMotorControl2(pandaUid, 9, 
                        p.POSITION_CONTROL, 0.0, force = 200)
        p.setJointMotorControl2(pandaUid, 10, 
                        p.POSITION_CONTROL, 0.0, force = 200)
    if current_state == 3:
        p.setJointMotorControl2(pandaUid, 1, 
                        p.POSITION_CONTROL,math.pi/4.-1)
        p.setJointMotorControl2(pandaUid, 3, 
                        p.POSITION_CONTROL,-math.pi/2.-1)

    if state_t >state_durations[current_state]:
        current_state += 1
        if current_state >= len(state_durations):
            current_state = 0
        state_t = 0
    p.stepSimulation()

 

 

참고한 자료:

https://www.oss.kr/news/show/8f7487ae-fae9-4399-b255-19f4d5ed335b

 

[알아봅시다] 물리엔진 - 공개SW 포털

2016년 01월 04일 (월) ⓒ 디지털타임스, 이형근 기자 bass007@dt.co.kr 가상으로 만들어진 중력·속도 실시간 구현 강체동역학·유동역학 등 물리시스템 시뮬...

www.oss.kr

https://www.etedal.net/2020/04/pybullet-panda.html

 

OpenAI Gym Environments with PyBullet (Part 1)

Many of the standard environments for evaluating continuous control reinforcement learning algorithms are built using the MuJoCo physics engine, a paid and licensed software. Bullet Physics provides a free and open source alternative to physics simulation.

www.etedal.net

https://blog.naver.com/PostView.nhn?blogId=einsbon&logNo=221549883110&parentCategoryNo=&categoryNo=&viewDate=&isShowPopularPosts=false&from=postView 

 

PyBullet 물리 시뮬레이션 기초 사용법 강좌

PyBullet 물리 시뮬레이션 기초 사용법 정리한 글입니다. 흠... 시뮬레이션에서 조인트와 로봇 만드는 방...

blog.naver.com

 

728x90
728x90