DC-motors (Part 1)

Part 1, 2, 3

DC-motors are every where in many form factors, many performances and configurations. The aim of this series of posts is not to cover all aspects of DC-motors as there are so many publications available, most of them being of excellent quality.

Firstly, I will concentrate on the most common brushed DC-motors . Next picture illustrates a typical DC-motor of this kind.

dc_motor_1

And here is a picture of the dismantled motor. From left to right, the  brushes holder flange, the rotor and the cage holding the two magnets

dc_motor_2

Next is a picture of the rotor showing the collector and the three magnetic poles

dc_motor_3

You may find these types of motors in many, many applications, from hair dryers, to telephone vibrators, through cordless drills, toys and servo-motors. On one hand these motors are cheap, on the other hand they have poor torque and balance.

Feeding these motors is easy as far as loose specifications are concerned. In most applications, a fixed DC voltage is applied to the motor. Over-current protection is provided by a fuse (on the left), or even a thermal fuse (middle or right in the next picture) or by an electronic function when the driver features a voltage regulator.

fuses

Care must be taken with over-currents because the brushes are generally weak and consist in simple copper blades:

dc_motor_4

If we leave apart DC-motors fitted with a tachometer or an optical or magnetic encoder, controlling the speed of a DC-motor is not so easy. A method of choice consists in using PWM (Pulse Width Modulation) to power the motor. Next diagram illustrates the principle of PWM.

pwm

While t is constant, the on/off time ratio is proportional to required rotational speed. Although many publications describe how to wire such a controller, here is the “ultimate” schematic

pwm_driver

R1 is 1 kOhm and T1 is an IRF540 N-Channel MOSFET (or one of its numerous cousins). In my case, VCC was 6 V in order to comply with the motors specifications.

As I plan to go in more details in the next posts, I designed and printed a specific test bench as shown below…

dc_motor_bench_3

… featuring the following components: 2 identical motors, a rubber tubing which acts as a flexible coupling, a barrel which holds a reflective band (for future tests) and the mainframe with its two clips

dc_motor_bench_2

Both DC motors come from a defective CD player (diameter 24 mm, height 20 mm).

Next post on same subject

Incremental rotary encoders (Part 10)

Part 1234567, 8, 9, 10

Let’s have some fun with encoders. As I was thinking about an alternative solution for the stepping motors of our friend Valério’s puppet, I started playing around with some DC motors which are quieter than servo-motors and less greedy and less complex to drive than the steppers. Problem is that DC-motor require additional features for controlling the amount of rotation. And I remembered very, very old projects that used Portescap motors featuring tandem motors: the big one as the real motor and a smaller one acting as a magneto. As the voltage generated by the magneto is proportional to the rotational speed, it performs as a tachometer.

escap_motor

This option is not suitable for my application, but I though about bending the original idea … from a tachometer to an encoder, there are just a few steps to fulfill !

The hardware is very, very simple. R1 and R2 (1 kOhm both), decoupled by C2 (0.1 µF, 10 V) are biasing one end of the motor while its other end is connected to an analog input. The analog reference is wired to the 3.3 output, decoupled by C1 (1.0 µF, 10 V).

 encoder

Next figure illustrates the voltage read at the analog input A0 of arduino:

encoder2

The motor that I used is very popular in the scrap boxes: the motor was disassembled from a CD player. It produces a clean positive or negative +/- 1 V output which is fully compatible with the 3.3 V adc range. An obvious improvement  would consist in protecting the analog input against excessive voltages.

encoder3

The code is very simple and was written in a quick and dirty mode in order to validate the concept.

uint32_t _interval = 250; 	/* In ms */
uint32_t _lastTime; 		/* In ms */
uint16_t _meanValue = 512;
uint8_t _analogPin = 0;
int32_t _counter;


void Calibrate(void)
{
	uint8_t samples = 16;
	_meanValue = 0;
	for (uint8_t i = 0; i < samples; i++) {
		_meanValue += analogRead(_analogPin);
	}
	_meanValue /= samples;
}


void setup(void)
{
	Serial.begin(115200);
	analogReference(EXTERNAL);
	Calibrate();
	Serial.print("Mean value: ");
	Serial.print(_meanValue);
	Serial.println();
	_lastTime = (millis() - _interval);
}


