diff --git a/ATOS_module_interaction_diagrams.pdf b/ATOS_module_interaction_diagrams.pdf new file mode 100644 index 000000000..b0778dfb0 Binary files /dev/null and b/ATOS_module_interaction_diagrams.pdf differ diff --git a/Dockerfile b/Dockerfile index b90a777db..75a805678 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,4 +24,9 @@ RUN --mount=type=cache,target=/var/cache/apt \ ./scripts/installation/install_deps.sh ${REPO_DIR} COPY . . RUN ./scripts/installation/install_atos.sh ${REPO_DIR} -WORKDIR /root/atos_ws \ No newline at end of file +WORKDIR /root/atos_ws +RUN chmod +x /root/atos_git/scripts/run_atosfleetmanagement.sh + +EXPOSE 8420 8765 9090 8114 + +CMD ["/bin/bash", "-lc", "/root/atos_git/scripts/run_atosfleetmanagement.sh"] diff --git a/README.md b/README.md index c3c554b97..b8c478f54 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,7 @@ docker run --network="host" --ipc=host --privileged -it -v ~/.astazero/ATOS/:/r ``` If you run Docker Desktop you will need to specify the ports to expose to the host computer. ```bash -docker run --ipc=host --privileged -it -v ~/.astazero/ATOS/:/root/.astazero/ATOS/ -p 80:80 -p 8080:8080 -p 8081:8081 -p 8082:8082 -p 3000:3000 -p 3443:3443 -p 55555:55555 -p 443:443 -p 9090:9090 astazero/atos_docker_env:latest bash -c "source /root/atos_ws/install/setup.sh ; ros2 launch atos launch_basic.py insecure:=True" +docker run --ipc=host --privileged -it -v ~/.astazero/ATOS/:/root/.astazero/ATOS/ -p 80:80 -p 8080:8080 -p 8081:8081 -p 8082:8082 -p 8420:8420 -p 3443:3443 -p 55555:55555 -p 443:443 -p 9090:9090 astazero/atos_docker_env:latest bash -c "source /root/atos_ws/install/setup.sh ; ros2 launch atos launch_basic.py insecure:=True" ``` See the [GUI](../Usage/GUI/foxglove.md) documentation on how to enable secure connections. @@ -46,6 +46,77 @@ ATOS comes with an installation script that automates the installation process. ## Building from source manually You can find instructions on how to manually install ATOS and its dependencies from source [here](https://atos.readthedocs.io/en/latest/Installation/installation/). +## ATOSFleetManagement development launch +ATOSFleetManagement is a lightweight development stack built around `truck_object_control` and `truck_object_gui` and does not start OpenScenarioGateway, JournalControl, or EsminiAdapter. + +After building, start it with: +```bash +ros2 launch atos launch_atosfleetmanagement.py insecure:=True +``` + +The placeholder COT input topic for TruckObjectControl is: +```text +/atos/truck_objects/cot +``` +Expected temporary payload format: +```text +id=;distance_m=;tcp_connected=<0|1> +``` +TruckObjectControl publishes speed commands on: +```text +/atos/truck_objects/speed_command +``` + +### ATOSFleetManagement in Docker (with or without simulators) +Build and start with docker compose: +```bash +docker compose -f docker-compose-fleetmanagement.yml up --build +``` + +Start with simulated trucks enabled: +```bash +WITH_TRUCK_SIMULATOR=True docker compose -f docker-compose-fleetmanagement.yml up --build +``` + +Start with simulated trucks disabled: +```bash +WITH_TRUCK_SIMULATOR=False docker compose -f docker-compose-fleetmanagement.yml up --build +``` + +Open TruckObjectGUI at: +```text +http://localhost:8420 +``` + +### Run as system service (Docker + systemd) +Example deployment folder on server: `/opt/atos`. + +1. Copy repository to server: +```bash +sudo mkdir -p /opt/atos +sudo rsync -a ./ /opt/atos/ +``` + +2. Install service env file and customize: +```bash +sudo cp /opt/atos/scripts/atosfleetmanagement.env.example /etc/default/atosfleetmanagement +sudo nano /etc/default/atosfleetmanagement +``` + +3. Install and enable service: +```bash +sudo cp /opt/atos/scripts/atosfleetmanagement.service /etc/systemd/system/atosfleetmanagement.service +sudo systemctl daemon-reload +sudo systemctl enable --now atosfleetmanagement +``` + +4. Service management: +```bash +sudo systemctl status atosfleetmanagement +sudo systemctl restart atosfleetmanagement +sudo journalctl -u atosfleetmanagement -f +``` + # Using ATOS with a Graphical User Interface (GUI) Please click [here](https://atos.readthedocs.io/en/latest/Usage/GUI/foxglove/) for instructions on how to use ATOS with a GUI. @@ -73,4 +144,3 @@ This project has partly been funded by the below organisations. The herein expre

