Introduction: Virtual Reality Sports

About: Electrical Engineer

I have seen virtual sports systems (mostly for football [soccer]) and I decided to create a cost effective yet reliable version that can be used for a multitude of other sports depending on the needs of the user. The project is based on infrared tracking and algorithms to estimate 3D positioning.

Step 1: Parts List

The two options presented differ only in the way the infrared light is emitted. Both will have similar results so choose according to personal needs or abilities. Those with lesser soldering and/or programming skills may want to refer to option 1 which presents an easier way of producing the desired effect.
Option 1:
Gino CCD CCTV Camera 5mm 48-LED Lamp 60 Degree IR Infrared Board
CCTV CCD Camera LED Board Single Head 2 Pin Connector Power Cable 5pcs
Soccer Ball
Wire
Wiimote
Computer with necessary Software
Velcro
Bluetooth Dongle

Option 2:
Arduino Uno
Printed Circuit Board
25 Infrared Leds
Soccer Ball
Wire
Wiimote
Computer with necessary Software
Velcro
Bluetooth Dongle


Note: Throughout the instructable, the wiimote will be referred to as the infrared tracking camera or infrared senor.

Step 2: Option 1

This option utilizes an infrared array taken from a security camera circuit. When obtaining the camera circuit test it with 1 Amp and 12 volts to make sure the array is functioning properly. This can be done by connecting a 12v power source through the 2 pin connector cable or by directly soldering to the board which can be done with the removal of the plastic casing that acts as the 2 pin connection holder. I had a variable ac-dc adapter that was written to provide 12 volts but did not supply the correct amperage, so I used a .5 amp 5v usb and set the adapter to 7.5 volts to give a total of 12.5 volts 1 amp which worked nicely to illuminate the LEDs. DO NOT however do this as it poses a risk of fire. After a power source is obtained, connect it to ensure the LEDs are illuminated. With this type of LED, a faint red glow should be emitted however by looking through a camera lens, the light becomes much more visible and may seem to glow a slightly violet shade. In order for this light to appear, you must cover the photocell (the "red" component) with duct tape or another substance. Do not remove the component from the board. When you are sure that the array is functioning properly, disconnect the power source and skip ahead to the assembly step as the following step details the assembly of option 2. If the light is not visible, you may not have sufficiently covered the photocell.

Step 3: Option 2

This option requires a bit more soldering skill and some intuitive programming however the final result will perform equally to that of the first option.
Step 1: Solder the 25 LEDs onto the board so that leads can run vertically for ground and horizontally for positive (see images)
Step 2: Run wires vertically to make 5 rows of ground connections
Step 3: Run wires horizontally to make 5 rows of positive connections
Step 4: Attach jumpers or other wires to each endpoint and fit them onto a breadboard
Step 5: Use resistors on the positice leads to protect the LEDs and then connect the resulting leads as seen here
Step 6: Create an arduino program such as the one seen here that uses the quick switching speed of the microprocessor to illuminate the LEDs seemingly at once

Make sure the matrix is fully working before proceeding

Step 4: Assembly

There are another two options from this point.
Here's a visualization:
Option 1- The matrix will be positioned behind the infrared tracking camera and will be bounced off of reflective tape that is placed directly onto the surface of the ball. (Attach either dots or strips of reflective tape to the ball surface.)

Option 2- The matrix will be placed directly onto the surface of the ball (used the bladder of an old 'soccer' ball) directly in front of the infrared tracking camera allowing for more precise tracking but distance is limited to power supply placement. (Attach the soft side of the Velcro to the back of whatever option you used. Attach the rough side to the ball preferably around the needle so that weight displacement is kept relatively normal. You may wish to secure the wires to the ball with an adhesive.)

Step 5: The Setup

The placement of the components of the project is important. To start, place the infrared sensor upside down (as seen in the images) on a surface roughly 9 centimeters high [3.5 inches](or)[two textbooks]. The ball with matrix attached will be placed roughly 30 cm away [1 ft.] facing the sensor so that the LEDs are directed toward the sensor. Since the 'front' of the ball will now be heavier, use a pencil or some other support to keep the ball from rolling forward. With the use of computer software, do this in a place that is both safe to any equipment being used and also near a computer so that changes can be made to the software as needs change.

Step 6: Computer Stuff

