Freebie LV-MaxSonar-EZ1 Code (Sonar Range Finder)

Disclaimer!! Currently this isn’t tested!

So, apparantly this code wasn’t included in WPILib, so I wrote one myself. I haven’t tested it yet so please use with caution, but it’s primarily copy-pasted from a mashup of gyroscope and accellerometer classes.

Reviews and corrections are welcome. This weekend when we get the thing actually wired I’ll post with any updates if needed.

(this is the h file)


#ifndef ANALOGRANGEFINDER_H
#define ANALOGRANGEFINDER_H

#include "SensorBase.h"
#include "PIDSource.h"

class AnalogChannel;
class AnalogModule;

#define ANALOG_RANGE_FINDER_DEFAULT_INCHES_PER_MV  0.1024F

//Designed for the MaxBotix LV-MaxSonar®-EZ1, but should work for any analog output sonar module
class AnalogRangeFinder : public SensorBase, public PIDSource
{
private:
	float inchesPerMillivolt;
	AnalogChannel *m_analogChannel;
	bool m_allocatedChannel;
	
	void InitAnalogRangeFinder(void)
	{
		inchesPerMillivolt = ANALOG_RANGE_FINDER_DEFAULT_INCHES_PER_MV;
	}
	
public:
	AnalogRangeFinder(UINT8 moduleNumber, UINT32 channel);
	explicit AnalogRangeFinder(UINT32 channel)
	{

		m_analogChannel = new AnalogChannel(channel);
		m_allocatedChannel = true;
		InitAnalogRangeFinder();
	}
	explicit AnalogRangeFinder(AnalogChannel *channel)
	{
		if (channel != NULL)
		{
			m_analogChannel = channel;
			InitAnalogRangeFinder();
			m_allocatedChannel = false;
		}
	}
	explicit AnalogRangeFinder(AnalogChannel &channel)
	{
		m_analogChannel = &channel;
		m_allocatedChannel = false;
		InitAnalogRangeFinder();
	}
	
	virtual ~AnalogRangeFinder()
	{
		if (m_allocatedChannel)
		{
			delete m_analogChannel;
		}
	}
	
	virtual float GetRangeInches()
	{
		return m_analogChannel->GetVoltage() * inchesPerMillivolt;
	}
	void SetSensitivity(float inches_per_millivolt)
	{
		inchesPerMillivolt = inches_per_millivolt;
	}
	
	// PIDSource interface
	double PIDGet()
	{
		return GetRangeInches();
	}
	
};


	
#endif


I would rather have tested this before putting it out here, but seeing as everyones at a crunch for time, I’d rather give someone hope. If this thing happens to be buggy, check back here saturday afternoon/evening and I should have it fixed.

AnalogRangeFinder.h (1.55 KB)


AnalogRangeFinder.h (1.55 KB)

First, you cannot put code like that in a header file. Header files are meant to declare variables and functions, not use them. You have to put all of the initialization and actual code in a .cpp file. Second, you can just set it up like this:


void GetDistance()
{
AnalogChannel *range;
range = new AnalogChannel(1,1);
float volt;
volt = range->GetVoltage();
float inches;
inches = volt * 100;
return inches;
}

Not true. When declaring an object class in a header file, you have an option to include the method bodies in the class definition. We did this throughout all our code. This is because if you separate the definition part in the header file and method bodies in a CPP file, any changes you made to the method interface require you to change two files to match each other. This arrangement is beneficial for releasing libraries without revealing how the methods are implemented (i.e. releasing only the header file without releasing the CPP file). But for our purpose, we don’t have this restriction and therefore, it is a lot simpler to include all method bodies in the class definitions in the header files.

I chose to write this as a class to make it more standardized with other similar sensors (and so I could use it with a PID).

As for all in the h file, my attempt was to just have all of the operations compiled as inline for the sake of simplicity, however it may not be completely legal (my native language is C, so I’m a bit rusty in C++fu). I know you can do this with individual functions, however the whole class may cause issues. I’ll upload it as a cpp and h file just to avoid the confusion/possibility of broke tomorrow.

Thanks

So, I made some fixes to the code, the primary change is that it now (properly) uses an inches/volt conversion factor instead of millivolts (which the analog channel is not providing).

Our range finder isn’t fully working yet, but we think we’re down to an issue with the frame of the robot being slightly within the cone of the audio pulse, and causing some erratic responses.