void loop(void)
{
	uint32_t now = millis();
	if ((now - _lastTime) >= _interval) {
		_lastTime = now;
		int16_t val = analogRead(_analogPin);
		val -= _meanValue;
		val /= 3;
		/*  */
		if (abs(val) > 0) {
			_counter += val;
			if(_counter > 0) {
				Serial.print('+');
			}
			Serial.print(_counter);
			Serial.println();			
		}
	}
}

The setup routine initializes the serial port and the analog port. It performs an automatic calibration of the mean voltage which corresponds to the idle mode (Motionless motor). The loop routine periodically reads the analog port and converts the positive and negative swings in digital counts. Simple, cheap and easy! Although there is a lot of room for improvements, starting with the reading of the analog voltage which should be performed in an timed interrupt.

Random number generator (Part 3)

Part 1, 2, 3

The early tests presented in this series of posts were dealing with a limited number of data. The risk exist that a long term repeated pattern may not be seen using these statistical tools. Here is an other approach to testing the randomness of data.

It consists in creating a black and white bitmap made out of millions of data points, each of them representing a unique random value. Although the result is a flat image, using gray scale for each cell from the matrix allows a clean and easy 3D graphical representation of these data points. The size of the matrix depends on the range of the random values. If the random values ranges from 1 to 65536 (so as to say 2^16), the matrix shall be squared and each side will be 256 (so as to say 2^8) cells wide. Each time the random number matches a cell, the content of this cell is increased resulting in brighter pixels. The test stops when a cell achieves the maximum brightness.

Here is a sample from multiple tests that I ran using the previously described hardware random generator:

pixels_map_9

As you can see, this image looks like sand paper without any obvious pattern. Compare it to the next one that I obtained after tweaking the biasing mechanism:

pixels_map_7

Interesting, isn’t it ?

Ultimately, these are the few lines of code that I used in processing for reading data out of Arduino and plotting random values. Oh! By the way, the random() function gives a very… random bit map too !

import processing.serial.*;

Serial myPort;

int _plotWidth;
int _plotHeight;
long _counter = 0;
float _maxBright = 0;
volatile int _inByte;
volatile boolean _readStatus = false;


void setup() 
{
  /* Set graphics */
  size(256, 256);
  _plotWidth = width;
  _plotHeight = height;
  colorMode(RGB, 255);
  background(0); 
  frameRate(60);
  /* Set serial port */
  String portName = "COM29";
  myPort = new Serial(this, portName, 1000000);
}



void draw()
{  
  _counter= 0;
  _maxBright = 0;
  clear();
  background(0); 
  loadPixels();
  while(true) {
    int data = 0;
    for (int i = 0; i < 2; i++) {
      _readStatus = false;
      while (_readStatus == false);
      data <<= 8;
      data |= _inByte;
    }

    // data /= 64;
    /* Get the actual pixel color */
    float actColor = red(pixels[data]);
    float newColor = (actColor + 4.0);
    /* Record maximum brightness */
    if (newColor > _maxBright) {
      _maxBright = newColor;
    }
    /* constrain brightness */
    if (newColor > 255.0) {
      break;
    }
    int c = color(newColor);
    pixels[data] = c;
    _counter += 1;
   
    print(_counter); 
    print(";");
    print(data); 
    //print(";");
    //print((newColor)); 
    print(";");
    print((_maxBright)); 
    println();
  }
  updatePixels();
  save("pixels_map.tif");
  //delay(2000);
  while(true);
}


void serialEvent(Serial myPort) 
{
  _inByte = myPort.read();
   //myPort.clear();
  _readStatus = true;
}

This is real raw code, however it does the job nicely and snappy. Note that the bitmap image is recorded in a compressed lossless TIF format so that it could be analyzed after acquisition.

Next is a copy of the code taken from one of the examples prepared for PlainRNG library. Setting the random number generator is plain easy. This code generates unsigned 2 bytes random numbers for ever and matches the processing code as shown above.

/*

	PlainRNG library, hardware random numbers generator library
	Copyright (C) 2016 Didier Longueville

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
*/

#include <PlainRNG.h>
PlainRNG RNG;

const uint8_t _adcPin = 0;

void setup()
{
	/* Initialize serial port */
	Serial.begin(1000000);
	/* Initialize analog port */
	RNG.InitializeRNG(_adcPin); 
	RNG.Calibrate(32768); 
}


void loop()
{
	uint8_t rn = (RNG.RandomNumber(8, BIA_REM_VON_NEUMANN) & 0xFF);
	Serial.write(rn);
}

 

Enjoy and keep me posted with your comments.

Last minute Xmas present… a storage box!

