Ultrasonic sensor hc-sr04 tracking and following object

Hi everyone,

I’m currently working on a school project involving a servo motor and an ultrasonic sensor. The goal of the project is to have the sensor track an object as it moves around. However, I'm facing an issue where the sensor isn't following the object correctly in all directions.

I've written some code, but it seems that the servo motor doesn't track the object as intended, especially when the object changes direction. I'm not sure what the problem is, and I would greatly appreciate it if someone could take a look at the code and suggest improvements or identify where the error might be.

If anyone has experience with object tracking using ultrasonic sensors and servo motors, your help would be invaluable!

Here’s the code I’ve been working on:

#include <Servo.h>

// Defines Trig and Echo pins of the Ultrasonic Sensor
const int trigPin = 10;
const int echoPin = 11;
long duration;
int distance;
int lastDistance = -1;  // Variable to store the last measured distance
Servo myServo;

void setup() {
  pinMode(trigPin, OUTPUT);  // Sets the trigPin as an Output
  pinMode(echoPin, INPUT);   // Sets the echoPin as an Input
  Serial.begin(9600);
  myServo.attach(12);        // Defines the pin to which the servo motor is attached
  myServo.write(90);         // Start position at 90 degrees (middle)
}

void loop() {
  int maxDetectionRange = 50;  // Maximum range to detect objects (50 cm)
  int currentAngle = 90;       // Start at the center
  int stepFast = 5;            // Fast step size for initial movement
  int stepSlow = 1;            // Slow step size when object is stationary

  while (true) {
    distance = calculateDistance();

    if (distance > 0 && distance <= maxDetectionRange) {
      // Check if the object has moved
      if (distance != lastDistance) {
        // Object is moving, track it with faster speed
        currentAngle = trackObject(currentAngle, stepFast);
        lastDistance = distance;  // Update the last known distance
      } else {
        // Object is stationary, reduce speed
        currentAngle = trackObject(currentAngle, stepSlow);
      }
    } else {
      // Object lost, scan in both directions to find it again
      currentAngle = scanForObject(currentAngle, stepFast);
      lastDistance = -1;  // Reset the last known distance
    }

    myServo.write(currentAngle);
    delay(20); // Small delay for smooth movement
  }
}

// Function to track and follow the object
int trackObject(int currentAngle, int step) {
  int maxScanAngle = 180;
  int minScanAngle = 0;

  distance = calculateDistance();

  if (distance > 0 && distance <= 50) {
    if (distance < lastDistance && currentAngle > minScanAngle) {
      // Object is getting closer, move to the left
      currentAngle -= step; 
    } else if (distance > lastDistance && currentAngle < maxScanAngle) {
      // Object is getting farther, move to the right
      currentAngle += step; 
    }
  }

  // Ensure the angle stays within bounds
  currentAngle = constrain(currentAngle, minScanAngle, maxScanAngle);

  return currentAngle;
}

// Function to scan for the object if it is lost
int scanForObject(int currentAngle, int step) {
  int maxScanAngle = 180;
  int minScanAngle = 0;

  // Scan right
  for (int i = currentAngle; i <= maxScanAngle; i += step) {
    myServo.write(i);
    delay(20); // Reduced delay for faster scanning
    distance = calculateDistance();
    if (distance > 0 && distance <= 50) {
      return i;  // Return the angle where the object is detected
    }
  }

  // Scan left
  for (int i = currentAngle; i >= minScanAngle; i -= step) {
    myServo.write(i);
    delay(20); // Reduced delay for faster scanning
    distance = calculateDistance();
    if (distance > 0 && distance <= 50) {
      return i;  // Return the angle where the object is detected
    }
  }

  return currentAngle;  // Return the current angle if the object is not found
}

// Function for calculating the distance measured by the Ultrasonic sensor
int calculateDistance() { 
  digitalWrite(trigPin, LOW); 
  delayMicroseconds(2);

  digitalWrite(trigPin, HIGH); 
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  duration = pulseIn(echoPin, HIGH); 
  distance = duration * 0.034 / 2;
  return distance;
}

This sensor only has a 15 degree cone and 400cm distance to reflect off an object... so that is about 100cm chord at 400cm.

c = 2r*sin(a/2)

c = chord
r = radius (400cm)
a = angle (15)

