Update to Bridges
This commit is contained in:
97
README.md
97
README.md
@@ -4,51 +4,83 @@ A GPS-denied drone landing simulation using relative sensors (IMU, altimeter, ca
|
|||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
### Standalone Mode (Any Platform)
|
||||||
# Install (choose your platform)
|
|
||||||
./setup/install_ubuntu.sh # Ubuntu/Debian
|
|
||||||
./setup/install_arch.sh # Arch Linux
|
|
||||||
./setup/install_macos.sh # macOS
|
|
||||||
.\setup\install_windows.ps1 # Windows (PowerShell)
|
|
||||||
|
|
||||||
# Activate and run
|
```bash
|
||||||
source activate.sh # Linux/macOS
|
source activate.sh # Linux/macOS
|
||||||
. .\activate.ps1 # Windows
|
. .\activate.ps1 # Windows
|
||||||
|
|
||||||
python standalone_simulation.py --pattern circular --speed 0.3
|
python standalone_simulation.py --pattern circular --speed 0.3
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Two-Terminal Mode - PyBullet (with ROS 2)
|
||||||
|
|
||||||
|
**Terminal 1 - Simulator:**
|
||||||
|
```bash
|
||||||
|
python simulation_host.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**Terminal 2 - Bridge + Controllers:**
|
||||||
|
```bash
|
||||||
|
python run_bridge.py --pattern circular --speed 0.3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Two-Terminal Mode - Gazebo (Linux only)
|
||||||
|
|
||||||
|
**Terminal 1 - Gazebo:**
|
||||||
|
```bash
|
||||||
|
gz sim gazebo/worlds/drone_landing.sdf
|
||||||
|
```
|
||||||
|
|
||||||
|
**Terminal 2 - Bridge + Controllers:**
|
||||||
|
```bash
|
||||||
|
python run_gazebo.py --pattern circular --speed 0.3
|
||||||
|
```
|
||||||
|
|
||||||
|
That's it! Only 2 terminals needed.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
| Platform | Command |
|
||||||
|
|----------|---------|
|
||||||
|
| Ubuntu/Debian | `./setup/install_ubuntu.sh` |
|
||||||
|
| Arch Linux | `./setup/install_arch.sh` |
|
||||||
|
| macOS | `./setup/install_macos.sh` |
|
||||||
|
| Windows | `.\setup\install_windows.ps1` |
|
||||||
|
|
||||||
## Platform Compatibility
|
## Platform Compatibility
|
||||||
|
|
||||||
| Feature | Ubuntu | Arch | macOS | Windows |
|
| Feature | Ubuntu | Arch | macOS | Windows | WSL2 |
|
||||||
|---------|--------|------|-------|---------|
|
|---------|--------|------|-------|---------|------|
|
||||||
| Standalone | ✅ | ✅ | ✅ | ✅ |
|
| Standalone | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
| ROS 2 | ✅ | ⚠️ | ❌ | ❌ |
|
| ROS 2 Mode | ✅ | ⚠️ | ❌ | ❌ | ✅ |
|
||||||
| Gazebo | ✅ | ⚠️ | ❌ | ❌ |
|
| Gazebo | ✅ | ⚠️ | ❌ | ❌ | ✅ |
|
||||||
|
|
||||||
**All platforms support standalone mode** - no ROS 2 required!
|
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
||||||
| File | Description |
|
| File | Description |
|
||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `standalone_simulation.py` | **All-in-one simulation (no ROS 2)** |
|
| `standalone_simulation.py` | All-in-one (no ROS 2 required) |
|
||||||
| `simulation_host.py` | PyBullet simulator (ROS 2 mode) |
|
| `simulation_host.py` | PyBullet simulator server |
|
||||||
| `ros_bridge.py` | UDP ↔ ROS 2 bridge |
|
| `run_bridge.py` | **PyBullet bridge + Controllers** |
|
||||||
| `gazebo_bridge.py` | Gazebo ↔ ROS 2 bridge |
|
| `run_gazebo.py` | **Gazebo bridge + Controllers** |
|
||||||
| `controllers.py` | Runs drone + rover controllers |
|
|
||||||
| `drone_controller.py` | Drone landing logic (edit this) |
|
| `drone_controller.py` | Drone landing logic (edit this) |
|
||||||
| `rover_controller.py` | Moving landing pad |
|
| `rover_controller.py` | Moving landing pad |
|
||||||
|
|
||||||
## Controller Options
|
## Options
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
python standalone_simulation.py --help
|
# Standalone
|
||||||
|
python standalone_simulation.py --pattern circular --speed 0.3
|
||||||
|
|
||||||
|
# ROS 2 mode
|
||||||
|
python run_bridge.py --pattern circular --speed 0.3 --host <SIMULATOR_IP>
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--pattern, -p stationary, linear, circular, square
|
--pattern, -p stationary, linear, circular, square, random
|
||||||
--speed, -s Speed in m/s (default: 0.5)
|
--speed, -s Speed in m/s (default: 0.5)
|
||||||
--amplitude, -a Amplitude in meters (default: 2.0)
|
--amplitude, -a Amplitude in meters (default: 2.0)
|
||||||
|
--host, -H Simulator IP (default: 0.0.0.0)
|
||||||
```
|
```
|
||||||
|
|
||||||
## GPS-Denied Sensors
|
## GPS-Denied Sensors
|
||||||
@@ -65,16 +97,21 @@ Options:
|
|||||||
|
|
||||||
| Document | Description |
|
| Document | Description |
|
||||||
|----------|-------------|
|
|----------|-------------|
|
||||||
| [Installation](docs/installation.md) | All platform setup guides |
|
| [Installation](docs/installation.md) | Platform setup guides + WSL2 |
|
||||||
| [Architecture](docs/architecture.md) | System components |
|
| [Architecture](docs/architecture.md) | System components |
|
||||||
| [Protocol](docs/protocol.md) | Sensor data formats |
|
| [Protocol](docs/protocol.md) | Sensor data formats |
|
||||||
| [Drone Guide](docs/drone_guide.md) | Landing algorithm guide |
|
| [Drone Guide](docs/drone_guide.md) | Landing algorithm guide |
|
||||||
| [PyBullet](docs/pybullet.md) | PyBullet setup |
|
|
||||||
| [Gazebo](docs/gazebo.md) | Gazebo setup (Linux) |
|
|
||||||
|
|
||||||
## Getting Started
|
## Network Setup (Remote Simulator)
|
||||||
|
|
||||||
1. Run `python standalone_simulation.py`
|
Run simulator on one machine, controllers on another:
|
||||||
2. Watch the drone land automatically
|
|
||||||
3. Edit `drone_controller.py` to implement your own algorithm
|
**Machine 1 (with display):**
|
||||||
4. Test: `python standalone_simulation.py --pattern circular`
|
```bash
|
||||||
|
python simulation_host.py # Listens on 0.0.0.0:5555
|
||||||
|
```
|
||||||
|
|
||||||
|
**Machine 2 (headless):**
|
||||||
|
```bash
|
||||||
|
python run_bridge.py --host 192.168.1.100 # Connect to Machine 1
|
||||||
|
```
|
||||||
105
build_exe.py
105
build_exe.py
@@ -1,9 +1,16 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
PyInstaller build script for simulation_host.py.
|
PyInstaller build script for drone simulation executables.
|
||||||
Creates a standalone executable that includes PyBullet and pybullet_data.
|
Creates standalone executables that include PyBullet and dependencies.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
python build_exe.py # Build standalone_simulation
|
||||||
|
python build_exe.py simulation_host # Build simulation_host
|
||||||
|
python build_exe.py standalone # Build standalone_simulation
|
||||||
|
python build_exe.py all # Build all
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
import sys
|
import sys
|
||||||
@@ -22,23 +29,20 @@ def get_pybullet_data_path() -> str:
|
|||||||
return pybullet_data.getDataPath()
|
return pybullet_data.getDataPath()
|
||||||
|
|
||||||
|
|
||||||
def build_executable():
|
def build_executable(source_name: str, output_name: str, console: bool = True):
|
||||||
print("=" * 60)
|
"""Build a single executable."""
|
||||||
print(" DRONE SIMULATION - BUILD EXECUTABLE")
|
|
||||||
print("=" * 60)
|
|
||||||
|
|
||||||
script_dir = Path(__file__).parent
|
script_dir = Path(__file__).parent
|
||||||
source_file = script_dir / "simulation_host.py"
|
source_file = script_dir / source_name
|
||||||
|
|
||||||
if not source_file.exists():
|
if not source_file.exists():
|
||||||
print(f"Error: {source_file} not found!")
|
print(f"Error: {source_file} not found!")
|
||||||
sys.exit(1)
|
return False
|
||||||
|
|
||||||
|
print(f"\nBuilding: {source_name} -> {output_name}")
|
||||||
|
print("-" * 40)
|
||||||
|
|
||||||
system = platform.system().lower()
|
system = platform.system().lower()
|
||||||
print(f"Platform: {system}")
|
|
||||||
|
|
||||||
pybullet_path = get_pybullet_data_path()
|
pybullet_path = get_pybullet_data_path()
|
||||||
print(f"PyBullet data path: {pybullet_path}")
|
|
||||||
|
|
||||||
if system == 'windows':
|
if system == 'windows':
|
||||||
separator = ';'
|
separator = ';'
|
||||||
@@ -51,43 +55,80 @@ def build_executable():
|
|||||||
str(source_file),
|
str(source_file),
|
||||||
'--onefile',
|
'--onefile',
|
||||||
'--clean',
|
'--clean',
|
||||||
'--name=simulation_host',
|
f'--name={output_name}',
|
||||||
f'--add-data={data_spec}',
|
f'--add-data={data_spec}',
|
||||||
]
|
]
|
||||||
|
|
||||||
if system == 'windows':
|
if console:
|
||||||
build_args.append('--windowed')
|
build_args.append('--console')
|
||||||
elif system == 'darwin':
|
else:
|
||||||
|
if system in ['windows', 'darwin']:
|
||||||
build_args.append('--windowed')
|
build_args.append('--windowed')
|
||||||
else:
|
else:
|
||||||
build_args.append('--console')
|
build_args.append('--console')
|
||||||
|
|
||||||
print("\nBuild configuration:")
|
|
||||||
for arg in build_args:
|
|
||||||
print(f" {arg}")
|
|
||||||
|
|
||||||
print("\nStarting PyInstaller build...")
|
|
||||||
print("-" * 60)
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
PyInstaller.__main__.run(build_args)
|
PyInstaller.__main__.run(build_args)
|
||||||
print("-" * 60)
|
|
||||||
print("\nBuild completed successfully!")
|
|
||||||
|
|
||||||
dist_dir = script_dir / "dist"
|
dist_dir = script_dir / "dist"
|
||||||
if system == 'windows':
|
if system == 'windows':
|
||||||
exe_path = dist_dir / "simulation_host.exe"
|
exe_path = dist_dir / f"{output_name}.exe"
|
||||||
elif system == 'darwin':
|
elif system == 'darwin' and not console:
|
||||||
exe_path = dist_dir / "simulation_host.app"
|
exe_path = dist_dir / f"{output_name}.app"
|
||||||
else:
|
else:
|
||||||
exe_path = dist_dir / "simulation_host"
|
exe_path = dist_dir / output_name
|
||||||
|
|
||||||
print(f"\nExecutable location: {exe_path}")
|
print(f" Created: {exe_path}")
|
||||||
|
return True
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"\nBuild failed: {e}")
|
print(f" Build failed: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description='Build simulation executables')
|
||||||
|
parser.add_argument(
|
||||||
|
'target',
|
||||||
|
nargs='?',
|
||||||
|
default='standalone',
|
||||||
|
choices=['standalone', 'simulation_host', 'all'],
|
||||||
|
help='What to build (default: standalone)'
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
print("=" * 60)
|
||||||
|
print(" DRONE SIMULATION - BUILD EXECUTABLES")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f"Platform: {platform.system()}")
|
||||||
|
print(f"PyBullet data: {get_pybullet_data_path()}")
|
||||||
|
|
||||||
|
success = True
|
||||||
|
|
||||||
|
if args.target in ['standalone', 'all']:
|
||||||
|
success &= build_executable(
|
||||||
|
'standalone_simulation.py',
|
||||||
|
'drone_simulation',
|
||||||
|
console=False
|
||||||
|
)
|
||||||
|
|
||||||
|
if args.target in ['simulation_host', 'all']:
|
||||||
|
success &= build_executable(
|
||||||
|
'simulation_host.py',
|
||||||
|
'simulation_host',
|
||||||
|
console=True
|
||||||
|
)
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("=" * 60)
|
||||||
|
if success:
|
||||||
|
print(" BUILD COMPLETE!")
|
||||||
|
print(" Executables in: dist/")
|
||||||
|
else:
|
||||||
|
print(" BUILD FAILED!")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
build_executable()
|
main()
|
||||||
|
|||||||
@@ -4,113 +4,107 @@ GPS-denied drone landing simulation with multiple operation modes.
|
|||||||
|
|
||||||
## Operation Modes
|
## Operation Modes
|
||||||
|
|
||||||
### Standalone Mode (Windows/Simple)
|
### 1. Standalone Mode (Any Platform)
|
||||||
|
|
||||||
All-in-one simulation - no ROS 2 or external dependencies:
|
Single-process simulation - no ROS 2 or networking required:
|
||||||
|
|
||||||
```
|
```
|
||||||
┌────────────────────────────────────────┐
|
┌────────────────────────────────────────┐
|
||||||
│ standalone_simulation.py │
|
│ standalone_simulation.py │
|
||||||
│ ┌──────────────────────────────────┐ │
|
│ ┌──────────────────────────────────┐ │
|
||||||
│ │ PyBullet Physics Engine │ │
|
│ │ PyBullet Physics + Camera │ │
|
||||||
│ │ ┌────────┐ ┌────────────────┐ │ │
|
│ │ Built-in Landing Controller │ │
|
||||||
│ │ │ Drone │ │ Landing Pad │ │ │
|
│ │ Rover Movement Patterns │ │
|
||||||
│ │ └────────┘ └────────────────┘ │ │
|
|
||||||
│ ├──────────────────────────────────┤ │
|
|
||||||
│ │ Built-in Controller │ │
|
|
||||||
│ │ • Landing algorithm │ │
|
|
||||||
│ │ • Rover movement patterns │ │
|
|
||||||
│ └──────────────────────────────────┘ │
|
│ └──────────────────────────────────┘ │
|
||||||
└────────────────────────────────────────┘
|
└────────────────────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
### Full Mode (Linux + ROS 2)
|
### 2. PyBullet + ROS 2 Mode (2 Terminals)
|
||||||
|
|
||||||
Modular architecture for development and testing:
|
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────────────────┐
|
Terminal 1 Terminal 2
|
||||||
│ Simulation System │
|
┌──────────────────┐ ┌──────────────────────────┐
|
||||||
├─────────────────────────────────────────────────────────────────────────┤
|
│ simulation_host │◄─UDP───►│ run_bridge.py │
|
||||||
│ │
|
│ (PyBullet) │ │ ┌────────────────────┐ │
|
||||||
│ ┌──────────────────┐ ┌──────────────────────────┐ │
|
│ Port 5555 │ │ │ ROS2SimulatorBridge│ │
|
||||||
│ │ simulation_host │◄── UDP:5555 ──────►│ ros_bridge.py │ │
|
│ │ │ │ DroneController │ │
|
||||||
│ │ (PyBullet) │ │ (UDP ↔ ROS Bridge) │ │
|
│ │ │ │ RoverController │ │
|
||||||
│ └──────────────────┘ └────────────┬─────────────┘ │
|
└──────────────────┘ │ └────────────────────┘ │
|
||||||
│ OR │ │
|
└──────────────────────────┘
|
||||||
│ ┌──────────────────┐ ┌────────────┴─────────────┐ │
|
```
|
||||||
│ │ Gazebo │◄── ROS Topics ────►│ gazebo_bridge.py │ │
|
|
||||||
│ │ (Linux only) │ │ (Gazebo ↔ ROS Bridge) │ │
|
### 3. Gazebo + ROS 2 Mode (2 Terminals, Linux Only)
|
||||||
│ └──────────────────┘ └────────────┬─────────────┘ │
|
|
||||||
│ │ │
|
```
|
||||||
│ ┌────────────▼─────────────┐ │
|
Terminal 1 Terminal 2
|
||||||
│ │ controllers.py │ │
|
┌──────────────────┐ ┌──────────────────────────┐
|
||||||
│ │ ┌─────────────────────┐ │ │
|
│ Gazebo │◄─ROS───►│ run_gazebo.py │
|
||||||
│ │ │ DroneController │ │ │
|
│ (gz sim ...) │ │ ┌────────────────────┐ │
|
||||||
│ │ │ RoverController │ │ │
|
│ │ │ │ GazeboBridge │ │
|
||||||
│ │ └─────────────────────┘ │ │
|
│ │ │ │ DroneController │ │
|
||||||
│ └──────────────────────────┘ │
|
│ │ │ │ RoverController │ │
|
||||||
└─────────────────────────────────────────────────────────────────────────┘
|
└──────────────────┘ │ └────────────────────┘ │
|
||||||
|
└──────────────────────────┘
|
||||||
```
|
```
|
||||||
|
|
||||||
## Components
|
## Components
|
||||||
|
|
||||||
### Standalone
|
| File | Description |
|
||||||
|
|------|-------------|
|
||||||
|
| `standalone_simulation.py` | All-in-one simulation |
|
||||||
|
| `simulation_host.py` | PyBullet physics server (UDP) |
|
||||||
|
| `run_bridge.py` | PyBullet bridge + controllers |
|
||||||
|
| `run_gazebo.py` | Gazebo bridge + controllers |
|
||||||
|
| `drone_controller.py` | Landing algorithm |
|
||||||
|
| `rover_controller.py` | Moving landing pad |
|
||||||
|
|
||||||
| Component | Description |
|
## ROS Topics
|
||||||
|-----------|-------------|
|
|
||||||
| **standalone_simulation.py** | Complete simulation with built-in controller |
|
|
||||||
|
|
||||||
### Full Mode
|
| Topic | Type | Description |
|
||||||
|
|-------|------|-------------|
|
||||||
|
| `/cmd_vel` | `Twist` | Drone velocity commands |
|
||||||
|
| `/drone/telemetry` | `String` | GPS-denied sensor data |
|
||||||
|
| `/rover/telemetry` | `String` | Rover position (internal) |
|
||||||
|
|
||||||
| Component | Description |
|
## Network Configuration
|
||||||
|-----------|-------------|
|
|
||||||
| **PyBullet** (`simulation_host.py`) | Physics engine, UDP networking, camera |
|
|
||||||
| **Gazebo** | Full robotics simulator (Linux only) |
|
|
||||||
| **ros_bridge.py** | Connects PyBullet ↔ ROS 2 via UDP |
|
|
||||||
| **gazebo_bridge.py** | Connects Gazebo ↔ ROS 2 |
|
|
||||||
| **controllers.py** | Runs drone + rover controllers |
|
|
||||||
| **drone_controller.py** | GPS-denied landing logic |
|
|
||||||
| **rover_controller.py** | Moving landing pad patterns |
|
|
||||||
|
|
||||||
## ROS Topics (Full Mode)
|
All components default to `0.0.0.0` for network accessibility.
|
||||||
|
|
||||||
| Topic | Type | Publisher | Subscriber |
|
### Remote Setup
|
||||||
|-------|------|-----------|------------|
|
|
||||||
| `/cmd_vel` | `Twist` | DroneController | Bridge |
|
|
||||||
| `/drone/telemetry` | `String` | Bridge | DroneController |
|
|
||||||
| `/rover/telemetry` | `String` | RoverController | DroneController |
|
|
||||||
|
|
||||||
## GPS-Denied Sensor Flow
|
**Machine 1 (with display):**
|
||||||
|
```bash
|
||||||
```
|
python simulation_host.py # Listens on 0.0.0.0:5555
|
||||||
Simulator Bridge DroneController
|
|
||||||
│ │ │
|
|
||||||
│ Render Camera │ │
|
|
||||||
│ Compute Physics │ │
|
|
||||||
│──────────────────────►│ │
|
|
||||||
│ │ GPS-Denied Sensors: │
|
|
||||||
│ │ - IMU │
|
|
||||||
│ │ - Altimeter │
|
|
||||||
│ │ - Velocity │
|
|
||||||
│ │ - Camera Image │
|
|
||||||
│ │ - Landing Pad Detection │
|
|
||||||
│ │─────────────────────────►│
|
|
||||||
│ │ /cmd_vel │
|
|
||||||
│◄──────────────────────│◄─────────────────────────│
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Platform Support
|
**Machine 2 (headless controller):**
|
||||||
|
```bash
|
||||||
|
python run_bridge.py --host 192.168.1.100
|
||||||
|
```
|
||||||
|
|
||||||
| Mode | Windows | Linux | macOS |
|
### UDP Ports
|
||||||
|------|---------|-------|-------|
|
|
||||||
| Standalone | ✅ | ✅ | ✅ |
|
|
||||||
| Full (ROS 2) | ⚠️ | ✅ | ⚠️ |
|
|
||||||
| Gazebo | ❌ | ✅ | ❌ |
|
|
||||||
|
|
||||||
## UDP Protocol (Full Mode)
|
|
||||||
|
|
||||||
| Port | Direction | Content |
|
| Port | Direction | Content |
|
||||||
|------|-----------|---------|
|
|------|-----------|---------|
|
||||||
| 5555 | Bridge → Simulator | Command JSON |
|
| 5555 | Bridge → Simulator | Commands |
|
||||||
| 5556 | Simulator → Bridge | Telemetry JSON |
|
| 5556 | Simulator → Bridge | Telemetry |
|
||||||
|
|
||||||
|
## GPS-Denied Sensors
|
||||||
|
|
||||||
|
All modes provide the same sensor data:
|
||||||
|
|
||||||
|
| Sensor | Data |
|
||||||
|
|--------|------|
|
||||||
|
| IMU | Orientation, angular velocity |
|
||||||
|
| Altimeter | Altitude, vertical velocity |
|
||||||
|
| Velocity | Estimated from optical flow |
|
||||||
|
| Camera | 320x240 downward JPEG |
|
||||||
|
| Landing Pad | Relative position (when visible) |
|
||||||
|
|
||||||
|
## Platform Support
|
||||||
|
|
||||||
|
| Mode | Ubuntu | Arch | macOS | Windows | WSL2 |
|
||||||
|
|------|--------|------|-------|---------|------|
|
||||||
|
| Standalone | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||||
|
| PyBullet+ROS | ✅ | ⚠️ | ❌ | ❌ | ✅ |
|
||||||
|
| Gazebo+ROS | ✅ | ⚠️ | ❌ | ❌ | ✅ |
|
||||||
|
|||||||
159
docs/gazebo.md
159
docs/gazebo.md
@@ -1,17 +1,8 @@
|
|||||||
# Gazebo Simulation
|
# Gazebo Simulation
|
||||||
|
|
||||||
Running the GPS-denied drone simulation with Gazebo.
|
Running the GPS-denied drone simulation with Gazebo (Linux only).
|
||||||
|
|
||||||
## Prerequisites
|
## Quick Start (2 Terminals)
|
||||||
|
|
||||||
Install Gazebo and ROS-Gazebo bridge:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./setup/install_ubuntu.sh
|
|
||||||
source activate.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
**Terminal 1 - Start Gazebo:**
|
**Terminal 1 - Start Gazebo:**
|
||||||
```bash
|
```bash
|
||||||
@@ -19,86 +10,54 @@ source activate.sh
|
|||||||
gz sim gazebo/worlds/drone_landing.sdf
|
gz sim gazebo/worlds/drone_landing.sdf
|
||||||
```
|
```
|
||||||
|
|
||||||
**Terminal 2 - Spawn drone and start bridge:**
|
**Terminal 2 - Run Controllers:**
|
||||||
```bash
|
```bash
|
||||||
source activate.sh
|
source activate.sh
|
||||||
|
python run_gazebo.py --pattern circular --speed 0.3
|
||||||
|
```
|
||||||
|
|
||||||
# Spawn drone
|
## Options
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python run_gazebo.py --help
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--pattern stationary, linear, circular, square, random
|
||||||
|
--speed, -s Rover speed in m/s (default: 0.5)
|
||||||
|
--amplitude, -a Movement amplitude (default: 2.0)
|
||||||
|
--no-rover Disable rover controller
|
||||||
|
```
|
||||||
|
|
||||||
|
## Spawning the Drone
|
||||||
|
|
||||||
|
If the drone isn't in the world, spawn it:
|
||||||
|
|
||||||
|
```bash
|
||||||
gz service -s /world/drone_landing_world/create \
|
gz service -s /world/drone_landing_world/create \
|
||||||
--reqtype gz.msgs.EntityFactory \
|
--reqtype gz.msgs.EntityFactory \
|
||||||
--reptype gz.msgs.Boolean \
|
--reptype gz.msgs.Boolean \
|
||||||
--req 'sdf_filename: "gazebo/models/drone/model.sdf", name: "drone"'
|
--req 'sdf_filename: "gazebo/models/drone/model.sdf", name: "drone"'
|
||||||
|
|
||||||
# Start bridge
|
|
||||||
python gazebo_bridge.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
**Terminal 3 - Run controllers:**
|
## GPS-Denied Sensors
|
||||||
```bash
|
|
||||||
source activate.sh
|
|
||||||
python controllers.py --pattern circular --speed 0.3
|
|
||||||
```
|
|
||||||
|
|
||||||
## World Description
|
The `run_gazebo.py` script provides the same sensor interface as PyBullet:
|
||||||
|
|
||||||
The `drone_landing.sdf` world contains:
|
| Sensor | Source |
|
||||||
|
|--------|--------|
|
||||||
| Object | Description |
|
| IMU | Gazebo odometry |
|
||||||
|--------|-------------|
|
| Altimeter | Gazebo Z position |
|
||||||
| Ground Plane | Infinite flat surface |
|
| Velocity | Gazebo twist |
|
||||||
| Sun | Directional light with shadows |
|
| Camera | Gazebo camera sensor |
|
||||||
| Landing Pad | Green box with "H" marker at origin |
|
| Landing Pad | Computed from relative position |
|
||||||
|
|
||||||
## Drone Model
|
|
||||||
|
|
||||||
Quadrotor drone with:
|
|
||||||
|
|
||||||
- **Body**: 0.3m × 0.3m × 0.1m, 1.0 kg
|
|
||||||
- **Rotors**: 4 spinning rotors
|
|
||||||
- **IMU**: Orientation and angular velocity
|
|
||||||
- **Camera**: 320x240 downward-facing sensor
|
|
||||||
- **Odometry**: Position and velocity
|
|
||||||
|
|
||||||
### Gazebo Plugins
|
|
||||||
|
|
||||||
| Plugin | Function |
|
|
||||||
|--------|----------|
|
|
||||||
| MulticopterMotorModel | Motor dynamics |
|
|
||||||
| MulticopterVelocityControl | Velocity commands |
|
|
||||||
| OdometryPublisher | Pose and twist |
|
|
||||||
|
|
||||||
## Camera System
|
|
||||||
|
|
||||||
The drone has a downward-facing camera:
|
|
||||||
|
|
||||||
| Property | Value |
|
|
||||||
|----------|-------|
|
|
||||||
| Resolution | 320 x 240 |
|
|
||||||
| FOV | 60 degrees |
|
|
||||||
| Format | Base64 encoded JPEG |
|
|
||||||
| Update Rate | 30 Hz (Gazebo) / ~5 Hz (in telemetry) |
|
|
||||||
| Topic | `/drone/camera` |
|
|
||||||
|
|
||||||
## Gazebo Topics
|
## Gazebo Topics
|
||||||
|
|
||||||
| Topic | Type | Description |
|
| Topic | Type | Description |
|
||||||
|-------|------|-------------|
|
|-------|------|-------------|
|
||||||
| `/drone/cmd_vel` | `gz.msgs.Twist` | Velocity commands |
|
| `/drone/cmd_vel` | `Twist` | Velocity commands |
|
||||||
| `/model/drone/odometry` | `gz.msgs.Odometry` | Drone state |
|
| `/model/drone/odometry` | `Odometry` | Drone state |
|
||||||
| `/drone/camera` | `gz.msgs.Image` | Camera images |
|
| `/drone/camera` | `Image` | Camera images |
|
||||||
| `/drone/imu` | `gz.msgs.IMU` | IMU data |
|
|
||||||
|
|
||||||
## GPS-Denied Sensors
|
|
||||||
|
|
||||||
The `gazebo_bridge.py` converts Gazebo data to GPS-denied sensor format:
|
|
||||||
|
|
||||||
| Sensor | Source |
|
|
||||||
|--------|--------|
|
|
||||||
| IMU | Odometry orientation + angular velocity |
|
|
||||||
| Altimeter | Odometry Z position |
|
|
||||||
| Velocity | Odometry twist |
|
|
||||||
| Camera | Camera sensor (base64 JPEG) |
|
|
||||||
| Landing Pad | Computed from relative position |
|
|
||||||
|
|
||||||
## Headless Mode
|
## Headless Mode
|
||||||
|
|
||||||
@@ -108,51 +67,29 @@ Run without GUI:
|
|||||||
gz sim -s gazebo/worlds/drone_landing.sdf
|
gz sim -s gazebo/worlds/drone_landing.sdf
|
||||||
```
|
```
|
||||||
|
|
||||||
## Using the Launch File
|
|
||||||
|
|
||||||
For ROS 2 packages:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ros2 launch <package_name> drone_landing.launch.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### "Cannot connect to display"
|
|
||||||
|
|
||||||
```bash
|
|
||||||
export DISPLAY=:0
|
|
||||||
# or use headless mode
|
|
||||||
gz sim -s gazebo/worlds/drone_landing.sdf
|
|
||||||
```
|
|
||||||
|
|
||||||
### Drone falls immediately
|
|
||||||
|
|
||||||
The velocity controller may need to be enabled:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
gz topic -t /drone/enable -m gz.msgs.Boolean -p 'data: true'
|
|
||||||
```
|
|
||||||
|
|
||||||
### Topics not visible in ROS
|
|
||||||
|
|
||||||
Ensure the bridge is running:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python gazebo_bridge.py
|
|
||||||
```
|
|
||||||
|
|
||||||
### Model not found
|
### Model not found
|
||||||
|
|
||||||
Set the model path:
|
Set the model path:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
export GZ_SIM_RESOURCE_PATH=$PWD/gazebo/models:$GZ_SIM_RESOURCE_PATH
|
export GZ_SIM_RESOURCE_PATH=$PWD/gazebo/models:$GZ_SIM_RESOURCE_PATH
|
||||||
```
|
```
|
||||||
|
|
||||||
### Camera image not in telemetry
|
### Drone falls immediately
|
||||||
|
|
||||||
Ensure PIL/Pillow is installed:
|
Enable the velocity controller:
|
||||||
```bash
|
```bash
|
||||||
pip install pillow
|
gz topic -t /drone/enable -m gz.msgs.Boolean -p 'data: true'
|
||||||
|
```
|
||||||
|
|
||||||
|
### "Cannot connect to display"
|
||||||
|
|
||||||
|
Use headless mode or WSLg:
|
||||||
|
```bash
|
||||||
|
# Headless
|
||||||
|
gz sim -s gazebo/worlds/drone_landing.sdf
|
||||||
|
|
||||||
|
# Or ensure DISPLAY is set
|
||||||
|
export DISPLAY=:0
|
||||||
```
|
```
|
||||||
|
|||||||
107
docs/pybullet.md
107
docs/pybullet.md
@@ -2,25 +2,19 @@
|
|||||||
|
|
||||||
Running the GPS-denied drone simulation with PyBullet.
|
Running the GPS-denied drone simulation with PyBullet.
|
||||||
|
|
||||||
## Windows (Standalone Mode)
|
## Standalone Mode (Recommended)
|
||||||
|
|
||||||
No ROS 2 required! Run the all-in-one simulation:
|
No ROS 2 required! Single terminal:
|
||||||
|
|
||||||
```powershell
|
```bash
|
||||||
. .\activate.ps1
|
source activate.sh
|
||||||
python standalone_simulation.py
|
python standalone_simulation.py --pattern circular --speed 0.3
|
||||||
```
|
```
|
||||||
|
|
||||||
### Options
|
### Options
|
||||||
|
|
||||||
```powershell
|
```bash
|
||||||
# Stationary landing pad
|
python standalone_simulation.py --help
|
||||||
python standalone_simulation.py
|
|
||||||
|
|
||||||
# Moving rover patterns
|
|
||||||
python standalone_simulation.py --pattern circular --speed 0.3
|
|
||||||
python standalone_simulation.py --pattern linear --speed 0.5
|
|
||||||
python standalone_simulation.py --pattern square --speed 0.4
|
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
--pattern, -p stationary, linear, circular, square
|
--pattern, -p stationary, linear, circular, square
|
||||||
@@ -28,11 +22,11 @@ Options:
|
|||||||
--amplitude, -a Movement amplitude in meters (default: 2.0)
|
--amplitude, -a Movement amplitude in meters (default: 2.0)
|
||||||
```
|
```
|
||||||
|
|
||||||
The simulation includes a built-in landing controller. Watch the drone automatically land on the rover!
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Linux (Full ROS 2 Mode)
|
## ROS 2 Mode (2 Terminals)
|
||||||
|
|
||||||
|
For distributed or remote simulation:
|
||||||
|
|
||||||
**Terminal 1 - Simulator:**
|
**Terminal 1 - Simulator:**
|
||||||
```bash
|
```bash
|
||||||
@@ -40,36 +34,49 @@ source activate.sh
|
|||||||
python simulation_host.py
|
python simulation_host.py
|
||||||
```
|
```
|
||||||
|
|
||||||
**Terminal 2 - ROS Bridge:**
|
**Terminal 2 - Controllers:**
|
||||||
```bash
|
```bash
|
||||||
source activate.sh
|
source activate.sh
|
||||||
python ros_bridge.py
|
python run_bridge.py --pattern circular --speed 0.3
|
||||||
```
|
|
||||||
|
|
||||||
**Terminal 3 - Controllers:**
|
|
||||||
```bash
|
|
||||||
source activate.sh
|
|
||||||
python controllers.py --pattern circular --speed 0.3
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Remote Setup
|
### Remote Setup
|
||||||
|
|
||||||
Run simulator on one machine, controllers on another.
|
Run simulator on one machine, controllers on another:
|
||||||
|
|
||||||
**Machine 1 (with display):**
|
**Machine 1 (with display):**
|
||||||
```bash
|
```bash
|
||||||
python simulation_host.py
|
python simulation_host.py # Listens on 0.0.0.0:5555
|
||||||
```
|
```
|
||||||
|
|
||||||
**Machine 2 (headless):**
|
**Machine 2 (headless):**
|
||||||
```bash
|
```bash
|
||||||
source activate.sh
|
python run_bridge.py --host 192.168.1.100 --pattern circular
|
||||||
python ros_bridge.py --host <MACHINE_1_IP>
|
|
||||||
python controllers.py
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Building Standalone Executable
|
||||||
|
|
||||||
|
Create a distributable executable:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
source activate.sh
|
||||||
|
|
||||||
|
# Build standalone simulation (recommended)
|
||||||
|
python build_exe.py
|
||||||
|
|
||||||
|
# Build simulation_host
|
||||||
|
python build_exe.py simulation_host
|
||||||
|
|
||||||
|
# Build all
|
||||||
|
python build_exe.py all
|
||||||
|
```
|
||||||
|
|
||||||
|
Executables are created in `dist/`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Simulation Parameters
|
## Simulation Parameters
|
||||||
|
|
||||||
| Parameter | Value |
|
| Parameter | Value |
|
||||||
@@ -77,7 +84,7 @@ python controllers.py
|
|||||||
| Physics Rate | 240 Hz |
|
| Physics Rate | 240 Hz |
|
||||||
| Telemetry Rate | 24 Hz |
|
| Telemetry Rate | 24 Hz |
|
||||||
| Drone Mass | 1.0 kg |
|
| Drone Mass | 1.0 kg |
|
||||||
| Gravity | -9.81 m/s² |
|
| UDP Port | 5555 (commands), 5556 (telemetry) |
|
||||||
|
|
||||||
## GPS-Denied Sensors
|
## GPS-Denied Sensors
|
||||||
|
|
||||||
@@ -89,40 +96,18 @@ python controllers.py
|
|||||||
| Camera | 320x240 downward JPEG image |
|
| Camera | 320x240 downward JPEG image |
|
||||||
| Landing Pad | Vision-based relative position |
|
| Landing Pad | Vision-based relative position |
|
||||||
|
|
||||||
## Camera System
|
|
||||||
|
|
||||||
| Property | Value |
|
|
||||||
|----------|-------|
|
|
||||||
| Resolution | 320 x 240 |
|
|
||||||
| FOV | 60 degrees |
|
|
||||||
| Format | Base64 encoded JPEG |
|
|
||||||
| Direction | Downward-facing |
|
|
||||||
|
|
||||||
## World Setup
|
|
||||||
|
|
||||||
| Object | Position | Description |
|
|
||||||
|--------|----------|-------------|
|
|
||||||
| Ground | z = 0 | Infinite plane |
|
|
||||||
| Rover | (0, 0, 0.15) | 1m × 1m landing pad |
|
|
||||||
| Drone | (0, 0, 5) | Starting position |
|
|
||||||
|
|
||||||
## Building Executable
|
|
||||||
|
|
||||||
Create standalone executable:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
source activate.sh
|
|
||||||
python build_exe.py
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### "Cannot connect to X server"
|
### "Cannot connect to X server"
|
||||||
|
|
||||||
PyBullet requires a display:
|
PyBullet GUI requires a display:
|
||||||
- Run on machine with monitor
|
```bash
|
||||||
- Use X11 forwarding: `ssh -X user@host`
|
# Use virtual display
|
||||||
- Virtual display: `xvfb-run python simulation_host.py`
|
xvfb-run python standalone_simulation.py
|
||||||
|
|
||||||
|
# Or use X11 forwarding
|
||||||
|
ssh -X user@host
|
||||||
|
```
|
||||||
|
|
||||||
### Drone flies erratically
|
### Drone flies erratically
|
||||||
|
|
||||||
@@ -134,7 +119,7 @@ Kd = 0.2
|
|||||||
|
|
||||||
### Camera image not appearing
|
### Camera image not appearing
|
||||||
|
|
||||||
Ensure PIL/Pillow is installed:
|
Install Pillow:
|
||||||
```bash
|
```bash
|
||||||
pip install pillow
|
pip install pillow
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ from std_msgs.msg import String
|
|||||||
class ROS2SimulatorBridge(Node):
|
class ROS2SimulatorBridge(Node):
|
||||||
"""Bridges ROS 2 topics to UDP-based simulator."""
|
"""Bridges ROS 2 topics to UDP-based simulator."""
|
||||||
|
|
||||||
DEFAULT_SIMULATOR_HOST = '127.0.0.1'
|
DEFAULT_SIMULATOR_HOST = '0.0.0.0'
|
||||||
DEFAULT_SIMULATOR_PORT = 5555
|
DEFAULT_SIMULATOR_PORT = 5555
|
||||||
SOCKET_TIMEOUT = 0.1
|
SOCKET_TIMEOUT = 0.1
|
||||||
RECEIVE_BUFFER_SIZE = 4096
|
RECEIVE_BUFFER_SIZE = 4096
|
||||||
@@ -171,8 +171,10 @@ Examples:
|
|||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--host', '-H', type=str, default='127.0.0.1',
|
'--host', '-H',
|
||||||
help='IP address of the simulator (default: 127.0.0.1)'
|
type=str,
|
||||||
|
default='0.0.0.0',
|
||||||
|
help='Simulator host IP address (default: 0.0.0.0)'
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
'--port', '-p', type=int, default=5555,
|
'--port', '-p', type=int, default=5555,
|
||||||
|
|||||||
136
run_bridge.py
Normal file
136
run_bridge.py
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Run Bridge - Runs ROS bridge and controllers together.
|
||||||
|
Connects to PyBullet simulator running on another machine or terminal.
|
||||||
|
|
||||||
|
Usage: python run_bridge.py [--host HOST] [--pattern PATTERN] [--speed SPEED]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import rclpy
|
||||||
|
from rclpy.executors import MultiThreadedExecutor
|
||||||
|
|
||||||
|
from ros_bridge import ROS2SimulatorBridge
|
||||||
|
from drone_controller import DroneController
|
||||||
|
from rover_controller import RoverController, MovementPattern
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Run ROS bridge and controllers together',
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog="""
|
||||||
|
Examples:
|
||||||
|
python run_bridge.py # Connect to local simulator
|
||||||
|
python run_bridge.py --host 192.168.1.100 # Connect to remote simulator
|
||||||
|
python run_bridge.py --pattern circular # With moving rover
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--host', '-H', type=str, default='0.0.0.0',
|
||||||
|
help='Simulator host IP (default: 0.0.0.0)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--port', '-p', type=int, default=5555,
|
||||||
|
help='Simulator port (default: 5555)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--pattern', type=str, default='stationary',
|
||||||
|
choices=['stationary', 'linear', 'circular', 'random', 'square'],
|
||||||
|
help='Rover movement pattern (default: stationary)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--speed', '-s', type=float, default=0.5,
|
||||||
|
help='Rover speed in m/s (default: 0.5)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--amplitude', '-a', type=float, default=2.0,
|
||||||
|
help='Rover movement amplitude in meters (default: 2.0)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--no-rover', action='store_true',
|
||||||
|
help='Disable rover controller'
|
||||||
|
)
|
||||||
|
args, _ = parser.parse_known_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
print("=" * 60)
|
||||||
|
print(" ROS Bridge + Controllers")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f" Simulator: {args.host}:{args.port}")
|
||||||
|
print(f" Rover Pattern: {args.pattern}")
|
||||||
|
print(f" Rover Speed: {args.speed} m/s")
|
||||||
|
print("=" * 60)
|
||||||
|
print()
|
||||||
|
|
||||||
|
rclpy.init()
|
||||||
|
|
||||||
|
nodes = []
|
||||||
|
executor = MultiThreadedExecutor(num_threads=4)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Create bridge
|
||||||
|
bridge = ROS2SimulatorBridge(
|
||||||
|
simulator_host=args.host,
|
||||||
|
simulator_port=args.port
|
||||||
|
)
|
||||||
|
nodes.append(bridge)
|
||||||
|
executor.add_node(bridge)
|
||||||
|
print("[OK] ROS Bridge started")
|
||||||
|
|
||||||
|
# Create drone controller
|
||||||
|
drone = DroneController()
|
||||||
|
nodes.append(drone)
|
||||||
|
executor.add_node(drone)
|
||||||
|
print("[OK] Drone Controller started")
|
||||||
|
|
||||||
|
# Create rover controller (optional)
|
||||||
|
if not args.no_rover:
|
||||||
|
pattern = MovementPattern[args.pattern.upper()]
|
||||||
|
rover = RoverController(
|
||||||
|
pattern=pattern,
|
||||||
|
speed=args.speed,
|
||||||
|
amplitude=args.amplitude
|
||||||
|
)
|
||||||
|
nodes.append(rover)
|
||||||
|
executor.add_node(rover)
|
||||||
|
print("[OK] Rover Controller started")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("All systems running. Press Ctrl+C to stop.")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Handle Ctrl+C gracefully
|
||||||
|
def signal_handler(sig, frame):
|
||||||
|
print("\nShutting down...")
|
||||||
|
executor.shutdown()
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
executor.spin()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
print("Cleaning up...")
|
||||||
|
for node in nodes:
|
||||||
|
if hasattr(node, 'shutdown'):
|
||||||
|
node.shutdown()
|
||||||
|
node.destroy_node()
|
||||||
|
|
||||||
|
if rclpy.ok():
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
print("Shutdown complete.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
122
run_gazebo.py
Normal file
122
run_gazebo.py
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Run Gazebo Bridge - Runs Gazebo bridge and controllers together.
|
||||||
|
Connects to Gazebo simulation running in another terminal.
|
||||||
|
|
||||||
|
Usage: python run_gazebo.py [--pattern PATTERN] [--speed SPEED]
|
||||||
|
"""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import signal
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import rclpy
|
||||||
|
from rclpy.executors import MultiThreadedExecutor
|
||||||
|
|
||||||
|
from gazebo_bridge import GazeboBridge
|
||||||
|
from drone_controller import DroneController
|
||||||
|
from rover_controller import RoverController, MovementPattern
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Run Gazebo bridge and controllers together',
|
||||||
|
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||||
|
epilog="""
|
||||||
|
Examples:
|
||||||
|
python run_gazebo.py # Stationary rover
|
||||||
|
python run_gazebo.py --pattern circular # Circular movement
|
||||||
|
python run_gazebo.py --pattern square --speed 0.3
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--pattern', type=str, default='stationary',
|
||||||
|
choices=['stationary', 'linear', 'circular', 'random', 'square'],
|
||||||
|
help='Rover movement pattern (default: stationary)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--speed', '-s', type=float, default=0.5,
|
||||||
|
help='Rover speed in m/s (default: 0.5)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--amplitude', '-a', type=float, default=2.0,
|
||||||
|
help='Rover movement amplitude in meters (default: 2.0)'
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--no-rover', action='store_true',
|
||||||
|
help='Disable rover controller'
|
||||||
|
)
|
||||||
|
args, _ = parser.parse_known_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
print("=" * 60)
|
||||||
|
print(" Gazebo Bridge + Controllers")
|
||||||
|
print("=" * 60)
|
||||||
|
print(f" Rover Pattern: {args.pattern}")
|
||||||
|
print(f" Rover Speed: {args.speed} m/s")
|
||||||
|
print("=" * 60)
|
||||||
|
print()
|
||||||
|
|
||||||
|
rclpy.init()
|
||||||
|
|
||||||
|
nodes = []
|
||||||
|
executor = MultiThreadedExecutor(num_threads=4)
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Create Gazebo bridge
|
||||||
|
bridge = GazeboBridge()
|
||||||
|
nodes.append(bridge)
|
||||||
|
executor.add_node(bridge)
|
||||||
|
print("[OK] Gazebo Bridge started")
|
||||||
|
|
||||||
|
# Create drone controller
|
||||||
|
drone = DroneController()
|
||||||
|
nodes.append(drone)
|
||||||
|
executor.add_node(drone)
|
||||||
|
print("[OK] Drone Controller started")
|
||||||
|
|
||||||
|
# Create rover controller (optional)
|
||||||
|
if not args.no_rover:
|
||||||
|
pattern = MovementPattern[args.pattern.upper()]
|
||||||
|
rover = RoverController(
|
||||||
|
pattern=pattern,
|
||||||
|
speed=args.speed,
|
||||||
|
amplitude=args.amplitude
|
||||||
|
)
|
||||||
|
nodes.append(rover)
|
||||||
|
executor.add_node(rover)
|
||||||
|
print("[OK] Rover Controller started")
|
||||||
|
|
||||||
|
print()
|
||||||
|
print("All systems running. Press Ctrl+C to stop.")
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Handle Ctrl+C gracefully
|
||||||
|
def signal_handler(sig, frame):
|
||||||
|
print("\nShutting down...")
|
||||||
|
executor.shutdown()
|
||||||
|
|
||||||
|
signal.signal(signal.SIGINT, signal_handler)
|
||||||
|
|
||||||
|
executor.spin()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
print("Cleaning up...")
|
||||||
|
for node in nodes:
|
||||||
|
node.destroy_node()
|
||||||
|
|
||||||
|
if rclpy.ok():
|
||||||
|
rclpy.shutdown()
|
||||||
|
|
||||||
|
print("Shutdown complete.")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -20,7 +20,7 @@ import pybullet_data
|
|||||||
class DroneSimulator:
|
class DroneSimulator:
|
||||||
"""PyBullet-based drone simulation with camera."""
|
"""PyBullet-based drone simulation with camera."""
|
||||||
|
|
||||||
UDP_HOST = '127.0.0.1'
|
UDP_HOST = '0.0.0.0'
|
||||||
UDP_PORT = 5555
|
UDP_PORT = 5555
|
||||||
UDP_BUFFER_SIZE = 65535
|
UDP_BUFFER_SIZE = 65535
|
||||||
SOCKET_TIMEOUT = 0.001
|
SOCKET_TIMEOUT = 0.001
|
||||||
|
|||||||
Reference in New Issue
Block a user