Time for tidying up my bench ! As electronic components are getting smaller and smaller, the need for suitable storage boxes emerges. Here is the description of an original storage cupboard that I designed for SMDs as well as for very small mechanical parts such as watches spare parts.

Here is the picture captured from FreeCad that I used to draw the box and bins. Each box features 10 bins.

storage_box

As you can see, multiple boxes can be stacked. Each bin fits in a cell and each cell features a spring loaded retaining restraint which prevents the bin from sliding and dropping while moving the storage cupboard. The carving on top is for sliding a  storage map as it is not possible to put labels in front of each bin. Here is a real life illustration of the box printed in 3D:

p1180296s

Do you like it ? Here are the > STL files <.

Random number generator (Part 2)

Part 1, 2, 3

Here is a noisy signal sampled at 100 kHz with an Arduino UNO. The signal has 8 bits of resolution and the divider bridge of the final amplifying stage has been set so that the ADC range matches (without exceeding) the input signal range. Here is the illustration of a vector of 1024 data points sampled from the noise generator.

noise

You may want to download these data in order to perform your own analysis, and I would be more than happy to publish your own results.

Firstly, let’s look at the distribution of data points versus their mean value. To do so, we compute the probability mass function:

pmf

This result is typical from thermal noise. The distribution of the number of data points from each bin looks like a Gaussian distribution. Starting from there it is possible to apply envelope modelers in order to get an overall picture of the distribution. Running an Hilbert transform might overwhelm our arduino UNO so I applied a simplified algorithm which gives pretty good results.

envelope

Now we know that we are really measuring what we expect. Let see if this signal is not contaminated by other signals… To do so, we apply a FFT in order to detect the possible presence of signals characterized by individualized frequencies (and possibly their harmonics) having significant strength (signal to noise ratio). Next is the frequency spectrum from the test signal:

fft

Well, at first glance, there is nothing special to say about it. Is there something near 8 kHz ? Running this test on multiple vectors of data does not confirm this hypothesis. Running the test on extra large vectors of data would bring us to some sort of flat spectrum.

An other interesting analysis tool is the Phase Portrait. Here is a plot of the phase portrait applied to the test data. The spot is centered on the 0;0 coordinate and no repeated trend line shows up from the plot, meaning that we are dealing with real white noise.

phase_portrait

Here and there are very good examples of phase portraits applied to clean and noisy signals.

Next post on same subject

Random number generator (Part 1)

Part 1, 2, 3

As I was stumbling the web looking for advanced information on thermal noise, I found some very interesting papers on RNGs, aka Random Number Generators. These devices feature hardware components which are responsible for a generating unpredictable random numbers !

This is a very interesting matter as it involves physics, electronics, mathematics, software and … ultimately cryptography. Credit is due to people smarter than I am which publications can be found at the end of this post. The idea here is to described a surprisingly simple and yet powerful method for generating random numbers of various length, actually from 1 to 32 bits.

The principle of operation relys on the amplification of the signal produced on the base of a reverse-biased transistor. The emitter is saturated with electrons and occasionally they will “tunnel” through the band gap and exit via the base. This signal is then amplified through two transistors one of them behind biased through a decoupling capacitor in order to get rid of the DC component of the signal.

Here is the electronic section of the project: All you need is a trio of general purpose NPN transistors such as the popular 2N3904 a pair of capacitor and few resistors. The schematic is rather simple.

schematics

I brought a little refinement to the original design by feeding the amplifying stage with a stable +5 V. Note that both Vin and +5 V should be decoupled by 10 µF capacitors (Watch out the operating voltage of the capacitor attached to Vin !). Under these conditions, the circuit outputs a 0 to 1  V signal which fits very well the analog to digital conversion using the 1.1  V internal reference. Please note that 5 V will not suffice to create a reverse bias condition on the base-emetter junction of T1. I managed to get a stable running state starting with Vin = 9 V.

scope

I also brought few refinements to the original code originally written by Rob Seward (v1.0 dated 4/20/2009 ). Firstly, the analog read function is replaced by a custom one which is way faster, thanks to its 8 bits resolution, low prescaler and refined code. the SetADC() function must be inserted within the setup() part of the code

