جلسه ۵ — ساختار حرفه‌ای پروژه Python

  • ساختار حرفه ای چگونه اس و چرا به ان نیاز داریم ؟
  • Module یعنی چه ؟
  • Package چیست؟
  • __init__.py چیست؟
  • Import یعنی چه؟
  • Virtual Environment چیست؟
  • برای پروژه ما چطور Virtual Environment بسازیم؟
  • Git چیست؟
  • GitHub چیست؟
  • ما دقیقاً در این جلسه چه کاری انجام می‌دهیم؟

ساخت ساختار اولیه:

AGV_Project/
├── main.py
├── robot/
├── world/
├── sensors/
├── vision/
├── navigation/
├── control/
├── mapping/
├── ai/
├── data/
└── utils/
  • مدیریت فایل‌ها
  • Module
  • Package
  • Import
  • Virtual Environment
  • Git مقدماتی

1. اصلاً چرا به این ساختار نیاز داریم؟

فرض کن تمام کدهای پروژه AGV را داخل یک فایل بنویسیم:

main.py

اول پروژه شاید این‌طور باشد:

connect_to_pybullet()   # تابع
create_robot()          # تابع
move_robot()            # تابع
read_lidar()            # تابع
process_camera()        # تابع
make_map()              # تابع
find_path()             # تابع
control_robot()         # تابع
train_ai()              # تابع

...

اوایل مشکلی نیست.

اما وقتی پروژه بزرگ شود، مثلاً ۱۰ هزار خط کد داشته باشیم، دیگر پیدا کردن و تغییر دادن کدها بسیار سخت می‌شود.

مثلاً بخواهی فقط کد مربوط به دوربین را تغییر بدهی.

باید در بین هزاران خط کد بگردی.

نکته : این‌ها در مثال بالا ، نه ماژول هستند و نه دستورهای واقعی Python/PyBullet. من برای توضیح ساده، اسمِ چند کار یا تابع فرضی را نوشته ام تا نشان بدهم اگر همه کارهای پروژه را در یک فایل main.py بریزیم چه شکلی می‌شود.


راه‌حل چیست؟

بهتر هست کد را بر اساس وظیفه‌اش تقسیم کنیم.

مثلاً:

AGV_Project/
│
├── main.py
│
├── robot/
│
├── world/
│
├── sensors/
│
├── vision/
│
├── navigation/
│
├── control/
│
├── mapping/
│
├── ai/
│
├── data/
│
└── utils/

یعنی به جای اینکه همه چیز را در یک فایل بریزیم، پروژه را به قسمت‌های منطقی تقسیم می‌کنیم.


هر پوشه برای چیست؟

حالا تک‌تک آنها را ببینیم.

نکته مهم : ممکن هست چیزهایی که در پایین برایتان شرح میدم کمی گیج کننده باشد ، اما نگران نباشید چون همه انها را در جلسات بعد قدم به قدم یاد خواهسم گرفت . در این قسمت فقط میخوام کلیات مبحث در ذهن تان شکل بگیرد

1- main.py

این فایل را می‌توانیم مرکز اجرای پروژه در نظر بگیریم.

مثلاً:

from robot.agv import create_agv
from world.environment import create_world

create_world()
agv = create_agv()

while True:
    ...

یعنی main.py قرار نیست تمام جزئیات پروژه را خودش انجام دهد.

وظیفه‌اش بیشتر این است که بگوید:

پروژه را از اینجا شروع کن و این قسمت‌ها را به این ترتیب اجرا کن.

مثل مدیر پروژه.


2- پوشه robot/

هر چیزی که مستقیماً مربوط به خود ربات باشد اینجا قرار می‌گیرد.

مثلاً:

robot/
│
├── agv.urdf
├── agv.py
└── __init__.py

agv.py می‌تواند شامل کدهایی برای ساخت و بارگذاری ربات باشد.

مثلاً:

def create_agv():
    ...

بعد main.py فقط می‌گوید:

from robot.agv import create_agv

agv = create_agv()

یعنی جزئیات ساخت AGV را از main.py خارج کرده‌ایم.


3- پوشه world/

هر چیزی مربوط به دنیای شبیه‌سازی.

مثلاً:

world/
├── warehouse.py
├── obstacles.py
├── floor.py
└── __init__.py

در آینده می‌توانیم داشته باشیم:

warehouse.py

که انبار را ایجاد کند.

مثلاً:

def create_warehouse():
    ...

4- پوشه sensors/

اینجا سنسورهای ربات قرار می‌گیرند.

مثلاً:

sensors/
├── lidar.py
├── imu.py
├── camera.py
└── __init__.py

بعداً اگر بخواهیم LiDAR شبیه‌سازی کنیم:

from sensors.lidar import Lidar

و دوربین:

from sensors.camera import Camera

این خیلی مهم است چون پروژه ما بعداً چند نوع سنسور خواهد داشت.


5- پوشه vision/

این قسمت با sensors/ فرق دارد.

مثلاً:

sensors/camera.py

مسئول گرفتن تصویر است.

اما:

vision/processing.py

مسئول تحلیل تصویر است.

مثلاً:

Camera
  ↓
Image
  ↓
OpenCV
  ↓
Object Detection

پس:

sensors/

یعنی:

داده را از سنسور بگیر.

و:

vision/

یعنی:

داده تصویری را تحلیل کن.

این تفکیک در پروژه‌های واقعی بسیار مهم است.


6- پوشه navigation/

مغز مسیریابی ربات.

مثلاً:

navigation/
├── astar.py
├── dwa.py
├── localization.py
└── planner.py

در اینجا الگوریتم‌هایی مثل:

  • A*
  • Dijkstra
  • DWA
  • Localization
  • Path Planning

قرار می‌گیرند.

مثلاً:

from navigation.astar import find_path

7- پوشه control/

این قسمت مسئول کنترل حرکت ربات است.

مثلاً:

control/
├── differential_drive.py
├── controller.py
└── pid.py

مثلاً Navigation می‌گوید:

باید به این نقطه بروی.

ولی Control تصمیم می‌گیرد:

چرخ چپ با چه سرعتی بچرخد؟
چرخ راست با چه سرعتی بچرخد؟

پس:

Navigation
    ↓
"به این نقطه برو"
    ↓
Control
    ↓
