edge lab LIVE! starts Tuesday in Munich. Grab a workshop seat and take home the board you build on. Code FRIEND-HACKSTER saves 20%. () Learn more!
Jallson Surjo
Published © GPL3+

Hand Gestures Control for Robotic Arm

Basic implementation of hand gesture detection in robotics, featuring real-time object tracking and intuitive robotic arm movement.

BeginnerFull instructions provided3 hours40
Hand Gestures Control for Robotic Arm

Things used in this project

Hardware components

Arduino UNO Q
Arduino UNO Q
×1
USB-C Power Adapter
eg. 27W Pi5 Power Adapter
×1
USB-C to USB A hub with PD
eg. Official Arduino USB C hub (8 in 1)
×1
USB Camera
eg. Logitech C920/ 922/ 270 ..
×1
PC/ laptop
Your existing computer
×1
Robotic arm with gripper (3 DoF)
with 3 servos
×1
Breadboard (generic)
Breadboard (generic)
×1
Jumper cables
×1
6V Power Supply
eg. 4x 1.5V battery
×1

Software apps and online services

Arduino App Lab
Edge Impulse Studio
Edge Impulse Studio

Story

Read more

Schematics

Diagram

Uno Q + usb cam + robotic arm + program

Code

Python code

Python
from arduino.app_utils import *
from arduino.app_bricks.web_ui import WebUI
from arduino.app_bricks.video_objectdetection import VideoObjectDetection

from datetime import datetime, UTC
import time

# ============================================================
# UI + DETECTION
# ============================================================

ui = WebUI()

detection_stream = VideoObjectDetection(
    confidence=0.6,
    debounce_sec=0.0
)

# ============================================================
# SERVO CONFIGURATION
# ============================================================

# PAN SERVO
PAN_MIN = 10
PAN_MAX = 170

# TILT SERVO
TILT_MIN = 90
TILT_MAX = 160

# CLAW SERVO
CLAW_OPEN = 40
CLAW_CLOSE = 100

# ============================================================
# INITIAL SERVO POSITIONS
# ============================================================

last_pan = 90
last_tilt = 120
last_claw = CLAW_OPEN

# ============================================================
# SMOOTHING
# ============================================================

SMOOTHING = 0.65

# ============================================================
# HELPER FUNCTIONS
# ============================================================

def clamp(val, min_val, max_val):
    return max(min_val, min(max_val, val))


def map_range(x, in_min, in_max, out_min, out_max):
    return (x - in_min) * (out_max - out_min) / \
           (in_max - in_min) + out_min


def smooth(current, target):
    return current + (target - current) * SMOOTHING

# ============================================================
# MAIN DETECTION CALLBACK
# ============================================================

def process_detections(detections):

    global last_pan
    global last_tilt
    global last_claw

    best_obj = None
    best_area = 0

    # ========================================================
    # FIND LARGEST DETECTED OBJECT
    # ========================================================

    for label in ["neut", "open", "close"]:

        if label not in detections:
            continue

        objs = detections[label]

        if not isinstance(objs, list):
            objs = [objs]

        for obj in objs:

            if "bounding_box_xyxy" not in obj:
                continue

            x1, y1, x2, y2 = obj["bounding_box_xyxy"]

            width = x2 - x1
            height = y2 - y1

            area = width * height

            if area > best_area:

                best_area = area

                best_obj = {
                    "label": label,
                    "bbox": (x1, y1, x2, y2),
                    "area": area
                }

    # ========================================================
    # NO DETECTION
    # ========================================================

    if best_obj is None:
        return

    label = best_obj["label"]

    x1, y1, x2, y2 = best_obj["bbox"]

    # ========================================================
    # TRACKING ONLY WHEN "neut" DETECTED
    # ========================================================

    if label == "neut":

        # ====================================================
        # PAN CONTROL
        # ====================================================

        center_x = (x1 + x2) / 2

        target_pan = map_range(
            center_x,
            80,
            560,
            PAN_MIN,
            PAN_MAX
        )

        target_pan = clamp(
            target_pan,
            PAN_MIN,
            PAN_MAX
        )

        # ====================================================
        # TILT CONTROL USING BBOX AREA
        # ====================================================

        width = x2 - x1
        height = y2 - y1

        area = width * height

        target_tilt = map_range(
            area,
            8500,
            49000,
            TILT_MIN,
            TILT_MAX
        )

        target_tilt = clamp(
            target_tilt,
            TILT_MIN,
            TILT_MAX
        )

        # ====================================================
        # SMOOTH MOVEMENT
        # ====================================================

        last_pan = smooth(
            last_pan,
            target_pan
        )

        last_tilt = smooth(
            last_tilt,
            target_tilt
        )

        # ====================================================
        # DEBUG
        # ====================================================

        print(
            f"TRACKING | "
            f"X={center_x:.1f} "
            f"AREA={area} "
            f"PAN={int(last_pan)} "
            f"TILT={int(last_tilt)}"
        )

    # ========================================================
    # CLAW CONTROL
    # ========================================================

    if label == "open":

        last_claw = smooth(
            last_claw,
            CLAW_OPEN
        )

        print("CLAW OPEN")

    elif label == "close":

        last_claw = smooth(
            last_claw,
            CLAW_CLOSE
        )

        print("CLAW CLOSE")

    # ========================================================
    # FINAL INTEGER VALUES
    # ========================================================

    pan = int(last_pan)
    tilt = int(last_tilt)
    claw = int(last_claw)

    # ========================================================
    # SEND TO ARDUINO
    # ========================================================

    Bridge.call(
        "set_arm",
        pan,
        tilt,
        claw
    )

    # ========================================================
    # SEND TO WEB UI
    # ========================================================

    entry = {
        "gesture": label,
        "timestamp": datetime.now(UTC).isoformat()
    }

    ui.send_message(
        "detection",
        message=entry
    )

    # ========================================================
    # SMALL DELAY FOR STABILITY
    # ========================================================

    time.sleep(0.005)