- diff --git a/ROS2_ATOSFleetManagement_cheatsheet.md b/ROS2_ATOSFleetManagement_cheatsheet.md new file mode 100644 index 000000000..1a41ce02e --- /dev/null +++ b/ROS2_ATOSFleetManagement_cheatsheet.md @@ -0,0 +1,185 @@ +# ROS2 for ATOSFleetManagement Cheat Sheet + +## 1) Source environment +```bash +source /opt/ros/humble/setup.bash +source ~/atos_ws/install/setup.bash +``` + +## 2) Build ATOSFleetManagement packages +```bash +cd ~/atos_ws +colcon build --packages-select atos atos_gui --symlink-install +source install/setup.bash +``` + +## 3) Start ATOSFleetManagement (normal) +```bash +ros2 launch atos launch_atosfleetmanagement.py insecure:=True +``` + +## 4) Start ATOSFleetManagement with 3 simulators +```bash +ros2 launch atos launch_atosfleetmanagement.py insecure:=True with_truck_simulator:=True +``` + +### Default simulator setup in launch +- `L5S-TRUCK-SIM-1`: `start_index=0`, `target_speed_kmh=80`, `ignore_warning_speed_commands=True` +- `L5S-TRUCK-SIM-2`: `start_index=250`, `target_speed_kmh=40` +- `L5S-TRUCK-SIM-3`: `start_index=500`, `target_speed_kmh=40` + +## 5) Open GUI +- URL: `http://localhost:8420` +- Go to tab: `RuralRoad Map` +- UI shows live trucks + `Distance To Next Truck Ahead` + +## 6) Run in Docker (same image, with/without simulators) +```bash +cd ~/Documents/repos/ATOS +docker compose -f docker-compose-fleetmanagement.yml up --build +``` +With simulators: +```bash +WITH_TRUCK_SIMULATOR=True docker compose -f docker-compose-fleetmanagement.yml up --build +``` +Without simulators: +```bash +WITH_TRUCK_SIMULATOR=False docker compose -f docker-compose-fleetmanagement.yml up --build +``` +Start without rebuild (if image already built): +```bash +docker compose -f docker-compose-fleetmanagement.yml up +``` +Start in background: +```bash +docker compose -f docker-compose-fleetmanagement.yml up -d +``` +When to use `--build`: +- Use `--build` only when image content changed (Dockerfile, dependencies, or source copied into image). +- For normal restart, do **not** use `--build`. + +## 7) Run Docker as a systemd service +```bash +sudo mkdir -p /opt/atos +sudo rsync -a ~/Documents/repos/ATOS/ /opt/atos/ +sudo cp /opt/atos/scripts/atosfleetmanagement.env.example /etc/default/atosfleetmanagement +sudo cp /opt/atos/scripts/atosfleetmanagement.service /etc/systemd/system/atosfleetmanagement.service +sudo systemctl daemon-reload +sudo systemctl enable --now atosfleetmanagement +``` +Service controls: +```bash +sudo systemctl status atosfleetmanagement +sudo systemctl restart atosfleetmanagement +sudo journalctl -u atosfleetmanagement -f +``` + +## 8) Copy to another server (what files are needed) +Recommended (safest): +```bash +rsync -a ~/Documents/repos/ATOS/ user@:/opt/atos/ +``` +Minimum required for Docker-based ATOSFleetManagement: +- `Dockerfile` +- `docker-compose-fleetmanagement.yml` +- `scripts/run_atosfleetmanagement.sh` +- `scripts/installation/` +- `atos/` +- `atos_gui/` +- `atos_interfaces/` +- `conf/` +- Optional for systemd service: + - `scripts/atosfleetmanagement.service` + - `scripts/atosfleetmanagement.env.example` + +## 9) Check running nodes +```bash +ros2 node list +``` + +## 10) Check key topics +```bash +ros2 topic list | rg "truck_objects|speed_command" +``` + +## 11) Watch live truck states for GUI +```bash +ros2 topic echo /atos/truck_objects/state +``` + +## 12) Watch control speed commands +```bash +ros2 topic echo /atos/truck_objects/speed_command +``` + +## 13) TruckObjectControl COT interfaces +- ROS topic input: `/atos/truck_objects/cot` (placeholder format) +- TCP input: `0.0.0.0:8114` listener in TruckObjectControl +- Truck clients connect to: `127.0.0.1:8114` (same machine) or `:8114` +- CoT `` is in `m/s` (GUI displays `km/h`) + +## 14) Placeholder ROS COT test (manual) +```bash +ros2 topic pub /atos/truck_objects/cot std_msgs/msg/String "data: 'id=truck_1;distance_m=1000;tcp_connected=1;lat=57.78357;lon=12.76389;speed_mps=2.8;course_deg=150'" -1 +``` + +## 15) Start one simulator manually (custom test) +```bash +ros2 run atos atos_truck_simulator --ros-args \ + -p uid:=L5S-TRUCK-UBUNTU \ + -p start_index:=300 \ + -p target_speed_kmh:=35.0 \ + -p acceleration_mps2:=0.6 \ + -p tcp_host:=127.0.0.1 \ + -p tcp_port:=8114 +``` + +## 16) Simulator option: ignore first-limit slowdown +Use this only on selected trucks: +```bash +-p ignore_warning_speed_commands:=true +``` +Behavior: +- Ignores non-zero warning command (for example `30 km/h` at `< 400 m`) +- Still obeys stop command (`0 km/h` at `< 200 m`) + +## 17) Fleet control algorithm (stateful, per truck) +Initial rules (distance `X` to truck ahead on same path): +- If `500 m >= X > 200 m` => send `command=SLOWDOWN` (`target_speed_mps=8.333333`) +- If `200 m >= X` => send `command=STOP` (`target_speed_mps=0.000000`) +- If `X > 300 m` AND previous command was `STOP` => send `command=SLOWDOWN` +- If `X > 700 m` AND previous command was `SLOWDOWN` => send `command=RESUME` (`target_speed_mps=nochange`) + +Notes: +- Commands are evaluated per truck, based on its own truck-ahead distance. +- For each path, trucks are sorted by distance along trajectory before truck-ahead checks. + +## 18) TCP speed command manual (per truck) +TruckObjectControl sends one newline-terminated command string per truck over the same TCP connection used by incoming CoT. + +Fields: +- `command`: `STOP`, `SLOWDOWN`, or `RESUME` +- `target_speed_mps`: target speed in `m/s`, or `nochange` to keep current truck speed +- `distance_to_truck_ahead_m`: distance in meters from this truck to the next truck ahead in sorted trajectory order +- `truck_ahead_path_index`: current path index for the truck ahead +- `truck_ahead_uid`: UID for the truck ahead +- `reason`: current command-state reason (`truck_ahead_stop_state`, `truck_ahead_slowdown_state`, `truck_ahead_resume_state`) +- `min_gap_m`: smallest gap found among all connected/fresh trucks in current evaluation cycle +- `connected_count`: number of trucks currently included in control logic +- `path_name`: path used for the truck-ahead computation + +Typical command strings: +```text +command=RESUME;target_speed_mps=nochange;distance_to_truck_ahead_m=812.432100;truck_ahead_path_index=310;truck_ahead_uid=L5S-TRUCK-SIM-2;reason=truck_ahead_resume_state;min_gap_m=612.432100;connected_count=3;path_name=RuralRoad_center_of_driving_lane_ccw.geojson +``` + +```text +command=SLOWDOWN;target_speed_mps=8.333333;distance_to_truck_ahead_m=312.500000;truck_ahead_path_index=412;truck_ahead_uid=L5S-TRUCK-SIM-3;reason=truck_ahead_slowdown_state;min_gap_m=154.270000;connected_count=3;path_name=RuralRoad_center_of_driving_lane_ccw.geojson +``` + +```text +command=STOP;target_speed_mps=0.000000;distance_to_truck_ahead_m=92.140000;truck_ahead_path_index=418;truck_ahead_uid=L5S-TRUCK-SIM-1;reason=truck_ahead_stop_state;min_gap_m=92.140000;connected_count=3;path_name=RuralRoad_center_of_driving_lane_ccw.geojson +``` + +## 19) Stop everything +- Press `Ctrl+C` in launch terminal diff --git a/docker-compose-fleetmanagement.yml b/docker-compose-fleetmanagement.yml new file mode 100644 index 000000000..49d35cb09 --- /dev/null +++ b/docker-compose-fleetmanagement.yml @@ -0,0 +1,22 @@ +version: "3.2" +services: + atos-fleetmanagement: + container_name: atos-fleetmanagement + image: astazero/atos_docker_env:latest + build: + context: . + dockerfile: ./Dockerfile + ipc: host + privileged: true + stdin_open: true + tty: true + restart: unless-stopped + environment: + - ROS_DOMAIN_ID=${ROS_DOMAIN_ID} + - INSECURE=${INSECURE:-True} + - WITH_TRUCK_SIMULATOR=${WITH_TRUCK_SIMULATOR:-False} + - FOXBRIDGE=${FOXBRIDGE:-True} + volumes: + - ~/.astazero/ATOS/:/root/.astazero/ATOS/ + network_mode: "host" + command: /bin/bash -lc "/root/atos_git/scripts/run_atosfleetmanagement.sh" diff --git a/scripts/atosfleetmanagement.env.example b/scripts/atosfleetmanagement.env.example new file mode 100644 index 000000000..07b91f8b6 --- /dev/null +++ b/scripts/atosfleetmanagement.env.example @@ -0,0 +1,5 @@ +# Copy this file to /etc/default/atosfleetmanagement and adjust values. +INSECURE=True +WITH_TRUCK_SIMULATOR=False +FOXBRIDGE=True +ROS_DOMAIN_ID=42 diff --git a/scripts/atosfleetmanagement.service b/scripts/atosfleetmanagement.service new file mode 100644 index 000000000..3c7c121f6 --- /dev/null +++ b/scripts/atosfleetmanagement.service @@ -0,0 +1,18 @@ +[Unit] +Description=ATOSFleetManagement Docker Service +After=docker.service network-online.target +Wants=network-online.target +Requires=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory=/opt/atos +EnvironmentFile=-/etc/default/atosfleetmanagement +ExecStart=/usr/bin/docker compose -f /opt/atos/docker-compose-fleetmanagement.yml up -d --build +ExecStop=/usr/bin/docker compose -f /opt/atos/docker-compose-fleetmanagement.yml down +TimeoutStartSec=0 +TimeoutStopSec=120 + +[Install] +WantedBy=multi-user.target diff --git a/scripts/build_atosfleetmanagement.sh b/scripts/build_atosfleetmanagement.sh new file mode 100755 index 000000000..a8431d345 --- /dev/null +++ b/scripts/build_atosfleetmanagement.sh @@ -0,0 +1,70 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Canonical build profile for ATOSFleetManagement. +# Default workspace: ~/atos_ws +# Usage: +# ./scripts/build_atosfleetmanagement.sh +# ./scripts/build_atosfleetmanagement.sh /path/to/ws +# ATOS_WS=/path/to/ws ./scripts/build_atosfleetmanagement.sh --event-handlers console_direct+ + +if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + cat <<'HELP' +ATOSFleetManagement build script + +Usage: + scripts/build_atosfleetmanagement.sh [workspace] [extra colcon args...] + +Examples: + scripts/build_atosfleetmanagement.sh + scripts/build_atosfleetmanagement.sh /home/user/atos_ws + scripts/build_atosfleetmanagement.sh --event-handlers console_direct+ + scripts/build_atosfleetmanagement.sh /home/user/atos_ws --executor sequential + +Behavior: + - Builds packages: atos, atos_gui + - Enables TruckObjectControl module + - Disables legacy ATOS modules not used in ATOSFleetManagement +HELP + exit 0 +fi + +DEFAULT_WS="${ATOS_WS:-$HOME/atos_ws}" +WS="$DEFAULT_WS" + +# If first arg is not an option, treat it as workspace path. +if [[ $# -gt 0 && "${1:0:1}" != "-" ]]; then + WS="$1" + shift +fi + +if [[ ! -d "$WS" ]]; then + echo "Workspace does not exist: $WS" + echo "Create it first or pass a valid workspace path." + exit 1 +fi + +if [[ ! -d "$WS/src" ]]; then + echo "Workspace is missing src directory: $WS/src" + exit 1 +fi + +echo "Building ATOSFleetManagement in workspace: $WS" + +cd "$WS" +colcon build --packages-select atos atos_gui --symlink-install --cmake-args \ + -DWITH_TRUCK_OBJECT_CONTROL=ON \ + -DWITH_OBJECT_CONTROL=OFF \ + -DWITH_OPEN_SCENARIO_GATEWAY=OFF \ + -DWITH_JOURNAL_CONTROL=OFF \ + -DWITH_ESMINI_ADAPTER=OFF \ + -DWITH_DIRECT_CONTROL=OFF \ + -DWITH_TRAJECTORYLET_STREAMER=OFF \ + -DWITH_OSI_ADAPTER=OFF \ + -DWITH_MQTT_BRIDGE=OFF \ + -DWITH_POINTCLOUD_PUBLISHER=OFF \ + -DWITH_INTEGRATION_TESTING=OFF \ + -DWITH_BACK_TO_START=OFF \ + -DWITH_REST_BRIDGE=OFF \ + -DWITH_MONR_RELAY=OFF \ + "$@" diff --git a/scripts/installation/install_deps.sh b/scripts/installation/install_deps.sh index 10385c45f..79ba25a23 100755 --- a/scripts/installation/install_deps.sh +++ b/scripts/installation/install_deps.sh @@ -19,10 +19,44 @@ fi source "${ATOS_REPO_PATH}/scripts/installation/install_functions.sh" +apt_update_retry() { + local attempts=5 + local delay=5 + local i=1 + while [ "$i" -le "$attempts" ]; do + if sudo apt-get update; then + return 0 + fi + echo "apt update failed (attempt ${i}/${attempts}); cleaning apt cache and retrying..." + sudo apt-get clean + sudo rm -rf /var/lib/apt/lists/* + sleep "$delay" + i=$((i + 1)) + done + return 1 +} + +apt_install_retry() { + local attempts=3 + local delay=5 + local i=1 + while [ "$i" -le "$attempts" ]; do + if sudo apt-get install -y "$@"; then + return 0 + fi + echo "apt install failed (attempt ${i}/${attempts}); retrying..." + sleep "$delay" + i=$((i + 1)) + done + return 1 +} + # Update and install required dependencies specified in dependencies.txt and requirements.txt file apt_deps=$(cat ${ATOS_REPO_PATH}/scripts/installation/dependencies.txt | tr '\n' ' ') echo "Installing dependencies... $apt_deps" -sudo apt update && sudo apt install -y ${apt_deps} +apt_update_retry +apt_install_retry ${apt_deps} +apt_install_retry python3-pip python3 -m pip install -r ${ATOS_REPO_PATH}/scripts/installation/requirements.txt # Check if apt failed to install dependencies @@ -38,7 +72,8 @@ if ! (apt list | grep -q "ros-$ROS_DISTRO-desktop"); then echo "Adding the ROS2 $ROS_DISTRO apt repository..." # Install ROS2 prerequisites - sudo apt update && sudo apt install -y lsb-release ros-dev-tools + apt_update_retry + apt_install_retry lsb-release ros-dev-tools # Authorize the ROS2 gpg key with apt sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key \ @@ -52,7 +87,8 @@ fi # Install ROS2 packages echo "Installing ROS2 packages..." -sudo apt install -y \ +apt_update_retry +apt_install_retry \ ros-${ROS_DISTRO}-desktop \ python3-rosdep \ ros-${ROS_DISTRO}-launch-pytest @@ -105,7 +141,7 @@ if [ -d "/usr/local/include/esmini" ] && [ "$REINSTALL" = false ]; then echo "esmini already installed, skipping installation..." else echo "Downloading esmini binaries..." - wget https://github.com/esmini/esmini/releases/download/v2.37.13/esmini-bin_Linux.zip -O $SOURCE_PATH/esmini-bin_Linux.zip + wget https://github.com/esmini/esmini/releases/download/v3.0.1/esmini-bin_Linux.zip -O $SOURCE_PATH/esmini-bin_Linux.zip check_command_failed $? "Failed to get esmini." unzip $SOURCE_PATH/esmini-bin_Linux.zip -d $SOURCE_PATH/esmini-bin_Linux cd $SOURCE_PATH/esmini-bin_Linux @@ -121,4 +157,4 @@ fi # Remove custom path for source installation echo "Removing custom path for source installation..." sudo rm -rf $SOURCE_PATH -check_command_failed $? "Failed to remove ${SOURCE_PATH} path for source installation." \ No newline at end of file +check_command_failed $? "Failed to remove ${SOURCE_PATH} path for source installation." diff --git a/scripts/installation/install_functions.sh b/scripts/installation/install_functions.sh index 9f8601a07..4b85d6876 100755 --- a/scripts/installation/install_functions.sh +++ b/scripts/installation/install_functions.sh @@ -41,10 +41,10 @@ add_source_line_if_needed() { local shell_type="$2" local source_line="$3$shell_type" - if [ ! grep -qF "$source_line" "$file" ]; then - # Ask the user if they want to add the source line. - # First check for noninteractive shell with DEBAIN_FRONTEND=noninteractive - if [ -z "$DEBIAN_FRONTEND" && ! -z $GITHUB_ACTION ]; then + if ! grep -qF "$source_line" "$file"; then + # Ask the user only for interactive local shells. + # In non-interactive environments (e.g. CI), append automatically. + if [ -t 0 ] && [ -z "$DEBIAN_FRONTEND" ] && [ -z "$GITHUB_ACTION" ]; then echo "Do you want to add the following line to your $shell_type config file $file:" echo "$source_line" echo "y/n" @@ -58,4 +58,4 @@ add_source_line_if_needed() { echo "$source_line" >> "$file" fi fi -} \ No newline at end of file +} diff --git a/scripts/run_atosfleetmanagement.sh b/scripts/run_atosfleetmanagement.sh new file mode 100644 index 000000000..e73f3071a --- /dev/null +++ b/scripts/run_atosfleetmanagement.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +set -euo pipefail + +INSECURE="${INSECURE:-True}" +WITH_TRUCK_SIMULATOR="${WITH_TRUCK_SIMULATOR:-False}" +FOXBRIDGE="${FOXBRIDGE:-True}" + +source /root/atos_ws/install/setup.sh + +echo "Starting ATOSFleetManagement with:" +echo " INSECURE=${INSECURE}" +echo " WITH_TRUCK_SIMULATOR=${WITH_TRUCK_SIMULATOR}" +echo " FOXBRIDGE=${FOXBRIDGE}" + +exec ros2 launch atos launch_atosfleetmanagement.py \ + insecure:="${INSECURE}" \ + with_truck_simulator:="${WITH_TRUCK_SIMULATOR}" \ + foxbridge:="${FOXBRIDGE}"