Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .coverage
Binary file not shown.
87 changes: 51 additions & 36 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
@@ -1,92 +1,107 @@
name: colav_protobuf CI/CD
run-name: ${{ github.actor }} is testing colav_protobuf pypi pkg 🚀
name: riskenv CI/CD
run-name: ${{ github.actor }} — riskenv build, test & publish 🚀

on:
push:
branches: [main]
branches: [stable]
pull_request:
branches: [main]
branches: [stable]
release:
types: [published]

jobs:
# -------------------------------------------------------------------------
build:
# -------------------------------------------------------------------------
name: Build Package
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.11"

- name: Install Build Tools
- name: Install build tools
run: |
pip install --upgrade pip
pip install hatch protobuf>=5.29.3
pip install hatch

- name: Build colav_protobuf Package
- name: Build riskenv
run: hatch build

- name: Upload Build Artifacts
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist-files
path: dist/

# -------------------------------------------------------------------------
test:
# -------------------------------------------------------------------------
name: Run Tests
needs: build
runs-on: ubuntu-latest

strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]

steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: ${{ matrix.python-version }}

- name: Install Dependencies
- name: Install riskenv and test dependencies
run: |
pip install --upgrade pip
pip install -r requirements.txt
pip install hatch protobuf>=5.29.3
pip install -e ".[test]"
pip install pytest pytest-cov

- name: Run unit tests
run: |
python -m pytest tests/unit_tests/ -v --tb=short

# - name: Run Tests
# run: hatch test
- name: Run integration tests
run: |
python -m pytest tests/integration_tests/ -v --tb=short

- name: Upload coverage report
if: matrix.python-version == '3.11'
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: .coverage

# -------------------------------------------------------------------------
deploy:
name: Publish to PyPI if Release
# -------------------------------------------------------------------------
name: Publish to PyPI
needs: test
if: github.event_name == 'release'
runs-on: ubuntu-latest

permissions:
id-token: write # required for PyPI trusted publishing

steps:
- name: Checkout Repository
uses: actions/checkout@v4
- uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
uses: actions/setup-python@v5
with:
python-version: "3.10"
python-version: "3.11"

- name: Download Build Artifacts
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist-files
path: dist/

- name: Install Twine
run: pip install twine

- name: Publish to PyPI
env:
PYPI_USERNAME: __token__
PYPI_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
run: |
twine upload dist/* -u "${PYPI_USERNAME}" -p "${PYPI_PASSWORD}" --non-interactive --verbose
uses: pypa/gh-action-pypi-publish@release/v1
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
./colav_unsafe_set_gen.egg-info/
./.vscode/
**/__pycache__/
**/.pytest_cache/
**/.pytest_cache/
./.claude
22 changes: 22 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"configurations": [
{
"browse": {
"databaseFilename": "${workspaceFolder}/.vscode/browse.vc.db",
"limitSymbolsToIncludedHeaders": false
},
"includePath": [
"/home/ryan/ros2_ws/install/hybraut_interfaces/include/**",
"/home/ryan/ros2_ws/install/colav_interfaces/include/**",
"/opt/ros/jazzy/include/**",
"/usr/include/**"
],
"name": "ros2",
"intelliSenseMode": "gcc-x64",
"compilerPath": "/usr/bin/gcc",
"cStandard": "gnu11",
"cppStandard": "c++17"
}
],
"version": 4
}
11 changes: 11 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"ROS2.distro": "jazzy",
"python.autoComplete.extraPaths": [
"/home/ryan/ros2_ws/install/hybraut_tb3/lib/python3.12/site-packages",
"/home/ryan/ros2_ws/install/hybraut_ros2/lib/python3.12/site-packages",
"/home/ryan/ros2_ws/install/hybraut_nav/lib/python3.12/site-packages",
"/home/ryan/ros2_ws/install/hybraut_interfaces/lib/python3.12/site-packages",
"/home/ryan/ros2_ws/install/colav_interfaces/lib/python3.12/site-packages",
"/opt/ros/jazzy/lib/python3.12/site-packages"
]
}
118 changes: 85 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,122 @@
# colav-unsafe-set
# riskenv

