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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 19 additions & 61 deletions CMakeLists.txt
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,79 +1,37 @@
cmake_minimum_required(VERSION 3.5)

set(PROJECT_ID gesture_controller)
PROJECT (${PROJECT_ID})
cmake_minimum_required(VERSION 3.28)

set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

# add_compile_options(-Wall -Wextra -Werror -Wpedantic -pedantic-errors -Wconversion)
set(PROJECT_ID delta_robot_example)

PROJECT (${PROJECT_ID})

set(PROJECT_BASE_PATH ${CMAKE_CURRENT_SOURCE_DIR})
set(SOFTWARE_SRC_PATH ${PROJECT_BASE_PATH}/src)
file(GLOB_RECURSE SOFTWARE_SRC "${SOFTWARE_SRC_PATH}/*.*")


set(USE_ASAN ON)
set(BUILD_TESTS ON)

if ((DEFINED USE_ASAN) AND (USE_ASAN STREQUAL "ON"))
message(STATUS "Using AddressSanitizer (ASan).")
if (UNIX)
# message(STATUS "ASAN_OPTIONS = $ENV{ASAN_OPTIONS}")
# set(ENV{ASAN_OPTIONS} allocator_may_return_null=1)
add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
add_link_options(-fsanitize=address)
endif(UNIX)
endif()

find_package(spdlog REQUIRED)
find_package(OpenCV REQUIRED PATHS /usr/local/debug/)

set( MAIN_SRC
src/main.cpp
src/controller.cpp
src/device.cpp
src/face_detection.cpp
src/gesture_detection.cpp
src/detection.cpp)
set(MAIN_EXEC ${PROJECT_ID}__main)

# cmrc_add_resource_library(models ALIAS models::rc NAMESPACE models
# resources/models/haarcascade_frontalface_default.xml
# resources/models/resnet18.onnx
# )

# cmrc_add_resource_library(test_res ALIAS test_res::rc NAMESPACE test_res
# test/data/test.png
# test/data/test_face.jpg
# resources/models/haarcascade_frontalface_default.xml
# resources/models/resnet18.onnx
# )

add_executable( ${MAIN_EXEC} ${MAIN_SRC} )
target_link_libraries(${MAIN_EXEC} PUBLIC ${OpenCV_LIBS} spdlog::spdlog)
target_compile_options(${MAIN_EXEC} PRIVATE -Werror -Wall -Wextra)
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/include ${OpenCV_INCLUDE_DIRS})
link_directories( ${CMAKE_BINARY_DIR}/bin)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
find_package(OpenCV REQUIRED)
find_package(open62541 REQUIRED)

if ((DEFINED BUILD_TESTS) AND (BUILD_TESTS STREQUAL "ON"))
include(FetchContent)
FetchContent_Declare(
googletest
URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip
)
FetchContent_MakeAvailable(googletest)
enable_testing()
include(FetchContent)
FetchContent_Declare(
spdlog
GIT_REPOSITORY https://github.com/gabime/spdlog.git
GIT_TAG v1.12.0
)
FetchContent_MakeAvailable(spdlog)