/* Set adc parameters for 8 bits resolution, fast mode */
void SetADC(uint8_t adcChannel) 
{
	/* Clear ADC control registers */
	ADCSRA = 0x00;
	ADCSRB = 0x00;
	ADMUX  = 0x00;
	// ADMUX |= (1 << REFS0); /* DEFAULT 5 V*/
	ADMUX |= (1 << REFS1) | (1 << REFS0); /* INTERNAL 1.1 V */
	ADMUX |= adcChannel; /* Set channel */
	ADMUX |= (1 << ADLAR); /* Left align adc value, so as to say 8 bits resolution */
	ADCSRA |= 3; /* Set ADC prescaler */
	ADCSRA |= (1 << ADEN); /* Enable ADC */
	ReadADC(); /* Run one mock read */
}

inline uint8_t ReadADC(void) 
{
	const uint8_t ADSCmask = (1 << ADSC);
	/* Start conversion */
	ADCSRA |= ADSCmask; 
	/* Wait for conversion */
	while (ADCSRA & ADSCmask);
	/* Read digital value */
	uint8_t result = ADCH;
	/* Returned value */
	return(result);
}

Please note that in spite of its higher sampling rate capacity (about 100 kHz), we are still far from optimal sampling rate (1 Mhz or better). However, that will be enough for an experiment.

I also brought few refinements to the application specific functions:

/* Porcess the input value, so as to say the adc reading */
void ProcessInput(uint8_t adcValue)
{
	/* Set default value */
	uint8_t input = 0;
	if (adcValue > _adcThreshold) {
		input = 1;
	}
	switch(_biasRemovalType){
	case VON_NEUMANN:
		VonNeumann(input); 
		break;
	case EXC_OR:
		ExclusiveOr(input);
		break;
	case NO_BIAS_REMOVAL:
		BuildRandomNumber(input);
		break;
	}
}

/* Apply exclusive or to pairs of bits (thanks to the flip flop flag) */
void ExclusiveOr(uint8_t input)
{
	static uint8_t flipFlop = 0;
	static uint8_t previousInput = 0;
	if (flipFlop) {
		BuildRandomNumber(previousInput ^ input);
	}
	previousInput = input;
	flipFlop = !flipFlop;
}


/* Apply Von Neumann to pairs of bits (thanks to the flip flop flag) */
void VonNeumann(uint8_t input)
{
	static uint8_t flipFlop = 0;
	static uint8_t previousInput = 0;
	if (flipFlop) {
		if (input & ~previousInput){
			BuildRandomNumber(0);
		} else if (~input & previousInput){
			BuildRandomNumber(1); 
		}
	}
	previousInput = input;
	flipFlop = !flipFlop;
}


/* Append new bit to the currently built random number */
void BuildRandomNumber(uint8_t input)
{
	/* Shift previous bits */
	_outputBuffer <<= 1UL;
	/* Append input bit as LSB */
	_outputBuffer |= (input & 0x01);
	/* Update bits counter */
	_bitsCounter += 1;
	if (_bitsCounter == _outputBits) {
		/* Reset bytes counter */
		_bitsCounter = 0;
		/* Output data */
		_mainCounter += 1;
		Serial.print(_mainCounter, DEC);
		Serial.print(';');
		switch(_outputFormat){
		case BINARY:
			Serial.print(_outputBuffer, BIN); 
			break;
		case DECIMAL:
			Serial.print(_outputBuffer, DEC);
			break;
		case HEXADECIMAL:
			Serial.print(_outputBuffer, HEX);
			break;
		}
		Serial.println();
		_outputBuffer = 0x00;
	}
}

/* Record occurences of adc values in their corresponding bins */
void SortAdcValue(uint8_t byteValue)
{
	/* Update occurences counter of the current adc vlaue */
	_vSorter[byteValue] += 1; 
	/* Update counter of sorted values */
	_sortedValuesCounter += 1;
}

/* 
Compute the adc threshold which is the median of the counts of occurences for 
each adc value void 
*/
void ComputeAdcThreshold(void)
{
	uint32_t sum = 0;
	/* Compute the sum of adc occurences in all bins */
	for (uint16_t i = 0; i < _sorterSize; i++){
		sum += _vSorter[i];
	}	
	/* Compute half the sum of all observations */
	uint32_t halfSum = (sum >> 1);
	/* Reset sum */
	sum = 0;
	/* Find the adc value corresponding to half the sum of occurences */
	_adcThreshold = 0;
	while (sum < halfSum) {
		sum += _vSorter[_adcThreshold];
		_adcThreshold += 1;
	} 
}

This is how the code was looking like when I performed early tests which were not so bad in the end !

Next picture illustrates the probability mass function (aka pmf) from 65536 random numbers ranging from 0 to 65535:

pmf

As you can see, the spectrum is pretty homogeneous, showing a fine spreading of random numbers over the expected range. Next is the FFT from the raw data:

fft

Once again, the spectrum is pretty homogeneous, showing no particular recurring pattern. Additional statistics show an average value of 32711.29 which turns to a -0.17 % error. The skewness (symetry of data versus average) is 0.003. All these statistics are very encouraging. However, I might have a use the Dieharder, “A Random Number Test Suite” which looks it is the perfect tool for checking randomness of numbers.

Next steps ? I think that I will pack this material in a library (Why not PlainRNG ?) and run some long term testing…

Links:

Next post on same subject

Trick of the day

This is a funny although very serious one: When you are looking for something very special, pretty complex such as papers on thermodynamics, astronomy, advanced electronics, etc. and you fail to find some information of interest, just insert the “arduino” word to your sequence of searched words.

From my own experience, the chances of finding the information of interest jumps to 90%.

Hum, great, just great  will you say, but “what about the 10% cases when it fails ?”

Well, the information found in these “failing” cases might just strike you as it did recently. I am more and more amazed by what the people manage to get from this insignificantly slow, rough, cheap piece of glass and silicon called arduino.

arduino316

Much more than just a piece of hardware and a bunch of lines of code, this is an awesome concept and the common denominator for a whole social network of makers. Long live arduino !

The word of the day …

Thank you Michel, a senior professor in economy with which we had a pleasant time last Sunday, for talking about Jugaad during talks about news paradigms in many domains.

I like very much this concept, and I ought to spend more time with Dr. Shiv Parakash Rathnam (alias Dr. Shiv) talking about this principle that I realize now he had in mind while we were sharing experiences about R&D.

This principle is very complementary to the Occam’s razor principle (aka law of parsimony) and adds the dimension of frugality which is very common in the world of Fab Labs or even startups.

Some time ago, I heard about the Foldscope (so amazing) without understanging that this was a perfect example of the Jugaad

Figure1-v1

Did you apply Jugaad recently ? I am looking forward to reading your comments soon !

Ultrasonic scanner (Part 2)

Part 1, 2

Most of the electronic diagram shown here should look familiar to the readers of arduinoos posts. The ultrasonic sensor is added to what was described as the test bench for stepper motors.

ultrasound-scanner_1

Here is the list of the required components:

  • An Arduino UNO baord (Should I introduce it to you ?)
    • A bread board featuring a switching voltage regulator
    • A stepper motor driver (in this case a A4988)
  • A stepper motor (Cheap 5 V, 1024 steps per turn stepper motor)
  • A 3D printed base plate and a 3D printed wheel
  • A 12 V DC power supply which current rating is compatible with the stepper motor (Do  not expect to feed the stepper through the USB 5 V!)
  • A HC-SR04 ultrasonic sensor.

Running the whole assembly requires some lines of code. This code features specific commands for the HC-SR04: any working library will do the job. The rest is a mix of stepper motor driving functions and some maths.

Next is an extract from the header section of the sketch:

/* Create objects */
/* Change parameters according to hardware configuration */
PlainHCSR04 SONAR; /* Create an instance of the SONAR object */
const uint32_t _oneSecond = 1000000; /* In us */
uint16_t _frequency = 500; /* Rotational speed */
const uint8_t _pulseDelay = 1; /* In us */
const uint16_t _motorStepsPerRound = 1024;
const float _sweepRad = (TWO_PI / 1.0); 
const float _sweepRadResolution = (TWO_PI / 90.0); 
const uint16_t _motorStepsPerSweepStep = uint16_t((_motorStepsPerRound * _sweepRadResolution) / TWO_PI);
const uint8_t _ultraSoundSensorSamples = 4;
const uint16_t _stabilizationDelay = 10; /* In ms */
/* Direction port and pin */
volatile uint8_t *_dirCtrlPort = &PORTB;
const uint8_t _directionPinMask = (1 << PINB0);
/* Pulse port and pin */
volatile uint8_t *_stepCtrlPort = &PORTB;
const uint8_t _stepPinMask = ( 1 << PINB1);  
/* Enable port and pin */
volatile uint8_t *_enablePort = &PORTB;
const uint8_t _enablePinMask = ( 1 << PINB2);  

