Friday, November 1, 2013

KY3361ABR-242 Mini Volt Meter

This is a mini review for a volt meter I picked up off eBay a while ago for about $1.50. I don't know that it has a name, so I have decided to call it the KY3362ABR-242 Mini voltmeter. I suspect KY3362ABR-242 actually refers to the display, but regardless it is written on the bottom of the volt meter. That is what I am calling it. As you can see in the pictures, V1303 and DV036A are also printed on the back, but I am ignoring those.

Not a whole lot of information was given with this purchase, but what the seller offered I saved for posterity below.
Specifications:
  • Measuring range: DC 0V--30V
  • Input range: DC 2.5V-30V (Max Input: DC 30V. The device can be damaged if input is over 30V)
  • Display Color: RED
  • Display: Three 0.36 "digital tube
  • Measurement accuracy: 0.1%
  • Refresh rate: about 300mS / times
  • Input impedance: About 100K
  • Dimensions:33mm*15mm*10mm
  • pitch of holes:28mm
  • aperture size :2.8mm
  • Operation Temp: -10℃-+65℃
Wiring:
  • red:power supply +
  • black:power supply -, measure -
  • white:measure +



I soldered on some nice male headers with some heat shrink and got to testing. Here are the photos.

Measuring the 3.3 V supply from my MB-102 power supply

Measuring the 5 V supply from my MB-102 power supply

Measuring the 5 V supply from my Arduino Mega powered over USB.

As you can see, the mini voltmeter consistently read close to the same thing that my craftsman meter read. Granted that is not the best benchmark, but it is what I have. It is also interesting to note that the KY3362ABR-242 can even measure the power supply from which it is being powered.

All in all, this is a very nice little purchase. I would definitely pick one up if you have a need. It is not going to replace your multimeter, but it is a very nice real time display for breadboard power supplies or similar. You can also do cool stuff like hack it into an I2C display.



Friday, October 18, 2013

Simple Face Tracking with OpenCV, myrobotlab GUI, and Arduino

In this post I will detail how to easily use OpenCV with an Arduino to detect and track a face. There are many methods out there, but this method gives you the best OpenCV GUIs that I have seen. Vision processing is never simple, but I think you will be pleasantly surprised.

First things first, I would probably get a lot more Google hits if my title said install OpenCV on Arduino. Many wayward Arduino users have traveled that road only to feel a little foolish when they realize just how impossible that is. An Arduino doesn't come close to cutting it in terms of processing power. There is a project porting OpenCV to a rasberrypi, but that is a different post. What we will be doing is using a computer to process video from a connected camera and then send the useful data to an Arduino in the form of (x,y) coordinates via serial. This is cool because it means that your Arduino can be connected via Bluetooth or USB and can do other things while the computer handles the heavy lifting.

The program that I will be using is called myrobotlab (MRL). I go into more detail in my Intro to myrobotlab post, but basically it is opensource, free, and in active development. When I set out to find an easy, GUI enabled OpenCV platform, this is what I found. While I have not done a full out comparison to ROS or RoboRealm, my general impression is that ROS is quite a bit more complicated, and RoboRealm is quite a bit more expensive (MRL is free). Regardless, you want a simple OpenCV, Arduino system. Let's go get one.

First you need to install myrobotlab. See the Quick Start section for instructions. Make sure you get the right version of Java. I seem to remember having some problems with that. If you do, post a comment or ask GroG on the site. I'd also recommend that you go ahead and create an account on myrobotlab.org. If you need help or have a question, that's where you need to be.

Next open MRL by clicking on the batch file and go to the runtime tab. Here you'll find a list of all the services MRL currently has to offer. Go ahead and scroll down to OpenCV. Right click on it to download and install it.

Then right click on it again and click start. It will want you to give it a name. Call it whatever you want. You do this so you can have multiple instances of the same service open if you want to.

Now you are come to the OpenCV GUI. It will default to camera 0. If you have multiple cameras connected, this might not be the one you want. Click capture to see what camera you have selected, and find the one you want.

Now we need to add our filters. First you need to scroll through and select PyramidDown. Either right click and add or select and hit the right arrow. Give it whatever name you want. PyramidDown makes the video smaller (you'll see). FaceDetect is pretty processor heavy, and unless you have a monster work station you'll want to use it. It will actually increase performance.

Next add the FaceDetect filter. Now click capture and go to town. You should see video in the box and a box drawn around any face. This is a good time to play around. The majority of the filters can be accessed through the GUI. See what filters help you isolate the face(or whatever you are trying to track). Try multiple PyramidDown filters or an InRange filter. Also note that if you right click in the display window, it will give you the coordinates in pixels. This is useful behavior.

Now I hit you with the bait and switch. It's not nice and GUI all the way to the end. Reason being, you need another service (the serial service) to actually get the data from your nice OpenCV box to your Arduino board. This requires you to use the python service.

The code really isn't that bad. I knew no python when I started working with myrobotlab. I still know very little, but I have pieced stuff together well enough from example sketches. You can look at the sketch below, but I would recommend you download it HERE.


# This code creates an opencv service and tracks a face.# It thens finds the center of the face and converts the position to a scale of 1-100# This information is then sent via serial service to any receiving device
# If the script needs to be restarted, completely close MRL and reopen it.# 8/27/13
import time
from java.lang import Stringfrom java.lang import Classfrom java.awt import Rectanglefrom org.myrobotlab.service import Runtimefrom org.myrobotlab.service import OpenCVfrom org.myrobotlab.opencv import OpenCVDatafrom com.googlecode.javacv.cpp.opencv_core import CvPoint;from org.myrobotlab.service import OpenCV
# create or get a handle to an OpenCV serviceopencv = Runtime.createAndStart("opencv","OpenCV")
# Convert the video to Black and Whiteopencv.addFilter("Gray1", "Gray")                             # Sometimes gray seems to help# reduce the size - face tracking doesn't need much detail. The smaller the fasteropencv.addFilter("PyramidDown1", "PyramidDown")# add the face detect filteropencv.addFilter("FaceDetect1", "FaceDetect")

#create a Serial service named serialserial = Runtime.createAndStart("serial","Serial")
# This function is called every time the OpenCV service has data available.# This will depend on the framerate of the video, but will probably be# somewhere around 15 times a second.def input():    global x    global y    global sposx    global sposy    global posx    global posy
    # Get OpenCV data    opencvData = msg_opencv_publishOpenCVData.data[0]
    if (opencvData.getBoundingBoxArray().size() > 0) :    # If the box surrounding a face exists     rect = opencvData.getBoundingBoxArray().get(0)       # Store the information in rect     posx = rect.x                                        # Get the x position of the corner     posy = rect.y                                        # Get the y position of the corner
     w = rect.width                                       # Get the width     h = rect.height                                      # Get the height     sposx = (w/2)     sposy = (h/2)     # Get the x and y of the center in pixels. Origin is in top left corner     x = (posx + sposx)
     y = (posy + sposy) 
     # Convert x,y pixels to (x,y) coordinates from top left.     # Note that 320 and 4240 will need to be changed if another pyramid down is used     # It may also need to be changed depending on your cameras specifications.     # This gets the position in a scale from 1, 100     x = int(translate(x, 1, 320, 1, 100));                  # translate() works the same way the Arduino map() function would     y = int(translate(y, 1, 240, 1, 100));     print 'x: ' ,  x                                        # print x to the python readout     print 'y: ' ,  y                                        # print y to the python readout     #write a series of bytes to the serial port     serial.write(250) # pan code     serial.write(x)   # x coordinate     serial.write(251) # tilt code     serial.write(y)   # y coordinate
 
#connect to a serial port COM15 57600 bitrate 8 data bits 1 stop bit 0 parity#This is what you want for an Arduino. Change the COM port to the one you are using.serial.connect("COM15", 57600, 8, 1, 0)#sometimes its important to wait a little for hardware to get readysleep(1)          # Note that this is 1 full second.
# create a message route from opencv to python so we can see the coordinate locationsopencv.addListener("publishOpenCVData", python.name, "input");
# Start capturing videoopencv.capture()  # Add a 1 inside the parenthesis to use camera 1


# Create function to scale values. Mimics Arduino map() function.def translate(value, leftMin, leftMax, rightMin, rightMax):    # Figure out how 'wide' each range is    leftSpan = leftMax - leftMin    rightSpan = rightMax - rightMin
    # Convert the left range into a 0-1 range (float)    valueScaled = float(value - leftMin) / float(leftSpan)
    # Convert the 0-1 range into a value in the right range.    return rightMin + (valueScaled * rightSpan)


Copy that into the python window and you're almost done. Towards the end you need to change the COM port to the one you are using. Below that, you will see opencv.capture(). If you are using a camera other than camera 0, put that number in the parenthesis.

Now connect your Arduino and click execute. You will see several readouts in the java window. The last one you should see should say
[opencv_videoProcessor] INFO  org.myrobotlab.opencv.VideoProcessor  - using com.googlecode.javacv.OpenCVFrameGrabber
That means you made it to the bottom of the script and you are getting video.

This is what is happening. The OpenCV service applies 3 filters: Gray, PyramidDown, and FaceDetect. It gets a box around the face and passes the coordinates in pixels to the python service. The python service finds the center of the box and the converts the pixel coordinates to a scale of 1-100 with the origin in the top left with positive down and to the right. The python service then passes the coordinates to the Serial service which connects to your COM port and begins sending x, y values across. It sends an identifier, then the value in the order below. Both the identifier and the coordinate are 1 byte in length.
  • 250
  • x coordinate
  • 251
  • y coordinate
What you do with those values on the Arduino side is up to you. That is why I used the serial service. It enables you to plug this system up to an existing Arduino project or any existing Arduino sketch and get vision processing data with nothing more that a Serial.read() and some if statements. Best of all, it is all in Arduino C, so anyone that has learned to program on an Arduino (like myself) can deal with the complicated stuff in a language they already know using the libraries that they already know.

I chose to make a pan-tilt camera mount that follows people, but like I said above, what you do with those (x,y) coordinates is really up to you. If you need help with serial communication, see my posts HERE and HERE. If you just want my pan-tilt Arduino code get it HERE. If you want the detailed description... wait until I write that post.

That's all I have. I plan on doing more specific examples in the future. For now, this should get you started. For more examples look at my labels. The one's labeled myrobotlab or vision processing will be the ones to check out. 

I hope this is of use to someone. I really do think myrobotlab is one of the best free, simple GUI system for OpenCV out there. It is fairly easy to use, and the support is great (just post on the site and ask for help). When I began using it, there was no serial service. I asked for one, and within a week GroG had added one.Who could ask for more?

Goodluck! May your frame rates always be high and your visions always be processed.
-Matthew

Wednesday, September 25, 2013

Combine 2 bytes into int on an Arduino

Recently I have been involved in a project using an iRobot Create. While writing programs for it, I reached an irritating roadblock. The incoming sensor values are transmitted over serial one byte at a time, but the values that actually had meaning were the int values that resulted when the two bytes were combined. Let me clarify.

I receive 2 values, 1 and 213. Now these two numbers are actually stored as 8 binary bits. The values of those bits are 00000001 and 11010101 respectively (HERE is a convenient calculator). The value I want is the 16 bit int variable that results when these two are added together. ie I want the number represented by 0000000111010101; that would be 469. Sounds like I need to do some Arduino data type conversion.

I Googled around and asked one of my computer science friends, but there doesn't appear to be a ready made solution for this. I should point out that if you are reading the value directly from a serial port, you can just use parseInt(). My problem is that I am using an external library to get the sensor data and then need to combine the bytes afterward.

After some deliberation I acquired a list of ways I thought I could combine two 8-bit bytes into one 16- bit int (or short) variable. Some of these ways I have not pursued, but a few I have. I have tested them, and clocked the processing times for a few values on an Arduino Mega2560 r3.

Combination using bitRead(): 108 microseconds
This is the first method I thought of. It reads each bit in the byte and then copies it over to the int. This is not very efficient, but it does get the job done. It does take quite a bit more clock time than the other methods, but it is a rather intuitive approach.

int BitReadCombine( unsigned int x_high, unsigned int x_low)
{
  int x;
  for( int t = 7; t >= 0; t--)
  {
    bitWrite(x, t,  bitRead(x_low, t));
  }
  for( int t = 7; t >= 0; t--)
  {
    bitWrite(x, t + 8,  bitRead(x_high, t));
  }
  return x;
}
Combination using bit shifting: 4 microseconds
In this method, a bit shift operator is applied to move the high byte into the correct position in the final int. This, to me, is the most elegant solution. I don't know that you will find anything much faster. Note that the micros() function has a resolution of 4, so measuring something like this directly is a little difficult. Code modified from HERE.
int BitShiftCombine( unsigned char x_high, unsigned char x_low)
{
  int combined;
  combined = x_high;              //send x_high to rightmost 8 bits
  combined = combined<<8;         //shift x_high over to leftmost 8 bits
  combined |= x_low;                 //logical OR keeps x_high intact in combined and fills in                                                             //rightmost 8 bits
  return combined;
}
Combination using multiplication: 4 microseconds
This method is basically identical to the one above. I read conflicting arguments for whether it would be any slower or not. If it is, it is pretty negligible. Basically, instead of shifting the bits using a bit shift, I  just multiply by 256. This is like if I wanted to shift the "1" in 10 to the 1000th place. I would multiply by 100 (10^2). In binary I want to shift it 8 places, so I multiply the variable by 2^8 or 256.

int MultiplicationCombine(unsigned int x_high, unsigned int x_low)
{
  int combined;
  combined = x_high;
  combined = combined*256;
  combined |= x_low;
  return combined;
}
Other possibilities
It was suggested to me that I should use strings. This seems like a rather roundabout way of getting there, but I would think it would work. You need to combine each byte into a binary string and then concatenate them by adding them together. Then you can convert them back to an int and you're good to go. This could be accomplished several ways with one being the itoa and atoi functions.

Edit: Thanks to Nigel Parker for adding another method and elaborating calculating the speed of each more precisely. In his comment he details how to use the union constructor to combine bytes into different data types. I have saved his code in a sketch available HERE. He notes that this method can be faster than any of the previous methods mentioned when used correctly.

Conclusion
There are probably other methods of combing bytes into an int that I have not looked at. If so, post a link in the comments. That will only broaden the scope of this post. All of the methods here could be adapted to match a 32 bit long if necessary and could be put into an unsigned variable just as easily as a signed one. With that in mind, I will probably use the bit shift method from now on. It seems like the most elegant and efficient solution.

I hope this post helped someone out. If you want my script to check the clock times for yourself, find it HERE. It also might help if you're a little fuzzy on exactly what is going on. If something doesn't work for you, comment below, and I'll do my best to help you.

-Matthew