Skip to content

Framework for simulating and evaluating autonomous ship collision avoidance (COLAV) control strategies. Developed as part of the Autoship Centre for Research-based Innovation (SFI). Open sourced in the autumn of 2025.

License

Notifications You must be signed in to change notification settings

NTNU-TTO/colav-simulator

Repository files navigation

colav-simulator

platform python version python version CI

This repository implements a framework for simulating and evaluating autonomous ship collision avoidance (COLAV) control strategies. The initial framework prototype is described in the CCTA2023 paper. As of September 2023, the simulation framework has been wrapped to be compatible with Gymnasium and Stable Baselines, such that you can now use it as a gym for training RL-agents.

The main functionality is contained in the Simulator class of simulator.py, which runs through fully defined scenarios generated by the ScenarioGenerator, which takes in scenario configuration files in .yaml format (examples under scenarios/). One can visualize the results underway and output the results for use in downstream applications. These main modules have default configurations specified under config/, change according to your own needs. The seacharts package is used to provide usage of Electronic Navigational Charts for visualization and anti-grounding purposes. See the examples and tests for simple tutorials on how to use the repository. The rrt-rs package enables grounding-free trajectory planning and behavior generations for vessels involved in a simulation. Lastly, the vimmjipda package can be used for enabling Multi-Target Tracking in the simulation.

The COLAVEnvironment represents the Gymnasium compatible environment wrapper around the above mentioned functionality that can be used to train reinforcement learning agents. Many example action and observation types exist under this colav_simulator/gym umbrella.

The framework uses the North-East coordinate system with values in meters, speed in meters per second, angles in radians, and angular velocities in radians per second.

Developed as part of the Autoship Centre for Research-based Innovation (SFI), and tested under a Unix-based operating system (Linux, macOS). Open sourced in the autumn of 2025.


Example visualization of a DRL-based MPC algorithm run in multiple evaluation episodes using the COLAVEnvironment Gymnasium functionality.


Example run of an anti-grounding tracking NMPC controller in the simulator.

Ecosystem

Downstream packages compatible with the colav-simulator for ship RRT and NMPC-based trajectory planning, target tracking and reinforcement learning include:

Dependencies

Are all outlined in the pyproject.toml file. The git modules are the following:

Generic Install Instructions