"چرخ چپ = 2 rad/s
 چرخ راست = 3 rad/s"

8- پوشه mapping/

هر چیزی مربوط به ساخت نقشه محیط.

مثلاً:

mapping/
├── occupancy_grid.py
├── map_builder.py
└── __init__.py

LiDAR داده می‌دهد:

فاصله تا موانع

و این قسمت آن را تبدیل می‌کند به:

Occupancy Grid

یعنی نقشه‌ای که مشخص می‌کند:

⬜ آزاد
⬛ مانع

9- پوشه ai/

این قسمت در پروژه ما خیلی مهم شده است.

هر چیزی که مربوط به هوش مصنوعی باشد می‌تواند اینجا قرار بگیرد.

مثلاً:

ai/
├── model.py
├── train.py
├── inference.py
├── rewards.py
└── __init__.py

بعداً ممکن است داشته باشیم:

ai/
    machine_learning/
    reinforcement_learning/

مثلاً:

Camera
   ↓
AI Model
   ↓
تشخیص مانع
   ↓
Navigation

یا:

AGV
 ↓
Reinforcement Learning
 ↓
یادگیری حرکت

10- پوشه data/

داده‌های پروژه.

مثلاً:

data/
├── images/
├── datasets/
├── logs/
└── results/

مثلاً تصاویر دوربین:

data/images/

داده آموزشی Machine Learning:

data/datasets/

نتایج آزمایش:

data/results/

لاگ‌ها:

data/logs/

11- پوشه utils/

این یکی کمی متفاوت است.

گاهی یک سری توابع عمومی داریم که به یک قسمت خاص تعلق ندارند.

مثلاً:

utils/
├── logger.py
├── math_utils.py
├── config.py
└── __init__.py

مثلاً تابع تبدیل درجه به رادیان:

def deg_to_rad(angle):
    ...

چنین تابعی مخصوص Navigation یا Robot نیست.

پس می‌تواند در utils باشد.


2. حالا Module یعنی چه؟

این اصطلاح خیلی مهم است.

در Python، یک فایل .py می‌تواند یک Module باشد.

مثلاً:

lidar.py

یک Module است.

داخلش تابع اسکن رو داریم :

def scan():
    ...

داریم.

بعد در فایل دیگری می‌توانیم بنویسیم:

from lidar import scan

یعنی:

تابع scan را از ماژول lidar وارد کن. به این میگن ماژول


3. Package چیست؟

Package را فعلاً خیلی ساده این‌طور در نظر بگیر:

یک پوشه که چند Module مرتبط را کنار هم نگه می‌دارد.

مثلاً:

sensors/
│
├── lidar.py
├── camera.py
└── imu.py

اینجا:

lidar.py
camera.py
imu.py

ماژول هستند.

و:

sensors/

یک Package است.

البته در Python مدرن، جزئیات Package و __init__.py کمی پیچیده‌تر است، ولی برای شروع همین تعریف کاملاً کافی است.


4. __init__.py چیست؟

ممکن است در پروژه ببینی:

sensors/
├── __init__.py
├── lidar.py
└── camera.py

این فایل برای تعریف/مدیریت Package استفاده می‌شود و می‌تواند خالی باشد.

مثلاً فعلاً داخلش میتونیم بنویسم :

# sensors package

که کافی است.

در آموزش ابتدایی لازم نیست وارد جزئیات پیشرفته __init__.py شویم.

اما تا اینجا به طور خلاصه بدونید

AGV_Project
│
├── sensors/          ← پوشه(پکیج)سنسورها 
│   ├── __init__.py   ←  معرفی/ساخت Package
│   ├── lidar.py      ← LiDAR
│   └── camera.py     ← Camera
│
├── robot/
├── navigation/
├── control/
├── simulation/
├── vision/
└── main.py

سنسوریک پوشه برای نگهداری تمام کدهای مربوط به سنسورهای AGV است.که توش چند ماژول داره و برای اینکه پایتون اونو به عنوان یه پکیج بدونه از __init__.py استفاده میشه .


5. Import یعنی چه؟

فرض کن در:

sensors/lidar.py

یه تابع داریم اسکن داریم :

def scan():
    print("LiDAR scanning...")

حالا در main.py می‌خواهیم از آن استفاده کنیم.

می‌نویسیم:

from sensors.lidar import scan

ینی تابع اسکن رو از ماژول لیدار وارد کن (ایمپورت کن) و بعد مینویسیم :

scan()

یعنی:

main.py
   ↓
sensors  --> برو به پوشه سنسور
   ↓
lidar.py --> ماژول لیدار و پیدا کن
   ↓
scan() --> تابع اسکن رو پیدا کن و بیار

این همان Import کردن است.


6. Virtual Environment چیست؟

این هم بسیار مهم است.

فرض کن امروز پروژه ما نیاز دارد:

Python 3.13
PyBullet
OpenCV
NumPy

اما پروژه دیگری داری که نیاز دارد:

Python 3.11

اگر همه چیز را روی Python اصلی کامپیوتر نصب کنیم، ممکن است نسخه‌ها با هم تداخل کنند.

راه‌حل:

برای هر پروژه یک محیط جدا بسازیم.

مثلاً:

AGV_Project
     ↓
Virtual Environment
     ↓
Python
PyBullet
NumPy
OpenCV
...

یعنی محیط پروژه AGV از پروژه‌های دیگر جدا باشد.


9. برای پروژه ما چه Virtual Environment بسازیم؟

مثلاً با Conda:

conda create -n agv_project python=3.13

بعد:

conda activate agv_project

حالا وقتی بنویسی:

python --version

نسخه Python همین محیط را می‌بینی.

بعد کتابخانه‌های پروژه را نصب می‌کنیم.

مثلاً:

pip install pybullet numpy opencv-python

و بعداً:

pip install matplotlib

و در مراحل Machine Learning کتابخانه‌های مورد نیاز خودمان را اضافه می‌کنیم.

نکته: لازم نیست از جلسه اول تمام کتابخانه‌های ۱۲۴ جلسه را نصب کنیم. هر فصل که به یک ابزار نیاز داشتیم، همان موقع نصبش می‌کنیم.


10. Git چیست؟

این هم برای پروژه Portfolio ما بسیار مهم است.

فرض کن امروز کد AGV خوب کار می‌کند.