For this to work you will need the latest version of GlovePIE (here), the arduino software if you chose option 1 (here), and a game to use the setup with.
First, connect a wiimote to the computer using either built in bluetooth or a bluetooth dongle.
Then, download GlovePIE and create a new file called IRloc.PIE
Input the following code:
if var.loopUntil=0{
var.startX=wiimote1.PointerX
var.startY=wiimote1.PointerY
var.sbd=wiimote1.z
var.ButtonFreezeTime = 250ms
var.PointerBump = KeepDown(Pressed(wiimote1.A),var.ButtonFreezeTime) or KeepDown(Pressed(wiimote1.B),var.ButtonFreezeTime)
Wiimote1.Led1 = true
Wiimote1.Led2 = true
Wiimote1.Led3 = true
Wiimote1.Led4 = true
wait(10ms)
var.difX=(wiimote1.PointerX-var.startX)
var.difY=(wiimote1.PointerY-var.startY)
var.err = 15

if wiimote.dot1vis and wiimote.dot2vis and not var.IgnoreSensorBar {
if var.UsedIR = false {
[ var.Hold_x, var.Hold_y ] = [ 0, 0 ]
[ var.Recenter_x, var.Recenter_y ] = [ 0, 0 ]
var.UsedIR = true
}
var.irDot1_xy = [ (512 - wiimote.dot1x) / 1024, (wiimote.dot1y - 384) / 768 ]
var.irDot2_xy = [ (512 - wiimote.dot2x) / 1024, (wiimote.dot2y - 384) / 768 ]
[ var.irMid_x, var.irMid_y ] = ( var.irDot1_xy + var.irDot2_xy ) / 2
var.irMid_angle = atan2( var.irMid_x, var.irMid_y )
var.irMid_length = [ var.irMid_x, var.irMid_y ]
var.irNew_angle = var.irMid_angle - var.Roll
var.irNew_xy = [ sin(var.irNew_angle) , cos(var.irNew_angle) ] * |var.irMid_length|
[ var.ir_x, var.ir_y ] = var.irNew_xy + [ .5, .5 ]
var.irSpeed_x = var.irSpeed_x * .8 + abs(delta(var.ir_x)) * 1000
var.irSpeed_y = var.irSpeed_y * .8 + abs(delta(var.ir_y)) * 1000
var.irSpeed_xy = [ var.irSpeed_x , var.irSpeed_y ]
if (var.irSpeed_x < 40 and var.wmSpeed_x < 40) {
if ((var.ir_x < var.irMin_x) or (var.MinMax_x = 2 and (var.irMax_x - var.ir_x) >= .15)) and ((var.wmMax_x - var.wm_x) > .075) {
[ var.wmMin_x, var.irMin_x ] = [ var.wm_x, var.ir_x ]
var.MinMax_x = 1
}
if ((var.ir_x > var.irMax_x) or (var.MinMax_x = 1 and (var.ir_x - var.irMin_x) >= .15)) and ((var.wm_x - var.wmMin_x) > .075) {
[ var.wmMax_x, var.irMax_x ] = [ var.wm_x, var.ir_x ]
var.MinMax_x = 2
}
}
if (var.irSpeed_y < 40 and var.wmSpeed_y < 40) {
if ((var.ir_y < var.irMin_y) or (var.MinMax_y = 2 and (var.irMax_y - var.ir_y) >= .15)) and ((var.wmMax_y - var.wm_y) > .075) {
[ var.wmMin_y, var.irMin_y ] = [ var.wm_y, var.ir_y ]
var.MinMax_y = 1
}
if ((var.ir_y > var.irMax_y) or (var.MinMax_y = 1 and (var.ir_y - var.irMin_y) >= .15)) and ((var.wm_y - var.wmMin_y) > .075) {
[ var.wmMax_y, var.irMax_y ] = [ var.wm_y, var.ir_y ]
var.MinMax_y = 2
}
}
if var.WMIR_switch = 1 {
[ var.IR_adj_x , var.IR_adj_y ] = [ var.Old_WM_x, var.Old_WM_y ] - [ var.ir_x, var.ir_y ]
var.WMIR_switch = 0
}
if var.WMIR_switch = 0 {
[ var.Rough_x , var.Rough_y ] = ([ var.ir_x, var.ir_y ] + [ var.IR_adj_x , var.IR_adj_y ] - [ .5, .5 ]) * var.NewZoom + [ .5, .5 ]
[ var.WMIR_x, var.WMIR_y ] = [ var.wmNew_x , var.wmNew_y ] - [ var.ir_x , var.ir_y ]
var.WMIR_xy = [ var.WMIR_x, var.WMIR_y ]
var.IR_adj_c = 1 - EnsureRange(|var.WMIR_xy|, 0, .5) / 5
[ var.IR_adj_x , var.IR_adj_y ] = [ var.IR_adj_x , var.IR_adj_y ] * var.IR_adj_c
}
[ var.WMIR_cal_x, var.WMIR_cal_y ] = [ var.wm_x, var.wm_y ] - [ var.ir_x, var.ir_y ]
else
[ var.Rough_x , var.Rough_y ] = ([ var.wmNew_x, var.wmNew_y ] - [ var.WMIR_x, var.WMIR_y ] + [ var.IR_adj_x , var.IR_adj_y ] - [ .5, .5 ]) * var.NewZoom + [ .5, .5 ]
[ var.Old_WM_x, var.Old_WM_y ] = ([ var.Rough_x , var.Rough_y ] - [ .5, .5 ]) / var.NewZoom + [ .5, .5 ]
var.WMIR_switch = 1
var.WMIR_xy = [ 0, 0 ]
if var.started = true and var.wmCalON = false then [ var.ir_x, var.ir_y ] = ([ var.Smooth_x , var.Smooth_y ] - [ .5, .5 ]) / var.NewZoom + [ .5, .5 ]
}
if(var.loopCount<10){
var.xs=var.StartX
var.ys=var.StartY
}
var.totalDistX=(var.xs-var.StartX)*10
var.totalDistY=(var.ys-var.StartY)*10
var.TDXS=0
var.TDYS=0
var.tx=var.TDXS-var.totalDistX
var.ty=var.TDYS-var.totalDistY
debug = "XDist: "+var.tx+" YDist: "+var.ty+" BDist: "+wiimote.PointerVisible
var.loopCount=var.loopCount+1
if wiimote.PointerVisible=0 {
var.inC=var.inC+1
}
if var.inC>30{
var.loopUntil=.01
}
}
}



