diff --git a/.DS_Store b/.DS_Store
new file mode 100644
index 00000000..3a37aef0
Binary files /dev/null and b/.DS_Store differ
diff --git a/archived/.DS_Store b/archived/.DS_Store
new file mode 100644
index 00000000..c5234abc
Binary files /dev/null and b/archived/.DS_Store differ
diff --git a/common/.DS_Store b/common/.DS_Store
new file mode 100644
index 00000000..6cc878a3
Binary files /dev/null and b/common/.DS_Store differ
diff --git a/docker-compose.yml b/docker-compose.yml
index c84fe675..0e6eaabd 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -22,20 +22,6 @@ services:
- CAP_SYS_RAWIO
volumes:
- volume_zerotier_config:/var/lib/zerotier-one:rw
- wireguard:
- cpuset: '0'
- image: masipcat/wireguard-go
- container_name: wireguard
- restart: always
- network_mode: host
- devices:
- - '/dev/net/tun'
- cap_add:
- - SYS_ADMIN
- - NET_ADMIN
- - CAP_SYS_RAWIO
- volumes:
- - volume_wireguard_config:/etc/wireguard:rw
httpd:
cpuset: '0'
build:
@@ -61,18 +47,6 @@ services:
volumes:
- volume_ftpd_config:/etc/pureftpd:rw
- volume_byodr_sessions:/home/ftpuser:rw
- rosnode:
- cpuset: '0'
- build:
- context: .
- dockerfile: rosnode/Dockerfile
- restart: always
- command: ['python3', 'app.py', '--name', 'rosnode']
- network_mode: host
- stop_signal: SIGKILL
- volumes:
- - volume_byodr_sockets:/byodr:rw
- - volume_byodr_config:/config:ro
mongodb:
cpuset: '0'
build:
@@ -116,7 +90,7 @@ services:
- volume_byodr_sockets:/byodr:rw
- volume_byodr_config:/config:rw
pilot:
- cpuset: '2'
+ cpuset: '1'
build:
context: .
dockerfile: pilot/Dockerfile
@@ -145,3 +119,15 @@ services:
volumes:
- volume_byodr_sockets:/byodr:rw
- volume_byodr_config:/config:ro
+ following:
+ cpuset: '3,2'
+ build:
+ context: .
+ dockerfile: following/Dockerfile
+ restart: always
+ privileged: true
+ network_mode: host
+ stop_signal: SIGKILL
+ volumes:
+ - volume_byodr_sockets:/byodr:rw
+ - volume_byodr_config:/config:rw
\ No newline at end of file
diff --git a/docker/bionic-torch1.10-py36-cuda10.2.dockerfile b/docker/bionic-torch1.10-py36-cuda10.2.dockerfile
new file mode 100644
index 00000000..c2012214
--- /dev/null
+++ b/docker/bionic-torch1.10-py36-cuda10.2.dockerfile
@@ -0,0 +1,71 @@
+# This pytorch version is strictly built with cp36. I did try to have a cp310 version
+# [torch-1.10.0-cp39-cp39-manylinux2014_aarch64.whl](https://files.pythonhosted.org/packages/03/f6/67e0ef29a03fd1cf585bdec03eb3aaf9f00498474f5c7b59f83d9779a7f1/torch-1.10.0-cp39-cp39-manylinux2014_aarch64.whl) but it didn't work with CUDA.
+
+# Some links I used while making this image
+# find supported versions between pytorch and torchvision => https://catalog.ngc.nvidia.com/orgs/nvidia/containers/l4t-pytorch
+# https://forums.developer.nvidia.com/t/pytorch-for-jetson/72048
+# https://developer.nvidia.com/embedded/jetson-linux-archive
+# Supported OS to run CUDA => https://developer.nvidia.com/cuda-10.2-download-archive?target_os=Linux&target_arch=x86_64&target_distro=Ubuntu (most of the other OS do not have optimized version with Balena os)
+# Download wheel version for pytorch (in case of updating) => https://download.pytorch.org/whl/torch/
+# find optimized builds made for Nano on Balena => Nvidia Jetson Nano SD-CARD in https://docs.balena.io/reference/base-images/base-images-ref/
+# numpy build https://gitlab.com/pdboef/tensorrt-balena/-/blob/master/Dockerfile.tensorrt and https://github.com/zrzka/python-wheel-aarch64/releases
+# for tensorflow https://gitlab.com/pdboef/tensorrt-balena/-/tree/master from https://forums.balena.io/t/jetson-nano-image-with-tensorrt-for-python-projects/19272
+# main optimized blena docker image => https://github.com/balena-io-library/base-images/blob/master/balena-base-images/device-base/jetson-nano/ubuntu/bionic/run/Dockerfile
+# the python optimized image =>https://github.com/balena-io-library/base-images/blob/master/balena-base-images/python/jetson-nano/ubuntu/bionic/3.9.16/run/Dockerfile
+
+#Images to switch between
+# FROM balenalib/jetson-nano-ubuntu-python:bionic
+# FROM balenalib/jetson-nano-ubuntu-python:3.6-bionic-run
+
+# Image name : jetson-nano-torch1.10-py36-cuda10.2
+# FROM balenalib/jetson-nano-ubuntu:bionic
+FROM balenalib/jetson-nano-ubuntu:bionic
+
+# Combine ENV statements and don't prompt with any configuration questions
+ENV DEBIAN_FRONTEND=noninteractive \
+ CUDA_HOME=/usr/local/cuda-10.2 \
+ UDEV=1
+
+# Update to 32.7 repository, add Universe repository, and install required packages
+RUN sed -i 's/r32.6 main/r32.7 main/g' /etc/apt/sources.list.d/nvidia.list && \
+ apt-get update -qq && \
+ apt-get install -y --no-install-recommends software-properties-common && \
+ add-apt-repository universe && \
+ apt-get install -y --no-install-recommends lbzip2 git wget unzip jq xorg tar python3 libegl1 binutils \
+ python3-gi python3-dev python3-gst-1.0 python3-pip \
+ nvidia-l4t-cuda nvidia-cuda libopenmpi-dev cuda-toolkit-10-2 \
+ cuda-samples-10-2 libcudnn8 libopenblas-base libomp-dev python3-zmq
+
+# Clean up in a separate RUN command to enforce your requirement
+RUN apt-get clean && \
+ rm -rf /var/lib/apt/lists/* /usr/local/cuda-10.2/doc
+
+RUN pip3 install --upgrade pip --quiet
+
+# Install BSP binaries for L4T 32.7.2 if they don't exist and perform cleanup
+RUN if [ ! -f jetson-210_linux_r32.7.2_aarch64.tbz2 ]; then \
+ wget -q https://developer.nvidia.com/embedded/l4t/r32_release_v7.2/t210/jetson-210_linux_r32.7.2_aarch64.tbz2; \
+ fi && \
+ if [ -f jetson-210_linux_r32.7.2_aarch64.tbz2 ]; then \
+ tar xf jetson-210_linux_r32.7.2_aarch64.tbz2 && \
+ cd Linux_for_Tegra && \
+ sed -i 's/config.tbz2\"/config.tbz2\" --exclude=etc\/hosts --exclude=etc\/hostname/g' apply_binaries.sh && \
+ sed -i 's/install --owner=root --group=root \"${QEMU_BIN}\" \"${L4T_ROOTFS_DIR}\/usr\/bin\/\"/#install --owner=root --group=root \"${QEMU_BIN}\" \"${L4T_ROOTFS_DIR}\/usr\/bin\/\"/g' nv_tegra/nv-apply-debs.sh && \
+ sed -i 's/chroot . \// /g' nv_tegra/nv-apply-debs.sh && \
+ ./apply_binaries.sh -r / --target-overlay && cd .. && \
+ rm -rf jetson-210_linux_r32.7.2_aarch64.tbz2 Linux_for_Tegra; \
+ fi && \
+ echo "/usr/lib/aarch64-linux-gnu/tegra" > /etc/ld.so.conf.d/nvidia-tegra.conf && ldconfig
+
+# PyTorch installation with quiet flag to suppress progress output
+ARG PYTORCH_URL=https://nvidia.box.com/shared/static/fjtbno0vpo676a25cgvuqc1wty0fkkg6.whl
+ARG PYTORCH_WHL=torch-1.10.0-cp36-cp36m-linux_aarch64.whl
+RUN wget -q --no-check-certificate ${PYTORCH_URL} -O ${PYTORCH_WHL} && \
+ pip3 install ${PYTORCH_WHL} --quiet && \
+ rm ${PYTORCH_WHL}
+
+# Numpy installation with quiet flag
+ARG WHL_URL_PREFIX=https://github.com/zrzka/python-wheel-aarch64/releases/download/jetson-nano-1.1
+RUN curl -LO ${WHL_URL_PREFIX}/numpy-1.16.4-cp36-cp36m-linux_aarch64.whl && \
+ python3 -m pip install numpy-1.16.4-cp36-cp36m-linux_aarch64.whl --quiet && \
+ rm numpy-1.16.4-cp36-cp36m-linux_aarch64.whl
\ No newline at end of file
diff --git a/docker/bionic-torch1.11-cp38-cuda10.2.Dockerfile b/docker/bionic-torch1.11-cp38-cuda10.2.Dockerfile
new file mode 100644
index 00000000..196f9997
--- /dev/null
+++ b/docker/bionic-torch1.11-cp38-cuda10.2.Dockerfile
@@ -0,0 +1,49 @@
+# Files inside yolo config directory will be referred to as /workspace/ultralytics/ultralytics/cfg/models/v8/
+# building command ==> docker buildx build --platform linux/arm64,linux/amd64 -t mwlvdev/jetson-nano-ubuntu:bionic-torch1.10-cp38-cuda10.2 . --push
+# Start from the base image
+FROM balenalib/jetson-nano-ubuntu:bionic
+
+# Environment variables for non-interactive installation and path configuration
+ENV DEBIAN_FRONTEND noninteractive
+ENV PATH="/workspace/ultralytics/venv/bin:$PATH"
+
+# Add Kitware GPG key to avoid errors on apt-get update
+RUN apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 1A127079A92F09ED
+
+# Add Kitware GPG key, update sources list for Nvidia, and install dependencies
+RUN apt-get update && \
+ # Update to 32.7 repository in case the base image is using 32.6
+ sed -i 's/r32.6 main/r32.7 main/g' /etc/apt/sources.list.d/nvidia.list && \
+ apt-get install -y --no-install-recommends cuda-toolkit-10-2 cuda-samples-10-2 libcudnn8 lbzip2 xorg wget tar libegl1 git \
+ python3.8 python3.8-venv python3.8-dev python3-pip \
+ libopenmpi-dev libomp-dev libopenblas-dev libblas-dev libeigen3-dev libcublas-dev && \
+ # Clean up
+ apt-get clean && \
+ rm -rf /var/lib/apt/lists/*
+
+# Download and install BSP binaries for L4T 32.7.2, and perform configurations
+RUN wget https://developer.nvidia.com/embedded/l4t/r32_release_v7.2/t210/jetson-210_linux_r32.7.2_aarch64.tbz2 && \
+ tar xf jetson-210_linux_r32.7.2_aarch64.tbz2 && \
+ cd Linux_for_Tegra && \
+ sed -i 's/config.tbz2\"/config.tbz2\" --exclude=etc\/hosts --exclude=etc\/hostname/g' apply_binaries.sh && \
+ sed -i 's/install --owner=root --group=root \"${QEMU_BIN}\" \"${L4T_ROOTFS_DIR}\/usr\/bin\/\"/#install --owner=root --group=root \"${QEMU_BIN}\" \"${L4T_ROOTFS_DIR}\/usr\/bin\/\"/g' nv_tegra/nv-apply-debs.sh && \
+ sed -i 's/chroot . \// /g' nv_tegra/nv-apply-debs.sh && \
+ ./apply_binaries.sh -r / --target-overlay && cd .. && \
+ rm -rf jetson-210_linux_r32.7.2_aarch64.tbz2 Linux_for_Tegra && \
+ echo "/usr/lib/aarch64-linux-gnu/tegra" > /etc/ld.so.conf.d/nvidia-tegra.conf && ldconfig
+
+# Setup the working environment
+WORKDIR /workspace
+
+# Clone the YOLOv8 repository and setup Python environment
+RUN git clone https://github.com/ultralytics/ultralytics.git && \
+ cd ultralytics && \
+ python3.8 -m venv venv && \
+ pip install -U pip wheel gdown && \
+ # Download pre-built PyTorch and TorchVision packages and install
+ gdown https://drive.google.com/uc?id=1hs9HM0XJ2LPFghcn7ZMOs5qu5HexPXwM && \
+ gdown https://drive.google.com/uc?id=1m0d8ruUY8RvCP9eVjZw4Nc8LAwM8yuGV && \
+ pip install torch-*.whl torchvision-*.whl && \
+ rm torch-*.whl torchvision-*.whl && \
+ # Install the Python package for YOLOv8
+ pip install .
\ No newline at end of file
diff --git a/docs/img/autopilot/ambiguous_corridor.png b/docs/img/autopilot/ambiguous_corridor.png
deleted file mode 100644
index d196c268..00000000
Binary files a/docs/img/autopilot/ambiguous_corridor.png and /dev/null differ
diff --git a/docs/img/autopilot/blocked_passage.png b/docs/img/autopilot/blocked_passage.png
deleted file mode 100644
index 20c39f09..00000000
Binary files a/docs/img/autopilot/blocked_passage.png and /dev/null differ
diff --git a/docs/img/autopilot/browser_navigationpoint_recognised1.jpg b/docs/img/autopilot/browser_navigationpoint_recognised1.jpg
deleted file mode 100644
index 9205c8f6..00000000
Binary files a/docs/img/autopilot/browser_navigationpoint_recognised1.jpg and /dev/null differ
diff --git a/docs/img/autopilot/browser_route_pause.jpg b/docs/img/autopilot/browser_route_pause.jpg
deleted file mode 100644
index e16079a8..00000000
Binary files a/docs/img/autopilot/browser_route_pause.jpg and /dev/null differ
diff --git a/docs/img/autopilot/browser_route_selector1.jpg b/docs/img/autopilot/browser_route_selector1.jpg
deleted file mode 100644
index 5b47eaab..00000000
Binary files a/docs/img/autopilot/browser_route_selector1.jpg and /dev/null differ
diff --git a/docs/img/autopilot/browser_route_start.jpg b/docs/img/autopilot/browser_route_start.jpg
deleted file mode 100644
index 8225fd68..00000000
Binary files a/docs/img/autopilot/browser_route_start.jpg and /dev/null differ
diff --git a/docs/img/autopilot/corridor_average.png b/docs/img/autopilot/corridor_average.png
deleted file mode 100644
index 5106960b..00000000
Binary files a/docs/img/autopilot/corridor_average.png and /dev/null differ
diff --git a/docs/img/autopilot/corridor_crossing.png b/docs/img/autopilot/corridor_crossing.png
deleted file mode 100644
index 65d8c610..00000000
Binary files a/docs/img/autopilot/corridor_crossing.png and /dev/null differ
diff --git a/docs/img/autopilot/corridor_merge.png b/docs/img/autopilot/corridor_merge.png
deleted file mode 100644
index 848f8260..00000000
Binary files a/docs/img/autopilot/corridor_merge.png and /dev/null differ
diff --git a/docs/img/autopilot/double_crossing.png b/docs/img/autopilot/double_crossing.png
deleted file mode 100644
index cb09dc94..00000000
Binary files a/docs/img/autopilot/double_crossing.png and /dev/null differ
diff --git a/docs/img/autopilot/filezilla_navigationpointfolder.jpg b/docs/img/autopilot/filezilla_navigationpointfolder.jpg
deleted file mode 100644
index ecf80367..00000000
Binary files a/docs/img/autopilot/filezilla_navigationpointfolder.jpg and /dev/null differ
diff --git a/docs/img/autopilot/filezilla_newroutes.jpg b/docs/img/autopilot/filezilla_newroutes.jpg
deleted file mode 100644
index 2cd31e7d..00000000
Binary files a/docs/img/autopilot/filezilla_newroutes.jpg and /dev/null differ
diff --git a/docs/img/autopilot/filezilla_robot_side.jpg b/docs/img/autopilot/filezilla_robot_side.jpg
deleted file mode 100644
index 538fdfc1..00000000
Binary files a/docs/img/autopilot/filezilla_robot_side.jpg and /dev/null differ
diff --git a/docs/img/autopilot/json_30_startrampup.jpg b/docs/img/autopilot/json_30_startrampup.jpg
deleted file mode 100644
index c6a6ad4f..00000000
Binary files a/docs/img/autopilot/json_30_startrampup.jpg and /dev/null differ
diff --git a/docs/img/autopilot/json_50_endrampup.jpg b/docs/img/autopilot/json_50_endrampup.jpg
deleted file mode 100644
index a6056c39..00000000
Binary files a/docs/img/autopilot/json_50_endrampup.jpg and /dev/null differ
diff --git a/docs/img/autopilot/json_80_stopbeforedown.jpg b/docs/img/autopilot/json_80_stopbeforedown.jpg
deleted file mode 100644
index ae48e430..00000000
Binary files a/docs/img/autopilot/json_80_stopbeforedown.jpg and /dev/null differ
diff --git a/docs/img/autopilot/labyrinth.png b/docs/img/autopilot/labyrinth.png
deleted file mode 100644
index af2e46f2..00000000
Binary files a/docs/img/autopilot/labyrinth.png and /dev/null differ
diff --git a/docs/img/autopilot/labyrinth2.png b/docs/img/autopilot/labyrinth2.png
deleted file mode 100644
index 942e2aef..00000000
Binary files a/docs/img/autopilot/labyrinth2.png and /dev/null differ
diff --git a/docs/img/autopilot/max_speed_zero.jpg b/docs/img/autopilot/max_speed_zero.jpg
deleted file mode 100644
index 9f686c7e..00000000
Binary files a/docs/img/autopilot/max_speed_zero.jpg and /dev/null differ
diff --git a/docs/img/autopilot/navigationpoint_choice.jpg b/docs/img/autopilot/navigationpoint_choice.jpg
deleted file mode 100644
index d430390c..00000000
Binary files a/docs/img/autopilot/navigationpoint_choice.jpg and /dev/null differ
diff --git a/docs/img/autopilot/navigationpointpictures.jpg b/docs/img/autopilot/navigationpointpictures.jpg
deleted file mode 100644
index 8fad0dba..00000000
Binary files a/docs/img/autopilot/navigationpointpictures.jpg and /dev/null differ
diff --git a/docs/img/autopilot/ros2_node_name_setting.jpg b/docs/img/autopilot/ros2_node_name_setting.jpg
deleted file mode 100644
index 876225ab..00000000
Binary files a/docs/img/autopilot/ros2_node_name_setting.jpg and /dev/null differ
diff --git a/docs/img/autopilot/rover_front.jpg b/docs/img/autopilot/rover_front.jpg
deleted file mode 100644
index 2108f416..00000000
Binary files a/docs/img/autopilot/rover_front.jpg and /dev/null differ
diff --git a/docs/img/autopilot/rover_frontOud.jpg b/docs/img/autopilot/rover_frontOud.jpg
deleted file mode 100644
index 9046c7d0..00000000
Binary files a/docs/img/autopilot/rover_frontOud.jpg and /dev/null differ
diff --git a/docs/img/autopilot/two_junctions.png b/docs/img/autopilot/two_junctions.png
deleted file mode 100644
index 22420018..00000000
Binary files a/docs/img/autopilot/two_junctions.png and /dev/null differ
diff --git a/docs/img/autopilot/uncertain.png b/docs/img/autopilot/uncertain.png
deleted file mode 100644
index 81397ae0..00000000
Binary files a/docs/img/autopilot/uncertain.png and /dev/null differ
diff --git a/docs/img/controller/cert_20lockiconclicked.jpg b/docs/img/controller/cert_20lockiconclicked.jpg
deleted file mode 100644
index bc6a346d..00000000
Binary files a/docs/img/controller/cert_20lockiconclicked.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_25connectionnotsecureddetailsclicked.jpg b/docs/img/controller/cert_25connectionnotsecureddetailsclicked.jpg
deleted file mode 100644
index 46ad619d..00000000
Binary files a/docs/img/controller/cert_25connectionnotsecureddetailsclicked.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_30moreinformationclicked.jpg b/docs/img/controller/cert_30moreinformationclicked.jpg
deleted file mode 100644
index a8595500..00000000
Binary files a/docs/img/controller/cert_30moreinformationclicked.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_35viewcertificateclicked.jpg b/docs/img/controller/cert_35viewcertificateclicked.jpg
deleted file mode 100644
index 99704766..00000000
Binary files a/docs/img/controller/cert_35viewcertificateclicked.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_40downloadcertificateclicked.jpg b/docs/img/controller/cert_40downloadcertificateclicked.jpg
deleted file mode 100644
index ed4a0501..00000000
Binary files a/docs/img/controller/cert_40downloadcertificateclicked.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_50certificateopeninkeychain.jpg b/docs/img/controller/cert_50certificateopeninkeychain.jpg
deleted file mode 100644
index e4617eac..00000000
Binary files a/docs/img/controller/cert_50certificateopeninkeychain.jpg and /dev/null differ
diff --git a/docs/img/controller/cert_55trustopen.jpg b/docs/img/controller/cert_55trustopen.jpg
deleted file mode 100644
index 6dd8f0d3..00000000
Binary files a/docs/img/controller/cert_55trustopen.jpg and /dev/null differ
diff --git a/docs/img/controller/dash_center_autopilot.png b/docs/img/controller/dash_center_autopilot.png
deleted file mode 100644
index 2392e0b6..00000000
Binary files a/docs/img/controller/dash_center_autopilot.png and /dev/null differ
diff --git a/docs/img/controller/dash_center_black.png b/docs/img/controller/dash_center_black.png
deleted file mode 100644
index 94f6c103..00000000
Binary files a/docs/img/controller/dash_center_black.png and /dev/null differ
diff --git a/docs/img/controller/dash_center_blue.png b/docs/img/controller/dash_center_blue.png
deleted file mode 100644
index 7abca9f6..00000000
Binary files a/docs/img/controller/dash_center_blue.png and /dev/null differ
diff --git a/docs/img/controller/dash_center_red.png b/docs/img/controller/dash_center_red.png
deleted file mode 100644
index 2bc93189..00000000
Binary files a/docs/img/controller/dash_center_red.png and /dev/null differ
diff --git a/docs/img/controller/dash_center_teleop.png b/docs/img/controller/dash_center_teleop.png
deleted file mode 100644
index 852a5ce5..00000000
Binary files a/docs/img/controller/dash_center_teleop.png and /dev/null differ
diff --git a/docs/img/controller/msg_another_user_in_control.png b/docs/img/controller/msg_another_user_in_control.png
deleted file mode 100644
index ea22cc33..00000000
Binary files a/docs/img/controller/msg_another_user_in_control.png and /dev/null differ
diff --git a/docs/img/controller/msg_connection_lost.png b/docs/img/controller/msg_connection_lost.png
deleted file mode 100644
index 45ffd86d..00000000
Binary files a/docs/img/controller/msg_connection_lost.png and /dev/null differ
diff --git a/docs/img/controller/msg_controller_not_detected.png b/docs/img/controller/msg_controller_not_detected.png
deleted file mode 100644
index c98e418a..00000000
Binary files a/docs/img/controller/msg_controller_not_detected.png and /dev/null differ
diff --git a/docs/img/controller/ps4_autopilot.jpg b/docs/img/controller/ps4_autopilot.jpg
deleted file mode 100644
index 643a5523..00000000
Binary files a/docs/img/controller/ps4_autopilot.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_autopilot_overrule.jpg b/docs/img/controller/ps4_autopilot_overrule.jpg
deleted file mode 100644
index b241d13a..00000000
Binary files a/docs/img/controller/ps4_autopilot_overrule.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_camera.jpg b/docs/img/controller/ps4_camera.jpg
deleted file mode 100644
index 0f076889..00000000
Binary files a/docs/img/controller/ps4_camera.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_cameraOud.jpg b/docs/img/controller/ps4_cameraOud.jpg
deleted file mode 100644
index 25ac2821..00000000
Binary files a/docs/img/controller/ps4_cameraOud.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_forward_backward.jpg b/docs/img/controller/ps4_forward_backward.jpg
deleted file mode 100644
index 0f2c468a..00000000
Binary files a/docs/img/controller/ps4_forward_backward.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_forward_backwardOud.jpg b/docs/img/controller/ps4_forward_backwardOud.jpg
deleted file mode 100644
index 951a0d8a..00000000
Binary files a/docs/img/controller/ps4_forward_backwardOud.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_overview.jpg b/docs/img/controller/ps4_overview.jpg
deleted file mode 100644
index 1fd2a674..00000000
Binary files a/docs/img/controller/ps4_overview.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_reverse_gear.jpg b/docs/img/controller/ps4_reverse_gear.jpg
deleted file mode 100644
index 2e07268e..00000000
Binary files a/docs/img/controller/ps4_reverse_gear.jpg and /dev/null differ
diff --git a/docs/img/controller/ps4_teleop.jpg b/docs/img/controller/ps4_teleop.jpg
deleted file mode 100644
index 077e47ec..00000000
Binary files a/docs/img/controller/ps4_teleop.jpg and /dev/null differ
diff --git a/docs/img/controller/teleop_feedback.jpg b/docs/img/controller/teleop_feedback.jpg
deleted file mode 100644
index f2d5994c..00000000
Binary files a/docs/img/controller/teleop_feedback.jpg and /dev/null differ
diff --git a/docs/img/controller/turn_left.png b/docs/img/controller/turn_left.png
deleted file mode 100644
index e78c0a25..00000000
Binary files a/docs/img/controller/turn_left.png and /dev/null differ
diff --git a/docs/img/controller/turn_right.png b/docs/img/controller/turn_right.png
deleted file mode 100644
index 33eb247a..00000000
Binary files a/docs/img/controller/turn_right.png and /dev/null differ
diff --git a/docs/img/controller/turn_straight.png b/docs/img/controller/turn_straight.png
deleted file mode 100644
index 40257479..00000000
Binary files a/docs/img/controller/turn_straight.png and /dev/null differ
diff --git a/docs/img/controller/ui_menu_options.png b/docs/img/controller/ui_menu_options.png
deleted file mode 100644
index 779e8ae0..00000000
Binary files a/docs/img/controller/ui_menu_options.png and /dev/null differ
diff --git a/docs/img/controller/ui_overview.jpg b/docs/img/controller/ui_overview.jpg
deleted file mode 100644
index 9b211d9a..00000000
Binary files a/docs/img/controller/ui_overview.jpg and /dev/null differ
diff --git a/docs/img/controller/ui_overviewOud.jpg b/docs/img/controller/ui_overviewOud.jpg
deleted file mode 100644
index 552630b0..00000000
Binary files a/docs/img/controller/ui_overviewOud.jpg and /dev/null differ
diff --git a/docs/img/controller/wheel_black.png b/docs/img/controller/wheel_black.png
deleted file mode 100644
index e16a2cf9..00000000
Binary files a/docs/img/controller/wheel_black.png and /dev/null differ
diff --git a/docs/img/controller/wheel_blue_speed.png b/docs/img/controller/wheel_blue_speed.png
deleted file mode 100644
index 312bccdf..00000000
Binary files a/docs/img/controller/wheel_blue_speed.png and /dev/null differ
diff --git a/docs/img/controller/wheel_red.png b/docs/img/controller/wheel_red.png
deleted file mode 100644
index 4c8170cc..00000000
Binary files a/docs/img/controller/wheel_red.png and /dev/null differ
diff --git a/docs/img/controller/wheel_red_speed.png b/docs/img/controller/wheel_red_speed.png
deleted file mode 100644
index 3a281cc9..00000000
Binary files a/docs/img/controller/wheel_red_speed.png and /dev/null differ
diff --git a/docs/img/controller/xbox_white.png b/docs/img/controller/xbox_white.png
deleted file mode 100644
index 8277ae5d..00000000
Binary files a/docs/img/controller/xbox_white.png and /dev/null differ
diff --git a/docs/img/drawings/ZZ_Archief/img204400v09.jpg b/docs/img/drawings/ZZ_Archief/img204400v09.jpg
deleted file mode 100644
index a0e76cfd..00000000
Binary files a/docs/img/drawings/ZZ_Archief/img204400v09.jpg and /dev/null differ
diff --git a/docs/img/drawings/ZZ_Archief/img206000P1v018.jpg b/docs/img/drawings/ZZ_Archief/img206000P1v018.jpg
deleted file mode 100644
index 0dd18844..00000000
Binary files a/docs/img/drawings/ZZ_Archief/img206000P1v018.jpg and /dev/null differ
diff --git a/docs/img/drawings/ZZ_Archief/img206000P1v019.jpg b/docs/img/drawings/ZZ_Archief/img206000P1v019.jpg
deleted file mode 100644
index 50503949..00000000
Binary files a/docs/img/drawings/ZZ_Archief/img206000P1v019.jpg and /dev/null differ
diff --git a/docs/img/drawings/ZZ_Archief/img206000P2v018.jpg b/docs/img/drawings/ZZ_Archief/img206000P2v018.jpg
deleted file mode 100644
index 7450ef6b..00000000
Binary files a/docs/img/drawings/ZZ_Archief/img206000P2v018.jpg and /dev/null differ
diff --git a/docs/img/drawings/ZZ_Archief/img206000P2v019.jpg b/docs/img/drawings/ZZ_Archief/img206000P2v019.jpg
deleted file mode 100644
index 1b636467..00000000
Binary files a/docs/img/drawings/ZZ_Archief/img206000P2v019.jpg and /dev/null differ
diff --git a/docs/img/drawings/img204400v011.jpg b/docs/img/drawings/img204400v011.jpg
deleted file mode 100644
index 3cb6538e..00000000
Binary files a/docs/img/drawings/img204400v011.jpg and /dev/null differ
diff --git a/docs/img/drawings/img206012v021.jpg b/docs/img/drawings/img206012v021.jpg
deleted file mode 100644
index 991f6872..00000000
Binary files a/docs/img/drawings/img206012v021.jpg and /dev/null differ
diff --git a/docs/img/drawings/img206020v022.jpg b/docs/img/drawings/img206020v022.jpg
deleted file mode 100644
index 8aa6f184..00000000
Binary files a/docs/img/drawings/img206020v022.jpg and /dev/null differ
diff --git a/docs/img/drawings/img208000v07.jpg b/docs/img/drawings/img208000v07.jpg
deleted file mode 100644
index 3d037453..00000000
Binary files a/docs/img/drawings/img208000v07.jpg and /dev/null differ
diff --git a/docs/img/index/rover_garden.jpg b/docs/img/index/rover_garden.jpg
deleted file mode 100644
index d3a2aee0..00000000
Binary files a/docs/img/index/rover_garden.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204110.jpg b/docs/img/instructions/img204110.jpg
deleted file mode 100644
index be7e6288..00000000
Binary files a/docs/img/instructions/img204110.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204120.jpg b/docs/img/instructions/img204120.jpg
deleted file mode 100644
index 4e9700b1..00000000
Binary files a/docs/img/instructions/img204120.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204215.jpg b/docs/img/instructions/img204215.jpg
deleted file mode 100644
index d2a0ec45..00000000
Binary files a/docs/img/instructions/img204215.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204405.jpg b/docs/img/instructions/img204405.jpg
deleted file mode 100644
index 2faff37a..00000000
Binary files a/docs/img/instructions/img204405.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204415.jpg b/docs/img/instructions/img204415.jpg
deleted file mode 100644
index e744e2c8..00000000
Binary files a/docs/img/instructions/img204415.jpg and /dev/null differ
diff --git a/docs/img/instructions/img204425.jpg b/docs/img/instructions/img204425.jpg
deleted file mode 100644
index c6ed06ca..00000000
Binary files a/docs/img/instructions/img204425.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206120.jpg b/docs/img/instructions/img206120.jpg
deleted file mode 100644
index a1717ef7..00000000
Binary files a/docs/img/instructions/img206120.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206230.jpg b/docs/img/instructions/img206230.jpg
deleted file mode 100644
index 192946c9..00000000
Binary files a/docs/img/instructions/img206230.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206235.jpg b/docs/img/instructions/img206235.jpg
deleted file mode 100644
index b2f3c644..00000000
Binary files a/docs/img/instructions/img206235.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206245.jpg b/docs/img/instructions/img206245.jpg
deleted file mode 100644
index dfd0d6a2..00000000
Binary files a/docs/img/instructions/img206245.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206275.jpg b/docs/img/instructions/img206275.jpg
deleted file mode 100644
index b3a63218..00000000
Binary files a/docs/img/instructions/img206275.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206310.jpg b/docs/img/instructions/img206310.jpg
deleted file mode 100644
index 8b2bf933..00000000
Binary files a/docs/img/instructions/img206310.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206315.jpg b/docs/img/instructions/img206315.jpg
deleted file mode 100644
index cc764744..00000000
Binary files a/docs/img/instructions/img206315.jpg and /dev/null differ
diff --git a/docs/img/instructions/img206510.jpg b/docs/img/instructions/img206510.jpg
deleted file mode 100644
index 18233e7f..00000000
Binary files a/docs/img/instructions/img206510.jpg and /dev/null differ
diff --git a/docs/img/readme/hover_vid.jpg b/docs/img/readme/hover_vid.jpg
deleted file mode 100644
index 9a9b4a0b..00000000
Binary files a/docs/img/readme/hover_vid.jpg and /dev/null differ
diff --git a/docs/img/readme/rover_front_small.jpg b/docs/img/readme/rover_front_small.jpg
deleted file mode 100644
index 9046c7d0..00000000
Binary files a/docs/img/readme/rover_front_small.jpg and /dev/null differ
diff --git a/docs/img/startup/managed_ip.png b/docs/img/startup/managed_ip.png
deleted file mode 100644
index 68c90e6d..00000000
Binary files a/docs/img/startup/managed_ip.png and /dev/null differ
diff --git a/docs/img/zerotier/client_application_menu.png b/docs/img/zerotier/client_application_menu.png
deleted file mode 100644
index 562ae0e4..00000000
Binary files a/docs/img/zerotier/client_application_menu.png and /dev/null differ
diff --git a/docs/img/zerotier/enter_network_id.png b/docs/img/zerotier/enter_network_id.png
deleted file mode 100644
index e6bf0d25..00000000
Binary files a/docs/img/zerotier/enter_network_id.png and /dev/null differ
diff --git a/docs/img/zerotier/hub_menu.png b/docs/img/zerotier/hub_menu.png
deleted file mode 100644
index d070a586..00000000
Binary files a/docs/img/zerotier/hub_menu.png and /dev/null differ
diff --git a/docs/img/zerotier/hub_network_members.png b/docs/img/zerotier/hub_network_members.png
deleted file mode 100644
index 64587188..00000000
Binary files a/docs/img/zerotier/hub_network_members.png and /dev/null differ
diff --git a/docs/img/zerotier/hub_overview_small.png b/docs/img/zerotier/hub_overview_small.png
deleted file mode 100644
index d66b9482..00000000
Binary files a/docs/img/zerotier/hub_overview_small.png and /dev/null differ
diff --git a/docs/img/zerotier/login_email_password.png b/docs/img/zerotier/login_email_password.png
deleted file mode 100644
index cd32b25b..00000000
Binary files a/docs/img/zerotier/login_email_password.png and /dev/null differ
diff --git a/docs/img/zerotier/login_to_zerotier.png b/docs/img/zerotier/login_to_zerotier.png
deleted file mode 100644
index 9804a20f..00000000
Binary files a/docs/img/zerotier/login_to_zerotier.png and /dev/null differ
diff --git a/docs/img/zerotier/manually_add_member.png b/docs/img/zerotier/manually_add_member.png
deleted file mode 100644
index 5f0f81c0..00000000
Binary files a/docs/img/zerotier/manually_add_member.png and /dev/null differ
diff --git a/docs/img/zerotier/network_id_and_name.png b/docs/img/zerotier/network_id_and_name.png
deleted file mode 100644
index 52be9f0e..00000000
Binary files a/docs/img/zerotier/network_id_and_name.png and /dev/null differ
diff --git a/docs/img/zerotier/your_networks.png b/docs/img/zerotier/your_networks.png
deleted file mode 100644
index 6ba1f778..00000000
Binary files a/docs/img/zerotier/your_networks.png and /dev/null differ
diff --git a/docs/mwlc_assembly.md b/docs/mwlc_assembly.md
deleted file mode 100644
index 08abd18d..00000000
--- a/docs/mwlc_assembly.md
+++ /dev/null
@@ -1,115 +0,0 @@
-# Introduction
-#### Disclaimer
-This documentation provides information intended to help people build a self driving robot and to use it. The development of the robot is ongoing and it is not complete. The information in the documentation is subject to change. The documentation is provided free of charge.The robot as described in the documentation is a vehicle for testing software. We do not deploy the robot for applications that carry risk of any kind. Please pay attention to the guidelines and suggestions given, also for the use and charging of the battery. No explicit regulatory permission of any kind has been sought for deployment of the robot. This documentation has been put together with the greatest possible care. We are in no way responsible for any omissions and inaccuracies with regard to the information provided on the website, for whatever reason. We do not accept any liability.The information provided on the website is subject to certain intellectual property rights. Unless explicitly authorized to do so, you are not allowed to reproduce information provided on the website for commercial purposes of any kind.
-#### Instructions
-The instructions are exported from the development database and used by our technicians. The information might be a bit cryptic.
We are always open for improvements. Don't hesitate to [contact](http://www.mwlc.global/contact/) us, if you need some help or an extra picture.
-#### Content
-The robot is assembled in main stages:
- Chassis
- Body: External skeleton
- Body: Enclosure
- Cables, equipment
- Configuration
-# Chassis
-#### Arrma Kraton 8S
-The Kraton 8s is the largest Arrma model, but much too fast and with minimal carrying capacity.
To be able to carry more payload, the vehicle must be converted so that the motors for steering and driving last longer.
-Before the chassis can work for the robot we have to change some parts.
- Springs
- Steering servo
- Tires
-After unpacking take off:
- Cover Green/Orange ARA409001/7
- 2* Battery holders ARA320496
- Back spoiler, ARA480022
- Spoiler mount, ARA320492
- ARA320485
- 4* Wheels including tires ARA510122/520055/550061
- Some small parts like bolts and nuts
-#### Springs
-Change the springs.
To make the suspension more firm, first we change the springs to reduce pressure on the shocks.
Put the large springs on the place of the originals and the small springs around the shock shaft.
Find a video with instructions.
Note: The front springs are shorter than the back springs(ARA330572 and ARA330573)
-#### Esc tuning
-Change ESC settings according to Kraton 8S instruction manual.
- Low voltage cutoff(variable 3) to Low(setting 1)
- Brake strength(variable 5), 10%(setting 7)
- Reverse strength(variable 6), 75%(setting 3)
- Punch setting(variable 7), level 1(setting 1)Leave battery connected for next step
-#### Change steering servo
-This is a tricky operation, take some time to look for the video's, with instructions.
- Place batteries in hand unit
- Start the Kraton and the hand unit, set the steering wheels in straight forward
- Mark position servo saver both on the saver and the casing with something sharp, like a screwdriver, so you know afterwards if it fits correctly.
- Switch everything off
- Release ESC/Servo unit, 4 screws bottom
- Release cover, 6 small screws
- Release receiver, and wires from the box. Take the antenna wire out of the little tube.
- Release steering rod from servo saver to the steering connector on steering. Watch out not to loose little black lock nut m3, the bold will be replaced by a Allen screw, M3, 22 mm, quality 12.9.
-Release plastic contra bolt in servo saver(hex4). Watch out, releasing is clockwise!!!
- release bolt(hex2,5) far in servo saver to release is form the servo. Release safer with Allen screw driver.
- Take the new servo, connect the wire to the servo first.
- Take the new receiver box. Check if the new servo fits.
- Swap the data cable on the receiver.
Of the content of the package we don’t use the spring and the steering linkage.
Change the servo, take care it is positioned exactly right.
-The steering rod bold should be replaced with an Allen screw M3x22mm, steel 12.9.
-After changing the servo you can remove the receiver
-#### Wire shortcuts
-__ Red wires of the on/off button__
Shortcut the red wires of the on/off button
Use the orange ferrule double and the heat shrink.
Heat shrink 2,5 mm 2:1 covering the ferrule metal tube.
Heats chink 6 mm, 3:1, covering the ferrule.
-#### Cable gland bottom
-Before putting the enclosure on the robot, first put the cable gland around the cable from the ESC.
- Take a M25 Cable gland with cable gland nut, lock nut and outer seal ring.
- Put the gland nut over the cables for the ESC.(2 power cables and 2*2 thin data cables).
-- Take the multi seal or grommet, M25, 3 holes. Cut the holes open to put the cables through.
- First put small cable through one hole then the orange and black from the ESC, each through their own hole.
Then put cable gland around it. Screw the cable gland nut on the cable gland, not to tight.
- Ensure that the shortest wire, the steering servo connector is reachable in the enclosure. The gland must be quite tide to the chassis.
You can check this after making the bottom hole.
-# External skeleton
-The skeleton is built with the aluminum profile. 20 x 20 mm, groove 5.
In the end it should look like the picture.
-
-We have two kinds, both for groove 5 but 1 kind for Allen screw M5 and 1 kind for Allen screw M4 that is used for the brackets.
These options are also available in plate versions.
The blocks easy to work with but more expensive.
-
-#### Crossbeams
-Connect 2 crossbeams, 0,23m to the bottom of the chassis.
Use the empty holes from the battery holders and the M4 screws.
-#### Pillars(vertical profiles)
-Attach 4 pillars, 0,14m with brackets on the low crossbeams. The bracket are under the chassis.
-#### Longitudinal beams
-On the pillars the longitudinal beams are fastened.
At the right side 0,79m.
At the left side 0,85m.(longer to connect the back camera)
To protect the front camera, at the front, both beams will end in front of the front camera.
At the back, the right beam stops 2 cm after the end of the enclosure. The left beam goes further to the 6 cm back, to mount the back camera.
The longitudinal beams should be close to the bottom of the enclosure but don't push it up. The enclosure should rest on the towers of the chassis.
-#### Cross beams high
-On the longitudinal beams the high crossbeams of 0,23m are attached. They are close to the enclosure to keep it in place
-#### Lock pillars
-At both the front and the back side, attached to the high crossbeams, there should be the lock pillars of 0,13m.
It might use full to connect the front lock pillar with to brackets as the fornt-side of is often used replace the robot.
-The lock longitudinal beam, 0,90m, wil be attached to the lock pillars.
This beam is a bit longer to catch the hits.
Connect the beam with 2 brackets at each side for easy opening.
-#### Mount front Camera
-Bracket 150*150mm with 3 holes per side
Bracket 60*60mm with per side 2 holes
1* Allan screw M5 6mm
1* Allan screw M5 10mm
Tripod screw 8mm
2 repairwasher m5
2 washer m5
2 Slide nut
-
-Mount back Camera
-Bracket 150*150mm with 3 holes per side
Tripod screw
1 * Allan screw M5 10
6 washer M5
Slide nut
Extra bracket to lock camera
-# Enclosure
-In the end the enclosure will look more or less like in the picture.
But we have to admit, every build we find an improvement. Less cables, less space used, more easy to open.
In the instructions you find our latest build.
The switch is below the Nano.
Sometimes we get a request for a larger battery. This takes some space.
-
-__ Silica __
Don't forget to put in a pack of silica gel. But this can be done any time.
-#### Spelsberg AKL-4-t
-The Spelsberg enclosure AKL-4-t has transparant cover, metric pre-embossings.
Slightly cheaper alternative enclosure has a non transparant cover.
The enclosure should rest on the front and back towers of the chassis.
As the center tower is just slightly lower and the bottom of the enclosure has ridges. To make the bottom of the enclosure resting on the towers only, make a cross at the top of the centre tower to fit the enclosure in.
At the end we put some double sided adhesive tape at the towers.
-
-#### Holes, glands, connectors and buttons
-See scheme voor the location and size of the holes.
We use step drill bit to make or widening the holes.
It might be a suggestion to keep the part that needs the hole, nearby to see if it fits.
We always start with the bottom-hole and make is fit perfectly, even by putting the cables through the gland and see if the enclosure fits well.
-
-__ Bottom hole __
One of the more tricky parts is the location of the bottom hole for the M25 cable gland with the ESC power and datacables.
The outside bottom of the enclosure has ridge wich should stay intact.
Check with the already attached cable gland the location.
-__ Charge connector __
The screws to tighten the Rosenberger-connector need some space.
Avoid that the hole gets more to the top of the enclosre. While widening we press the drill to the bottom of the enclosure.
-__ Antenna cables __
The 5 antenna cables should be at the back-side left. Bring the cables around 0,15m within the enclosure.
- Put the cable gland nut over the cable connectors
- Take the black single seal ring from the gland and put it also over the connectors.
- Take the multi-seal with 5 holse, cut the holes en put the cables through.
Put around the multi-seal the big single seal from the cable gland.
First put the cable gland over the cables before putting is in het enclosure.
Don't make the cable gland to tide yet.
-__ WAN __
The WAN connector should be right-side of the enlcore most at the back.
The connector at the inside should point up with a small angle to the back.(The Lan connector will be above it)Use a self-adhesive cable mount, under the enclosure, to keep the cap to the vehicle wen not on the connector.
-__ LAN __
The Lan connector should be just above the WAN connector and a bit to the front.
-__ Cable gland back Camera __
Grommet qt 6, around camera cable between camera and first split.
Not too close to split. It should be possible to have the cable bend within the enclosure.
Take the two white parts ant put them around the grommet. The grommet fits just in one position exact right.
Take the sealing ring over the cable and put it around the gland.<>Pucht the gland though the enclosure.
Take the black splittable contra nut and tighten the gland to the enclosure.
- Put the black 0,25m ethernet cable in the connector.
- Put the 0,5m low voltage power cable in the connector.
-__ Cable gland front Camera __
Grommet around camera cable between camera and first split.
Not to close to split.
It should be possible to have the cable bend within the enclosure.
Cabel gland front camera works the same as back.
-__ Ignition switch __
Ignition switch is mounted at the same side as the LAN and WAN, just a bit to the fornt of the LAN.
-__ Stop button __
Stop button is mounted at the opposite side, left, the front, of the enclosure.
-# Cables and equipment
-#### Battery and internal frame
-__ Backside cable duct __
Put a piece, 0,28m, of cable duct against the back.
- Make it fit by taking the back side corners off.
- Take teeth off by the cable cland of the antenna's.
- Take teethparts off for the camera cable gland.
- Take 3 teethparts off for the Rosenbergerconnector.
Depending on the battery it might be handy to split the backside cable duct so it fits at both sides of the length cable duct.
-__Battery __
Put battery at back-left side in the enclosure, against the back side cable duct.
-__ Length cable duct __
Put a 0,58m cable duct in the middel over the full length of the enclosure against the right site of the battery.
- Make the back 3 cm flat so you can put it under the back cable duct or have the backside cable duct at both sides.
-__ Enclosure lay-out __
The battery is the largest component within the enclosure. As soon as the battery is in place, you can make a lay-out for the other components.
Collect the main components that should be in the enclosure and prepare the equiment holders for the Nano, Pi and the two USB-relays.
Think about how to fasten the ethernet-switch.
The best opption is not to need two layers and have all equipment on the bottom of the enclosure and on top of the battery.
The battery should be fixated.
-#### Equipment holders
-__ Holder for the Nano steered Relay __
Use Axxatronic 72mm:
- 2 end parts
- 1 narrow part
- 1 widepart.
-__ Holder for the Pi steered Relay __
See Relay Nano steered holder.
-__ Nano holder __
First, place SD card.
- Take PVC plate 107 x 100mm (or 72x80mm, if there is not a lot of space.
- Take 4 allen screws, 2,5x10mm
- Take 4 lock nuts 2,5mm
- Take 4 spacers
- Assemble Nano on the pvc plate, nuts topside, cable connections on the nano to the left.
- Put the plate with Nano on axxatronic 107mm( or 72mm) with foot.
-__ Pi holder __
First, place SD card.
Then mount the hat on the Pi with the mounting set that comes with the Pi- Take PVC plate 80 x 72mm
- Assemble the hat on the Pi on the pvc plate, wiIth the spacers and screws from the set delivered with the hat.
- Assemble the Pi on the pvc plate, wiIth allen screw 2,5x12.
- Put the plate with Pi on Axxatronic 72mm with foot.
-__Optional: internal frame __
You can use this if you can't find enough space on the bottom of the enclosure.
The internal frame consists of a few pieces of alluminium profile.
A small pillar close to the battery.
Connected to a horizontal piece to the front of the enclosure, that is conncted with a crossbeam to keep it stable.
Make a fitting frame.
3 pieces of profile:
- Pillar, from the bottom of the enclosure to the cover.
- Longitudinal, from the baterry to the front side.
- Crossbeam, at the front from left to right.
-__ Din rail __
1. Put 0,58m din rail over the length of the right side of the enclosure but not on the incoming ESC cables.
Put it against the backside cable duct.
2. Put din rail over the length of the left side of the enclosure. From the battery to the inner front side of the enclosure.
Put it against the backside cable duct.
-For a second layer you have to improvise with the din rail. For instance:
- ethernet switch to crossbeam profile of internal frame, 0,05m
- Computers, 0,25m
- 12vdc converter, 0,10m
- 2*5vdc converter, 0,10m
-# Cabling
-Start with putting in all component, losely, in the enclosure.
After al cables and wires are connected, fixate the component.
It might be usefull to have the schemes at hand.
While doing we also connect the ethernet cables, so keeps this scheme also nearby.
-__Hints__
Step by step the cabling voor the power will be assembled.
To avoid problems also some equipment and computer cables will be placed.
Some basics:
- Put cables and wires as much as possible in the cable ducts, without forcing of course.
- Use ferrules and cables with the right colours. This will help you later on.
- We connect the power per cicuit. I suppose there is more efficient methode but per circuit is simple.
- The cable length is more complex as we thought. If there are lengths on the scheme, just ignore them.
- I tailor the length on the moment I put it in. Take a wire in the right colour, put a ferrule on it. Connect 1 side. Lay it via the cable duct to the other end. Then cut it to the right length and put a ferrule on it.
- Not all wire have a ferrule at both sides. For instance in the fuse-holders and the RUT955 green terminal.
- Worst mistake is not tighten a wire enough to a connector. This is hard to test.
- The schemes with remarks are leading.
- Our working order is described.
-__ Back camera __
Fold the ethernet cable and power cable in the cable duct.
Plug in power wires in accoring to scheme, lay them in de cable duct direction RUT-distribution.
Connect the Ethernet with the router with a ethernet cable according to scheme.
Fold them in cable duct.
-__ WAN __
Put ethernet cable in.
-
-__ LAN __
Put ethernet cable in.
-__Ignition switch__
Put the adaptor plate horizontal on the inginition switch.
Put the NO-contact breaker on the lower position of the adaptor plate
-__ Stopbutton __
Put the adaptor plate on the stop button.
Put the NC-contact breaker on the adaptor plate
The positon can be choosen as suits you the best.
-__Battery and charge__Place 4 fuseholders on the din rail, between the ethernet passthrough's and the adaptar plate from the ignition switch.
Place 2 red terminal blocks on the din rail(option to disconnect on inside).
Place 2 black terminal blocks on the din rail(option to disconnect on inside).
Option: if the battery cable is more the 4mm2, take thick terminal block.
Option: if the battery has seperate cable for plus(red)-pole, take extra red terminal block.
Option: if the battery has seperate cable for minus(black)-pole, take extra black terminal block.
Put a shield against the open side of the last terminal block.
- Red wire from battery in the first fuse holder.
- Red wire from first fuseholder to first red terminal block.
- Red wire from charge connector into second fuse holder.
- Red wire from second fuseholder to second red terminal block.
- Option: If there is seperate extra red charge wire on the battery, connect this to the extra terminal block and lay the red wire from the second fuse not to the red terminal block but also to the extra red teminal block.
- Black wire from battery into (thick) black terminal block
- Black wire from charge connector into black terminal block
- Option: If there is seperate extra black charge wire on the battery put both the charging wire as the wire from the charger into the extra black terminal block.
-Create main distribution:
- Connect the black terminal blocks with bridge.
- Connect the red terminal blocks with bridge.
This thus not include the optional charge terminal blocks.
-__ Terminal blocks ESC input __
Put the terminals in back from the cable-gland and connect the power.
-
-__ SSR(Steady State Relay)__
Put the SSR losely on the din rail in the second part of the enclosure.
Connect the power according to scheme, via the third fuse.
After connecting the control wires in port 1 and 2, the SSR can be fixated.
-
-__ Power cabling to 12VDC __
Put the 12vdc losely in place.
The 12vdc power start with the ignition key. After the ignition al other system will be started.
Connect according to scheme with the battery power.
The incoming power should be place against the wall of the enclosure.
-
-__ Rut955 PoE __
Connect power from the first set of 12vdc-converter with the PoE-injector for the RUT955 via the fourth fuse.
-__ Place RUT-distrbution __
Put beside the ESC connector:
- 6 red thin terminal blocks
- Connect them with a 5 bridge and a 2 bridge
- 6 black thin terminal blocks
- Connect them with a 5 bridge and a 2 bridge
Put them losely.
Plug in the incoming wires from the power source in the back side.
Then you can fixate them.
Put wires to the end-power consumer on the fornt.
-
-__ Connect RUT-distribution __
See scheme.
This goes via the relay on the RUT955.
-
-__ Ethernet switch __
Connect the power of the ethernet switch.
-
-__ Front camera __
Plug in power wires in according to scheme
-__ Back camera __
Plug in power wires in according to scheme
-__ Power Nano__
Plug in power wires in according to scheme, via a 5vdc converter
-__ Power Pi __
Plug in power wires in according to scheme via the 5vdc-conveter and the stop-button.
-__Remaing SSR control wires__
Make the remaing connetions of the control for the SSR with the RUT-distribution.
-__ Grove cable Pi to steering and ESC __
Plug in Grove-cable in according to scheme.
-__ Ethernet Pi Nano front camera __
Plug in power wires in according to scheme
-# Testing
-When starting upo the following light and sound should apperar:
- Blue 12vdc
- Orange RUT
Wait
- 5vdc
- Camera check
- Lights ethernet
- Lights Nano
- Light Nano steered relay
- Lights Pi
-Close the encloser and use the locker profile to fixate the encloser on the chassis.
-# Questions and suggestion
-The assembly instructions are user driven and created based on questions to give guidance on certain aspects.
If you have any question, don't hesitate to contact us at [www.mwlc.global/contact/](http://www.mwlc.global/contact/).
diff --git a/following/.DS_Store b/following/.DS_Store
new file mode 100644
index 00000000..09f64491
Binary files /dev/null and b/following/.DS_Store differ
diff --git a/following/320_20k.pt b/following/320_20k.pt
new file mode 100644
index 00000000..0c2d71ab
Binary files /dev/null and b/following/320_20k.pt differ
diff --git a/following/400_20k.pt b/following/400_20k.pt
new file mode 100644
index 00000000..8d049566
Binary files /dev/null and b/following/400_20k.pt differ
diff --git a/following/480_20k.pt b/following/480_20k.pt
new file mode 100644
index 00000000..cb753612
Binary files /dev/null and b/following/480_20k.pt differ
diff --git a/following/640_20k.pt b/following/640_20k.pt
new file mode 100644
index 00000000..a9399cee
Binary files /dev/null and b/following/640_20k.pt differ
diff --git a/following/Dockerfile b/following/Dockerfile
new file mode 100644
index 00000000..5eb4a6e6
--- /dev/null
+++ b/following/Dockerfile
@@ -0,0 +1,24 @@
+FROM mwlvdev/jetson-nano-ubuntu:bionic-torch1.11-cp38-cuda10.2
+#FROM ultralytics/ultralytics:latest-arm64
+
+
+RUN apt-get update && apt-get install -y \
+ build-essential \
+ python3-dev \
+ python3-pip
+
+# Install necessary Python libraries
+RUN python3 -m pip install --upgrade pip
+# libraries from utils libraries
+RUN python3 -m pip install numpy pyzmq lap simple-pid
+
+# Copy your application files
+COPY ./common/ /common/
+COPY ./following/ /app/
+WORKDIR /app
+
+# Set PYTHONPATH environment variable
+ENV PYTHONPATH "${PYTHONPATH}:/common"
+
+# Command to run your application
+CMD ["python3", "app.py"]
\ No newline at end of file
diff --git a/following/app.py b/following/app.py
new file mode 100644
index 00000000..ba1789c1
--- /dev/null
+++ b/following/app.py
@@ -0,0 +1,358 @@
+import os
+import glob
+import configparser
+import logging
+import multiprocessing
+import threading
+import math
+import numpy as np
+import time
+from ultralytics import YOLO
+import cv2
+from byodr.utils import timestamp
+from byodr.utils.ipc import JSONPublisher, json_collector
+from byodr.utils.option import parse_option
+
+# Constants
+SCREEN_CENTER = 320
+CENTER_OFFSET = 0
+SECOND_OFFSET = 80
+THIRD_OFFSET = 80
+PAN_OFFSET = 50
+START_HEIGHT = 340
+UNSAFE_HEIGHT = 360
+MAX_HUMAN_ABSENCE_FRAMES = 5
+MIN_CLEAR_PATH_FRAMES = 3
+SMOOTH_CONTROL_STEP = 0.1
+
+
+class FollowingController:
+ def __init__(self, model_path, config_path="/config"):
+ self.quit_event = multiprocessing.Event()
+ self.model = YOLO(model_path)
+ self.no_human_counter = 0
+ self.clear_path = 4
+ self.logger = self.setup_logger()
+ self.config = self.load_config(config_path)
+ self.teleop = self.setup_teleop_receiver()
+ self.publisher = self.setup_publisher()
+ self.current_throttle = 0
+ self.current_steering = 0
+ self.current_spin = None
+ self.current_camera_pan = 0
+ self.current_method = "Absolute"
+
+ self.image_counter = 0
+ self.image_save_path = "/byodr/yolo_person"
+ self.prev_time = int(timestamp())
+ self.camera_following_flag = 0
+ self.going_home_flag = 0
+ self.last_azimuth = 0
+
+ os.makedirs(self.image_save_path, exist_ok=True)
+
+ def request_check(self):
+ """Constantly fetches the request from Teleop to start following"""
+ while True:
+ time.sleep(0.05)
+ try:
+ follow_request = self.teleop.get()["following"]
+ except:
+ self.current_steering = 0
+ self.current_throttle = 0
+ self.current_spin = None
+ self.current_camera_pan = 0
+ self.current_method = "Absolute"
+ continue
+
+ if follow_request == "Start Following":
+ self.start_yolo_model()
+
+ def start_yolo_model(self):
+ """Accesses the results of the detection in images from the stream. After running this function, the program gets stuck in the 'for loop' with results"""
+ self.publish_command(self.current_throttle, self.current_steering, self.current_spin, self.current_camera_pan, self.current_method) # Initializing with safe values
+ self.reset_tracking_session()
+ time.sleep(2) # Let the camera reset to home position
+ try:
+ self.control_logic(self.results) # Calculating the control commands based on the model results
+ except Exception as e:
+ self.logger.warning("Exception loading results: " + str(e) + ". Waiting additional 10 seconds \n")
+ time.sleep(8) # Waiting 10 sec in case the user pressed the follow button before model was loaded (temporary)
+ self.control_logic(self.results)
+
+ def reset_tracking_session(self):
+ """Reset the image counter and clear all images in the directory."""
+ self.image_counter = 0
+ for existing_file in os.listdir(self.image_save_path):
+ os.remove(os.path.join(self.image_save_path, existing_file))
+ self.logger.info("Tracking session reset: Image counter zeroed and folder cleared.")
+
+ def setup_logger(self):
+ logging.basicConfig(format="%(levelname)s: %(asctime)s %(filename)s %(funcName)s %(message)s", datefmt="%Y%m%d:%H:%M:%S %p %Z")
+ logger = logging.getLogger(__name__)
+ logger.setLevel(logging.INFO)
+ return logger
+
+ def track_and_save_image(self, result):
+ """Tracks objects in video stream and saves the latest image with annotations."""
+ # https://github.com/ultralytics/ultralytics/issues/1696#issuecomment-1948021841
+ full_annotated_image = result.plot(show=False, pil=False) # Ensuring it returns a numpy array
+ full_annotated_image = cv2.cvtColor(full_annotated_image, cv2.COLOR_RGB2BGR) # Convert RGB to BGR for OpenCV
+ filename = os.path.join(self.image_save_path, f"image_{self.image_counter}.jpg")
+ cv2.imwrite(filename, full_annotated_image)
+ self.image_counter += 1
+
+ # Check the number of images in the directory and delete the oldest if more than 10
+ all_images = sorted(os.listdir(self.image_save_path), key=lambda x: os.path.getctime(os.path.join(self.image_save_path, x)))
+
+ if len(all_images) > 10:
+ oldest_image = all_images[0]
+ os.remove(os.path.join(self.image_save_path, oldest_image))
+
+ def load_config(self, path):
+ parser = configparser.ConfigParser()
+ [parser.read(f) for f in glob.glob(os.path.join(path, "*.ini"))]
+ return dict(parser.items("vehicle")) if parser.has_section("vehicle") else {}
+
+ def setup_teleop_receiver(self):
+ teleop = json_collector(url="ipc:///byodr/teleop_c.sock", topic=b"aav/teleop/chatter", pop=True, event=self.quit_event, hwm=1)
+ teleop.start()
+ return teleop
+
+ def setup_publisher(self):
+ return JSONPublisher(url="ipc:///byodr/following.sock", topic="aav/following/controls")
+
+ def publish_command(self, throttle=0, steering=0, spin=None, camera_pan=0, method="Momentary"):
+ """Publishes the control commands to Teleop."""
+ cmd = {"throttle": throttle, "steering": steering, "spin": spin}
+ # cmd = {"throttle": 0.2, "steering": 0, "spin": 1}
+ # cmd["camera_pan"] = -50
+ # cmd["method"] = "Momentary"
+ if camera_pan is not None:
+ cmd["camera_pan"] = camera_pan
+ cmd["method"] = method
+ self.publisher.publish(cmd)
+ self.logger.info(f"Sending command to teleop: {cmd}")
+ print((int(timestamp()) - self.prev_time) / 1000, "ms between messages")
+ self.prev_time = timestamp()
+
+ def check_obstacles(self, boxes):
+ """Checks the height of all detected objects and only lets the robot spin if anything is in close proximity.
+ Allows regular movement if the path has been clear for more than 3 frames."""
+ clear_path = self.clear_path
+ for box in boxes: # Checking every detected person in the frame
+ x1, y1, x2, y2 = box.xyxy[0, :]
+ if (y2 - y1) >= UNSAFE_HEIGHT: # Detected person is too close to the robot if the height is too large
+ return 0
+ clear_path += 1 # Path is clear in this frame
+ return clear_path
+
+ def control_logic(self, results):
+ """Processes each frame of the stream."""
+ for r in results: # Running the loop for each frame of the stream
+ self.request = self.teleop.get() # Checking for request to stop following
+ try:
+ if self.request["following"] == "Stop Following": # Sending no movement if following stopped
+ self.logger.info("Stopping Following")
+ self.current_throttle = 0
+ self.current_steering = 0
+ self.current_spin = None
+ self.current_camera_pan = 0
+ self.current_method = "Absolute"
+ self.camera_following_flag = 0
+ self.going_home_flag = 0
+ self.publish_command(self.current_throttle, self.current_steering, self.current_spin, self.current_camera_pan, self.current_method)
+ return
+ except:
+ pass
+ boxes = r.boxes.cpu().numpy() # List of bounding boxes in the frame
+ self.clear_path = self.check_obstacles(boxes) # Checking for obstructions
+ throttle, steering, spin, camera_pan, method = self.decide_control(boxes) # Calculating control commands based on the results of image detection
+ self.track_and_save_image(r)
+ self.publish_command(throttle, steering, spin, camera_pan, method) # Sending calculated control commands
+
+ def smooth_controls(self, target_throttle, target_steering):
+ """Smoothes the acceleration. Unlike the 'mobile controller smoothing', this only works when throttle is increasing."""
+ if self.current_throttle <= (target_throttle - SMOOTH_CONTROL_STEP): # Smoothing only if the difference is greater than the control step
+ self.current_throttle += SMOOTH_CONTROL_STEP
+ else:
+ self.current_throttle = target_throttle # Passing without smoothing if the difference is too small
+ # if abs(self.current_steering) <= abs(target_steering) - SMOOTH_CONTROL_STEP: # Steering can be negative or positive
+ # self.current_steering += math.copysign(SMOOTH_CONTROL_STEP, target_steering) # Making sure steering has the correct sign
+ # else:
+ # self.current_steering = target_steering
+ self.current_steering = target_steering # steering is not being smoothed
+
+ def spin_robot(self, steering):
+ """Makes the wheels of the robot turn in opposite directions. The 'spin' flag decides which turn the robot will spin. 1 = turning right, -1 = turning left.
+ The speed of turning is determined by throttle. Steering must always be 0 when using 'spin' flag"""
+ spin = int(np.sign(steering))
+ throttle = max(0.1, min(0.3, (abs(steering)) / 2))
+ steering = 0
+ return throttle, steering, spin
+
+ def going_home(self, azimuth, box_center):
+ """Moves the camera back to the home position and turns the robot in the opposite direction to camera movement"""
+ if abs(box_center - SCREEN_CENTER) >= (SCREEN_CENTER - THIRD_OFFSET):
+ self.going_home_flag = 0
+ throttle, steering, spin, camera_pan, method = self.camera_following(box_center)
+ else:
+ if 2500 <= azimuth <= 3550 - PAN_OFFSET:
+ camera_pan = 50
+ method = "Momentary"
+ steering = -0.4
+ throttle, steering, spin = self.spin_robot(steering)
+ elif PAN_OFFSET <= azimuth <= 1000:
+ camera_pan = -50
+ method = "Momentary"
+ steering = 0.4
+ throttle, steering, spin = self.spin_robot(steering)
+
+ if PAN_OFFSET >= azimuth >= 0 or 3550 >= azimuth >= 3550 - PAN_OFFSET:
+ camera_pan = 0
+ method = "Absolute"
+ spin = None
+ throttle = 0
+ steering = 0
+ self.going_home_flag = 0
+ self.camera_following_flag = 0
+
+ return throttle, steering, spin, camera_pan, method
+
+ def camera_following(self, box_center):
+ """Calculates control commands based on the 'azimuth' values of the camera."""
+ throttle, steering, spin, camera_pan, method = 0, 0, None, None, "Absolute" # No movement by default
+ try:
+ self.last_azimuth = azimuth = int(self.request["camera_azimuth"])
+ print("fetched azimuth:", azimuth)
+ except:
+ azimuth = self.last_azimuth
+ print("last azimuth:", azimuth)
+ if box_center is not None:
+
+ if self.going_home_flag == 1:
+ throttle, steering, spin, camera_pan, method = self.going_home(azimuth, box_center)
+
+ else:
+ # print("box center:",box_center)
+ # Keeping the user in the center of the camera view
+ if box_center < (SCREEN_CENTER - PAN_OFFSET) and ((1000 > azimuth >= 0) or (3550 >= azimuth > 3050)): # ugly
+ camera_pan = int(min(-30, max(-52, (0.1 * box_center - 60)))) # -30 at 300p, -53 at 80p
+ # camera_pan = -50
+ method = "Momentary"
+ if box_center < 320 - THIRD_OFFSET and ((1000 > azimuth >= 0) or (3550 >= azimuth > 3050)):
+ steering = -0.25
+ throttle, steering, spin = self.spin_robot(steering)
+
+ elif box_center > (SCREEN_CENTER + PAN_OFFSET) and ((500 > azimuth >= 0) or (3550 >= azimuth > 2500)):
+ camera_pan = int(max(30, min(52, (0.1 * box_center - 4)))) # 20 at 340p, 64 at 560p
+ # camera_pan = 50
+ method = "Momentary"
+ if box_center > 320 + THIRD_OFFSET and ((500 > azimuth >= 0) or (3550 >= azimuth > 2500)):
+ steering = 0.25
+ throttle, steering, spin = self.spin_robot(steering)
+
+ if 320 - THIRD_OFFSET <= box_center <= 320 + THIRD_OFFSET:
+ print("going home")
+ self.going_home_flag = 1
+ throttle, steering, spin, camera_pan, method = self.going_home(azimuth, box_center)
+
+ return throttle, steering, spin, camera_pan, method
+
+ def decide_control(self, boxes):
+ """Calculates control commands based on the operator's position on the screen"""
+ throttle, steering, spin, camera_pan, method = 0, 0, None, 0, "Absolute" # No movement by default
+
+ if not boxes.xyxy.size: # No people detected in the frame
+ self.no_human_counter += 1
+ self.logger.info(f"No person detected for: {self.no_human_counter} frames")
+ if self.no_human_counter >= MAX_HUMAN_ABSENCE_FRAMES:
+ self.current_throttle = throttle
+ self.current_steering = steering
+ self.current_spin = spin
+ self.current_camera_pan = camera_pan
+ self.current_method = method
+ return throttle, steering, spin, camera_pan, method
+ else:
+ try:
+ return self.current_throttle, self.current_steering, self.current_spin, self.current_camera_pan, self.current_method # Passing the previous control command if it exists
+ except Exception as e:
+ self.logger.warning("Exception fetching control commands: " + str(e) + "\n")
+ return 0, 0, None, 0, "Absolute"
+
+ self.no_human_counter = 0 # Resetting the counter if a person is detected
+ for box in boxes: # Checking every detected person in the frame
+ try:
+ if box.id == boxes.id[0]: # Get the first ID if it was assigned
+ x1, y1, x2, y2 = box.xyxy[0, :] # Coordinates of the top left and bottom right corners of bbox
+ box_center = (x1 + x2) / 2
+ box_height = y2 - y1
+ box_width = x2 - x1
+ self.logger.info(f"Box center: {int(box_center)}, Box height: {int(box_height)}")
+ except Exception as e:
+ self.logger.warning("Exception tracking: " + str(e) + "\n")
+ if (box.xyxy == boxes.xyxy[0]).all: # Get the first result on the list if ID was not assigned (first result has most confidence)
+ x1, y1, x2, y2 = box.xyxy[0, :]
+ box_center = (x1 + x2) / 2
+ box_height = y2 - y1
+ box_width = x2 - x1
+ self.logger.info(f"Box center: {int(box_center)}, Box height: {int(box_height)}")
+
+ # Smaller bbox height means the detected person is further away from the camera
+ if box_height <= START_HEIGHT: # Starting movement if person is far enough
+ throttle = max(0.2, min(1, ((-(0.01) * box_height) + 3.6))) # 0.2 at 340p height; 1 at 260p height
+
+ if self.clear_path <= MIN_CLEAR_PATH_FRAMES:
+ # self.logger.info(f"Path obstructed, only spinning allowed") # No movement if too few frames with clear path passed or too many frames without any detection
+ throttle = 0
+
+ if self.camera_following_flag == 1:
+ print("camera position taking over")
+ throttle, steering, spin, camera_pan, method = self.camera_following(box_center)
+
+ else:
+ # Keeping the user in the center of the camera view
+ if box_center < (SCREEN_CENTER - CENTER_OFFSET): # left = negative steering
+ steering = min(0, max(-0.1, (0.00125 * box_center - 0.4))) # 0 at 320p; 0.1 at 240p
+ if box_center < (SCREEN_CENTER - CENTER_OFFSET - SECOND_OFFSET):
+ steering = min(-0.1, max(-0.6, (0.004375 * box_center - 1.15))) # 0.1 at 400p; 0.8 at 560 (forcing max 0.5)
+ # steering = steering*(1.2-throttle) # Max steering is scaled down proportionally to the throttle value, max steering = 0.16 at throttle = 1
+ if throttle == 0 or box_center < (THIRD_OFFSET): # Turning in place
+ throttle, steering, spin = self.spin_robot(steering)
+ steering = steering * (1.15 - throttle * 0.75) # Max steering is scaled down proportionally to the throttle value, max steering = 0.32 at throttle = 1
+
+ elif box_center > (SCREEN_CENTER + CENTER_OFFSET): # right = positive steering
+ steering = max(0, min(0.1, (0.00125 * box_center - 0.4))) # 0 at 320p; 0.1 at 400p
+ if box_center > (SCREEN_CENTER + CENTER_OFFSET + SECOND_OFFSET):
+ steering = max(0.1, min(0.6, (0.004375 * box_center - 1.65))) # 0.1 at 400p; 0.8 at 560 (forcing max 0.5)
+ if throttle == 0 or box_center > (640 - THIRD_OFFSET):
+ throttle, steering, spin = self.spin_robot(steering)
+ # steering = steering*(1.2-throttle) # Max steering is scaled down proportionally to the throttle value, max steering = 0.16 at throttle = 1
+ steering = steering * (1.15 - throttle * 0.75) # Max steering is scaled down proportionally to the throttle value, max steering = 0.32 at throttle = 1
+
+ if abs(box_center - SCREEN_CENTER) >= (SCREEN_CENTER - THIRD_OFFSET):
+ self.logger.info("Operator on the far side")
+ self.camera_following_flag = 1
+ throttle, steering, spin, camera_pan, method = self.camera_following(box_center)
+
+ self.smooth_controls(throttle, steering) # Smoothing the movement if the commands are immediately large
+ self.current_spin = spin
+ self.current_camera_pan = camera_pan
+ self.current_method = method
+ return self.current_throttle, self.current_steering, self.current_spin, self.current_camera_pan, self.current_method
+
+ def run(self):
+ self.publish_command(self.current_throttle, self.current_steering, self.current_spin, self.current_camera_pan, self.current_method) # Initializing with safe values
+ errors = []
+ _config = self.config
+ stream_uri = parse_option("ras.master.uri", str, "192.168.1.32", errors, **_config)
+ stream_uri = f"rtsp://user1:HaikuPlot876@{stream_uri[:-2]}65:554/Streaming/Channels/103" # Setting dynamic URI of the stream
+ self.logger.info("Loading Yolov8 model")
+ self.results = self.model.track(source=stream_uri, classes=0, stream=True, conf=0.35, persist=True, verbose=False) # Image recognition with assigning IDs to objects
+ self.logger.info("Yolov8 model loaded")
+ threading.Thread(target=self.request_check).start()
+
+
+if __name__ == "__main__":
+ controller = FollowingController("480_20k.pt")
+ controller.run()
diff --git a/following/big480_20k.pt b/following/big480_20k.pt
new file mode 100644
index 00000000..0a2944ff
Binary files /dev/null and b/following/big480_20k.pt differ
diff --git a/following/fo_utils.py b/following/fo_utils.py
new file mode 100644
index 00000000..e69de29b
diff --git a/following/yolov8n.engine b/following/yolov8n.engine
new file mode 100644
index 00000000..6517e0d1
Binary files /dev/null and b/following/yolov8n.engine differ
diff --git a/inference/.DS_Store b/inference/.DS_Store
new file mode 100644
index 00000000..fa535432
Binary files /dev/null and b/inference/.DS_Store differ
diff --git a/pilot/pilot/relay.py b/pilot/pilot/relay.py
index 60b063dd..c6f70fdb 100644
--- a/pilot/pilot/relay.py
+++ b/pilot/pilot/relay.py
@@ -100,13 +100,13 @@ def _send_config(self, data):
if self._pi_client is not None and data is not None:
self._pi_client.call(dict(time=timestamp(), method="ras/driver/config", data=data))
- def _send_drive(self, throttle=0.0, steering=0.0, reverse_gear=False, wakeup=False):
+ def _send_drive(self, throttle=0.0, steering=0.0, reverse_gear=False, wakeup=False, spin=None):
if self._pi_client is not None:
throttle = max(-1.0, min(1.0, throttle))
steering = max(-1.0, min(1.0, steering))
_reverse = 1 if reverse_gear else 0
_wakeup = 1 if wakeup else 0
- self._pi_client.call(dict(time=timestamp(), method="ras/servo/drive", data=dict(steering=steering, throttle=throttle, reverse=_reverse, wakeup=_wakeup)))
+ self._pi_client.call(dict(time=timestamp(), method="ras/servo/drive", data=dict(steering=steering, throttle=throttle, reverse=_reverse, wakeup=_wakeup, spin=spin)))
def _drive(self, pilot, teleop):
pi_status = None if self._pi_status is None else self._pi_status.pop_latest()
@@ -117,7 +117,7 @@ def _drive(self, pilot, teleop):
else:
_reverse = teleop and teleop.get("arrow_down", 0)
_wakeup = teleop and teleop.get("button_b", 0)
- self._send_drive(steering=pilot.get("steering"), throttle=pilot.get("throttle"), reverse_gear=_reverse, wakeup=_wakeup)
+ self._send_drive(steering=pilot.get("steering"), throttle=pilot.get("throttle"), reverse_gear=_reverse, wakeup=_wakeup, spin=pilot.get("spin"))
def _config(self):
parser = SafeConfigParser()
diff --git a/raspi/.DS_Store b/raspi/.DS_Store
new file mode 100644
index 00000000..5dd0981a
Binary files /dev/null and b/raspi/.DS_Store differ
diff --git a/raspi/docker-compose.yml b/raspi/docker-compose.yml
index e5b4f1b3..a1ada11f 100644
--- a/raspi/docker-compose.yml
+++ b/raspi/docker-compose.yml
@@ -9,7 +9,7 @@ services:
privileged: true
user: root
restart: always
- network_mode: "host"
+ network_mode: 'host'
command: bash -c "/bin/rm -rf /var/run/pigpio.pid && /pigpio/pigpiod -gl"
zerotier:
image: zyclonite/zerotier:1.6.6
@@ -35,14 +35,14 @@ services:
user: root
restart: always
depends_on:
- - "pigpiod"
- network_mode: "host"
+ - 'pigpiod'
+ network_mode: 'host'
command: bash -c "modprobe i2c-dev && python3 -m ras.servos --config /config/driver.ini"
environment:
GPIOZERO_PIN_FACTORY: 'pigpio'
volumes:
-
- volume_local_config:/config:rw
+ #Bottom camera
stream0:
build:
context: .
@@ -50,11 +50,12 @@ services:
privileged: true
user: root
restart: always
- network_mode: "host"
- command: ["python3", "-m", "stream.camera", "--port", "9101", "--config", "/config/camera0.ini"]
+ network_mode: 'host'
+ command: ['python3', '-m', 'stream.camera', '--port', '9101', '--config', '/config/camera0.ini', '--ip-ending', '64']
stop_signal: SIGKILL
volumes:
- volume_local_config:/config:rw
+ #Upper camera
stream1:
build:
context: .
@@ -62,9 +63,8 @@ services:
privileged: true
user: root
restart: always
- network_mode: "host"
- command: ["python3", "-m", "stream.camera", "--port", "9102", "--config", "/config/camera1.ini"]
+ network_mode: 'host'
+ command: ['python3', '-m', 'stream.camera', '--port', '9102', '--config', '/config/camera1.ini', '--ip-ending', '65']
stop_signal: SIGKILL
volumes:
- - volume_local_config:/config:rw
-
+ - volume_local_config:/config:rw
\ No newline at end of file
diff --git a/raspi/ras/servos.py b/raspi/ras/servos.py
index e638bfb3..70b805d7 100644
--- a/raspi/ras/servos.py
+++ b/raspi/ras/servos.py
@@ -73,7 +73,7 @@ def velocity(self):
raise NotImplementedError()
@abstractmethod
- def drive(self, steering, throttle):
+ def drive(self, steering, throttle, spin):
raise NotImplementedError()
@abstractmethod
@@ -104,7 +104,7 @@ def is_configured(self):
def velocity(self):
return 0
- def drive(self, steering, throttle):
+ def drive(self, steering, throttle, spin):
return 0
def quit(self):
@@ -200,7 +200,7 @@ def is_configured(self):
def velocity(self):
raise NotImplementedError()
- def drive(self, steering, throttle):
+ def drive(self, steering, throttle, spin):
self._apply_steering(steering)
_motor_effort = 0
if self._motor_servo is not None:
@@ -263,7 +263,7 @@ def velocity(self):
logger.warning(e)
return 0
- def drive(self, steering, throttle):
+ def drive(self, steering, throttle, spin):
_motor_effort = self._throttle_config.get("scale") * throttle
_operational = self._drive.set_effort(_motor_effort)
if _operational:
@@ -345,7 +345,7 @@ def velocity(self):
logger.warning(e)
return 0
- def drive(self, steering, throttle):
+ def drive(self, steering, throttle, spin):
_motor_scale = self._throttle_config.get("scale")
# Scale down throttle for one wheel, the other retains its value.
steering = min(1.0, max(-1.0, steering + self._steering_offset))
@@ -355,8 +355,15 @@ def drive(self, steering, throttle):
right = throttle if steering < 0 else throttle * effect
a = (right if self._axes_ordered else left) * self._axis0_multiplier * _motor_scale
b = (left if self._axes_ordered else right) * self._axis1_multiplier * _motor_scale
- self._drive1.set_effort(a)
- self._drive2.set_effort(b)
+ if spin == 1 and abs(steering) <= 1e-9:
+ self._drive1.set_effort(-a)
+ self._drive2.set_effort(b)
+ elif spin == -1 and abs(steering) <= 1e-9:
+ self._drive1.set_effort(a)
+ self._drive2.set_effort(-b)
+ else:
+ self._drive1.set_effort(a)
+ self._drive2.set_effort(b)
return np.mean([a, b])
def quit(self):
@@ -487,6 +494,7 @@ def step(self):
v_steering = 0 if c_drive is None else c_drive.get("steering", 0)
v_throttle = 0 if c_drive is None else c_drive.get("throttle", 0)
v_wakeup = False if c_drive is None else bool(c_drive.get("wakeup"))
+ v_spin = None if c_drive is None else c_drive.get("spin", None)
self._cmd_history.touch(steering=v_steering, throttle=v_throttle, wakeup=v_wakeup)
if self._cmd_history.is_missing():
@@ -496,7 +504,7 @@ def step(self):
# Immediately zero out throttle when violations start occurring.
v_throttle = 0 if n_violations > 0 else v_throttle
- _effort = self._chassis.drive(v_steering, v_throttle)
+ _effort = self._chassis.drive(v_steering, v_throttle, v_spin)
_data = dict(time=timestamp(), configured=int(self._chassis.is_configured()), motor_effort=_effort)
if self._chassis.has_sensors():
_data.update(dict(velocity=self._chassis.velocity()))
diff --git a/raspi/stream/camera.py b/raspi/stream/camera.py
index 005c15a9..701791df 100644
--- a/raspi/stream/camera.py
+++ b/raspi/stream/camera.py
@@ -64,48 +64,32 @@ def step(self):
}
-def change_segment_config(config_dir):
+def change_segment_config(config_file, ip_ending):
"""Change the ips in all the config files the segment is using them.
It will count on the ip of the pi"""
# Get the local IP address's third octet
- ip_address = subprocess.check_output("hostname -I | awk '{for (i=1; i<=NF; i++) if ($i ~ /^192\\.168\\./) print $i}'", shell=True).decode().strip().split()[0]
- third_octet_new = ip_address.split(".")[2]
- # Regular expression to match IP addresses
- ip_regex = re.compile(r"(\d+\.\d+\.)(\d+)(\.\d+)")
+ ip_address = subprocess.check_output("hostname -I | awk '{for (i=1; i<=NF; i++) if ($i ~ /^192\\.168\\./) print $i}'", shell=True).decode().strip().split()[0]
+ base_ip = ip_address.rsplit(".", 1)[0] # Get the base IP without the last octet
+ new_ip = f"{base_ip}.{ip_ending}" # Form the new IP with the custom ending
- with open(config_dir, "r") as f:
- content = f.readlines()
+ with open(config_file, "r") as file:
+ content = file.readlines()
updated_content = []
- changes_made = []
- changes_made_in_file = False # Flag to track changes in the current file
+ ip_regex = re.compile(r"camera.ip = 192\.168\.\d+\.\d+")
for line in content:
- match = ip_regex.search(line)
- if match:
- third_octet_old = match.group(2)
- if third_octet_old != third_octet_new:
- # Replace the third octet
- new_line = ip_regex.sub(r"\g<1>" + third_octet_new + r"\g<3>", line)
- updated_content.append(new_line)
- changes_made.append((third_octet_old, third_octet_new))
- changes_made_in_file = True
-
- continue
- updated_content.append(line)
+ new_line = ip_regex.sub(f"camera.ip = {new_ip}", line)
+ updated_content.append(new_line)
- # Write changes back to the file
- with open(config_dir, "w") as f:
- f.writelines(updated_content)
+ with open(config_file, "w") as file:
+ file.writelines(updated_content)
- # Print changes made
- if changes_made_in_file:
- logger.info("Updated {} with a new ip address of {}".format(config_dir, third_octet_new))
+ logger.info(f"Updated {config_file} to new IP address {new_ip}")
def create_stream(config_file):
- change_segment_config(config_file)
parser = SafeConfigParser()
parser.read(config_file)
kwargs = dict(parser.items("camera"))
@@ -150,40 +134,44 @@ def create_stream(config_file):
def main():
parser = argparse.ArgumentParser(description="Camera web-socket server.")
- parser.add_argument("--config", type=str, default="/config/stream.ini", help="Configuration file.")
- parser.add_argument("--port", type=int, default=9101, help="Socket port.")
+ parser.add_argument("--config", type=str, required=True, help="Configuration file.")
+ parser.add_argument("--port", type=int, default=9101, help="Port for the web server.")
+ parser.add_argument("--ip-ending", type=str, required=True, help="Last octet of the IP address.")
args = parser.parse_args()
config_file = args.config
- if os.path.exists(config_file) and os.path.isfile(config_file):
- video_stream, socket_type = create_stream(config_file)
- application = CameraApplication(stream=video_stream, event=quit_event)
-
- threads = [threading.Thread(target=application.run)]
- if quit_event.is_set():
- return 0
-
- [t.start() for t in threads]
-
- asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())
- asyncio.set_event_loop(asyncio.new_event_loop())
-
- io_loop = ioloop.IOLoop.instance()
- class_ref = HttpLivePlayerVideoSocket if socket_type == "http-live" else JMuxerVideoStreamSocket
- web_app = web.Application([(r"/", class_ref, dict(video_source=video_stream, io_loop=io_loop))])
- rear_server = web.HTTPServer(web_app, xheaders=True)
- rear_server.bind(args.port)
- rear_server.start()
- logger.info("Web service started on port {}.".format(args.port))
- io_loop.start()
-
- logger.info("Waiting on threads to stop.")
- [t.join() for t in threads]
- else:
+ if not os.path.exists(config_file):
shutil.copyfile("/app/stream/camera.template", config_file)
- logger.info("Created a new camera configuration file from template.")
- while not quit_event.is_set():
- time.sleep(1)
+ logger.info(f"Created a new camera configuration file from template at {config_file}")
+
+ change_segment_config(config_file, args.ip_ending)
+
+ video_stream, socket_type = create_stream(config_file)
+ application = CameraApplication(stream=video_stream, event=quit_event)
+
+ threads = [threading.Thread(target=application.run)]
+ if quit_event.is_set():
+ return 0
+
+ [t.start() for t in threads]
+
+ asyncio.set_event_loop_policy(AnyThreadEventLoopPolicy())
+ asyncio.set_event_loop(asyncio.new_event_loop())
+
+ io_loop = ioloop.IOLoop.instance()
+ class_ref = HttpLivePlayerVideoSocket if socket_type == "http-live" else JMuxerVideoStreamSocket
+ web_app = web.Application([(r"/", class_ref, dict(video_source=video_stream, io_loop=io_loop))])
+ rear_server = web.HTTPServer(web_app, xheaders=True)
+ rear_server.bind(args.port)
+ rear_server.start()
+ logger.info("Web service started on port {}.".format(args.port))
+ io_loop.start()
+
+ logger.info("Waiting on threads to stop.")
+ [t.join() for t in threads]
+
+ while not quit_event.is_set():
+ time.sleep(1)
if __name__ == "__main__":
diff --git a/raspi/stream/camera.template b/raspi/stream/camera.template
index 3a1452cb..e58f54ba 100644
--- a/raspi/stream/camera.template
+++ b/raspi/stream/camera.template
@@ -2,5 +2,3 @@
camera.type = h264/rtsp
camera.ip = 192.168.1.64
-#camera.type = raw/usb/h264/udp
-#camera.uri = /dev/video0
diff --git a/teleop/.DS_Store b/teleop/.DS_Store
new file mode 100644
index 00000000..ba38dbcd
Binary files /dev/null and b/teleop/.DS_Store differ
diff --git a/teleop/Dockerfile b/teleop/Dockerfile
index e2eb114e..ad98c27f 100644
--- a/teleop/Dockerfile
+++ b/teleop/Dockerfile
@@ -12,7 +12,7 @@ RUN apt-get update && apt-get install -y \
# /\ unzip utility
RUN python3 -m pip install -U pip
-RUN pip3 install pymongo tornado folium Flask flask_socketio paramiko user-agents pysnmp pyasn1 pyasn1-modules
+RUN pip3 install pymongo tornado folium Flask flask_socketio paramiko user-agents pysnmp pyasn1 pyasn1-modules py-spy
# Ignore deprecation problem from cryptography
#/usr/local/lib/python3.6/dist-packages/pymongo/pyopenssl_context.py:26: CryptographyDeprecationWarning: Python 3.6 is no longer supported by the Python core team. Therefore, support for it is deprecated in cryptography. The next release of cryptography will remove support for Python 3.6.
diff --git a/teleop/htm/static/CSS/mobileController.css b/teleop/htm/static/CSS/mobileController.css
index a590095a..fd265840 100644
--- a/teleop/htm/static/CSS/mobileController.css
+++ b/teleop/htm/static/CSS/mobileController.css
@@ -6,6 +6,8 @@ html {
height: 100%;
overflow: hidden;
/* This will prevent scrollbar from appearing */
+ overflow: hidden;
+ /* This will prevent scrollbar from appearing */
}
canvas {
@@ -147,4 +149,17 @@ input[type=range]::-webkit-slider-thumb {
.hidden {
display: none;
-}
\ No newline at end of file
+}
+
+
+canvas#following_imageCanvas {
+ display: none; /* Initially hidden */
+ position: fixed; /* Fixed position to keep the canvas in the center of the viewport */
+ top: 50%; /* Align center vertically */
+ left: 50%; /* Align center horizontally */
+ transform: translate(-50%, -50%); /* Adjust position to the exact center */
+ border: 1px solid black; /* Optional: Adds a border around the canvas */
+ max-width: 640px; /* Maximum width of the canvas */
+ max-height: 480px; /* Maximum height of the canvas */
+ aspect-ratio: 4 / 3; /* Maintain 4:3 aspect ratio */
+}
diff --git a/teleop/htm/static/JS/mobileController/mobileController_a_app.js b/teleop/htm/static/JS/mobileController/mobileController_a_app.js
index d89d4d00..a476f37f 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_a_app.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_a_app.js
@@ -4,6 +4,7 @@ import { detectTriangle, getSavedDeadZoneWidth, handleDotMove, handleTriangleMov
import { cursorFollowingDot } from "./mobileController_b_shape_dot.js";
import { MotorDataInput } from "./mobileController_e_scale_offset_input.js";
import { ToggleButtonHandler } from "./mobileController_b_shape_confidence.js"
+import { followingButtonHandler } from "./mobileController_b_following.js"
import { app, changeTrianglesColor, redraw } from "./mobileController_d_pixi.js"
import CTRL_STAT from './mobileController_z_state.js'; // Stands for control state
@@ -31,10 +32,13 @@ document.getElementById('deadZoneWidth').addEventListener('input', function () {
});
window.addEventListener('resize', () => {
- app.renderer.resize(window.innerWidth, window.innerHeight);
- topTriangle.updateDimensions();
- bottomTriangle.updateDimensions();
- redraw();
+ // To not show the triangles if the stream canvas is shown
+ if (followingButtonHandler.getFollowingState != "active") {
+ app.renderer.resize(window.innerWidth, window.innerHeight);
+ topTriangle.updateDimensions();
+ bottomTriangle.updateDimensions();
+ redraw();
+ }
});
app.view.addEventListener('touchstart', (event) => {
@@ -50,6 +54,9 @@ app.view.addEventListener('touchstart', (event) => {
console.error("Connection lost with the robot. Please reconnect");
break;
default:
+ if (followingButtonHandler.toggleButton.innerText === "Stop Following") {
+ followingButtonHandler.sendSwitchFollowingRequest("Stop Following")
+ }
document.getElementById("mobile-controller-top-input-container").style.display = "none";
document.getElementById("mobile-controller-bottom-input-container").style.display = "none";
startOperating(event)
@@ -63,6 +70,10 @@ app.view.addEventListener('touchstart', (event) => {
function startOperating(event) {
+
+ // Hide the button when triangles are pressed
+ followingButtonHandler.setStyle('display', 'none');
+
cursorFollowingDot.show()
handleDotMove(event.touches[0].clientX, event.touches[0].clientY, inferenceToggleButton.getInferenceState);
handleTriangleMove(event.touches[0].clientY, inferenceToggleButton);
@@ -104,6 +115,8 @@ app.view.addEventListener('touchend', () => {
cursorFollowingDot.hide()
clearTimeout(intervalId);
}
+ // Show the button again when touch ends
+ followingButtonHandler.setStyle('display', 'block');
// Making the text boxes show the current data that exist on the robot
MotorDataInput.showInputElements();
diff --git a/teleop/htm/static/JS/mobileController/mobileController_b_following.js b/teleop/htm/static/JS/mobileController/mobileController_b_following.js
new file mode 100644
index 00000000..aebd7515
--- /dev/null
+++ b/teleop/htm/static/JS/mobileController/mobileController_b_following.js
@@ -0,0 +1,151 @@
+import { redraw, removeTriangles } from './mobileController_d_pixi.js';
+
+class ToggleButtonHandler {
+ constructor(buttonId) {
+ this.toggleButton = document.getElementById(buttonId);
+ this.canvas = document.getElementById("following_imageCanvas");
+ this.ctx = this.canvas.getContext('2d');
+ this.checkSavedState();
+ this.initialSetup(); // New method to setup canvas dimensions
+ this._followingState = "inactive";
+
+ }
+ get getFollowingState() {
+ return this._followingState;
+ }
+
+ initialSetup() {
+ //related to canvas
+ this.resizeCanvas();
+ window.addEventListener('resize', () => this.resizeCanvas());
+ //related to the toggle button
+ this.toggleButton.addEventListener('click', () => this.handleButtonClick());
+ }
+
+ handleButtonClick() {
+ // Determine the command based on the opposite of the current button text
+ let currentText = this.toggleButton.innerText;
+ this.sendSwitchFollowingRequest(currentText);
+ }
+
+ sendSwitchFollowingRequest(command) {
+ fetch('/switch_following', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded',
+ },
+ body: `command=${encodeURIComponent(command)}`,
+ })
+ .then(response => response.json())
+ .then(data => {
+ this.assignFollowingState(data.received_command.following)
+ this.toggleButtonAppearance();
+ })
+ .catch(error => console.error("Error sending command:", error));
+ }
+
+ assignFollowingState(backendCommand) {
+ switch (backendCommand) {
+ case "Start Following":
+ this._followingState = "active"; // The system is actively following
+ break;
+ case "Stop Following":
+ this._followingState = "inactive"; // The system is not following
+ break;
+ case "loading":
+ this._followingState = "loading"; // The system is initializing or loading
+ break;
+ default:
+ console.log("Following: Unknown command received form the backend")
+ }
+ }
+
+ toggleButtonAppearance() {
+ if (this._followingState == "active") {
+ removeTriangles();
+ this.showCanvas();
+ this.toggleButton.innerText = "Stop Following"
+ this.toggleButton.style.backgroundColor = "#ff6347"
+ } else {
+ redraw(undefined, true, true, true);
+ this.hideCanvas();
+ this.toggleButton.innerText = "Start Following"
+ this.toggleButton.style.backgroundColor = "#67b96a"
+ }
+ sessionStorage.setItem("innerText", this.toggleButton.innerText);
+ sessionStorage.setItem("backgroundColor", this.toggleButton.style.backgroundColor);
+ }
+
+ showCanvas() {
+ this.canvas.style.display = 'block';
+ if (!this.streamActive && !this.intervalId) {
+ this.streamActive = true;
+ this.intervalId = setInterval(() => this.refreshImage(), 150); // Start streaming
+ }
+ }
+
+ hideCanvas() {
+ this.canvas.style.display = 'none';
+ if (this.streamActive && this.intervalId) {
+ clearInterval(this.intervalId); // Stop streaming
+ this.intervalId = null;
+ this.streamActive = false;
+ }
+ }
+
+ refreshImage() {
+ if (!this.streamActive) return; // Do not proceed if streaming is not active
+
+ const img = new Image();
+ img.onload = () => {
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // Clear previous image
+ this.ctx.drawImage(img, 0, 0, this.canvas.width, this.canvas.height); // Draw new image
+ };
+ img.src = '/latest_image?' + new Date().getTime(); // Include cache busting to prevent loading from cache
+ }
+
+
+ resizeCanvas() {
+ if (this._followingState == "active") {
+ removeTriangles()
+ console.log("removing")
+ }
+ let maxWidth = window.innerWidth * 0.8; // 80% of the viewport width
+ if (maxWidth > 640) maxWidth = 640; // Ensuring the width does not exceed 640 pixels
+ const maxHeight = maxWidth * 3 / 4; // Maintain 4:3 ratio
+
+ this.canvas.width = maxWidth;
+ this.canvas.height = maxHeight;
+ this.canvas.style.width = `${maxWidth}px`;
+ this.canvas.style.height = `${maxHeight}px`;
+ }
+
+ checkSavedState() {
+ if (sessionStorage.getItem("innerText") !== null) {
+ this.toggleButton.innerText = sessionStorage.getItem("innerText");
+ this.toggleButton.style.backgroundColor = sessionStorage.getItem("backgroundColor");
+ }
+ else {
+ }
+ }
+
+ getAttribute(attributeName) {
+ return this.toggleButton.getAttribute(attributeName);
+ }
+
+ setAttribute(attributeName, value) {
+ this.toggleButton.setAttribute(attributeName, value);
+ }
+
+ getStyle(property) {
+ return this.toggleButton.style[property];
+ }
+
+ setStyle(property, value) {
+ this.toggleButton.style[property] = value;
+ }
+}
+
+const followingButtonHandler = new ToggleButtonHandler('following_toggle_button');
+
+export { followingButtonHandler };
diff --git a/teleop/htm/static/JS/mobileController/mobileController_b_shape_red_rectangle.js b/teleop/htm/static/JS/mobileController/mobileController_b_shape_red_rectangle.js
index cb0ff30b..f25fac91 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_b_shape_red_rectangle.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_b_shape_red_rectangle.js
@@ -10,7 +10,6 @@ class Rectangle
// Initiator methods of the class
this.container.addChild(this.graphics);
- this.drawRectangle();
this.drawText();
}
diff --git a/teleop/htm/static/JS/mobileController/mobileController_c_logic.js b/teleop/htm/static/JS/mobileController/mobileController_c_logic.js
index cd583a7b..723a7d8d 100644
--- a/teleop/htm/static/JS/mobileController/mobileController_c_logic.js
+++ b/teleop/htm/static/JS/mobileController/mobileController_c_logic.js
@@ -235,7 +235,7 @@ function handleTriangleMove(y, inferenceToggleButton) {
document.getElementById('toggle_button_container').style.display = 'none';
drawTopTriangle_BottomRectangle(yOffset);
}
- else if (CTRL_STAT.detectedTriangle === 'bottom') {
+ else if (CTRL_STAT.detectedTriangle === 'bottom' && INFState != "true") {
document.getElementById('toggle_button_container').style.display = 'none';
drawBottomTriangle_TopRectangle(yOffset);
}
diff --git a/teleop/htm/templates/mobile_controller_ui.html b/teleop/htm/templates/mobile_controller_ui.html
index 7a71540e..cf5dd11f 100644
--- a/teleop/htm/templates/mobile_controller_ui.html
+++ b/teleop/htm/templates/mobile_controller_ui.html
@@ -41,6 +41,7 @@
+