├── .gitignore ├── .gitlab-ci.yml ├── CMakeLists.txt ├── Code-Of-Conduct.md ├── Dockerfile ├── FindLibRT.cmake ├── LICENSE ├── README.md └── src ├── catch.hpp ├── cluon-complete-v0.0.127.hpp ├── opendlv-standard-message-set-v0.9.6.odvd └── template-opencv.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2022 Christian Berger 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | # Which Docker image shall be used on the GitLab runner? 17 | image: registry.git.chalmers.se/courses/dit638/students/docker/docker:19.03.3 18 | 19 | # Details about how to connect to the Docker service to run this build. 20 | variables: 21 | DOCKER_HOST: tcp://docker:2375 22 | DOCKER_TLS_CERTDIR: "" 23 | 24 | #New 25 | DOCKER_DRIVER: overlay2 26 | DOCKER_BUILDKIT: 1 27 | DOCKER_CLI_EXPERIMENTAL: enabled 28 | BUILDX_URL: https://github.com/docker/buildx/releases/download/v0.3.1/buildx-v0.3.1.linux-amd64 29 | BUILDX_BUILDER: Multiplatform_builder 30 | BUILDX_PLATFORM: linux/amd64,linux/arm64,linux/arm/v7 31 | IMAGE: registry.git.chalmers.se/courses/dit638/students/2022-group-02 32 | 33 | 34 | services: 35 | - name: registry.git.chalmers.se/courses/dit638/students/docker/docker:19.03.3-dind 36 | alias: docker 37 | 38 | stages: 39 | - build 40 | - deploy 41 | 42 | # Display information before we start the build. 43 | before_script: 44 | - docker info 45 | - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY 46 | 47 | 48 | # This section describes what shall be done to build and test the project. 49 | build-and-test: 50 | only: 51 | - branches 52 | tags: 53 | - docker-build 54 | stage: build 55 | script: 56 | - docker build -f Dockerfile . 57 | 58 | 59 | # This section describes what shall be done to deploy artefacts from the project. 60 | release: 61 | when: on_success 62 | only: 63 | refs: 64 | - tags 65 | variables: 66 | - $CI_COMMIT_TAG =~ /^v(\d+\.)?(\d+\.)?(\*|\d+)$/ 67 | tags: 68 | - docker-build 69 | stage: deploy 70 | before_script: 71 | - mkdir -p $HOME/.docker/cli-plugins/ 72 | - wget -O $HOME/.docker/cli-plugins/docker-buildx $BUILDX_URL 73 | - chmod a+x $HOME/.docker/cli-plugins/docker-buildx 74 | - "echo -e '{\n \"experimental\": \"enabled\"\n}' | tee $HOME/.docker/config.json" 75 | - docker run --rm --privileged multiarch/qemu-user-static --reset -p yes 76 | - docker buildx create --use --driver docker-container --name ${BUILDX_BUILDER} --platform=${BUILDX_PLATFORM} 77 | - docker buildx inspect --bootstrap ${BUILDX_BUILDER} 78 | - docker buildx ls 79 | - docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY} 80 | script: 81 | - docker buildx build --platform=${BUILDX_PLATFORM} -t ${IMAGE}:"$CI_COMMIT_TAG" --push "." -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Christian Berger 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | cmake_minimum_required(VERSION 3.2) 17 | 18 | project(template-opencv) 19 | 20 | ################################################################################ 21 | # Defining the relevant versions of OpenDLV Standard Message Set and libcluon. 22 | # The OpenDLV Standard Message Set contains a set of messages usually used in automotive research project. 23 | set(OPENDLV_STANDARD_MESSAGE_SET opendlv-standard-message-set-v0.9.6.odvd) 24 | # libcluon is a small and portable middleware to easily realize high-performance microservices with C++: https://github.com/chrberger/libcluon 25 | set(CLUON_COMPLETE cluon-complete-v0.0.127.hpp) 26 | 27 | ################################################################################ 28 | # Set the search path for .cmake files. 29 | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" ${CMAKE_MODULE_PATH}) 30 | 31 | ################################################################################ 32 | # This project requires C++14 or newer. 33 | set(CMAKE_CXX_STANDARD 14) 34 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 35 | set(CMAKE_CXX_EXTENSIONS OFF) 36 | # Build a static binary. 37 | set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") 38 | # Add further warning levels to increase the code quality. 39 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ 40 | -D_XOPEN_SOURCE=700 \ 41 | -D_FORTIFY_SOURCE=2 \ 42 | -O2 \ 43 | -fstack-protector \ 44 | -fomit-frame-pointer \ 45 | -pipe \ 46 | -Weffc++ \ 47 | -Wall -Wextra -Wshadow -Wdeprecated \ 48 | -Wdiv-by-zero -Wfloat-equal -Wfloat-conversion -Wsign-compare -Wpointer-arith \ 49 | -Wuninitialized -Wunreachable-code \ 50 | -Wunused -Wunused-function -Wunused-label -Wunused-parameter -Wunused-but-set-parameter -Wunused-but-set-variable \ 51 | -Wunused-value -Wunused-variable -Wunused-result \ 52 | -Wmissing-field-initializers -Wmissing-format-attribute -Wmissing-include-dirs -Wmissing-noreturn") 53 | # Threads are necessary for linking the resulting binaries as the network communication is running inside a thread. 54 | set(THREADS_PREFER_PTHREAD_FLAG ON) 55 | find_package(Threads REQUIRED) 56 | 57 | ################################################################################ 58 | # Extract cluon-msc from cluon-complete.hpp. 59 | # cluon-msc is the message compiler that compiles a .odvd message specification into a header-only C++ file. 60 | add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/cluon-msc 61 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 62 | COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/src/${CLUON_COMPLETE} ${CMAKE_BINARY_DIR}/cluon-complete.hpp 63 | COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_BINARY_DIR}/cluon-complete.hpp ${CMAKE_BINARY_DIR}/cluon-complete.cpp 64 | COMMAND ${CMAKE_CXX_COMPILER} -o ${CMAKE_BINARY_DIR}/cluon-msc ${CMAKE_BINARY_DIR}/cluon-complete.cpp -std=c++14 -pthread -D HAVE_CLUON_MSC 65 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/${CLUON_COMPLETE}) 66 | 67 | ################################################################################ 68 | # Generate opendlv-standard-message-set.hpp from ${OPENDLV_STANDARD_MESSAGE_SET} file. 69 | add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/opendlv-standard-message-set.hpp 70 | WORKING_DIRECTORY ${CMAKE_BINARY_DIR} 71 | COMMAND ${CMAKE_BINARY_DIR}/cluon-msc --cpp --out=${CMAKE_BINARY_DIR}/opendlv-standard-message-set.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/${OPENDLV_STANDARD_MESSAGE_SET} 72 | DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/${OPENDLV_STANDARD_MESSAGE_SET} ${CMAKE_BINARY_DIR}/cluon-msc) 73 | # Add current build directory as include directory as it contains generated files. 74 | include_directories(SYSTEM ${CMAKE_BINARY_DIR}) 75 | include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) 76 | 77 | ################################################################################ 78 | # Gather all object code first to avoid double compilation. 79 | set(LIBRARIES Threads::Threads) 80 | 81 | if(UNIX) 82 | if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") 83 | find_package(LibRT REQUIRED) 84 | set(LIBRARIES ${LIBRARIES} ${LIBRT_LIBRARIES}) 85 | include_directories(SYSTEM ${LIBRT_INCLUDE_DIR}) 86 | endif() 87 | endif() 88 | 89 | # This project uses OpenCV for image processing. 90 | find_package(OpenCV REQUIRED core highgui imgproc) 91 | include_directories(SYSTEM ${OpenCV_INCLUDE_DIRS}) 92 | set(LIBRARIES ${LIBRARIES} ${OpenCV_LIBS}) 93 | 94 | ################################################################################ 95 | # Create executable. 96 | add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/${PROJECT_NAME}.cpp) 97 | target_link_libraries(${PROJECT_NAME} ${LIBRARIES}) 98 | 99 | # Add dependency to OpenDLV Standard Message Set. 100 | add_custom_target(generate_opendlv_standard_message_set_hpp DEPENDS ${CMAKE_BINARY_DIR}/opendlv-standard-message-set.hpp) 101 | add_dependencies(${PROJECT_NAME} generate_opendlv_standard_message_set_hpp) 102 | 103 | ################################################################################ 104 | # Install executable. 105 | install(TARGETS ${PROJECT_NAME} DESTINATION bin COMPONENT ${PROJECT_NAME}) 106 | -------------------------------------------------------------------------------- /Code-Of-Conduct.md: -------------------------------------------------------------------------------- 1 | **How do we plan to collaborate?** 2 | 3 | We are a well knit group that during this project will work all together on all tasks (if possible), this to broaden the individual learning outcome. We plan to avoid splitting up assignments to “save time”, because it has lessened learning outcomes in the past. 4 | Most of our collaboration will take place online and on Discord (or zoom if needed). We prefer to run the code-sessions this way to make it so that all can see and interact (live share or similar software) simultaneously. After these years of distance studies we have developed some working techniques and strategies regarding meeting and other groups related interactions that we find to work well, we intend to use these in this project as well and therefore we will conduct most of our non-programming sessions online as well. 5 | We plan on having planned and set meetings/session slots on Wednesday as well as Fridays, these days suit our individual schedules well and let us put down multiple hour in continuum to not lose the “flow”. We feel that we, as a group, aren't performing at our best with small sessions and rather use long ones so as not to lose the momentum. 6 | As we plan on working together on all assignments we feel that the scrum practises as a whole don’t work for us. For instance would not a scrum meeting make much sense because we intend to all work together on all parts. But we will still use the essence of agile development in this project. 7 | 8 | **How do we ensure that everyone in our group stays informed about the individual Contributions?** 9 | 10 | The majority of the communication between team members, including individual contributions will be through Discord. Individual contributions may be through voice calls during the meetings. Individual contributions may also occur through text communication, which provides an alternative resource to look up previous individual contributions. Every team member is expected to regularly attend voice meetings and check for new text-based messages. 11 | If one or more team members lose access to Discord or are unavailable during meetings, then vital information may be communicated through phone-based messages/calls. 12 | 13 | **How do we ensure knowledge transfer among our team members?** 14 | 15 | Through regular meetings, where every team member is involved, our plan is to jointly develop and produce a solution. Due to team-programming, our gain in knowledge should be equivalent across all team members, primarily since questions and/or progressing through the development phase, may lead to clarifications. Team-programming ensures that knowledge is jointly accumulated. 16 | 17 | **What is our usual communication plan?** 18 | 19 | The plan is to utilize Discord for communication within the team. Both voice and text communication within the group occurs in Discord. If one or more team members miss out on information, then the other team members will communicate the most vital information/summary to that team member. Communication and collaboration occurs primarily online, but in the cases where on-site collaboration is preferred by the team members, or where online collaboration becomes difficult to accomplish, then the collaboration may be shifted towards on-site collaboration, with the possibility to discuss whether any temporary or permanent changes would be made. Currently, on-site collaboration will play a minor role for our group, with the exception for cases mentioned above. 20 | 21 | **How will we solve conflicts?** 22 | 23 | In order to prevent conflicts from occuring, each team member agrees to respect one another, and should avoid conflicts and hostile behavior towards any other team member. By showing respect, and allowing all team members to voice their opinions and concerns, the likelihood of conflicts may be considered reduced to a minimum. As mentioned below, our group prioritizes openness, which includes the ability to speak out on cases of unfair treatment/hostility. Our utmost priority is to make sure that the team members enjoy collaborating together, which also means that our utmost priority is to treat everyone with respect. 24 | 25 | As of yet, no conflicts have occured. The priority of our group is to have ‘openness’ and ‘transparency’ between all team members. Each member is free to voice their opinions, concerns, and views, free of conflicts. We expect each and every team member to act in a respectful manner towards another team member. 26 | In the unexpected case where conflicts do occur, then a discussion between all team members will take place, and where every team member is able to voice their opinion. 27 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Christian Berger 2 | # 3 | # This program is free software: you can redistribute it and/or modify 4 | # it under the terms of the GNU General Public License as published by 5 | # the Free Software Foundation, either version 3 of the License, or 6 | # (at your option) any later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, 9 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 10 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 | # GNU General Public License for more details. 12 | # 13 | # You should have received a copy of the GNU General Public License 14 | # along with this program. If not, see . 15 | 16 | # First stage for building the software: 17 | FROM ubuntu:18.04 as builder 18 | MAINTAINER Christian Berger "christian.berger@gu.se" 19 | 20 | ENV DEBIAN_FRONTEND noninteractive 21 | 22 | # Upgrade the Ubuntu 18.04 LTS base image 23 | RUN apt-get update -y && \ 24 | apt-get upgrade -y && \ 25 | apt-get dist-upgrade -y 26 | 27 | # Install the development libraries for OpenCV 28 | RUN apt-get install -y --no-install-recommends \ 29 | ca-certificates \ 30 | cmake \ 31 | build-essential \ 32 | libopencv-dev 33 | 34 | # Include this source tree and compile the sources 35 | ADD . /opt/sources 36 | WORKDIR /opt/sources 37 | RUN cd /opt/sources && \ 38 | mkdir build && \ 39 | cd build && \ 40 | cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/tmp .. && \ 41 | make && make install 42 | 43 | 44 | # Second stage for packaging the software into a software bundle: 45 | FROM ubuntu:18.04 46 | MAINTAINER Christian Berger "christian.berger@gu.se" 47 | 48 | ENV DEBIAN_FRONTEND noninteractive 49 | 50 | RUN apt-get update -y && \ 51 | apt-get upgrade -y && \ 52 | apt-get dist-upgrade -y 53 | 54 | RUN apt-get install -y --no-install-recommends \ 55 | libopencv-core3.2 \ 56 | libopencv-highgui3.2 \ 57 | libopencv-imgproc3.2 58 | 59 | WORKDIR /usr/bin 60 | COPY --from=builder /tmp/bin/template-opencv . 61 | # This is the entrypoint when starting the Docker container; hence, this Docker image is automatically starting our software on its creation 62 | ENTRYPOINT ["/usr/bin/template-opencv"] 63 | -------------------------------------------------------------------------------- /FindLibRT.cmake: -------------------------------------------------------------------------------- 1 | # You may redistribute this program and/or modify it under the terms of 2 | # the GNU General Public License as published by the Free Software Foundation, 3 | # either version 3 of the License, or (at your option) any later version. 4 | # 5 | # This program is distributed in the hope that it will be useful, 6 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 7 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 8 | # GNU General Public License for more details. 9 | # 10 | # You should have received a copy of the GNU General Public License 11 | # along with this program. If not, see . 12 | 13 | if(NOT LIBRT_FOUND) 14 | 15 | IF(${CMAKE_C_COMPILER} MATCHES "arm") 16 | # We are on ARM. 17 | find_path(LIBRT_INCLUDE_DIR 18 | NAMES 19 | time.h 20 | PATHS 21 | ${LIBRTDIR}/include/ 22 | ) 23 | 24 | find_file( 25 | LIBRT_LIBRARIES librt.a 26 | PATHS 27 | ${LIBRTDIR}/lib/ 28 | /usr/lib/arm-linux-gnueabihf/ 29 | /usr/lib/arm-linux-gnueabi/ 30 | ) 31 | set (LIBRT_DYNAMIC "Using static library.") 32 | 33 | if (NOT LIBRT_LIBRARIES) 34 | find_library( 35 | LIBRT_LIBRARIES rt 36 | PATHS 37 | ${LIBRTDIR}/lib/ 38 | /usr/lib/arm-linux-gnueabihf/ 39 | /usr/lib/arm-linux-gnueabi/ 40 | ) 41 | set (LIBRT_DYNAMIC "Using dynamic library.") 42 | endif (NOT LIBRT_LIBRARIES) 43 | ELSE() 44 | IF("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") 45 | # We are on x86_64. 46 | find_path(LIBRT_INCLUDE_DIR 47 | NAMES 48 | time.h 49 | PATHS 50 | ${LIBRTDIR}/include/ 51 | ) 52 | 53 | find_file( 54 | LIBRT_LIBRARIES librt.a 55 | PATHS 56 | ${LIBRTDIR}/lib/ 57 | /usr/lib/x86_64-linux-gnu/ 58 | /usr/local/lib64/ 59 | /usr/lib64/ 60 | /usr/lib/ 61 | ) 62 | set (LIBRT_DYNAMIC "Using static library.") 63 | 64 | if (NOT LIBRT_LIBRARIES) 65 | find_library( 66 | LIBRT_LIBRARIES rt 67 | PATHS 68 | ${LIBRTDIR}/lib/ 69 | /usr/lib/x86_64-linux-gnu/ 70 | /usr/local/lib64/ 71 | /usr/lib64/ 72 | /usr/lib/ 73 | ) 74 | set (LIBRT_DYNAMIC "Using dynamic library.") 75 | endif (NOT LIBRT_LIBRARIES) 76 | ELSE() 77 | # We are on x86. 78 | find_path(LIBRT_INCLUDE_DIR 79 | NAMES 80 | time.h 81 | PATHS 82 | ${LIBRTDIR}/include/ 83 | ) 84 | 85 | find_file( 86 | LIBRT_LIBRARIES librt.a 87 | PATHS 88 | ${LIBRTDIR}/lib/ 89 | /usr/lib/i386-linux-gnu/ 90 | /usr/local/lib/ 91 | /usr/lib/ 92 | ) 93 | set (LIBRT_DYNAMIC "Using static library.") 94 | 95 | if (NOT LIBRT_LIBRARIES) 96 | find_library( 97 | LIBRT_LIBRARIES rt 98 | PATHS 99 | ${LIBRTDIR}/lib/ 100 | /usr/lib/i386-linux-gnu/ 101 | /usr/local/lib/ 102 | /usr/lib/ 103 | ) 104 | set (LIBRT_DYNAMIC "Using dynamic library.") 105 | endif (NOT LIBRT_LIBRARIES) 106 | ENDIF() 107 | ENDIF() 108 | 109 | if (LIBRT_INCLUDE_DIR AND LIBRT_LIBRARIES) 110 | set (LIBRT_FOUND TRUE) 111 | endif (LIBRT_INCLUDE_DIR AND LIBRT_LIBRARIES) 112 | 113 | if (LIBRT_FOUND) 114 | message(STATUS "Found librt: ${LIBRT_INCLUDE_DIR}, ${LIBRT_LIBRARIES} ${LIBRT_DYNAMIC}") 115 | else (LIBRT_FOUND) 116 | if (Librt_FIND_REQUIRED) 117 | message (FATAL_ERROR "Could not find librt, try to setup LIBRT_PREFIX accordingly") 118 | endif (Librt_FIND_REQUIRED) 119 | endif (LIBRT_FOUND) 120 | 121 | endif (NOT LIBRT_FOUND) 122 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2022] [Group 2] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2022-group-02 | Algorithm for steering wheel angle 2 | 3 | ## Description 4 | 5 | This algorithm is data driven and is integrated with [opendlv-vehicle-view](https://github.com/chalmers-revere/opendlv-vehicle-view). The algorithm uses object detection in the data from the video feed from [opendlv-vehicle-view](https://github.com/chalmers-revere/opendlv-vehicle-view) along side with other sensor readings to compute an accurate steering angle output close to what the actual steering wheel angle was at the time of the video feed. 6 | 7 | 8 | ## Installation 9 | Latest release: 10 | 11 | ![version](https://img.shields.io/badge/version-1.1.0-blue) 12 | 13 | ## Usage 14 | The system is ran alongside with [opendlv-vehicle-view](https://github.com/chalmers-revere/opendlv-vehicle-view) and [h264decoder](https://github.com/chalmers-revere/opendlv-video-h264-decoder) (to extract h264 frames into a shared memory area to provide access to ARGB pixels in the frames). By using shared memory, the system is able to detect cones of different color and detemine the heading direction including the needed steering wheel angles for the wheels. 15 | 16 | The algorithm produces an output in the terminal for each video frame containing the sample timestamp together with the calculated steering wheel angle. 17 | 18 | 19 | ## Authors and acknowledgment 20 | **Authors and contributors:** 21 | 22 | Profile Picture Caisesiume Caisesiume [(GitHub)](https://github.com/Caisesiume) [(GitLab)](https://git.chalmers.se/simonar) 23 | 24 | Profile Picture JohanAxell JohanAxell [(GitHub)](https://github.com/johanaxell) [(GitLab)](https://git.chalmers.se/johanaxe) 25 | 26 | Profile Picture Jidarv Jidarv [(GitHub)](https://github.com/Jidarv)[(GitLab)](https://git.chalmers.se/jidarv) 27 | 28 | Profile Picture RobbanGit RobbanGit [(GitHub)](https://github.com/RobbanGit) [(GitLab)](https://git.chalmers.se/robinhan) 29 | 30 | Profile Picture Asiya-Ismail Asiya [(GitHub)](https://github.com/Asiya-Ismail)[(GitLab)](https://git.chalmers.se/asiya) 31 | 32 | **Acknowledgment:** 33 | 34 | Thanks to University of Gothenburg, Chalmers University of Technology and Christian Berger for setting up this project possiblility. 35 | 36 | 37 | ## License 38 | For this project a [MIT License](https://git.chalmers.se/courses/dit638/students/2022-group-02/-/blob/main/LICENSE) applies. 39 | 40 | ## Project status 41 | The current project state: 42 | 43 | The project development is in its ending phase. There might only be occational updates to this repository. 44 | 45 | 46 | ## Collaboration with the team members 47 | 48 | See Code-of-Conduct. 49 | 50 | -------------------------------------------------------------------------------- /src/opendlv-standard-message-set-v0.9.6.odvd: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Chalmers Revere 3 | * 4 | * This program is free software; you can redistribute it and/or 5 | * modify it under the terms of the GNU General Public License 6 | * as published by the Free Software Foundation; either version 2 7 | * of the License, or (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program; if not, write to the Free Software 16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | */ 18 | 19 | message opendlv.sim.Frame [id = 1001] { 20 | float x [id = 1]; 21 | float y [id = 2]; 22 | float z [id = 3]; 23 | float roll [id = 4]; 24 | float pitch [id = 5]; 25 | float yaw [id = 6]; 26 | } 27 | 28 | message opendlv.sim.KinematicState [id = 1002] { 29 | float vx [id = 1]; 30 | float vy [id = 2]; 31 | float vz [id = 3]; 32 | float rollRate [id = 4]; 33 | float pitchRate [id = 5]; 34 | float yawRate [id = 6]; 35 | } 36 | 37 | message opendlv.body.ComponentInfo [id = 1021] { 38 | string description [id = 1]; 39 | float x [id = 2]; 40 | float y [id = 3]; 41 | float z [id = 4]; 42 | } 43 | 44 | message opendlv.body.ActuatorInfo [id = 1022] { 45 | string description [id = 1]; 46 | float x [id = 2]; 47 | float y [id = 3]; 48 | float z [id = 4]; 49 | uint32 signalId [id = 5]; 50 | float minValue [id = 6]; 51 | float maxValue [id = 7]; 52 | } 53 | 54 | message opendlv.body.SensorInfo [id = 1023] { 55 | string description [id = 1]; 56 | float x [id = 2]; 57 | float y [id = 3]; 58 | float z [id = 4]; 59 | uint32 signalId [id = 5]; 60 | float accuracyStd [id = 6]; 61 | uint16 minFrequency [id = 7]; 62 | } 63 | 64 | message opendlv.body.SignalInfo [id = 1024] { 65 | string description [id = 1]; 66 | uint32 signalId [id = 2]; 67 | float accuracyStd [id = 3]; 68 | uint16 minFrequency [id = 4]; 69 | } 70 | 71 | message opendlv.proxy.AccelerationReading [id = 1030] { 72 | float accelerationX [id = 1]; 73 | float accelerationY [id = 2]; 74 | float accelerationZ [id = 3]; 75 | } 76 | 77 | message opendlv.proxy.AngularVelocityReading [id = 1031] { 78 | float angularVelocityX [id = 1]; 79 | float angularVelocityY [id = 2]; 80 | float angularVelocityZ [id = 3]; 81 | } 82 | 83 | message opendlv.proxy.MagneticFieldReading [id = 1032] { 84 | float magneticFieldX [id = 1]; 85 | float magneticFieldY [id = 2]; 86 | float magneticFieldZ [id = 3]; 87 | } 88 | 89 | message opendlv.proxy.AltitudeReading [id = 1033] { 90 | float altitude [id = 1]; 91 | } 92 | 93 | message opendlv.proxy.PressureReading [id = 1034] { 94 | float pressure [id = 1]; 95 | } 96 | 97 | message opendlv.proxy.TemperatureReading [id = 1035] { 98 | float temperature [id = 1]; 99 | } 100 | 101 | message opendlv.proxy.TorqueReading [id = 1036] { 102 | float torque [id = 1]; 103 | } 104 | 105 | message opendlv.proxy.VoltageReading [id = 1037] { 106 | float voltage [id = 1]; 107 | } 108 | 109 | message opendlv.proxy.AngleReading [id = 1038] { 110 | float angle [id = 1]; 111 | } 112 | 113 | message opendlv.proxy.DistanceReading [id = 1039] { 114 | float distance [id = 1]; 115 | } 116 | 117 | message opendlv.proxy.SwitchStateReading [id = 1040] { 118 | int16 state [id = 1]; 119 | } 120 | 121 | message opendlv.proxy.PedalPositionReading [id = 1041] { 122 | float position [id = 1]; 123 | } 124 | 125 | message opendlv.proxy.GroundSteeringReading [id = 1045] { 126 | float groundSteering [id = 1]; 127 | } 128 | 129 | message opendlv.proxy.GroundSpeedReading [id = 1046] { 130 | float groundSpeed [id = 1]; 131 | } 132 | 133 | message opendlv.proxy.WheelSpeedReading [id = 1047] { 134 | float wheelSpeed [id = 1]; 135 | } 136 | 137 | message opendlv.proxy.WeightReading [id = 1050] { 138 | float weight [id = 1]; 139 | } 140 | 141 | message opendlv.proxy.GeodeticHeadingReading [id = 1051] { 142 | float northHeading [id = 1]; 143 | } 144 | 145 | message opendlv.proxy.GeodeticWgs84Reading [id = 19] { 146 | double latitude [id = 1]; 147 | double longitude [id = 3]; 148 | } 149 | 150 | message opendlv.proxy.ImageReading [id = 1055] { 151 | string fourcc [id = 1]; 152 | uint32 width [id = 2]; 153 | uint32 height [id = 3]; 154 | bytes data [id = 4]; 155 | } 156 | 157 | message opendlv.proxy.ImageReadingShared [id = 14] { 158 | string name [id = 1]; 159 | uint32 size [id = 2]; 160 | uint32 width [id = 3]; 161 | uint32 height [id = 4]; 162 | uint32 bytesPerPixel [id = 5]; 163 | } 164 | 165 | message opendlv.proxy.PointCloudReading [id = 49] { 166 | float startAzimuth [id = 1]; 167 | float endAzimuth [id = 2]; 168 | uint8 entriesPerAzimuth [id = 3]; 169 | bytes distances [id = 4]; 170 | uint8 numberOfBitsForIntensity [id = 5]; 171 | } 172 | 173 | message opendlv.proxy.PointCloudReadingShared [id = 28] { 174 | string name [id = 1]; 175 | uint32 size [id = 2]; 176 | uint32 width [id = 3]; 177 | uint32 height [id = 4]; 178 | uint8 numberOfComponentsPerPoint [id = 5]; 179 | } 180 | 181 | // V2xReading? 182 | 183 | 184 | message opendlv.proxy.PressureRequest [id = 1080] { 185 | float pressure [id = 1]; 186 | } 187 | 188 | message opendlv.proxy.TemperatureRequest [id = 1081] { 189 | float temperature [id = 1]; 190 | } 191 | 192 | message opendlv.proxy.TorqueRequest [id = 1082] { 193 | float torque [id = 1]; 194 | } 195 | 196 | message opendlv.proxy.VoltageRequest [id = 1083] { 197 | float voltage [id = 1]; 198 | } 199 | 200 | message opendlv.proxy.AngleRequest [id = 1084] { 201 | float angle [id = 1]; 202 | } 203 | 204 | message opendlv.proxy.SwitchStateRequest [id = 1085] { 205 | int16 state [id = 1]; 206 | } 207 | 208 | message opendlv.proxy.PedalPositionRequest [id = 1086] { 209 | float position [id = 1]; 210 | } 211 | 212 | message opendlv.proxy.PulseWidthModulationRequest [id = 1087] { 213 | uint32 dutyCycleNs [id = 1]; 214 | } 215 | 216 | message opendlv.proxy.GroundSteeringRequest [id = 1090] { 217 | float groundSteering [id = 1]; 218 | } 219 | 220 | message opendlv.proxy.GroundSpeedRequest [id = 1091] { 221 | float groundSpeed [id = 1]; 222 | } 223 | 224 | message opendlv.proxy.GroundAccelerationRequest [id = 1092] { 225 | float groundAcceleration [id = 1]; 226 | } 227 | 228 | message opendlv.proxy.GroundDecelerationRequest [id = 1093] { 229 | float groundDeceleration [id = 1]; 230 | } 231 | 232 | message opendlv.proxy.WheelSpeedRequest [id = 1094] { 233 | float wheelSpeed [id = 1]; 234 | } 235 | 236 | // V2xRequest? 237 | 238 | 239 | message opendlv.system.SignalStatusMessage [id = 1100] { 240 | int32 code [id = 1]; 241 | string description [id = 2]; 242 | } 243 | 244 | message opendlv.system.SystemOperationState [id = 1101] { 245 | int32 code [id = 1]; 246 | string description [id = 2]; 247 | } 248 | 249 | message opendlv.system.NetworkStatusMessage [id = 1102] { 250 | int32 code [id = 1]; 251 | string description [id = 2]; 252 | } 253 | 254 | 255 | 256 | message opendlv.logic.sensation.Direction [id = 1110] { 257 | float azimuthAngle [id = 1]; 258 | float zenithAngle [id = 2]; 259 | } 260 | 261 | message opendlv.logic.sensation.Point [id = 1111] { 262 | float azimuthAngle [id = 1]; 263 | float zenithAngle [id = 2]; 264 | float distance [id = 3]; 265 | } 266 | 267 | message opendlv.logic.sensation.Geolocation [id = 1116] { 268 | double latitude [id = 1]; 269 | double longitude [id = 2]; 270 | float altitude [id = 3]; 271 | float heading [id = 4]; 272 | } 273 | 274 | message opendlv.logic.sensation.Equilibrioception [id = 1017] { 275 | float vx [id = 1]; 276 | float vy [id = 2]; 277 | float vz [id = 3]; 278 | float rollRate [id = 4]; 279 | float pitchRate [id = 5]; 280 | float yawRate [id = 6]; 281 | } 282 | 283 | 284 | 285 | message opendlv.logic.perception.Object [id = 1130] { 286 | uint32 objectId [id = 1]; 287 | } 288 | 289 | message opendlv.logic.perception.ObjectType [id = 1131] { 290 | uint32 objectId [id = 1]; 291 | uint32 type [id = 2]; 292 | } 293 | 294 | message opendlv.logic.perception.ObjectProperty [id = 1132] { 295 | uint32 objectId [id = 1]; 296 | string property [id = 2]; 297 | } 298 | 299 | message opendlv.logic.perception.ObjectDirection [id = 1133] { 300 | uint32 objectId [id = 1]; 301 | float azimuthAngle [id = 2]; 302 | float zenithAngle [id = 3]; 303 | } 304 | 305 | message opendlv.logic.perception.ObjectDistance [id = 1134] { 306 | uint32 objectId [id = 1]; 307 | float distance [id = 2]; 308 | } 309 | 310 | message opendlv.logic.perception.ObjectAngularBlob [id = 1135] { 311 | uint32 objectId [id = 1]; 312 | float width [id = 2]; 313 | float height [id = 3]; 314 | } 315 | 316 | message opendlv.logic.perception.GroundSurface [id = 1140] { 317 | uint32 surfaceId [id = 1]; 318 | } 319 | 320 | message opendlv.logic.perception.GroundSurfaceType [id = 1141] { 321 | uint32 surfaceId [id = 1]; 322 | uint32 type [id = 2]; 323 | } 324 | 325 | message opendlv.logic.perception.GroundSurfaceProperty [id = 1142] { 326 | uint32 surfaceId [id = 1]; 327 | string property [id = 2]; 328 | } 329 | 330 | message opendlv.logic.perception.GroundSurfaceArea [id = 1143] { 331 | uint32 surfaceId [id = 1]; 332 | float x1 [id = 2]; 333 | float y1 [id = 3]; 334 | float x2 [id = 4]; 335 | float y2 [id = 5]; 336 | float x3 [id = 6]; 337 | float y3 [id = 7]; 338 | float x4 [id = 8]; 339 | float y4 [id = 9]; 340 | } 341 | 342 | 343 | message opendlv.logic.action.AimDirection [id = 1171] { 344 | float azimuthAngle [id = 1]; 345 | float zenithAngle [id = 2]; 346 | } 347 | 348 | message opendlv.logic.action.AimPoint [id = 1172] { 349 | float azimuthAngle [id = 1]; 350 | float zenithAngle [id = 2]; 351 | float distance [id = 3]; 352 | } 353 | 354 | message opendlv.logic.action.PreviewPoint [id = 1173] { 355 | float azimuthAngle [id = 1]; 356 | float zenithAngle [id = 2]; 357 | float distance [id = 3]; 358 | } 359 | 360 | message opendlv.logic.cognition.GroundSteeringLimit [id = 1191] { 361 | float steeringLimit [id = 1]; 362 | } 363 | 364 | message opendlv.logic.cognition.GroundSpeedLimit [id = 1192] { 365 | float speedLimit [id = 1]; 366 | } 367 | -------------------------------------------------------------------------------- /src/template-opencv.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Christian Berger 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | // Include the single-file, header-only middleware libcluon to create high-performance microservices 19 | #include "cluon-complete.hpp" 20 | // Include the OpenDLV Standard Message Set that contains messages that are usually exchanged for automotive or robotic applications 21 | #include "opendlv-standard-message-set.hpp" 22 | 23 | // Include the GUI and image processing header files from OpenCV 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | auto calculateSteering(double rightIR, double leftIR, int rightCones, int leftCones, double steering) 31 | { 32 | double incrementSteering = 0.045; 33 | steering = 0; 34 | 35 | if (rightIR <= 0.007) 36 | { 37 | steering = steering + incrementSteering; 38 | } 39 | if (leftIR <= 0.007) 40 | { 41 | steering = steering - incrementSteering; 42 | } 43 | 44 | if (rightCones == 0) 45 | { 46 | steering = -0.15; 47 | } 48 | if (leftCones == 0) 49 | { 50 | steering = 0.15; 51 | } 52 | return steering; 53 | } 54 | 55 | int32_t main(int32_t argc, char **argv) 56 | { 57 | int32_t retCode{1}; 58 | int yellowCones = 6; 59 | int blueCones = 0; 60 | double leftIR; 61 | double rightIR; 62 | double steering = 0.0; 63 | int directionEstablished = 0; 64 | std::string carDirection; 65 | int numberClockwise = 0; 66 | int numberCounterclockwise = 0; 67 | 68 | 69 | 70 | // Parse the command line parameters as we require the user to specify some mandatory information on startup. 71 | auto commandlineArguments = cluon::getCommandlineArguments(argc, argv); 72 | if ((0 == commandlineArguments.count("cid")) || 73 | (0 == commandlineArguments.count("name")) || 74 | (0 == commandlineArguments.count("width")) || 75 | (0 == commandlineArguments.count("height"))) 76 | { 77 | std::cerr << argv[0] << " attaches to a shared memory area containing an ARGB image." << std::endl; 78 | std::cerr << "Usage: " << argv[0] << " --cid= --name= [--verbose]" << std::endl; 79 | std::cerr << " --cid: CID of the OD4Session to send and receive messages" << std::endl; 80 | std::cerr << " --name: name of the shared memory area to attach" << std::endl; 81 | std::cerr << " --width: width of the frame" << std::endl; 82 | std::cerr << " --height: height of the frame" << std::endl; 83 | std::cerr << "Example: " << argv[0] << " --cid=253 --name=img --width=640 --height=480 --verbose" << std::endl; 84 | } 85 | else 86 | { 87 | // Extract the values from the command line parameters 88 | const std::string NAME{commandlineArguments["name"]}; 89 | const uint32_t WIDTH{static_cast(std::stoi(commandlineArguments["width"]))}; 90 | const uint32_t HEIGHT{static_cast(std::stoi(commandlineArguments["height"]))}; 91 | const bool VERBOSE{commandlineArguments.count("verbose") != 0}; 92 | 93 | // Attach to the shared memory. 94 | std::unique_ptr sharedMemory{new cluon::SharedMemory{NAME}}; 95 | if (sharedMemory && sharedMemory->valid()) 96 | { 97 | std::clog << argv[0] << ": Attached to shared memory '" << sharedMemory->name() << " (" << sharedMemory->size() << " bytes)." << std::endl; 98 | 99 | // Interface to a running OpenDaVINCI session where network messages are exchanged. 100 | // The instance od4 allows you to send and receive messages. 101 | cluon::OD4Session od4{static_cast(std::stoi(commandlineArguments["cid"]))}; 102 | 103 | opendlv::proxy::GroundSteeringRequest gsr; 104 | opendlv::proxy::VoltageReading infrared; 105 | std::mutex infraredMutex; 106 | std::mutex gsrMutex; 107 | auto onGroundSteeringRequest = [&gsr, &gsrMutex](cluon::data::Envelope &&env) 108 | { 109 | // The envelope data structure provide further details, such as sampleTimePoint as shown in this test case: 110 | // https://github.com/chrberger/libcluon/blob/master/libcluon/testsuites/TestEnvelopeConverter.cpp#L31-L40 111 | std::lock_guard lck(gsrMutex); 112 | gsr = cluon::extractMessage(std::move(env)); 113 | }; 114 | od4.dataTrigger(opendlv::proxy::GroundSteeringRequest::ID(), onGroundSteeringRequest); 115 | 116 | ///Infrared sensor 117 | 118 | auto onVoltageReading = [&infrared, &infraredMutex, &rightIR, &leftIR, &yellowCones, &blueCones, &steering](cluon::data::Envelope &&env) 119 | { 120 | std::lock_guard lck(infraredMutex); 121 | infrared = cluon::extractMessage(std::move(env)); 122 | if (env.senderStamp() == 3) 123 | { 124 | rightIR = infrared.voltage(); 125 | } 126 | else if (env.senderStamp() == 1) 127 | { 128 | leftIR = infrared.voltage(); 129 | } 130 | }; 131 | 132 | od4.dataTrigger(opendlv::proxy::VoltageReading::ID(), onVoltageReading); 133 | 134 | // Endless loop; end the program by pressing Ctrl-C. 135 | while (od4.isRunning()) 136 | { 137 | // OpenCV data structure to hold an image. 138 | cv::Mat img; 139 | 140 | // Wait for a notification of a new frame. 141 | sharedMemory->wait(); 142 | 143 | // Lock the shared memory. 144 | sharedMemory->lock(); 145 | { 146 | // Copy the pixels from the shared memory into our own data structure. 147 | cv::Mat wrapped(HEIGHT, WIDTH, CV_8UC4, sharedMemory->data()); 148 | img = wrapped.clone(); 149 | } 150 | 151 | std::pair pair = sharedMemory->getTimeStamp(); 152 | cluon::data::TimeStamp sampleT = pair.second; 153 | int64_t tStamp = cluon::time::toMicroseconds(sampleT); 154 | std::string ts = std::to_string(tStamp); 155 | std::string sampleTimeVar = "Sample Time: " + ts; 156 | sharedMemory->unlock(); 157 | 158 | 159 | 160 | // HSV values reference: https://www.codespeedy.com/splitting-rgb-and-hsv-values-in-an-image-using-opencv-python/ 161 | // Solution partly inspired by: https://stackoverflow.com/questions/9018906/detect-rgb-color-interval-with-opencv-and-c 162 | // AND: https://solarianprogrammer.com/2015/05/08/detect-red-circles-image-using-opencv/ 163 | 164 | // Cone color detection 165 | 166 | using namespace cv; 167 | using namespace std; 168 | 169 | cv::Mat originalImg; 170 | img.copyTo(originalImg); 171 | 172 | cv::Mat hsvIMG; 173 | img.copyTo(hsvIMG); 174 | 175 | // Draw box around unnecessary part of car(to avoid conflicts with inrange below) 176 | cv::rectangle(img, cv::Point(150, 385), cv::Point(500, 500), cv::Scalar(0, 0, 0), CV_FILLED); 177 | // Draw box in region above cones, to avoid conflicts with irrelevant objects. 178 | cv::rectangle(img, cv::Point(0, 0), cv::Point(650, 250), cv::Scalar(0, 0, 0), CV_FILLED); 179 | 180 | cv::cvtColor(img, hsvIMG, cv::COLOR_BGR2HSV); 181 | 182 | cv::Mat justYellowColor; 183 | cv::Mat justYellowColor2; 184 | cv::Mat justBlueColor; 185 | 186 | inRange(hsvIMG, cv::Scalar(12, 20, 20), cv::Scalar(70, 100, 250), justYellowColor); // Yellow(low, high) - Yellow cones 187 | inRange(hsvIMG, cv::Scalar(8, 20, 20), cv::Scalar(11, 100, 250), justYellowColor2); // Yellow(low, high) - Yellow cones copy for lower ranges 188 | inRange(hsvIMG, cv::Scalar(80, 125, 8), cv::Scalar(135, 255, 210), justBlueColor); // Blue(low, high) - Blue cones 189 | 190 | justYellowColor = justYellowColor | justYellowColor2; 191 | 192 | cv::Rect bounding_rect; 193 | vector> yellowcontours; // Vector for storing yellow contours 194 | vector> bluecontours; // Vector for storing blue contours 195 | cv::findContours(justBlueColor, bluecontours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); 196 | cv::findContours(justYellowColor, yellowcontours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); 197 | 198 | 199 | 200 | cv::Rect boundRectangleYellow; 201 | cv::Rect boundRectangleBlue; 202 | 203 | cv::Rect largestboundRectangleBlue; 204 | cv::Rect largestboundRectangleYellow; 205 | 206 | int amountOfYellowCones = 0; 207 | int amountOfBlueCones = 0; 208 | 209 | int largestAreaYellow = 0; 210 | int largestAreaBlue = 0; 211 | 212 | for (unsigned int i = 0; i < bluecontours.size(); i++) 213 | { 214 | boundRectangleBlue = cv::boundingRect(bluecontours[i]); 215 | if (boundRectangleBlue.area() > 80) 216 | { 217 | cv::rectangle(img, boundRectangleBlue.tl(), boundRectangleBlue.br(), cv::Scalar(0, 255, 0), 3); //<-- Light green rectangles 218 | 219 | if (boundRectangleBlue.area() > largestAreaBlue) 220 | { 221 | largestAreaBlue = boundRectangleBlue.area(); 222 | largestboundRectangleBlue = cv::boundingRect(bluecontours[i]); 223 | } 224 | 225 | if (boundRectangleBlue.area() > 120) 226 | { 227 | amountOfBlueCones += 1; 228 | } 229 | } 230 | } 231 | 232 | for (unsigned int i = 0; i < yellowcontours.size(); i++) 233 | { 234 | boundRectangleYellow = cv::boundingRect(yellowcontours[i]); 235 | if (boundRectangleYellow.area() > 80) 236 | { 237 | 238 | cv::rectangle(img, boundRectangleYellow.tl(), boundRectangleYellow.br(), cv::Scalar(6, 82, 58), 3); //<-- Dark green rectangles 239 | 240 | if (boundRectangleYellow.area() > largestAreaYellow) 241 | { 242 | largestAreaYellow = boundRectangleYellow.area(); 243 | largestboundRectangleYellow = cv::boundingRect(yellowcontours[i]); 244 | } 245 | 246 | if (boundRectangleYellow.area() > 120) 247 | { 248 | amountOfYellowCones += 1; 249 | } 250 | } 251 | } 252 | 253 | if (directionEstablished < 10 && (largestboundRectangleBlue.x != 0) && (boundRectangleYellow.x != 0)) 254 | { 255 | if (largestboundRectangleBlue.x < largestboundRectangleYellow.x) 256 | { 257 | numberClockwise++; 258 | } 259 | else if (largestboundRectangleBlue.x > largestboundRectangleYellow.x) 260 | { 261 | numberCounterclockwise++; 262 | } 263 | 264 | if (numberClockwise > numberCounterclockwise) 265 | { 266 | carDirection = "Clockwise"; 267 | } 268 | else if (numberCounterclockwise > numberClockwise) 269 | { 270 | carDirection = "Counter-Clockwise"; 271 | } 272 | 273 | directionEstablished++; 274 | } 275 | 276 | if (carDirection == "Clockwise") 277 | { 278 | steering = calculateSteering(rightIR, leftIR, amountOfYellowCones, amountOfBlueCones, steering); 279 | } 280 | if (carDirection == "Counter-Clockwise") 281 | { 282 | steering = calculateSteering(rightIR, leftIR, amountOfBlueCones, amountOfYellowCones, steering); 283 | } 284 | std::cout << "Group_02;" << tStamp << ";" << steering << std::endl; 285 | 286 | cv::rectangle(img, cv::Point(50, 50), cv::Point(100, 100), cv::Scalar(0, 0, 255)); 287 | 288 | { 289 | std::lock_guard lck(gsrMutex); 290 | 291 | } 292 | 293 | 294 | if (VERBOSE) 295 | { 296 | cv::imshow(sharedMemory->name().c_str(), img); 297 | cv::waitKey(1); 298 | } 299 | } 300 | } 301 | retCode = 0; 302 | } 303 | return retCode; 304 | } 305 | --------------------------------------------------------------------------------