[![PyPI - Version](https://img.shields.io/pypi/v/colav-unsafe-set.svg)](https://pypi.org/project/colav-unsafe-set)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/colav-unsafe-set.svg)](https://pypi.org/project/colav-unsafe-set)
<!--[![PyPI - Protobuf Version]()]-->
This package contains implementation of the custom risk assesment method for motion planners called unsafe set as defined in paper [geometric motion planning in dynamic environments]().
The following is the high level equation which this package implements.
[![PyPI - Version](https://img.shields.io/pypi/v/riskenv.svg)](https://pypi.org/project/riskenv)
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/riskenv.svg)](https://pypi.org/project/riskenv)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

![image](./docs/unsafe_set_calculation.png)
**riskenv** computes the **Risk Envelope** — a convex hull bounding all collision-risk regions around an agent given a set of moving obstacles. It is designed for real-time motion planning in dynamic environments and works in both 2D and 3D spatial contexts across any application domain (autonomous vessels, ground robots, UAVs, etc.).

Show image of geometric unsafe set:
![image](./docs/unsafe_set_diagram.png)
The Risk Envelope is defined by three filtering criteria (Indices of Interest I1, I2, I3) derived from closest-point-of-approach (CPA) geometry, and is returned as a set of convex hull vertices ready for use in a downstream planner.

![Risk Envelope equation](./docs/unsafe_set_calculation.png)

![Risk Envelope diagram](./docs/unsafe_set_diagram.png)

-----

## Table of Contents

- [Installation](#installation)
- [Structure](#structure)
- [Usage](#usage)
- [CPA metric summary](#cpa-metric-summary)
- [Structure](#structure)
- [References](#references)
- [License](#license)

## Installation

```bash
pip install colav-unsafe-set
pip install riskenv
```

risk assessment collision metrics summary
Requires Python ≥ 3.10.

| Case | v_rel_norm_sq | p_rel == [0, 0] | tcpa > 0 | DCPA | TCPA |
|-----------------------|---------------|-----------------|----------|------------------------------|---------------------------|
| 1.1 Identical position | < 1e-6 | ✅ | – | NaN | inf |
| 1.2 Stationary, offset | < 1e-6 | ❌ | – | ‖p_rel‖ | ‖p_rel‖ / ‖v1‖ (or inf) |
| 2.1 Future CPA | ≥ 1e-6 | – | ✅ | ‖p_rel + tcpa * v_rel‖ | computed tcpa |
| 2.2 CPA in past | ≥ 1e-6 | – | ❌ | NaN | NaN |
## Usage

### Minimal example

## Structure
```python
import math
from riskenv import create_unsafe_set, Agent, Obstacle

agent = Agent(
position=(10.0, 10.0), # (x, y) or (x, y, z) in metres
heading=0.0, # yaw angle in radians
speed=15.0, # m/s
yaw_rate=0.2, # rad/s
safety_radius=5.0, # metres
)

obstacles = [
Obstacle(
position=(30.0, 20.0),
heading=math.pi, # facing along -x axis
speed=20.0,
yaw_rate=0.1,
safety_radius=10.0,
tag='vessel_a',
),
]

# dsf: distance safety factor (metres) — the proximity threshold for I1/I2/I3
# time_of_interest: TCPA horizon in seconds for the I3 filter (default 15 s)
# Returns: list of [x, y] hull vertices, or [] if no risk region exists.
vertices = create_unsafe_set(agent=agent, obstacles=obstacles, dsf=10.0)
```

## Usage
When pkg is installed, Using it is simple imports are as follows.
### Quaternion users

Convert to a heading angle before constructing `Agent` or `Obstacle`:

```python
from riskenv import heading_from_quaternion

heading = heading_from_quaternion(qx, qy, qz, qw)
```

### Lower-level API

## Collaborators
All internal building blocks are importable directly from the top-level package:

This repository is developed and maintained by:
```python
from riskenv import (
calc_cpa, # DCPA / TCPA for a single obstacle
calculate_obstacle_metrics_for_agent, # annotate all obstacles with CPA metrics
predict_position, # dead-reckoning position at time dt
calc_I1, calc_I2, calc_I3, # individual index-of-interest filters
unionise_indices_of_interest, # merge I1 / I2 / I3 without duplicates
gen_uIoI_convhull, # convex hull from a union set
ObstacleWithMetrics, # Obstacle + tcpa/dcpa container
)
```

- **[Ryan McKee](https://github.com/rymc-dev)**
- **[Nikolaos Athanasopoulos](https://github.com/<username>)**
- **[Wasif Naeem](https://github.com/<username>)**
## CPA metric summary

| Case | v\_rel\_norm\_sq | p\_rel == [0, 0] | tcpa > 0 | DCPA | TCPA |
|---|---|---|---|---|---|
| 1.1 Identical position, same velocity | < 1e-6 | ✅ | – | NaN | inf |
| 1.2 Zero relative velocity, offset | < 1e-6 | ❌ | – | ‖p\_rel‖ | ‖p\_rel‖ / ‖v1‖ (or inf) |
| 2.1 Future CPA | ≥ 1e-6 | – | ✅ | ‖p\_rel + tcpa · v\_rel‖ | computed tcpa |
| 2.2 CPA in past or at t=0 | ≥ 1e-6 | – | ❌ | NaN | NaN |

## References
## Structure

This project provides a Python implementation of the unsafe set risk-assessment method for motion planning as defined in the following research work:
```
src/riskenv/
├── objects/ — Agent, Obstacle, ObstacleWithMetrics
├── risk_assessment/ — calc_cpa, heading_from_quaternion, calculate_obstacle_metrics_for_agent
├── indices_of_interest/ — calc_I1, calc_I2, calc_I3, unionise_indices_of_interest
├── collision_geometry/ — gen_uIoI_convhull
├── position_prediction/ — predict_position
└── unsafe_set/ — create_unsafe_set (top-level orchestrator)
```

### Primary Reference (Unsafe Set Definition)
## References

> A. Author(s), *“Geometric Motion Planning in Dynamic Environments,”* Journal/Conference, Year.
> *(Replace with full citation once available.)*
This package implements the Risk Envelope method for motion planning originally described in:

If the specific paper is published, provide the full citation (title, authors, venue, year), and I can format it in **IEEE**, **APA**, or **BibTeX** style.
> R. McKee, N. Athanasopoulos, W. Naeem, *"Geometric Motion Planning in Dynamic Environments"* — *(citation pending publication)*

## License

`colav-unsafe-set` is distributed under the terms of the [MIT](https://github.com/RyanMcKeeQUB/colav-unsafe-set/tree/main/LICENSE) license.
`riskenv` is distributed under the terms of the [MIT](LICENSE) license.
Loading
Loading