فردا تغییرش می‌دهی و ناگهان همه چیز خراب می‌شود.

اگر Git داشته باشیم می‌توانیم نسخه‌های قبلی پروژه را نگه داریم.

مثلاً:

Version 1
AGV حرکت می‌کند

        ↓

Version 2
LiDAR اضافه شد

        ↓

Version 3
Mapping اضافه شد

        ↓

Version 4
A* اضافه شد

و بعد اگر Version 4 خراب شد، می‌توانیم به نسخه قبلی برگردیم.


12. GitHub چیست؟

Git روی کامپیوتر خودمان کار می‌کند.

GitHub جایی است که می‌توانیم پروژه را آنلاین منتشر کنیم.

و این برای هدف Portfolio ما خیلی مهم است.

مثلاً در نهایت کارفرما می‌تواند وارد GitHub ما شود و ببیند:

AGV_Project
│
├── Robot
├── Sensors
├── Computer Vision
├── Navigation
├── AI
├── Mapping
└── Documentation

بعد README را بخواند و Demo Video را ببیند.

این خیلی بهتر از این است که در رزومه فقط بنویسیم:

PyBullet, Python, OpenCV, Machine Learning


13. حالا دقیقاً در این جلسه چه کاری انجام می‌دهیم؟

من پیشنهاد می‌کنم جلسه ۵ را فقط تئوری نگذاریم.

دانشجو باید در پایان جلسه واقعاً یک پروژه ساخته باشد.

*** خب حالا بیاید عملی شروع کنیم ***

اول باید کوندا را نصب کرده باشید
حالا باهم یه محیط جدید داخل کوندا ایجاد میکنیم تا پایتون مخصوص خودش رو داشته باشه و پایبالت متناسب با اونو نصب کیم

حتماً. این بار خیلی ساده و خطی می‌چینیم. فقط سه مفهوم را از هم جدا کن:

  • Python سیستم = Pythonی که مستقل از Anaconda روی ویندوز نصب شده.
  • Python Conda = Python داخل یک Conda Environment.
  • Conda = خود برنامه مدیریت Environment و Packageها.

و یک نکته مهم: defaults و conda-forge مربوط به منبع دانلود Packageها هستند، نه نسخه Python و نه خود Environment.


13.1 اول وضعیت فعلی سیستم را ببینیم

در Anaconda Prompt یا PowerShell این دستورات را بزن.

Python فعال فعلی
python --version
مسیر Python فعال
where python
Pythonهای نصب‌شده روی ویندوز
py -0p
نسخه خود Conda
conda --version
Environmentهای Conda
conda env list

مثلاً ممکن است ببینی:

base
D:\MyProjects\pybullet
Channel فعلی Conda
conda config --show channels

13.2 الان سیستم خودت را به defaults برگردان

اگر قبلا به هر دلیل اناکوندا داشتید و و از کانال اکوندا فورج پکیج دانلود کردید ، اونو به حالت دیفالت برگردونید. اما اگه همیشه از کوندا فورج استفاده میکنید مشکلی نداره و با اون راحت هستید و میتونید از اون استفاده کنید، این مرحله رو رد کنید. برای برگشت به حالت قبلی:

conda config --remove channels conda-forge

بعد:

conda config --add channels defaults

حالا بررسی:

conda config --show channels

باید ببینی:

channels:
  - defaults

تمام.

این کار Environment های شمارو حذف نمی‌کند.

یعنی مثلا اگه محیط زیر رو با فورج ایجاد کردید الان با برگشت به حتالت دیفالت ، مشکلی برای اون محیط به وجود نمیاد:

D:\MyProjects\pybullet

همچنان وجود دارد.


13.3 روش اول: همه چیز با defaults

فرض کنیم می‌خواهی یک محیط جدید بسازی دو راه داره یا از کانال اناکوندا دیفالت استفاده کنید یااز کانال اناکوندا فورج:

چون مسیر کانال اناکوندا دیفالت معمولا ارور میده بهتره از چنل فورج استفاده مینم اما من هردو روش رو گذاشتم تا شما امتحان کنین

اگه هر کدوم ارور داد از اون یکی استفاده کنین.

در این روش میتونید با فیلتر شکن و بدون هم جلو برید تا ببینین نتیجه میگیرید یا نه.

فرض کنیم اسم محیط جدید رو در مسیر زیر و به اسم پایبالت دیفالت میذارید

D:\MyProjects\pybullet_default

ومیخوای Python 3.12 داشته باشی.

شروع ساخت Environment

اوال باید از منوی استارت ویندوز دنبال anaconda prompt بگردی و اونو باز کنی یا anacoda powershell حالا در اول اون خطی شبیه این میبینی:

(base) C:\Windows\System32>

حالا دستور زیر رو کپی میکنی و تو اناکوندا پرامپ یا پاورشل اون میزنی

conda create --prefix D:\MyProjects\pybullet_default python=3.12

Conda از defaults استفاده می‌کند و سعی میکنه تا این محیط رو با این اسم ایجاد کنه و داره از دیفالت استفاده میکنه

بعد اگه ارور نداشت و کامل انجلم شد باید اون محیط رو فعال کنیم تا اسم اون پوشه اول اناکوندا پرامپت بیافته:

conda activate D:\MyProjects\pybullet_default

بررسی Python:

python --version

و:

where python

باید چیزی شبیه:

Python 3.12.x

و:

D:\MyProjects\pybullet_default\python.exe

ببینی.


نصب PyBullet با defaults

حالا:

conda install pybullet

اگر PyBullet در defaults برای شرایط تو موجود باشد، Conda خودش نسخه مناسب را پیدا می‌کند.

اگر گفت:

PackagesNotFoundError

یعنی در Channelهای فعلی، Build مناسب PyBullet پیدا نشده؛ آن موقع می‌رویم سراغ conda-forge.


13.4 روش دوم: یک Environment کاملاً conda-forge

این روشی است که برای پروژه PyBullet پیشنهاد می‌کنم.

نکته مهم:

یادت باشه که لازم نیست تنظیمات کل Conda را تغییر بدهی.ینی ممکنه شما قبلا برای نصب پکیج ها و محیط از کوندا دیفالت استفاده کردی الان با تغییر کامل اون به کوندا فورج ، ممکن است یک پروژه قدیمی داشته باشی که Packageهایش با اکوسیستم defaults ساخته شده‌اند. حالا تغییر Channel سراسری می‌تواند باعث شود در نصب‌ها یا Updateهای بعدی، Conda ترکیبی از Buildهای متفاوت را وارد محیط کند و احتمال dependency conflict بیشتر شود و تداخل یا ارورهایی پیش بیاد . همینطور برعکس ممکنه مثلا کلا از کوندا فورج همیشه استفاده میکردی الان با تغییر سراسری به کوندا دیفالت باز همون مشکل پیش میاد ==> بهتره همیشه قبل ساخت محیط و پکیج ها از کد زیر استفاده کنی تا ببینی رو کانال دیفالت هستی یا فورج

conda config –show channels

اگه تو حالت دیفالت بود که از روش قبل تو شماره 3 استفاده میکنیم و اگه نصب ها کامل انجام شد که دیگه از این روش دوم ینی بخش شماره 4 که الان درحال خوندن اون هستی رد شو

وگرنه به جای دیفالت از فورج استفاده میکنیم که احتمال نصب اون به شدت بالاست

یادت باشه برای این حالت غالبا بدون فیلتر شکن میتونی کارهات رو انجام بدی

*** نکته به شدت مهم *** : کل اناکوندا روبه conda-forge تبدیل نمیکنیم فقط برای محیطی که داریم میسازیم اونو تبدیل به conda-forge میکنیم و بیرون اون محیط کوندا دیفالت هست و این باعث میشه تا برای پروژه های دیگه تون مشکل پیش نیاد

پس می‌توانی فقط برای همین Environment بگویی:

همه چیز را از conda-forge بگیر.

این بهترین قسمت ماجراست.


ساخت Environment با conda-forge

مثلاً:

conda create --prefix D:\MyProjects\pybullet_forge -c conda-forge --override-channels python=3.12

این دستور یعنی:

Environment:
D:\MyProjects\pybullet_forge

Python:
3.12

Channel:
فقط conda-forge

--override-channels خیلی مهم است؛ یعنی:

Channelهای دیگری مثل defaults را برای این دستور نادیده بگیر.


فعال کردن
conda activate D:\MyProjects\pybullet_forge

بررسی:

python --version

و:

where python

نصب PyBullet در محیط conda-forge

حالا:

conda install -c conda-forge --override-channels pybullet

این یعنی:

PyBullet را فقط از conda-forge نصب کن.

Conda خودش نسخه سازگار با Python 3.12 را انتخاب می‌کند.

بعد تست:

python -c "import pybullet; print(pybullet.__version__)"

13.5 اگر بعداً خواستی Package جدید نصب کنی، از کجا بفهمی Environment تو forge است یا defaults؟

این قسمت خیلی مهم است. فرض کن تو محیط رو با فعال کردن کانال conda forge برای اون محیط ساختی اما بعد یه مدتی میخوای اپدیت کنی یا چیز جدید تو اون محیط نصب کنی برای اینکه تداخلی پیش نیاد باید اول بفهمی قبلا اینو با دیفالت ساختی یا با فورج تا الان ادامه نصب هاتو هم با اونانجام بدی برای همین از مسیر زیر میریم.

اول Environment که قراره باهاش کار کنی یا داخلش چیزی نصب و اضافه کنی را فعال کن:

conda activate D:\MyProjects\pybullet_forge

بعد:

conda config --show channels

اما یک نکته: این دستور Channel تنظیم‌شده در Conda را نشان می‌دهد نه الزاماً اینکه تک‌تک Packageهای نصب‌شده از کجا آمده‌اند. ینی ممکنه تو برای این محیط خاص ، کوندا فورج رو فعال کردی اما برای اناکوندا سراسری ، هنوز همون دیفالت فعال هست. اما برای اینکه بفهمی مثلا پایبالت چطور دان شده از دستورات زیر برو

برای اینکه بفهمی یک Package مشخص از کجا آمده:

conda list    # or --> conda list pybullet

در خروجی معمولاً ستونی مثل:

channel

می‌بینی.

مثلاً:

pybullet    3.25    ...    conda-forge
numpy       2.x     ...    conda-forge

پس می‌توانی بفهمی Package از کجا آمده.


13.6 پس برای اینکه هیچ‌وقت قاطی نشود، این روش را استفاده کن

اگر Environment را با conda-forge ساخته‌ای، برای نصب‌های بعدی هم صریحاً بنویس:

conda install -c conda-forge --override-channels numpy

یا:

conda install -c conda-forge --override-channels pandas

یا:

conda install -c conda-forge --override-channels opencv

اینطوری مطمئن هستی که برای آن دستور، فقط conda-forge استفاده می‌شود.


13.7 حالا کل دو مسیر را کنار هم ببین

مسیر A — Defaults
1. Channel = defaults
        ↓
2. ساخت محیط
        ↓
conda create --prefix D:\MyProjects\project python=3.12
        ↓
3. فعال کردن
        ↓
conda activate D:\MyProjects\project
        ↓
4. نصب PyBullet
        ↓
conda install pybullet

مسیر B — Conda-forge
1. Channel = conda-forge
        ↓
2. ساخت محیط
        ↓
conda create --prefix D:\MyProjects\project python=3.12 -c conda-forge --override-channels
        ↓
3. فعال کردن
        ↓
conda activate D:\MyProjects\project
        ↓
4. نصب PyBullet
        ↓
conda install -c conda-forge --override-channels pybullet

13.8 یک نکته خیلی مهم برای آینده

به جای اینکه دائم تنظیمات کل Conda را این‌طور عوض کنی:

defaults
    ↓
conda-forge
    ↓
defaults
    ↓
conda-forge

برای پروژه‌های مختلف بهتر است Channel را هنگام ساخت/نصب مشخص کنی.

مثلاً پروژه PyBullet:

conda create --prefix D:\MyProjects\pybullet -c conda-forge --override-channels python=3.12

و بعد همیشه:

conda install -c conda-forge --override-channels ...

این خیلی تمیزتر است.


خلاصه‌ای که ارزش حفظ کردن دارد

