Metadata-Version: 2.4
Name: zenmav
Version: 0.2.0
Summary: Pymavlink wrappers for easy drone control developped by Zenith Polymtl
Author-email: Colin Rousseau <colin.rousseau@polymtl.ca>
License-Expression: Apache-2.0
Project-URL: Homepage, https://github.com/zenith-polymtl/Zenmav
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: pymavlink
Requires-Dist: geopy
Requires-Dist: numpy
Requires-Dist: tomli
Requires-Dist: shapely
Requires-Dist: pyproj
Requires-Dist: matplotlib
Requires-Dist: pyserial
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: pytest-timeout; extra == "test"

# Zenmav – Drone Control Library

[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)

Zenmav is a lightweight Python wrapper that lets you write quick and simple scripts for ArduPilot-controlled drones in either SITL or real hardware.
Developed by **Zenith Polytechnique Montréal**.

**AI help for zenmav!**


[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/zenith-polymtl/Zenmav)

## Key Features

- **Simplified Pymavlink commands with feedback** – one-line TCP/UDP/serial connection with heartbeat check
- **Precise navigation and commands**
  - Global GPS waypoints with user-defined accuracy
  - Local NED waypoints relative to home
  - Real-time body-frame velocity control and yaw commands
  - Orbit around a point
  - RC override commands
- **Autonomous area scans**
  - Rectilinear / lawn-mower pattern
  - Spiral pattern
- **MAVLink commands and utilities**
  - Mode change, arming, takeoff, RTL
  - Reading, setting and downloading parameters
  - ArduPilot 4.6 / 4.7 parameter name compatibility
- **MAVLink connection relay** – TCP servers relaying the drone link to GCS applications and other scripts
- **Live telemetry** – local position, global GPS, heading, attitude, RC channels, battery voltage and current
- **Gimbal control** – modes, pitch/yaw angles, ROI pointing
- **Software fence** – TOML-defined keep-in polygon that triggers BRAKE or RTL on breach
- **CSV logging utilities**

## Safety Disclaimer

**Always test new scripts in SITL first.**
When flying a real aircraft you are solely responsible for airworthiness, regulatory compliance and safe operation.
Operate in a clear area, keep visual line-of-sight and have a manual RC transmitter ready to take over. It is highly recommended **NOT** to use the arming command inside a script for a real drone unless thoroughly tested.

---

## Installation

```bash
pip install zenmav
```

SITL users only need ArduPilot SITL running on port 5762 (the default connection string). It is possible to use other simulators such as Gazebo too: make sure you pass the right connection string when initializing `Zenmav()`.

---

## Quick Start

Below are three concise, copy-paste-ready examples that showcase different parts of Zenmav’s API.
Each script follows the same basic pattern — connect, set GUIDED, arm, take off, fly, RTL — while using a different feature set.

---

Example 1 — Fly to a local waypoint and come back
--------------------------------------------------

```python
# example_local_target.py

import time
from zenmav.core import Zenmav

def main() -> None:
    drone = Zenmav()                 # connect (default SITL TCP link)
    drone.set_mode("GUIDED")
    drone.arm()
    drone.takeoff(altitude=15)       # climb to 15 m above home

    # Fly 30 m North, 20 m East, D = -10 m (NED: Down is positive, so 10 m above home)
    print("Navigating to local waypoint …")
    drone.local_target([30, 20, -10])

    # Hold position 5 s
    time.sleep(5)

    # Return-to-Launch (waits for landing & disarm, then closes the connection)
    drone.RTL()

if __name__ == "__main__":
    main()
```

---

Example 2 — Quick lawnmower of a 50 m radius area
--------------------------------------------------

```python
# example_rectilinear_scan.py
# Demonstrates rectilinear_scan()

from zenmav.core import Zenmav

def main() -> None:
    drone = Zenmav(gps_thresh=3)     # waypoint-reach distance in metres, default is WP_RADIUS_M (WPNAV_RADIUS before ArduPilot 4.7) + 1 m
    drone.set_mode("GUIDED")
    drone.arm()
    drone.takeoff(altitude=25)       # climb to 25 m above home

    print("Starting rectilinear scan …")
    drone.rectilinear_scan(
        detection_width=8,           # 8 m sensor footprint, used as spacing between passes
        altitude=25,                 # scan altitude above home
        scan_radius=50,              # cover a 50 m radius around the current position
    )

    drone.RTL()

if __name__ == "__main__":
    main()
```

