Go Down

Topic: Ultrasonic sensor with very narrow beam (Read 421 times) previous topic - next topic

Pezhetairoi

Hi, I'm developing an application for mesuring box columns heights in a pallet for count the total boxes. The basic principle is putting some sensors (one per box column) at 3m high looking down on the boxes and take the column height of each column.

My problem is that probably some sensors will take the measure of the adjacent columns due the beam angle. So the solution is to find a sensor that has the most narrow beam angle possible.

Box sizes are of: Width: 30, Depth: 60, Height: 5cms

I was looking on the MaxSonar HRXL / SCXL but from as the charts, I still get approx. 30 cms of "error".

https://www.maxbotix.com/documents/HRXL-MaxSonar-WR_Datasheet.pdf
https://www.maxbotix.com/documents/SCXL-MaxSonar-WR_Datasheet.pdf

Basically I need a straight line sensor. I ignore if it exists, but if not, at least the nearest one.

Any help will be appreaciated!

pylon

I doubt that you will find ultrasonic sensor that will match that criteria. I guess that only type of sensor that will fit here are laser distance sensors.

Pezhetairoi

I'm afraid of that, do you have any kind of model for this?

wvmarle

#3
Sep 03, 2019, 06:45 am Last Edit: Sep 03, 2019, 06:46 am by wvmarle
The VL53L1X will do this just fine, with a range of up to 4m.

It uses I2C, to use multiple such sensors on a single Arduino you have to change the sensor's addresses when starting up.

Note: sensor works best in an indoor environment with little stray IR light around. Outdoor in the sun it doesn't work.
Quality of answers is related to the quality of questions. Good questions will get good answers. Useless answers are a sign of a poor question.

Pezhetairoi

Thank you very much! It looks great that model, fully customizable. What do you think about TFMini? I was looking its datasheet and has a very narrow FoV degree (2.3ยบ). And it does take measures till 12 meters.

Idahowalker

I've had a TFMini running 24/7 for a bit over 13 months. Product works great and, so far, lasts a long time.

The TFMini can be single triggered.

Here are the h and cpp files for single trigger control of the TFMini:

Idahowalker

TFMini.h

Code: [Select]
/*
Arduino driver for Benewake TFMini time-of-flight distance sensor.
by Peter Jansen (December 11/2017)
This code is open source software in the public domain.
THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
The names of the contributors may not be used to endorse or promote products
derived from this software without specific prior written permission.
*/

#if (ARDUINO >= 100)
 #include "Arduino.h"
#else
 #include "WProgram.h"
#endif


// Defines
#define TFMINI_BAUDRATE   115200
#define TFMINI_DEBUGMODE  0

// The frame size is nominally 9 characters, but we don't include the first two 0x59's marking the start of the frame
#define TFMINI_FRAME_SIZE                 7

// Timeouts
#define TFMINI_MAXBYTESBEFOREHEADER       30
#define TFMINI_MAX_MEASUREMENT_ATTEMPTS   10

// States
#define READY                             0
#define ERROR_SERIAL_NOHEADER             1
#define ERROR_SERIAL_BADCHECKSUM          2
#define ERROR_SERIAL_TOOMANYTRIES         3
#define MEASUREMENT_OK                    10


//
// Driver Class Definition
//
class TFMini {
  public:
    TFMini(void);

    // Configuration
    boolean begin(Stream* _streamPtr);
    void setSingleScanMode();
   
    // Data collection
    uint16_t getDistance();
    uint16_t getRecentSignalStrength();
    void externalTrigger();

  private:
    Stream* streamPtr;
    int state;
    uint16_t distance;
    uint16_t strength;
   
    // Low-level communication
    void setStandardOutputMode();
    void setConfigMode();
    int takeMeasurement();
   
};

Idahowalker

TFMini.cpp:
Code: [Select]

/*
  Arduino driver for Benewake TFMini time-of-flight distance sensor.
  by Peter Jansen (December 11/2017)
  This code is open source software in the public domain.
  THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY
  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
  DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  The names of the contributors may not be used to endorse or promote products
  derived from this software without specific prior written permission.
*/

#include "TFMini.h"

// Constructor
TFMini::TFMini() {
  // Empty constructor
}


boolean TFMini::begin(Stream* _streamPtr) {
  // Store reference to stream/serial object
  streamPtr = _streamPtr;

  // Clear state
  distance = -1;
  strength = -1;
  state = READY;

  // Set standard output mode
  setStandardOutputMode();

  return true;
}