set(SOURCES_FILES_TESTS
test/test_gesture_detection.cpp
test/test_face_detection.cpp
test/test_controller.cpp)
set( TEST_SRC
src/controller.cpp
src/device.cpp
src/face_detection.cpp
src/gesture_detection.cpp
src/detection.cpp)
set(EXEC_TEST ${PROJECT_ID}__test)
add_executable(${EXEC_TEST} ${TEST_SRC} ${SOURCES_FILES_TESTS} test/main_gtest.cpp)
target_link_libraries(${EXEC_TEST} PRIVATE GTest::gtest_main ${OpenCV_LIBS} spdlog::spdlog)
include(GoogleTest)
gtest_discover_tests(${EXEC_TEST})
endif()
add_subdirectory(examples)
59 changes: 45 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,52 @@
[![CMake](https://github.com/mboiar/gesture_controller/actions/workflows/cmake.yml/badge.svg?branch=main)](https://github.com/mboiar/gesture_controller/actions/workflows/cmake.yml)

# Gesture controller
# Gesture Controller

Modular, versatile gesture-based controller written in C++.
A modular, high‑performance gesture‑based controller written in C++.
It translates hand gestures captured by a camera into control commands for robots or drones, with built‑in support for **OPC UA** communication and real‑time video processing.

## Demo
## Examples

[![Watch the video](https://img.youtube.com/vi/OrVqN6P2TyY/hqdefault.jpg)](https://youtu.be/OrVqN6P2TyY)
| Drone Software‑in‑the‑Loop Simulation | Delta Robot PLC (OPC UA) |
|:-------------------------------------:|:-------------------------:|
| [![Watch the demo](https://img.youtube.com/vi/OrVqN6P2TyY/hqdefault.jpg)](https://youtu.be/OrVqN6P2TyY) | [![Watch the demo](doc/images/thumb.png)](https://youtube.com/shorts/yFY8yK7BDtM) |

## Features
- Easily remappable gesture commands
- Connect to any device over serial supporting camera video stream
- Good performance on resource constrained hardware
- Face detection with the HaarCascade model
- Gesture detection with the ResNet18 model trained on a custom dataset

## Usage
Compile project with `cmake --build .`
Run tests with `bin/gesture_controller__test`
Run app with `bin/gesture_controller`

- **Remappable gesture commands** – easily change which gesture triggers which action.
- **Multi‑device support** – connect to any device that provides a video stream and accepts commands (serial, OPC UA, etc.).
- **Resource‑efficient** – runs well on constrained hardware (e.g., Raspberry Pi, PLC Companion Computer).
- **Face detection** – using OpenCV’s Haar cascade to focus the gesture recognition region.
- **Gesture detection** – ResNet18 model trained on a custom dataset, executed with ONNX Runtime.
- **OPC UA integration** – read robot status (position, mode, servo OK) and send jog commands to industrial controllers.
- **Simulation mode** – test the control logic without hardware.
- **Asynchronous logging** – spdlog provides colour‑coded, thread‑safe logs.

## Architecture

The project is split into several modules:

- **`Controller`** – gesture/face detection, and command dispatching.
- **`GenericDevice`** – abstract interface for any controllable device (simulated, serial, OPC UA).
- **`OPCUA_Device`** – concrete implementation that talks to an OPC UA server (e.g., a PLC controlling a delta robot).
- **Face & Gesture detectors** – wrappers around OpenCV and ONNX Runtime.

## Dependencies

- **C++17** compiler (gcc, clang)
- **CMake** 3.10+
- **OpenCV** (≥4.5)
- **spdlog** -logging
- **open62541** – OPC UA client
- **argparse** – command‑line argument parsing

## Building

```
git clone https://github.com/mboiar/gesture_controller.git
cd gesture_controller
mkdir build && cd build
cmake ..
make -j$(nproc)
```

Binary file added doc/images/thumb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add_subdirectory(DeltaRobot)
15 changes: 15 additions & 0 deletions examples/DeltaRobot/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
set(NAME DeltaRobotExample)

add_executable(${NAME})

target_sources(${NAME} PRIVATE
${SOFTWARE_SRC}
)

target_include_directories(${NAME} PRIVATE ${SOFTWARE_SRC_PATH})
target_compile_definitions(${NAME} PRIVATE UA_ENABLE_AMALGAMATION)

target_include_directories(${NAME} PUBLIC ${OpenCV_INCLUDE_DIRS})
link_directories( ${CMAKE_BINARY_DIR}/bin)
target_link_libraries(${NAME} PUBLIC ${OpenCV_LIBS} spdlog::spdlog)
set(EXECUTABLE_OUTPUT_PATH ${CMAKE_BINARY_DIR}/bin)
99 changes: 99 additions & 0 deletions examples/DeltaRobot/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#include "spdlog/spdlog.h"
#include <algorithm>
#include <cmath>
#include <dep/argparse.hpp>
#include <fstream>
#include <iostream>
#include <regex>
#include <stdexcept>
#include <string>
#include <vector>

#include "core/controller.hpp"
#include <interfaces/opc_ua/dev_OPC_UA.hpp>

using std::cerr;
using std::cout;
using std::endl;
using std::string;
using std::vector;

int main(int argc, char *argv[]) {

std::cout << "test" << std::endl;

auto logger_ = spdlog::get("MAIN");

if (!logger_) {
logger_ = spdlog::stdout_color_mt("MAIN");
}
logger_->set_level(spdlog::level::info);

logger_->info("Parsing input arguments");

argparse::ArgumentParser parser("controller");
parser.add_argument("-v", "--verbose")
.help("Display additional information during execution")
.default_value(false)
.implicit_value(true);

parser.add_argument("--log-level")
.help("Choose logging level")
.scan<'d', int>()
.default_value(0);

parser.add_argument("--save-video")
.help("Save video feed to a specified file")
.default_value(string{""});

parser.add_argument("mode")
.help("Choose operation mode")
.action([](const string &value) {
static const vector<string> choices = {"SIM", "WEBCAM"};
if (std::find(choices.begin(), choices.end(), value) != choices.end()) {
return value;
}
throw std::invalid_argument("Choose a valid mode option.");
});

parser.add_argument("--server-path")
.help("Save video feed to a specified file")
.default_value(string{"127.0.0.1:4840"});

parser.add_description("Control a delta robot with gestures.");

try {
parser.parse_args(argc, argv);
} catch (const std::runtime_error &err) {
logger_->error(err.what());
return EXIT_FAILURE;
}

string mode = parser.get<string>("mode");
string server_addr = parser.get<string>("--server-path");
int log_level = parser.get<int>("--log-level");
string video_filepath = parser.get<string>("--save-video");

spdlog::set_level(static_cast<spdlog::level::level_enum>(log_level));

logger_->info("Connecting to device");
std::string opc_ua_server_name = "opc.tcp://" + server_addr;
Device device = OPCUA_Device{};
if (device.connect(opc_ua_server_name) < 0) {
logger_->info("Program exited with status {}", EXIT_FAILURE);
return EXIT_FAILURE;
}
device.streamon();

std::string gesture_detector_path = "../resources/models/resnet18.onnx";
std::string face_detector_path =
"../resources/models/haarcascade_frontalface_default.xml";

Controller controller =
Controller(&device, true, face_detector_path, gesture_detector_path);
logger_->info("Running");
controller.run(50);

logger_->info("Program exited with status {}", EXIT_SUCCESS);
return EXIT_SUCCESS;
}
Loading
Loading