کاردستور
Python فعالpython --version
مسیر Pythonwhere python
Pythonهای ویندوزpy -0p
نسخه Condaconda --version
محیط‌هاconda env list
Channel فعلیconda config --show channels
ساخت محیط در Dconda create --prefix D:\MyProjects\project python=3.12
فعال کردنconda activate D:\MyProjects\project
ساخت با فقط conda-forgeconda create --prefix D:\MyProjects\project -c conda-forge --override-channels python=3.12
نصب با defaultsconda install package
نصب فقط از forgeconda install -c conda-forge --override-channels package
برگشت تنظیمات به defaultsconda config --remove channels conda-forge سپس conda config --add channels defaults

13.9 چطور میتونم محیطی که ساختم رو به هر دلیلی حذف کنم ؟

بله، خیلی راحت. چون محیط‌های قبلی را با --prefix داخل D:\MyProjects ساخته‌ای، بهتر است با خود Conda حذفشان کنی، نه اینکه دستی پوشه را Delete کنی.

1. اول محیط‌هایت را ببین
conda env list

مثلاً ممکن است ببینی:

base
D:\MyProjects\pybullet
D:\MyProjects\pybullet_default
D:\MyProjects\Anaconda_PyEnv_pybullet_forge
2. محیط آزمایشی را حذف کن

مثلاً اگر این را می‌خواهی حذف کنی:

D:\MyProjects\pybullet_default

بزن:

conda env remove --prefix D:\MyProjects\pybullet_default

Conda ازت تأیید می‌خواهد. y بزن.


3. اگر چند محیط آزمایشی داری

مثلاً:

D:\MyProjects\test1
D:\MyProjects\test2
D:\MyProjects\pybullet_old

هرکدام را جدا حذف کن:

conda env remove --prefix D:\MyProjects\test1
conda env remove --prefix D:\MyProjects\test2
conda env remove --prefix D:\MyProjects\pybullet_old
4. بعد دوباره بررسی کن
conda env list

باید فقط محیط‌هایی که واقعاً می‌خواهی نگه داری باقی بمانند.


⚠️ یک نکته مهم

اگر همین محیط فعلی فعال است:

(D:\MyProjects\Anaconda_PyEnv_pybullet_forge)

آن را حذف نکن.

برای حذف یک محیط، بهتر است اول از آن خارج شوی:

conda deactivate

بعد حذفش کنی.

و پوشه را دستی از File Explorer پاک نکن؛ روش درست برای Environmentهای Conda این است:

conda env remove --prefix مسیر_محیط

************************************

13.10 حالا که محیط جدید را ساختیم و پایبالت رو در اون نصب کردیم بریم سراغ ادامه کار یعنی ساخت پوشه پروژه AGV :

ما اول تو درایو D، محیط جدید را تو مسیر زیر ساختیم و پای بالت رو تو اون نصب کردیم

D:\MyProjects\Anaconda_PyEnv_pybullet_forge

برای همین باید یه نکته رو بدونیم .اینی که ما ساختیم یه محیط جدید بود تا کتابخانه های مخصوص پای بالت و خود اون و پکیج های لازم تو این محیط نصب بشن که با پروژه های دیگه ما که از اناکوندا استفاده میکنن تداخل پیدا نکنن.

فرض کن یه پروژه حسابداری داری با pyqt میسازی و پایتون و پکیج های خاص خودش رو داره برای همین برای اونم یه محسط مخصوص خودش رو به وسیله اناکوندا میسازی که مثلا اون از پایتون 3.9 استفاده میکنه اما پروژه رباتیک(پایبالت) تو از پایتون 3.14 استفاده میکنه ==> اینا برای اینکه تداخل نداشته باشن تو از محیط های جدا استفاده کردی
اما الان که من یه محیط برای کارهای رباتیک و شبیه سازی ایجاد کردم ، قراره چندین زیر پروژه از پای بالت داشته باشم . مثلا یه پروژه مقدماتی برای راه اندازی یه چرخ با پای بالت هست و یه پروژه برای ساخت یه ربات انسان نما و یه پروژه هم برای ساخت ربات چهار چرخ agv که قراره ما بسازیم. فرض کن ما برنامه اکسل رو نصب کردی (همون پای بالت) اما قراره باهاش ندین برنامه بسازیم تو یکی حساب کتاب های ماهانه مون رو . تو یه شیت اکسل دیگه میخوام برنامه درسی مون رو داشته باشیم و همینطور الی اخر

اینجا هم ما پای بالت رو تو محیط مون به اسم و مسیر D:\MyProjects\Anaconda_PyEnv_pybullet_forge داریم الان میخوایم با اون پروژه های مختلف انجام بدیم

الان ایا باید همه این پروژه ها تو همون محیطی که ساختیم برای پایبالت (D:\MyProjects\Anaconda_PyEnv_pybullet_forge) باید همه شون درهم تو اونجا باشن یا میتونیم باز کارمون رو حرفه ای تر پیش ببریم ؟!!

الان ما این را داریم:

D:\MyProjects\Anaconda_PyEnv_pybullet_forge

این Environment است، نه محل نگهداری پروژه‌های PyBullet.

داخلش فایل‌ها و پوشه‌هایی مثل python.exe، Lib، DLLs و … وجود دارد. داخل این پوشه پروژه AGV یا هر پروژه دیگه رو نساز.


پس در نتیجه : آیا باید داخل Environment پوشه Project بسازم؟

خیر بلکه بهتر است D:\MyProjects را این‌طوری سازمان‌دهی کنی یه پوشه برای محیط مون که پای بالت هست و پوشه های دیگه برای پروژه مون کنار این محیط نه داخل محیط. مثل زیر:

D:\MyProjects
│
├── Anaconda_PyEnv_pybullet_forge       ← Environment
│   ├── python.exe
│   ├── Lib
│   ├── DLLs
│   └── ...
│
├── AGV_Project                         ← پروژه 1
│   ├── main.py
│   ├── robot
│   ├── world
│   ├── sensors
│   ├── navigation
│   ├── vision
│   └── ...
│
├── PickAndPlace_Project                ← پروژه 2
│   ├── main.py
│   └── ...
│
└── DigitalTwin_Project                 ← پروژه 3
    ├── main.py
    └── ...

یعنی:

Environment یک بار ساخته می‌شود، ولی چندین Project می‌توانند از آن استفاده کنند.

حالا AGV چطور از PyBullet استفاده می‌کند؟

تو داری:

D:\MyProjects\Anaconda_PyEnv_pybullet_forge

که داخلش داری:

Python 3.12
PyBullet
NumPy
...

حالا می‌سازی:

D:\MyProjects\AGV_Project

داخلش مثلاً:

AGV_Project
│
├── main.py
├── robot
├── world
├── sensors
├── navigation
├── vision
└── ...

بعد وقتی می‌خواهی AGV را اجرا کنی، Environment را فعال می‌کنی:

conda activate D:\MyProjects\Anaconda_PyEnv_pybullet_forge

بعد می‌روی داخل پروژه:

cd D:\MyProjects\AGV_Project

و:

python main.py

Python از Environment استفاده می‌کند و بنابراین:

AGV_Project
     ↓
Python موجود در Environment
     ↓
PyBullet موجود در Environment

نکته خیلی مهم

این دو تا را از هم جدا کن:

Environment
Anaconda_PyEnv_pybullet_forge

یعنی:

«ابزار و کتابخانه‌هایی که پروژه‌ها می‌توانند استفاده کنند.»

Project
AGV_Project
PickAndPlace_Project
DigitalTwin_Project

یعنی:

«کد و فایل‌های مربوط به یک پروژه مشخص.»


آیا چند پروژه می‌توانند از یک PyBullet استفاده کنند؟

بله، کاملاً.

مثلاً:

                    Anaconda_PyEnv_pybullet_forge

├── Python 3.12
├── PyBullet
├── NumPy
├── OpenCV
└── SciPy

┌──────────────┼──────────────┐
│ │ │
↓ ↓ ↓
AGV_Project PickPlace_Project DigitalTwin

همه این پروژه‌ها می‌توانند از همان PyBullet استفاده کنند.

پس فعلا برای ایجاد پوشه ای که بتونیم پروژه مون ور توش داشته باشیم اینطوری پیش میریم

یک نکته برای پروژه AGV مون :

با توجه به اینکه قرار است بعداً پروژه AGV را جدی و نسبتاً بزرگ بسازیم، پیشنهاد من این است که بری تو پوشه ای که محیط رو ساختیم و کنار محیط ساخته شده یه پوشه جدید با اسم پروژه مون که agv هست بسازی.:

D:\MyProjects
│
├── Anaconda_PyEnv_pybullet_forge
│
└── AGV_Project
    │
    ├── main.py
    ├── robot
    ├── world
    ├── sensors
    ├── navigation
    ├── mapping
    ├── vision
    ├── ai
    ├── control
    ├── data
    └── utils

حالا که مثلاً در Windows در درایو D ساختیم :

D:
└── MyProjects
    └── Anaconda_PyEnv_pybullet_forge
    └── AGV_Project 

داخلش میخواهیم اینارو بسازیم:

AGV_Project/
│
├── main.py
│
├── robot/
├── world/
├── sensors/
├── vision/
├── navigation/
├── control/
├── mapping/
├── ai/
├── data/
└── utils/

و داخل هر پوشه فعلاً یک فایل __init__.py قرار می‌دهیم.


13.11 ساخت محتویات داخل پوشه پروژه ( AGV پروژه)

اولین : فایل main.py

اول باید یه محیط کد نویسی داشته باشید که پیشنهاد من پایچارم و بعد وی اس کد هست اما چون قراه پروژه بزرگتر بشه:

برای این پروژه PyCharm را ترجیح می‌دهم، چون پروژه ما قرار است بزرگ شود و تعداد زیادی Package و Module خواهیم داشت.

Spyder را برای این پروژه انتخاب نمی‌کنیم.

Jupyter هم بعداً برای تحلیل داده و آزمایش‌های Machine Learning می‌تواند مفید باشد، اما محیط اصلی توسعه پروژه ما PyCharm خواهد بود.

PyCharm را باز کن
مرحله 1 — ساخت AGV_Project

در PyCharm برو به:

File → New Project

یا اگر الان یک پروژه باز است:

File → New Project

مسیر پروژه را بگذار:

D:\MyProjects\AGV_Project

اگر PyCharm ازت Python Interpreter خواست، همان Environmentای که ساخته‌ای را انتخاب کن:

D:\MyProjects\Anaconda_PyEnv_pybullet_forge\python.exe

یعنی:

کد پروژه در AGV_Project باشد، ولی Python و PyBullet از Environment قبلی استفاده شوند.


مرحله 2 — ساخت main.py

وقتی پروژه AGV_Project باز شد، در پنل سمت چپ روی:

AGV_Project

راست‌کلیک → New → Python File

bandicam 2026 08 24 13 24 07 673

اسم:

main

بگذار.

PyCharm خودش می‌سازد:

main.py

فعلاً داخلش فقط این را بنویس:

print("AGV Project Started")

دومین : ساخت پوشه‌های پروژه

حالا روی AGV_Project راست‌کلیک کن:

New → Directory

bandicam 2026 08 24 13 27 49 388

و به‌ترتیب این‌ها را بساز:

robot
world
sensors
vision
navigation
control
mapping
ai
data
utils

در نهایت باید در سمت چپ PyCharm ببینی:

bandicam 2026 08 24 13 30 54 163
AGV_Project
│
├── main.py
│
├── robot
├── world
├── sensors
├── vision
├── navigation
├── control
├── mapping
├── ai
├── data
└── utils

فعلاً داخل این پوشه‌ها هیچ فایل Python نساز. چون هنوز داریم اسکلت پروژه را ایجاد می‌کنیم.


مرحله تست خیلی مهم

در PyCharm روی main.py راست‌کلیک کن:

Run ‘main’

باید پایین PyCharm ببینی:

AGV Project Started

بعد برای اطمینان از اینکه PyCharm واقعاً از Environment خودت استفاده می‌کند، داخل main.py این را بگذار:

import sys

print(sys.executable)

اجرا کن.

باید مسیر شبیه این باشد:

D:\MyProjects\Anaconda_PyEnv_pybullet_forge\python.exe
bandicam 2026 08 24 13 33 23 287

اگر این مسیر را دیدی، یعنی PyCharm → AGV_Project → Python 3.12 Environment → PyBullet درست به هم وصل شده‌اند.


13.13 تکمیل فایل های داخل هریک از پوشه های AGV