---

Example 3 — Log a GPS point to CSV and adjust cruise speed
-----------------------------------------------------------

```python
# example_csv_and_params.py
# Shows get_global_pos(), insert_coordinates_to_csv(), set_param() and speed_target()

import time
from pathlib import Path
from zenmav.core import Zenmav

CSV_FILE = Path("waypoints.csv")

def main() -> None:
    drone = Zenmav(ip="/dev/ttyACM0", baud=115200)  # example connection to a Pixhawk via USB
    drone.set_mode("GUIDED")
    drone.arm()
    drone.takeoff(altitude=10)

    # Grab current location (a wp object) and write it to CSV
    pos = drone.get_global_pos()
    pos.name = "Take-off point"
    drone.insert_coordinates_to_csv(CSV_FILE, pos)
    print(f"Saved {pos.lat}, {pos.lon}, {pos.alt} to {CSV_FILE}")

    # Slow the aircraft to 3 m/s (WP_SPD is in m/s, it was WPNAV_SPEED in cm/s before ArduPilot 4.7)
    drone.set_param("WP_SPD", 3.0)
    print("WP_SPD set to 3 m/s")

    # Fly forward at 3 m/s for ~3 s using body-frame velocity
    for _ in range(30):
        drone.speed_target([3, 0, 0])     # 3 m/s forward, level flight
        time.sleep(0.1)
    drone.speed_target([0, 0, 0])         # stop
    print("Short cruise complete")

    drone.RTL()

    # Optional: show CSV content
    print("\nCSV content:")
    with CSV_FILE.open() as fp:
        print(fp.read())

if __name__ == "__main__":
    main()
```

---

## ArduPilot 4.6 / 4.7 compatibility

Copter 4.7 renamed the waypoint navigation parameters from `WPNAV_*` to `WP_*` and converted them from centimetres to SI units (e.g. `WPNAV_RADIUS` in cm → `WP_RADIUS_M` in m, `WPNAV_SPEED` in cm/s → `WP_SPD` in m/s).
Zenmav detects the firmware version at connection and `get_param` / `set_param` accept both spellings: the name is translated for the connected firmware and the value stays in the units of the name you used. The full mapping is in `src/zenmav/zenparams.py`.

---

## Zenmav Docs

- API reference: [docs/zenmav.md](docs/zenmav.md)
- Software fence configuration: [docs/Boundary_TOML.md](docs/Boundary_TOML.md), with examples in [docs/fence_configs_examples](docs/fence_configs_examples)

---

## Troubleshooting

| Symptom                                           | Fix                                                                 |
| ------------------------------------------------- | ------------------------------------------------------------------- |
| Stuck on `Waiting for heartbeat...`               | Check connection string / port / firewall. `Still waiting for a vehicle heartbeat` lists the other MAVLink systems seen on the link |
| `PermissionError: ttyACM0`                        | `sudo usermod -aG dialout $USER`                                    |
| `RuntimeError: Could not read WP_RADIUS_M nor WPNAV_RADIUS` | Parameters can't be read: check the link, and that the vehicle runs ArduCopter |
| Waypoint never “reached”                          | Provide a larger `gps_thresh` at construction                       |
| Takeoff not working                               | Make sure to be in GUIDED mode and armed                            |

---

## Contributing

Issues and pull requests are welcome!
Please fork the repository, create a feature branch and open a PR when ready.

---

## Maintainer

To push a new version, update the version in pyproject.toml and CHANGELOG.md, run the tests (`python -m pytest tests`, and `python3 -m pytest tests/sitl` where ArduPilot SITL is built), then build with

``python3 -m build``

Check and upload only the new files (dist/ also holds older versions)

``twine check dist/zenmav-<version>*``

``twine upload dist/zenmav-<version>*``

Enter API Token (Currently only accessible by maintainer Colin Rousseau)