Untitled

1 Like

Tell us in your own words how you are attempting to follow the object in all directions. You would need at least two and possibly three sensors to follow the object.

1 Like

Thank you for your response!

The goal of my project is to track an object using a single ultrasonic sensor mounted on a servo motor. The idea is that the sensor continuously rotates from 0 to 180 degrees, scanning for an object. Once the object is detected within a certain distance (in this case, 50 cm was not sufficiant), the servo motor should adjust its position to keep the object centered in its field of view. However, I'm finding that the sensor doesn't effectively follow the object when it changes direction, especially if the object moves from left to right. The tracking seems to only work in one direction, and when the object moves out of the initial direction, the sensor doesn't always adjust properly to follow it. I understand that using multiple sensors could provide more accurate tracking in multiple directions. Do you have an example how to set um the 2-3 Sensors on the servo motor?

Thank you for this information! It looks like my ultrasonic sensor's 15-degree cone and the associated coverage area might be contributing to the issues I'm seeing with tracking. Since the sensor has such a narrow detection cone, it seems like it might be missing the object when it moves outside of this area.

Would you recommend any specific adjustments to the servo movement or suggest adding additional sensors to cover a wider area? Any other tips to optimize the setup for better tracking would be greatly appreciated!

Obviously with three sensors it's easier because you can compare distance between them continuously, but 1 sensor approach is more interesting.
Maybe you should set some threshold to be considered different movements. You should have a logic to separate left/right movements from backward/forward movements.
case1 distance same, no movement
case2 distance changes slowly, bw/fw movement
case3 distance changes a lot suddenly (measuring backround, not object), L/R movement >> make fast scan

Hi, @meriseee

How do you move the centre the field of view?

Can you post some images of your project?
So we can see your component layout.

Can you please post a copy of your circuit, a picture of a hand drawn circuit in jpg, png?
Hand drawn and photographed is perfectly acceptable.
Please include ALL hardware, power supplies, component names and pin labels.

Tom.. :smiley: :+1: :coffee: :australia:

What was the logic behind moving the servo left or right depending on whether the object was moving away or towards you.

    if (distance < lastDistance && currentAngle > minScanAngle) {
      // Object is getting closer, move to the left
      currentAngle -= step; 
    } else if (distance > lastDistance && currentAngle < maxScanAngle) {
      // Object is getting farther, move to the right
      currentAngle += step; 

Doe that make sense to you?
I think it explains your left/right problem.


Hi Tom,

here is a picture of the setup.
Components used:
-Arduino Uno
-HC-SR04-Ultrasonic
-Micro Servo 9G

Thank you

I did not look, but it seems you are only moving the sensor in one direction when it senses something. Also, are you stopping the movement while you are sensing and NOT moving again until either an echo is received or there is a time-out from the sensor code?

Your code is obscure, so try some different ideas.

One would be to scan and determine the range of angles over which you receive an echo. The center of that range is a good guess for the "best" angle to use. If that changes, maybe the object has moved.

It is a bad idea to power the servo from the Arduino 5V output, as you can damage the Arduino doing so. The servo can also cause other types of malfunctions. Use a separate power supply, like 4xAA:

When you receive the "shortest distance" echo, that will be the center of the object... store the angle... steer toward that angle. The more scans across the object, the more precise the "object center" is known... but that takes time away from pinging and steering... you would need to find the best balance of scan, ping, steer.

In some cases.

Here are the results of an experiment I did a while back, to see how ultrasonic ranging actually "views" the world. The experiment was instructive and suggests caution in interpreting the ranging results.

I used a much higher quality ranger (one capable of recording multiple echos), than the HC-SR04, scanning the room in ~2 degree steps.

1 Like

Very nice. I will use this as a seed for thought.

Hi, @meriseee

Your centre of field is at 90degree of the 0 to 180degree sweep relative to your project.

To bring the detected object back into centre of view you will need to rotate your project.
OR
Do you have a different concept?

Tom.. :smiley: :+1: :coffee: :australia:

Can you share or have you already shared the setup and code you used for that?

The code for the experiment described above was written in BASIC for a non-Arduino microcontroller, and lost. I just happen to have the figure around!

The sensor is this one (10m range, mounted on a pancake stepper) and is Arduino compatible.