# ============================================================
# REGISTER CALLBACK
# ============================================================

detection_stream.on_detect_all(
    process_detections
)

# ============================================================
# START APP
# ============================================================

App.run()

Sketch

Arduino
// NEW PROJECT: HAND GESTURES controlled ROBOTIC ARM

#include "Arduino_RouterBridge.h"
#include "Servo.h"
#include <Arduino_LED_Matrix.h>

// ============================================================
// LED MATRIX
// ============================================================

Arduino_LED_Matrix matrix;

const uint32_t smiley_face[] = {
  0x000200a8,
  0x0a802000,
  0x04403e00,
  0x00000000
};

// ============================================================
// SERVOS
// ============================================================

Servo servoPan;
Servo servoTilt;
Servo servoClaw;

// ============================================================
// PINS
// ============================================================

const int panPin = 9;
const int tiltPin = 10;
const int clawPin = 11;

// ============================================================
// POSITIONS
// ============================================================

int panPos = 90;
int tiltPos = 120;
int clawPos = 40;

// ============================================================
// SETUP
// ============================================================

void setup() {

  Serial.begin(115200);

  matrix.begin();

  servoPan.attach(panPin);
  servoTilt.attach(tiltPin);
  servoClaw.attach(clawPin);

  servoPan.write(panPos);
  servoTilt.write(tiltPos);
  servoClaw.write(clawPos);

  // ==========================================================
  // BRIDGE START
  // ==========================================================

  if (!Bridge.begin()) {
    Serial.println("Cannot setup Bridge");
  }

  if (!Monitor.begin()) {
    Serial.println("Cannot setup Monitor");
  }

  // ==========================================================
  // PROVIDE FUNCTION
  // ==========================================================

  Bridge.provide("set_arm", set_arm);

  Serial.println("Robotic Arm Ready");
}

// ============================================================
// LOOP
// ============================================================

void loop() {

}

// ============================================================
// FUNCTION CALLED FROM PYTHON
// ============================================================

void set_arm(int pan, int tilt, int claw) {

  panPos = constrain(pan, 10, 170);
  tiltPos = constrain(tilt, 60, 160);
  clawPos = constrain(claw, 40, 100);

  servoPan.write(panPos);
  servoTilt.write(tiltPos);
  servoClaw.write(clawPos);

  matrix.loadFrame(smiley_face);

  Monitor.print("PAN: ");
  Monitor.print(panPos);

  Monitor.print(" TILT: ");
  Monitor.print(tiltPos);

  Monitor.print(" CLAW: ");
  Monitor.println(clawPos);
}

Sketch YAML

YAML
profiles:
  default:
    fqbn: arduino:zephyr:unoq
    platforms:
      - platform: arduino:zephyr
    libraries:
      - MsgPack (0.4.2)
      - DebugLog (0.8.4)
      - ArxContainer (0.7.0)
      - ArxTypeTraits (0.3.1)
      - Servo (1.3.0)
default_profile: default

Credits

Jallson Surjo
7 projects • 26 followers
Tech integrator for schools. Also works as a maker and his activities include disassembling, fixing, and making things.

Comments