Install uv (https://docs.astral.sh/uv/getting-started/installation/):

curl -LsSf https://astral.sh/uv/install.sh | sh

Install project:

uv sync

To use seacharts in the simulator for map visualizations and grounding hazard considerations, you need to download .gdb files from https://kartkatalog.geonorge.no in UTM 32 or 33 (see https://github.com/trymte/seacharts for instructions), and put the unzipped .gdb-files e.g. in an enc_data/ folder of your choice. By default, the ScenarioGenerator will expand relative map data file paths to data/enc" unless absolute.

If you get troubles installing gdal, this might be due to:

To also install the rrt-star-lib and vimmjipda optional packages, do

uv sync --group optional

Test the installation by running any of the files under tests/, e.g.

uv run pytest tests/test_simulator.py

use these and the examples to get familiar with the simulator.

Citation

If you are using the colav_simulator in your work, please use the following citation:

@Article{Tengesdal2023sfse,
  author  = {Trym Tengesdal and Tor A. Johansen},
  journal = {7th IEEE Conference on Control Technology and Applications (CCTA)},
  title   = {Simulation Framework and Software Environment for Evaluating Automatic Ship Collision Avoidance Algorithms},
  year={2023},
  volume={},
  number={},
  pages={186-193},
  doi={10.1109/CCTA54093.2023.10252863},
}

If you are using RRTs from rrt-rs for ship behavior generation in your work based on A Comparative Study of Rapidly-exploring Random Tree Algorithms Applied to Ship Trajectory Planning and Behavior Generation, please also use the following citation:

@article{tengesdal2025comparative,
  title={A Comparative Study of Rapidly-exploring Random Tree Algorithms Applied to Ship Trajectory Planning and Behavior Generation},
  author={Tengesdal, Trym and Pedersen, Tom Arne and Johansen, Tor Arne},
  journal={Journal of Intelligent \& Robotic Systems},
  volume={111},
  number={1},
  pages={1--19},
  year={2025},
  publisher={Springer}
}

Main branch

The main branch shall always be working. This means that:

  • All of its features/modules shall be properly documented through quality code + descriptive text where appropriate, and the CI pipeline should pass before any PR can be merged into main.
  • The ScenarioGenerator is successfully able to generate any scenario from valid config files.
  • The Simulator is successfully able to run through any number of scenarios, either generated or loaded from file. Furthermore, it should be problem free to save and load scenarios to/from (valid) scenario files.
  • The Visualizeris successfully able to visualize each of these scenarios live, if toggled on.
  • Subsystems added to the Ship class properly adheres to their interfaces (prefixed with I in front of the class name, e.g. interface IModel for the ship model interface).

Project and master thesis work

For students who use this repository for their project/master work, we want you to be free to experiment with the code. In order to enable this, such work shall be performed in branches prepended with project/<year>/. For example, if Per in 2023 is working on his master thesis creating a deep reinforcement learning-based trajectory tracking controller, he should do so by branching out from main into his own branch called e.g. project/2023/drl-controller. This makes browsing the branches later easier.

Features and dedicated improvements

A lot of the work being done on code repositories is adding needed features, or improving the platform in general.

The branches used to develop these features shall be prepended with feature/, e.g. feature/improved_ship_configuration.

It is also a good idea to keep the features small, so that they are easy to test, and can quickly be merged into main.

Workflow

When you're developing a feature based on the simulator, the workflow begins by checking out the main branch, and creating a new branch from there.

Retrieving main branch

cd colav_simulator
git checkout main
git pull
# If you need to rebase because you have uncommited (non-important) changes in your working directory, you can run:
git reset --hard origin/main

Creating new branch

A new branch for new features is created like this:

git checkout -b feature/name_of_feature

Performing work and updating branch

Now that you have a separate branch, you can code, commit, run experiments and cooperate in that branch.

# Do some work
git add <files>
git commit
git push -u origin feature/name_of_feature # -u flag with origin only needed first time to set upstream.

Performing tests before merging into main

After doing some development and thorough testing, it might be time to merge the feature into the main branch. Before doing that, make sure that all checkpoints under the heading Main branch are satisfied.

To make sure new issues don't arise when merging into main later (because of other changes to main), it is important to merge main into feature/name_of_feature first:

git checkout main   # make sure we have the latest
git pull            # version of main locally
git checkout feature/name_of_feature
git pull # in case you or someone else made changes remotely
git merge main # This attempts to merge `main` into `feature/name_of_feature`
# At this point conflicts might arise which you have to solve.

Now you can perform the tests.

After the tests have been completed, you can initiate a pull request on github, where eventually the changes will be merged to main.

Pull request

Go to the colav_simulator repo on https://github.com and navigate to your branch (feature/name_of_feature). In the status bar above the commit message there is a link for Pull request. Click that and describe your feature. Assign people to it to review the changes, and create the pull request.

The pull request should now be reviewed by someone, and if it is ok, it will be merged into main.

Main modules in the repository

Each main module mostly have their own test files, to enable easier debug/fixing and also for making yourself familiar with the code. When developing new modules, you are encouraged to simultaneously develop test files for these, such that yourself and others more conveniently can fix/debug and test the modules separately.

A core concept is the usage of Cerberus for configuration validation, where the validation schemas are used to verify configuration keys and fail fast if erroneous settings are provided. Furthermore, another core concept is the usage of dataclasses for keeping parameters and configuration objects. Each scope (models, guidances, ship, etc..) have its own Config object containing its local configuration parameters. This leads to better readability of the code by keeping all aspects of a given subsystem in its own scope, as opposed to passing global configuration objects everywhere. Dataclasses also increases readability and reduces bugs related to specifying wrong dictionary string keys that would happen when only using dictionaries for configuration settings.

The following describes the main modules superficially. Rely mainly on the code itself for the documentation.

COLAVEnvironment

Gymnasium-wrapper for the simulation framework. See the source code for available rewards, actions and observations that can be used.

Simulator

The simulator runs through a set of scenarios, each consisting of n_episodes specified from the config, visualizes these and saves the results. The scenarios can be generated through a ScenarioConfig object, or loaded from file using an existing scenario definition (examples under scenarios/).

It is recommended to pass the COLAV planning algorithm you want to test on the own-ship at run-time to the run(...) function in the simulator (see its documentation and an example for this in tests/test_simulator.py). The planning algorithm must inherit/adhere to the ICOLAV-interface under colav_simulator/core/colav/colav_interface.py (same principle goes for all the Ship subsystems).

The simulator is configured using the simulator.yaml config file.

Scenario Generator

The scenario generator is used by the simulator to create new scenarios for COLAV testing with 1+ ships. The main method is the generate() function, which generates a scenario from a scenario config file, which is converted into a ScenarioConfig object. An Electronic Navigational Chart object (from Seacharts) is used to define the environment. The n_episodes (defaults to 1) parameter is used to facilitate Monte Carlo simulation when using random data, and one can use an EpisodeGenerationConfig for further MC specifications.

Scenarios are configured through a scenario .yaml file as e.g. the example head_on.yaml file under scenarios in the root folder. Look at predefined scenario files and the schemas folder under the package source code for further clues constraints/tips on how to write a new scenario config files.

Seacharts is used to provide access to Electronic Navigational Charts, and an ENC object is used inside the ScenarioGenerator class for this. One must here make sure that the seacharts package is properly setup with .gdb data in the data/external folder of the package, with correctly matching UTM zone for the chart data. An example default seacharts.yamlconfig file for the module is found under config/. One can specify map data, map origin, map size etc. for the ENC object from the scenario .yamlconfig file.

Troubles with "freezing" when you generate a scenario? Check if you have specified new_load_of_map_data=Truein the scenario configuration file. If this is false, and the map data is not loaded/wrong data is used, errors can happen.

In addition to random waypoint generation and/or straight line motion generation for the own-ship and/or target ships through the BehaviorGenerator class, the rrt-rs library can optionally be used for generating random ship behaviors, where Rapidly-exploring Random Trees (RRTs) are built for each ship initial state. See the source code for the BehaviorGenerator for more information.

NOTE: The random generation of scenarios (poses, waypoints, speed plans etc.) are not guaranteed to succeed every time, especially since a finite number of iterations are considered in sampling procedures (for time and robustness reasons). Many edge cases can occur that makes a particular ship-ship encounter scenario unrealistic or not interesting. This makes it important that you check the generated scenarios.

Visualizer

Class responsible for visualizing scenarios run through by the Simulator, and visualizing/saving the results from these. A basic live plotting feature when simulating scenarios is available. The class can, as most other main modules, be configured from the example simulator configuration file under config/.

Note that the visualizer uses Matplotlib, and scales badly with a large number of vessels and plot data. Use with care. See Roadmap below.

Ship

Interface and core functionality

The Ship class simulates the behaviour of an individual ship and must adhere to the IShip interface, which necessitates that the ship class provides among others, a:

  • forward(self, dt: float, w = Optional[DisturbanceData] = None) -> Tuple[np.ndarray, np.ndarray, np.ndarray] function that allows simple forward simulation of the vessel, with disturbance consideration if data is available. Returns the new state, inputs to get there and the references used.
  • plan(self, t: float, dt: float, do_list: List[Tuple[int, np.ndarray, np.ndarray, float, float]], enc: Optional[ENC] = None, w: Optional[DisturbanceData] = None) -> np.ndarray function that plans a new trajectory/generates new references for the ship, either using the guidance system or COLAV system. A list of dynamic obstacle data, possibly Electronic Navigational Chart (ENC) object and disturbance information can be used by the planner.
  • track_obstacles(self, t: float, dt: float, true_do_states: List[Tuple[int, np.ndarray, float, float]]) -> Tuple[List[Tuple[int, np.ndarray, np.ndarray, float, float]], List[Tuple[int, np.ndarray]]]: function that tracks nearby dynamic obstacles.

Standardized input/output formats are used for the interfaces to make the code for each subsystem easy to switch in/out. For the Guidance, Navigation and Control (GNC) system, we consider interface input/output dimensions based on a typical 3DOF surface vessel model (see Fossen, 2011 for reference):

  • State of dimension nx = 6 x 1 consisting of [x, y, psi, u, v, r]^T for the typical 3DOF model case (^T for transposed vectors). For kinematic models or other models with reduced state dimension, the first entries are filled out. For the kinematic model in the framework this equates to setting the state as [x, y, chi, U, 0, 0]^T where chi is the course over ground and U the speed over ground.
  • Inputs of dimension nu = 3 x 1 consisting of the generalized forces and moments [X, Y, N]^T for the 3DOF ship model. When using e.g. a kinematic model that has different state and input dimensions, the ship controller will nominally just feed references (typically in course and speed) directly forward to the ship model (Must be configured. See below for examples).
  • References are of dimension 9 x N, where N is the number of trajectory samples, consisting of 3DOF reference poses, velocities and accelerations in general. E.g. for LOS-guidance the references will be populated as [0, 0, chi_ref, U_ref, 0, 0, 0, 0, 0]^T and passed to the ship low-level controller.

If you need to implement a model which has state, input or reference dimensions larger than the aforementioned, make an issue on the topic and pull request with the necessary interface changes.

Subsystem Configurations

The ship can be configured to use different combinations of collision avoidance algorithms, guidance systems, controllers, estimators, sensors, and models. The key element here is that each subsystem provides a standard inferface, which any external module using the subsystem must adhere to. See the source code and test files for more in depth info on the functionality. Check out the ship_list entry under the schemas/scenario.yaml to get a clue on what you can configure for the ship. NOTE: The own-ship must always have ID=0 and is always the first ship to configure under ship_list.

If you want to expand the Ship object by adding support for a new model, controller, guidance law etc., a rough recipe is the following (in this example for a new model, but the procedure will be the same for any subsystem or module in the framework):

  • 1: Implement the model under core/models.py or in your own repository, inherit from the IModel interface and implement the required interface functions.
  • 2: Add a parameter class for the model, and add this parameter class entry to the models.Config class to add support for parsing the model from configuration files. This also entails that you implement the from_dict(config_dict: dict) -> ModelParams and to_dict() -> dict: functions, to allow for easy parsing of dictionaries into dataclasses. However, note that some of the implemented ship models existing in the framework have fixed and non-configurable parameters. In this case, the configuration entry scheme for the model is an empty string (see e.g. for Viknes under models in the scenario schema).
  • 3: Use the new model parameter class as input to the new model object constructor.
  • 4: Add support for the new model by adding its parameter object class to the models part of the schemas/scenario.yaml files under ship_list.
  • 5: Test the new model in a scenario where you specify the model parameters in a scenario configuration file, or directly during run-time (as in test_ship.py).

In all these steps, adhere to the used code style and docstring format.

Some common configurations of the ship subsystems are detailed below.

External Control

In case you are employing an RL-agent through the COLAVEnvironment, the remote_actor parameter inside the simulator step(..)-function is set to true. In this case, the RL-agent sets the ship references (pose, velocity and acceleration / inputs) externally.

Guidance, Navigation and Control With a Kinetic Model

The scenarios/head_on.yaml scenario contains a typical GNC/autopilot-configuration of the own-ship, where LOS-guidance (given waypoints and a speed plan) provides course and speed references to a low-level controller (in this case a feedback-linearizing surge-heading controller).

The ship plan step will then only entail that the configured guidance object LOS algorithm computes course+speed references, which are provided to the onboard controller during the forward call. The onboard controller then computes the required force vector to track the reference setpoints.

Guidance, Navigation and Control With a Kinematic Model

The scenarios/ais_scenario1.yaml scenario contains a typical GNC/autopilot-configuration of the own-ship, where LOS-guidance (given waypoints and a speed plan) provides course and speed references that are directly passed through to a simple kinematic ship model. Here, a PassThroughCS "controller" is used to allow for passing the guidance system course and speed references straight through the controller step and directly to the ship model.

The ship plan step will then only entail that the configured guidance object LOS algorithm computes course+speed references, which are provided to the onboard controller and directly forwarded during the forward call. The ship model will then directly use the guidance system references as inputs.

Planner Providing Inputs and Not (Pose, Velocity, Acceleration) References

In case you want to develop a motion planning algorithm that provides low-level inputs (e.g. generalized force inputs) to the ship instead of the standard 9-entry pose, velocity, acceleration reference format, you can specify a PassThroughInputs type of controller. This "controller" essentially lets the input references (which are now low-level inputs from your algorithm) go straight through, such that the controller is in practice disabled. An example of this is found in the scenarios/simple_planning_example.yaml, where a rudder-propeller mapping with a specificed lever arm is used to map forces in x and y to include the yaw moment as well.

The ship plan step will then entail that you use your wrapped colav system, that provide references that are low-level inputs. When these are passed to the controller object during the forward call, they will pass straight through and go into the ship model object.

Godlike Target Tracking (Ground Truth Tracking)

If you want to test your planning algorithm with perfect knowledge on nearby vessels, you can specify the GodTracker to be used under tracker in the scenario configuration file. As this object has no parameters, the configuration entry is an empty string god_tracker: '' (see schemas/scenario.yaml for clues).

Simple Kalman-filter based Target Tracking

The standard support for target tracking in the simulator is to use a Kalman Filter for estimating the states of nearby vessels. Most of the scenario files have examples on how to configure this tracker. Tune the measurement covariance (R) through the sensor configuration, and adjust the scenario configuration based on whether or not you want to consider AIS-measurements, Radar-measurements or both. See the tests/test_simulator.py for example usage and tests/test_radar.py for the Radar class functionality.

Multi-Target Tracking with Realistic Track Initiation and Termination

An interface at https://github.com/ntnu-itk-autonomous-ship-lab/vimmjipda exist for coupling the Visibility Interacting Multiple Models Joint Integrated Probabilistic Data Association (VIMMJIPDA) Multi-Target Tracker (MTT) with the simulator, and can be used with the Radar sensor (no AIS consideration implemented yet) for tracking multiple ships with simple clutter noise. See the tests/test_simulator_jipda.pyfor example usage.

COLAV

The colav_interface.py provides an interface for arbitrary COLAV planning algorithms and hierarchys within. See the file for examples/inspiration on how to wrap your own COLAV-planner to make it adhere to the interface. Alternatively, you can provide your own COLAV system through the ownship_colav_system input to the simulator run(.) function. In any case, the COLAV algorithm should adhere to the ICOLAV interface (see colav_interface.py). This enables the usage of both internally developed COLAV planners in addition to third-party ones.

Future Enhancements (Roadmap)

  • Mandate unittesting of all modules and their core functionality.
  • Improve random generation of vessel COLREGS scenarios. E.g. use AIS data to sample "realistic" vessel trajectories based on a fitted distribution for historical vessel positions and velocities.
  • Improve live-visualization in the simulator w.r.t. code readability and run-time. Switch out matplotlib for a faster backend. Matplotlib is known to leak memory, so switching it for e.g. PyGtGraph https://www.pyqtgraph.org/ or VisPy https://vispy.org/ is promising.
  • Add functionality for saving simulation results to file.
  • Separate the large schemas/scenario.yaml validation schema into multiple sub-schemas for easier readability.
  • Create IsaacGym-wrapper for GPU-enabled parallelized RL in the environment.

About

Framework for simulating and evaluating autonomous ship collision avoidance (COLAV) control strategies. Developed as part of the Autoship Centre for Research-based Innovation (SFI). Open sourced in the autumn of 2025.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages