Skip to content

PyBullet VSCode dev environment

Simulation / PyBullet

1
2
3
4
5
6
7
8
9
.
├── .devcontainer
│   ├── devcontainer.json
│   └── Dockerfile
├── docker-compose.yml
├── README.md
├── requirements.txt
└── .vscode
    └── settings.json
.devcontainer/devcontainer.json
{
    "name": "PyBullet",
    "dockerComposeFile": "../docker-compose.yaml",
    "service": "dev",
    "workspaceFolder": "/workspace",
    "shutdownAction": "stopCompose",
    "features": {},
    "customizations": {
        "vscode": {
            "extensions": [
                "ms-python.python",
                "ms-python.flake8",
                "ms-python.black-formatter",
                "ms-toolsai.jupyter",
                "ms-vscode.cmake-tools",
                "ms-vscode.cpptools",
                "twxs.cmake"
            ],
            "settings": {
                "python.defaultInterpreterPath": "/usr/local/bin/python",
                "python.linting.enabled": true,
                "python.linting.flake8Enabled": true,
                "python.formatting.provider": "black",
                "files.watcherExclude": {
                    "**/node_modules/**": true,
                    "**/.git/objects/**": true,
                    "**/.git/subtree-cache/**": true,
                    "**/build/**": true
                }
            }
        }
    }
}
.devcontainer/Dockerfile
FROM ubuntu:24.04

# Set environment variables
ENV DEBIAN_FRONTEND=noninteractive
ENV PYTHONUNBUFFERED=1
ENV PYTHONDONTWRITEBYTECODE=1

# Install system dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    cmake \
    python3 \
    python3-pip \
    git \
    curl \
    wget \
    vim \
    sudo \
    && rm -rf /var/lib/apt/lists/*

# Create a non-root user
ARG USERNAME=user
ARG USER_UID=1000
ARG USER_GID=$USER_UID

# Check if "ubuntu" user exists, delete it if it does, then create the desired user
RUN if getent passwd ubuntu > /dev/null 2>&1; then \
        userdel -r ubuntu && \
        echo "Deleted existing ubuntu user"; \
    fi && \
    groupadd --gid $USER_GID $USERNAME && \
    useradd -s /bin/bash --uid $USER_UID --gid $USER_GID -m $USERNAME && \
    echo "Created new user $USERNAME"

# Add sudo support for the non-root user
RUN apt-get update && apt-get install -y sudo \
  && echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME\
  && chmod 0440 /etc/sudoers.d/$USERNAME \
  && rm -rf /var/lib/apt/lists/*

# Install Python packages commonly used with PyBullet
RUN apt-get update && apt-get install -y --no-install-recommends \
    python3 \
    python3-venv \
    && rm -rf /var/lib/apt/lists/*

# 2. Create isolated environment

RUN apt-get update && apt-get install -y --no-install-recommends \
    mesa-utils \
    && rm -rf /var/lib/apt/lists/*


COPY requirements.txt /tmp/requirements.txt
RUN pip install --break-system-packages --no-cache-dir -r /tmp/requirements.txt

RUN apt-get update && apt-get install -y \
    mesa-utils \
    libgl1 \
    libglvnd0 \
    libglx-mesa0 \
    libglu1-mesa \
    libx11-6 \
    libxext6 \
    && rm -rf /var/lib/apt/lists/*
docker-compose.yml
services:
  dev:
    build:
      context: .
      dockerfile: .devcontainer/Dockerfile
    user: "user"
    volumes:
      - ./:/workspace:cached
      - /tmp/.X11-unix:/tmp/.X11-unix:rw
    environment:
      - DISPLAY=${DISPLAY}
      - QT_X11_NO_MITSHM=1
      - NVIDIA_VISIBLE_DEVICES=all
      - NVIDIA_DRIVER_CAPABILITIES=all
      - __NV_PRIME_RENDER_OFFLOAD=1
      - __GLX_VENDOR_LIBRARY_NAME=nvidia
    runtime: nvidia
    network_mode: host
    stdin_open: true
    tty: true
    hostname: dev
    extra_hosts:
      - "dev:127.0.0.1"
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia        # explicitly use NVIDIA runtime
              count: all
              capabilities: [gpu]
requirements.txt
pybullet

Check OpenGL backend

glxinfo | grep -i vendor
#
server glx vendor string: SGI
client glx vendor string: NVIDIA Corporation
OpenGL vendor string: NVIDIA Corporation



glxinfo | grep -i renderer
#
OpenGL renderer string: NVIDIA GeForce MX450/PCIe/SSE2
hello_bullet.py
import pybullet as p
import time
import pybullet_data

physicsClient = p.connect(p.GUI)
p.setAdditionalSearchPath(pybullet_data.getDataPath())
# Add gravity
p.setGravity(0, 0, -9.8)
plane_id = p.loadURDF("plane.urdf")
# cube_id = p.loadURDF("urdf/self_balance.urdf", [0, 0, 1])
robot = p.loadURDF("kuka_iiwa/model.urdf", useFixedBase=True)

# Run simulation loop
while True:
    p.stepSimulation()
    time.sleep(1./240.)
python3 hello_bullet.py
#
pybullet build time: Nov 18 2025 13:39:23
startThreads creating 1 threads.
starting thread 0
started thread 0 
argc=2
argv[0] = --unused
argv[1] = --start_demo_name=Physics Server
ExampleBrowserThreadFunc started
X11 functions dynamically loaded using dlopen/dlsym OK!
X11 functions dynamically loaded using dlopen/dlsym OK!
Creating context
Created GL 3.3 context
Direct GLX rendering context obtained
Making context current
GL_VENDOR=NVIDIA Corporation
GL_RENDERER=NVIDIA GeForce MX450/PCIe/SSE2
GL_VERSION=3.3.0 NVIDIA 560.35.03
GL_SHADING_LANGUAGE_VERSION=3.30 NVIDIA via Cg compiler
pthread_getconcurrency()=0
Version = 3.3.0 NVIDIA 560.35.03
Vendor = NVIDIA Corporation
Renderer = NVIDIA GeForce MX450/PCIe/SSE2
b3Printf: Selected demo: Physics Server
startThreads creating 1 threads.
starting thread 0
started thread 0 
MotionThreadFunc thread started
ven = NVIDIA Corporation
ven = NVIDIA Corporation