Check some of the critical variables:

  • _frequency set the rotational speed
  • _motorStepsPerRound defines the number of steps per round (1024 in my case)
  • _sweepRad defines the full rotational swing for each scan
  • _sweepRadResolution defines the rotational angle between two distance measurements
  • _ultraSoundSensorSamples defines the number of samples acquired for each distance measurements: the more samples the better the accuracy, the longer the measurement time

The set section initializes the ultrasonic sensor and the stepper motor driver:

void setup(void) 
{
	Serial.begin(115200);
	SONAR.HCSR04Initialization(&PORTD, 6, 7);
	/* Set direction ports and pins */
	*(_enablePort - 1) |= _enablePinMask; /* EnableStepper pin */
	*(_enablePort) |= _enablePinMask; /* Disable driver */
	*(_dirCtrlPort - 1) |= _directionPinMask;
	*(_dirCtrlPort) |= _directionPinMask; /* Set default direction */
	*(_stepCtrlPort - 1) |= _stepPinMask; /* Set pin direction */
	*(_stepCtrlPort) &= ~_stepPinMask; /* Set default pin state to LOW */	
	while(Serial.available()) {
		Serial.read(); /* Empty buffer */
	}
}

And here is the loop section which is executed any time any character is entered on the console. The console outputs the data acquired during a whole scan:

void loop(void) 
{
	if (Serial.available()) {
		while(Serial.available()) {
			Serial.read(); /* Empty buffer */
		}
		EnableStepper(1);
		float totalSweepRad = 0.0;
		uint16_t sweepSteps = 0;
		/* Sweep forward */
		do {
			/* Wait for stabilization before acquiring data */
			delay(_stabilizationDelay);
			/* Acquire distance */
			float distance = (SONAR.Distance(_ultraSoundSensorSamples) / 1000.0);
			/* Rotate stepper forward to next sub division */
			RotateStepper(1, _motorStepsPerSweepStep);
			/* Compute angle and cartesian coordinates */
			float x = (sin(totalSweepRad) * distance);
			float y = (cos(totalSweepRad) * distance);
			/* Upload data */
			Serial.print(sweepSteps);
			Serial.print(';');
			Serial.print(totalSweepRad, 3);
			Serial.print(';');
			Serial.print(distance, 3);
			Serial.print(';');
			Serial.print(x, 3);
			Serial.print(';');
			Serial.print(y, 3);
			Serial.println();
			/* Update variables */
			sweepSteps += 1;
			totalSweepRad = (_sweepRadResolution * sweepSteps);
		} while (totalSweepRad <= _sweepRad);
		/* Sweep back */
		RotateStepper(0, (_motorStepsPerSweepStep * sweepSteps));
		EnableStepper(0);
	}
}

The console will output fields delimited with a semi-colon. The first field contains the measurement index. Next two fields are the polar coordinates (angle and distance) while the newt two fields contain the Cartesian coordinates (so as to say x and y). In this way, it is easy to build a graph out of the acquired data such as this one, using any spreadsheet editor:

scanner_6

Enjoy !

 

 

Ultrasonic scanner (Part 1)

Part 1, 2

And now, something completely different : here is arduinoos’s ultrasonic scanner !

scanner_11

This ultrasonic scanner results from the combination of two main components: an ultrasonic sensor and a stepper motor. Both have already been covered in this blog (here and there).

scanner_7

scanner_10

Both component form the head of the scanner which is screwed on top of a basic camera tripod thanks to additional mechatronic components.

These components are very popular and available at cheap prices on the web or straight from your scrap box. An Arduino platform, a voltage regulator and a stepper-motor driver are also required. Once again these are pretty cheap components that you may already have. Both the voltage regulator and the stepper motor driver will be plugged onto a bread board.

scanner_8

Both the arduino board and the bread board sit in a custom shell which fits to the stands of the tripod thanks to a pair of clips…

scanner_9

The principle of operation of this ultrasonic scanner is rather simple. The ultrasonic sensor is mounted directly on top of the shaft of the stepper motor. On each step pulsed to the stepper motor, the ultrasonic sensor performs one (or more) measurements. So that on each completed step, the scanner exports an angular value and a distance. Applying some math results in exporting x and y coordinates too. These data can be used to reconstruct an image of the solid objects surrounding the scanner.

scanner_6

As the stepper that I choose features 1024 steps, the code will manage to set larger angular resolution that the user may change according to his needs. Same for the full swing of the scan, the user may decide to sweep any range from 0 to 360°.

Although this scanner looks like it has been designed for building maps, it can be used for  measuring the actual sonic beam of the sensor too.

Next post on same subject