// Public: The main function to measure distance.
uint16_t TFMini::getDistance() {
  int numMeasurementAttempts = 0;
  while (takeMeasurement() != 0) {
    numMeasurementAttempts += 1;
    if (numMeasurementAttempts > TFMINI_MAX_MEASUREMENT_ATTEMPTS) {
      Serial.println ("TF Mini error: too many measurement attempts");
      Serial.println ("Last error:");
      if (state == ERROR_SERIAL_NOHEADER)     Serial.println("ERROR_SERIAL_NOHEADER");
      if (state == ERROR_SERIAL_BADCHECKSUM)  Serial.println("ERROR_SERIAL_BADCHECKSUM");
      if (state == ERROR_SERIAL_TOOMANYTRIES) Serial.println("ERROR_SERIAL_TOOMANYTRIES");
      // Serial.flush();
      state = ERROR_SERIAL_TOOMANYTRIES;
      distance = -1;
      strength = -1;
      return -1;
    }
  }

  if (state == MEASUREMENT_OK) {
    return distance;
  } else {
    return -1;
  }
}

// Public: Return the most recent signal strength measuremenet from the TF Mini
uint16_t TFMini::getRecentSignalStrength() {
  return strength;
}


// Private: Set the TF Mini into the correct measurement mode
void TFMini::setStandardOutputMode() {
  // Set to "standard" output mode (this is found in the debug documents)
  streamPtr->write((uint8_t)0x42);
  streamPtr->write((uint8_t)0x57);
  streamPtr->write((uint8_t)0x02);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x01);
  streamPtr->write((uint8_t)0x06);
}

// Set configuration mode
void TFMini::setConfigMode() {
  // advanced parameter configuration mode
  streamPtr->write((uint8_t)0x42);
  streamPtr->write((uint8_t)0x57);
  streamPtr->write((uint8_t)0x02);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x01);
  streamPtr->write((uint8_t)0x02);
}

// Set single scan mode (external trigger)
void TFMini::setSingleScanMode() {
  setConfigMode();
  // setting trigger source to external
  streamPtr->write((uint8_t)0x42);
  streamPtr->write((uint8_t)0x57);
  streamPtr->write((uint8_t)0x02);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x40);
}

// Send external trigger
void TFMini::externalTrigger() {
  setConfigMode();
  // send trigger
  streamPtr->write((uint8_t)0x42);
  streamPtr->write((uint8_t)0x57);
  streamPtr->write((uint8_t)0x02);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x00);
  streamPtr->write((uint8_t)0x41);
}

// Private: Handles the low-level bits of communicating with the TFMini, and detecting some communication errors.
int TFMini::takeMeasurement() {
  int numCharsRead = 0;
  uint8_t lastChar = 0x00;

  // Step 1: Read the serial stream until we see the beginning of the TF Mini header, or we timeout reading too many characters.
  while (1) {

    if (streamPtr->available()) {
      uint8_t curChar = streamPtr->read();

      if ((lastChar == 0x59) && (curChar == 0x59)) {
        // Break to begin frame
        break;

      } else {
        // We have not seen two 0x59's in a row -- store the current character and continue reading.
        lastChar = curChar;
        numCharsRead += 1;
      }
    }

    // Error detection:  If we read more than X characters without finding a frame header, then it's likely there is an issue with
    // the Serial connection, and we should timeout and throw an error.
    if (numCharsRead > TFMINI_MAXBYTESBEFOREHEADER) {
      state = ERROR_SERIAL_NOHEADER;
      distance = -1;
      strength = -1;
      if (TFMINI_DEBUGMODE == 1) Serial.println("ERROR: no header");
      return -1;
    }

  }

  // Step 2: Read one frame from the TFMini
  uint8_t frame[TFMINI_FRAME_SIZE];

  uint8_t checksum = 0x59 + 0x59;
  for (int i = 0; i < TFMINI_FRAME_SIZE; i++) {
    // Read one character
    while (!streamPtr->available()) {
      // wait for a character to become available
    }
    frame[i] = streamPtr->read();

    // Store running checksum
    if (i < TFMINI_FRAME_SIZE - 2) {
      checksum += frame[i];
    }
  }

  // Step 2A: Compare checksum
  // Last byte in the frame is an 8-bit checksum
  uint8_t checksumByte = frame[TFMINI_FRAME_SIZE - 1];
  if (checksum != checksumByte) {
    state = ERROR_SERIAL_BADCHECKSUM;
    distance = -1;
    strength = -1;
    if (TFMINI_DEBUGMODE == 1) Serial.println("ERROR: bad checksum");
    return -1;
  }


  // Step 3: Interpret frame
  uint16_t dist = (frame[1] << 8) + frame[0];
  uint16_t st = (frame[3] << 8) + frame[2];
  uint8_t reserved = frame[4];
  uint8_t originalSignalQuality = frame[5];


  // Step 4: Store values
  distance = dist;
  strength = st;
  state = MEASUREMENT_OK;

  // Return success
  return 0;
}