If you end up using this code, please let me know :smiley: (if nobody finds it helpful, I’ll be alot less likely to make the effort to post this stuff in the future :rolleyes: )

Here’s the C file for those who just want to peak:


#include "AnalogRangeFinder.h"
#include "AnalogModule.h"
#include "AnalogChannel.h"

void AnalogRangeFinder::InitAnalogRangeFinder(void)
{
	inchesPerVolt = ANALOG_RANGE_FINDER_DEFAULT_INCHES_PER_VOLT;
}

AnalogRangeFinder::AnalogRangeFinder(UINT8 moduleNumber, UINT32 channel)
{
	m_analogChannel = new AnalogChannel(moduleNumber,channel);
	m_allocatedChannel = true;
	InitAnalogRangeFinder();
}

AnalogRangeFinder::AnalogRangeFinder(UINT32 channel)
{
	m_analogChannel = new AnalogChannel(channel);
	m_allocatedChannel = true;
	InitAnalogRangeFinder();
}

AnalogRangeFinder::AnalogRangeFinder(AnalogChannel *channel)
{
	if (channel != NULL)
	{
		m_analogChannel = channel;
		InitAnalogRangeFinder();
		m_allocatedChannel = false;
	}
}

AnalogRangeFinder::AnalogRangeFinder(AnalogChannel &channel)
{
	m_analogChannel = &channel;
	m_allocatedChannel = false;
	InitAnalogRangeFinder();
}

AnalogRangeFinder::~AnalogRangeFinder()
{
	if (m_allocatedChannel)
	{
		delete m_analogChannel;
	}
}

float AnalogRangeFinder::GetRangeInches()
{
	return m_analogChannel->GetVoltage() * inchesPerVolt;
}

float AnalogRangeFinder::GetVoltage()
{
	return m_analogChannel->GetVoltage();
}

void AnalogRangeFinder::SetSensitivity(float inches_per_volt)
{
	inchesPerVolt = inches_per_volt;
}


// PIDSource interface
double AnalogRangeFinder::PIDGet()
{
	return GetRangeInches();
}
	

```<br><br><a class='attachment' href='/uploads/default/original/3X/e/7/e76063c18b471cb9ea35d6d7ac6005773494b388.cpp'>AnalogRangeFinder.cpp</a> (1.4 KB)<br><a class='attachment' href='/uploads/default/original/3X/1/4/143e1979609617d1afd6eb2b605dd3552a1bc54b.h'>AnalogRangeFinder.h</a> (1020 Bytes)<br><br><br><a class='attachment' href='/uploads/default/original/3X/e/7/e76063c18b471cb9ea35d6d7ac6005773494b388.cpp'>AnalogRangeFinder.cpp</a> (1.4 KB)<br><a class='attachment' href='/uploads/default/original/3X/1/4/143e1979609617d1afd6eb2b605dd3552a1bc54b.h'>AnalogRangeFinder.h</a> (1020 Bytes)<br>

Just wanted to reply and say that I used you code, and it works very nice… Like you, we have some hardware issues, but I think that I’ve pretty much worked them out. One question I have is that the rangefinder seems to max out at about 250 inches, and not function well in small-mid sized rooms is that what other people are getting?

Been using this code until the other day we hit the sensor against the bridge and that was the end of that. It worked fine before that.

Great :), sorry about your luck, you could try asking teams in your area (if there are any) for a loaner/spare.

I know we’re really not using ours anymore, I found the potential for interference from robots (or the ball return under the hoop) to be too risky to use it. (the drivers also tend to have better luck eyeballing the shot than the pid’s have at getting it right)

How did you apply the sonar on your 2012 robot? We are working with sensors over the summer and program in LabVIEW, but can understand some equivalent C code. We were hoping to use the sonar to backup to a fixed object about 5" away, but the sonar does not update in real time for us and when testing the sensor of the bot, it varies wildly in accuracy. Outside of 6" we seem to have repeatability within an inch. Actually the farther away we get, the more accurate it seems to be. We would appreciate any feedback. Thanks.

It’s been a while since I’ve looked at the sonar sensor, but if I remember right, near objects are not detected very accurately (if the echo returns quickly, it’s likely interfering with itself as it emits the pulse).

We used ours from the key to detect how close the basket was. However, we found that the ball returns caused were being detected in competition rather than the backboard, and due to lack of time on the field to diagnose it, we just dropped the functionality.