بعد اینکه فایل main و پوشه های robot -world و… رو ساختیم الان باید داخل این پوشه ها (robot-world,…) رو تکمیل تر کنیم ==> یک Module واقعی می‌سازیم

تکمیل پوشه اول : robot
مرحله بعد: ساخت اولین Module

ساختار فعلی تو:

AGV_Project/
│
├── main.py
│
├── robot/
├── world/
├── sensors/
├── vision/
├── navigation/
├── control/
├── mapping/
├── ai/
├── data/
└── utils/
قدم 1 — داخل robot فایل بساز

در PyCharm:

robot
   ↓ راست‌کلیک
New
   ↓
Python File

اسمش را بگذار:

agv

پس:

robot/
└── agv.py

قدم 2 — داخل agv.py

بنویس:

def create_agv():
    print("AGV created!")

فعلاً همین.

این فایل الان یک Module است.

یعنی:

agv.py

یک Module پایتون است.


قدم 3 — برو داخل main.py

بنویس:

from robot.agv import create_agv

print("AGV Project Started!")

create_agv()

قدم 4 — اجرا کن

روی main.py راست‌کلیک:

Run 'main'

باید ببینی:

AGV Project Started!
AGV created!

اگر این را دیدی، یعنی Import درست کار کرده.


اما دقیقاً چه اتفاقی افتاد؟

این خط:

from robot.agv import create_agv

را به سه قسمت تقسیم کن:

from robot.agv import create_agv
     │       │          │
     │       │          └── تابع
     │       │
     │       └── فایل agv.py
     │
     └── پوشه robot

یعنی:

برو داخل پوشه robot، فایل agv.py را پیدا کن واز داخل اون، تابع create_agv را در اینجا وارد کن.

بعد:

create_agv()

تابع را اجرا می‌کند.


یک نکته مهم درباره Package

در Python مدرن، پوشه robot می‌تواند بدون __init__.py هم به‌عنوان namespace package کار کند؛ اما برای آموزش ساختار پروژه پیشنهاد می‌کنم فعلاً داخل پوشه‌های کدی مثل robot یک فایل خالی به نام:

__init__.py

هم بسازیم. همان کار قبل رو تکرار میکنیم روی robot در سمت چپ در پایچارم راست کلیک کن –> انتخاب اولین گزینه (NEW) –> انتخاب python file –> نوشتن __init__.py –> دیگه داخلش نمیخواد چیزی بنویسیم فعلا !

پس:

robot/
│
├── __init__.py
└── agv.py

__init__.py فعلاً می‌تواند کاملاً خالی باشد.

این کمک می‌کند مفهوم Package را هم واضح ببینی:

robot/       ← Package
   │
   ├── __init__.py
   │
   └── agv.py    ← Module

بنابراین:

Package = پوشه Python

Module = فایل .py

🎯 فایل -ماژول – پکیج – init : اینا 4 تا مفهوم اساسی که شاید شمارو گیج کرده باشه رو توضیح میدم تا ساختار پروژه و فعالیتمون رو بهتر درک کنین

بیا این را بدون اصطلاحات پیچیده و با همین پروژه AGV خودمان روشن کنیم. اگر این چهار مفهوم را بفهمی، ساختار پروژه Python خیلی قابل‌فهم می‌شود.


1. اول «فایل» چیست؟

هر چیزی که با پسوند .py می‌سازی، یک فایل Python است.

مثلاً:

main.py
agv.py
lidar.py
camera.py

این‌ها همگی فایل هستند.


2. Module چیست؟

در Python، یک فایل .py که بتوانی از آن کد را در فایل دیگری استفاده کنی، یک Module محسوب می‌شود.

مثلاً:

robot/
└── agv.py

agv.py یک فایل است و در عین حال یک Module است.

داخلش:

class AGV:
    def move(self):
        print("AGV is moving")

نوشته شده . حالا در main.py می‌توانی از آن استفاده کنی:

from robot.agv import AGV

یعنی:

از Module به نام agv، کلاس AGV را بیاور.

پس:

agv.py
│
├── یک فایل است
└── یک Module هم هست

این دو تا متناقض نیستند. ینی از نوشته های داخل فایل agv میتونی تو فایل مثلا main استفاده کنی


3. Package چیست؟

حالا فرض کن تعداد زیادی Module داری:

robot/
│
├── agv.py
├── motor.py
├── wheel.py
└── battery.py

این‌ها همه Module هستند.

ما آنها را داخل یک پوشه به نام robot گذاشته‌ایم.

این پوشه نقش Package را دارد.

پس:

robot/
│
├── agv.py       ← Module
├── motor.py     ← Module
├── wheel.py     ← Module
└── battery.py   ← Module

یعنی:

Package = یک مجموعه از Moduleهای مرتبط

در پروژه ما:

robot

برای چیزهای مربوط به خود ربات است.


4. پس main.py چیست؟

main.py هم یک فایل Python و در نتیجه یک Module است.

اما ما از آن به عنوان نقطه شروع برنامه استفاده می‌کنیم.

مثلاً:

from robot.agv import AGV

agv = AGV()

agv.move()

وقتی در PyCharm روی main.py می‌زنی:

Run

برنامه از آنجا شروع می‌شود.

پس:

main.py از نظر Python فقط یک Module است، اما از نظر ساختار برنامه ما نقش Entry Point یا نقطه شروع اجرای برنامه را دارد.


5. حالا __init__.py چیست؟

این قسمت مهم است.

فرض کن داریم:

robot/
│
├── __init__.py
├── agv.py
└── motor.py

فایل:

__init__.py

به Python می‌گوید:

این پوشه را به عنوان یک Package Python در نظر بگیر.

البته در Python 3، در بسیاری از شرایط Package می‌تواند بدون __init__.py هم کار کند؛ به آن Namespace Package می‌گویند.

اما برای پروژه‌ای مثل AGV، وجود __init__.py باعث می‌شود ساختار Package واضح‌تر و قابل‌کنترل‌تر باشد.


6. آیا __init__.py باید چیزی داخلش داشته باشد؟

نه.

فعلاً می‌تواند کاملاً خالی باشد:

robot/
│
├── __init__.py    ← خالی
└── agv.py

و هیچ مشکلی ندارد.


7. پس چرا اصلاً وجود دارد؟
کاربرد اول: مشخص کردن Package

ساختار:

robot/
├── __init__.py
└── agv.py

خیلی واضح می‌گوید:

robot = Package
agv   = Module

کاربرد دوم: اجرای کد هنگام Import

بعداً می‌توانی داخل __init__.py چیزی قرار دهی.

مثلاً:

print("Robot package loaded")

وقتی بنویسی:

import robot

کد __init__.py اجرا می‌شود.

اما فعلاً این کار را نکن؛ فقط مفهومش را بدان.


کاربرد سوم: کنترل چیزهایی که از Package بیرون می‌دهی

مثلاً بعداً می‌توانیم در:

robot/__init__.py

بنویسیم:

from .agv import AGV

بعد بتوانی بنویسی:

from robot import AGV

به جای:

from robot.agv import AGV

این کاربرد را بعداً در پروژه خودمان خواهیم دید.


8. حالا کل پروژه خودت را ببین

تو داری:

AGV_Project/
│
├── main.py
│
├── robot/
│   ├── __init__.py
│   └── agv.py
│
├── sensors/
│   ├── __init__.py
│   └── lidar.py
│
├── navigation/
│   ├── __init__.py
│   └── navigator.py
│
└── vision/
    ├── __init__.py
    └── camera.py

حالا می‌توانی این‌طور نگاه کنی:

AGV_Project
│
├── main.py
│     ↑
│     نقطه شروع برنامه
│
├── robot
│     ↑
│     Package
│
│     ├── __init__.py
│     └── agv.py
│                  ↑
│                  Module
│
├── sensors
│     ↑
│     Package
│
│     └── lidar.py
│                  ↑
│                  Module
│
└── vision
      ↑
      Package

      └── camera.py
                   ↑
                   Module

9. یک نکته خیلی مهم: Package با Environment فرق دارد

این را حتماً از هم جدا کن:

Anaconda_PyEnv_pybullet_forge

Environment است.

اما:

robot/
sensors/
vision/

Packageهای پروژه Python هستند.

این دو هیچ ارتباط مستقیمی با هم ندارند.

ساختار تو:

D:\MyProjects
│
├── Anaconda_PyEnv_pybullet_forge    ← Python + PyBullet + NumPy...
│
└── AGV_Project                      ← کد پروژه
    │
    ├── main.py
    ├── robot/                       ← Package
    ├── sensors/                     ← Package
    ├── vision/                      ← Package
    └── ...

مرحله بعد: پوشه دوم سنسور

پوشه اول تمام شد میریم پوشه دوم

تا اینجا اینو درست کردیم

AGV_Project/
├── main.py
├── robot/
│   ├── __init__.py
│   └── agv.py
├── sensors/
├── vision/
.
.

یعنی فعلاً برای بقیه پوشه‌ها __init__.py نساز.

چرا؟ چون هنوز آنها Packageهای واقعی نشده‌اند و می‌خواهیم وقتی به هر بخش رسیدیم، همان موقع ساختار آن را ایجاد کنیم.


1. داخل sensors یک __init__.py بساز

در PyCharm:

sensors
→ Right Click
→ New
→ Python File
→ __init__

حالا:

sensors/
└── __init__.py

2. یک Module به نام lidar.py بساز

داخل sensors:

sensors
→ Right Click
→ New
→ Python File
→ lidar

ساختار:

sensors/
├── __init__.py
└── lidar.py

3. داخل lidar.py

فعلاً خیلی ساده:

class Lidar:
    def read(self):
        print("Lidar is reading distance...")

اینجا یک Class ساختیم به نام Lidar.


4. حالا main.py

کد قبلی AGV را نگه دار و سنسور را هم اضافه کن:

from robot.agv import create_agv
from sensors.lidar import Lidar

print("AGV Project Started!")

create_agv()

lidar = Lidar()
lidar.read()

5. اجرا کن

باید ببینی:

AGV Project Started!
AGV created!
Lidar is reading distance...

در اینجا دوباره این زنجیره را می‌بینی:

sensors/             ← Package
    │
    ├── __init__.py
    │
    └── lidar.py     ← Module
          │
          └── Lidar ← Class

و در main.py:

from sensors.lidar import Lidar

یعنی:

از Package sensors، از Module lidar، کلاس Lidar را بیاور.

55555555


26. در نهایت ساختار پروژه ما به این شکل می‌شود

AGV_Project/
│
├── main.py
│
├── robot/
│   ├── __init__.py
│   └── agv.py
│
├── world/
│   ├── __init__.py
│   └── warehouse.py
│
├── sensors/
│   ├── __init__.py
│   ├── lidar.py
│   ├── camera.py
│   └── imu.py
│
├── vision/
│   ├── __init__.py
│   └── processing.py
│
├── navigation/
│   ├── __init__.py
│   ├── astar.py
│   └── dwa.py
│
├── control/
│   ├── __init__.py
│   └── controller.py
│
├── mapping/
│   ├── __init__.py
│   └── occupancy_grid.py
│
├── ai/
│   ├── __init__.py
│   ├── train.py
│   └── inference.py
│
├── data/
│   ├── images/
│   ├── datasets/
│   ├── logs/
│   └── results/
│
└── utils/
    ├── __init__.py
    └── math_utils.py

اما الان قرار نیست همه این فایل‌ها را پر کنیم.

این فقط اسکلت ساختمان پروژه است.

مثل این است که قبل از ساخت کارخانه، ابتدا نقشه ساختمان را مشخص کنیم.


چرا این جلسه برای دوره خیلی مهم است؟

چون اگر این قسمت را حذف کنیم، احتمالاً در فصل‌های بعد به یک مشکل جدی می‌رسیم:

یک فایل 5000 خطی

که در آن:

PyBullet
+
AGV
+
LiDAR
+
Camera
+
OpenCV
+
A*
+
AI

همه قاطی شده‌اند.

اما ما از همین ابتدا به شما یاد می‌دهیم:

هر قسمت پروژه مسئولیت خودش را دارد.

مثلاً:

robot/
→ ربات

sensors/
→ سنسورها

vision/
→ بینایی ماشین

mapping/
→ نقشه

navigation/
→ مسیریابی

control/
→ کنترل

ai/
→ هوش مصنوعی

و این دقیقاً همان چیزی است که بعدها در یک پروژه واقعی رباتیک به آن نیاز خواهیم داشت.


Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top