Idahowalker

Decelration:
Code: [Select]
#include "TFMini.h"

setup(){
Code: [Select]
// Initialize the TFMini LIDAR
  tfmini.begin(&SerialTFMini);
  vTaskDelay( pdMS_TO_TICKS( 3 ) );
  // Initialize single measurement mode with external trigger
  tfmini.setSingleScanMode();


Use:
Code: [Select]
void fDoLIDAR( void * pvParameters )
{
  int iLIDAR_Distance = 0;
  while (1)
  {
    xEventGroupWaitBits (eg, evtDoLIDAR, pdTRUE, pdTRUE, portMAX_DELAY) ;
    tfmini.externalTrigger();
    iLIDAR_Distance = tfmini.getDistance();
    if ( iLIDAR_Distance > LIDAR_Max_Distance )
    {
      Serial.print ( " TFMini distance issue " );
      Serial.println (  iLIDAR_Distance );
    }
    xSemaphoreTake( sema_LIDAR_INFO, xSemaphoreTicksToWait );
    x_LIDAR_INFO.Range[x_LIDAR_INFO.CurrentCell] = iLIDAR_Distance;
    xSemaphoreGive( sema_LIDAR_INFO );
    xEventGroupSetBits( eg, evtLIDAR_ServoAspectChange );
  }
  vTaskDelete( NULL );
}

Pezhetairoi

I've had a TFMini running 24/7 for a bit over 13 months. Product works great and, so far, lasts a long time.

The TFMini can be single triggered.

Here are the h and cpp files for single trigger control of the TFMini:
Glad to know this! I think this sensor could help me a lot. But a little question, what is "single trigger mode"? What advantages could bring me instead of continuous. I took a look on Google about the concept, but cannot find a good answer.

Idahowalker

But a little question, what is "single trigger mode"? What advantages could bring me instead of continuous. I took a look on Google about the concept, but cannot find a good answer.
Normal operation of the TFMini is continuous send/receive. My TFMini sits upon a rotating head (Z-axis) that travels a short distance. During time of Z aspect change I do not want the TFMini sending and receiving information nor do I want the TFMini returning data until the running taskings are ready to receive and processes the information. The advantage, for me, is more control. Also, the TFMini runs cooler and uses less power.

Pezhetairoi

Normal operation of the TFMini is continuous send/receive. My TFMini sits upon a rotating head (Z-axis) that travels a short distance. During time of Z aspect change I do not want the TFMini sending and receiving information nor do I want the TFMini returning data until the running taskings are ready to receive and processes the information. The advantage, for me, is more control. Also, the TFMini runs cooler and uses less power.
I understand. It's a discrete type of measuring and at will of user. Thank you very much! I think single mode is what I need too.

MarkT

Narrow beam angle is specialized, standard ultrasonic sensors are 40kHz so cannot have narrow beams unless wired in a phased-array.  40kHz has a wavelength of 8mm, comparable in size to the transducer itself,
for narrow beam the wavelength needs to be a small fraction of transducer diameter, or use an acoustic lens
(impractical in air).
[ I will NOT respond to personal messages, I WILL delete them, use the forum please ]

johnwasser

I would switch to one of the IR distance sensors from Sharp:
http://www.sharp-world.com/products/device/lineup/selection/opto/haca/diagram.html

What are the minimum and maximum heights of your columns?    If the difference is more than 130cm (1.3m) you will need to use the GP2Y0A710K0F which covers a 4.4m range (1.0m to 5.5m).  $19.95 each from DigiKey.com
Send Bitcoin tips to: 1G2qoGwMRXx8az71DVP1E81jShxtbSh5Hp

Go Up