var.lr=0
if var.tx>1.75{
var.lr=1
}
if var.tx<-1.75{
var.lr=-1
}
var.ud=-1
if var.ty>1.6
var.ud=0
}
if var.ty>2.6
var.ud=1
}
if var.loopUntil!=0{
debug="LR: "+var.lr+" UD: "+var.ud
var.loopUntil=0
}

if var.inC=31{
if var.lr=-1 AND var.ud=1{
press numpad1
wait 1ms
release numpad1
}
if var.lr=-1 AND var.ud=0{
press numpad2
wait 1ms
release numpad2
}
if var.lr=-1 AND var.ud=-1{
press numpad3
wait 1ms
release numpad3
}
if var.lr=0 AND var.ud=1{
press numpad4
wait 1ms
release numpad4
}
if var.lr=0 AND var.ud=0{
press numpad5
wait 1ms
release numpad5
}
if var.lr=0 AND var.ud=-1{
press numpad6
wait 1ms
release numpad6
}
if var.lr=1 AND var.ud=1{
press numpad7
wait 1ms
release numpad7
}
if var.lr=1 AND var.ud=0{
press numpad8
wait 1ms
release numpad8
}
if var.lr=1 AND var.ud=-1{
press numpad9
wait 1ms
release numpad9
press shift
}
}
if pressed(key.enter){
var.loopUntil=0
var.inC=0
}



Here's a quick explanation of the program and how to use it:
When you first run the program, make sure the infrared sensor can "see" the infrared LEDs emitting light. It starts with a display in the debug panel above that reads " XDist: 0.00 YDist: 0.00 BDist: 1.00"
If the double value 1.00 for BDist shows 0.00 then the infrared sensor cannot detect the infrared light source. After a source is detected, it will calibrate the movement of the ball and output keyboard values 1-9 depending on calculated 3D positioning. When the light source can no longer be seen by the infrared sensor, the debug display will read "LR: VALUE UD: VALUE" where VALUE is replaced by an integer from -1 to 1.
This shows the overall positioning of the "ball" in a 3x3 square grid like the one shown in the diagram.

After that, controlling the glovePIE software is easy. When you run the software, wait until the BDist is at 1 and then kick the ball past the infrared sensor. This will trigger a debug positioning and then in turn trigger a numerical keyboard output of 1-9 depending on the calculated position. You can now integrate this into a game environment by creating a game that utilizes a 9 value input or you can message me for a game I created specifically for this project by clicking here.

Step 7: Modifying the Code (GlovePIE)

Depending on light sources and positioning as well as light intensity, the glovePIE code may need to be modified to fit the specific needs of the user to alter sensitivity.

Take a look at the glovePIE code starting with the section
var.lr=0
if var.tx>1.75{
var.lr=1
}
if var.tx<-1.75{
var.lr=-1
}
var.ud=-1
if var.ty>1.6
var.ud=0
}
if var.ty>2.6
var.ud=1
}



The values 1.75 and 1.6 were those that need to be altered to increase precision and accuracy of the program depending on the situation of the user. The tx value checks are for limiting left and right movement of the ball and the ty value checks are for limiting up and down movement of the ball. By increasing the tx value, the ball will have to travel a greater side-to-side distance before moving out of view of the infrared sensor to allow for the position of the ball to be seen as moving side-to-side and vice versa for lowering the values. The same can be said for the ty values.

Step 8: Done! Now What?

Here comes the part where your creativity comes into play. The applications for this setup are only limited to that of your needs. This system can be reconfigured to serve a multitude of purposes, sports related or not, so don't stop when you've finished. Build on top of this and always continue to learn. If you have any questions feel free to comment or send a message.


Note: This project was based off of the type of sports simulator seen in the image on this step.

Supercharged Contest

Participated in the
Supercharged Contest

Hardware Hacking

Participated in the
Hardware Hacking