<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://hades.mech.northwestern.edu//api.php?action=feedcontributions&amp;feedformat=atom&amp;user=AliceZhao</id>
	<title>Mech - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://hades.mech.northwestern.edu//api.php?action=feedcontributions&amp;feedformat=atom&amp;user=AliceZhao"/>
	<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php/Special:Contributions/AliceZhao"/>
	<updated>2026-04-23T05:08:07Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.35.9</generator>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8649</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8649"/>
		<updated>2008-03-21T10:24:30Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 2 MOSFETs&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic (includes original H-Bridge in design --&amp;gt; use MOSFETs instead)|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; MOSFETs &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The MOSFETs are used as intermediate components between the PIC and the servo motors. It allows the Pittman motors to run in one direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the encoders on the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The L298N H-bridge ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8648</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8648"/>
		<updated>2008-03-21T10:22:10Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 2 MOSFETs&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt;PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; MOSFETs &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The MOSFETs are used as intermediate components between the PIC and the servo motors. It allows the Pittman motors to run in one direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the encoders on the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The L298N H-bridge ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8647</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8647"/>
		<updated>2008-03-21T10:21:41Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 2 MOSFETs&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt; &lt;br /&gt;
&amp;lt;b&amp;gt;PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt; MOSFETs &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The MOSFETs are used as intermediate components between the PIC and the servo motors. It allows the Pittman motors to run in one direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the encoders on the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The L298N H-bridge ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8646</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8646"/>
		<updated>2008-03-21T10:20:48Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 2 MOSFETs&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt; &lt;br /&gt;
&amp;lt;b&amp;gt;PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt; MOSFETs &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The MOSFETs are used as intermediate components between the PIC and the servo motors. It allows the Pittman motors to run in one direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the encoders on the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The L298N H-bridge ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8145</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8145"/>
		<updated>2008-03-20T16:42:38Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Final Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt; &lt;br /&gt;
&amp;lt;b&amp;gt;PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt; H-Bridge &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8144</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8144"/>
		<updated>2008-03-20T16:40:17Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt; &lt;br /&gt;
&amp;lt;b&amp;gt;PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt; H-Bridge &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt;&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = -1&amp;gt;&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8141</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8141"/>
		<updated>2008-03-20T16:39:11Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;font size = +1&amp;gt; &lt;br /&gt;
&amp;lt;b&amp;gt;PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; H-Bridge &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8140</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8140"/>
		<updated>2008-03-20T16:37:22Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; H-Bridge &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8139</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8139"/>
		<updated>2008-03-20T16:26:14Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; H-Bridge &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8138</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8138"/>
		<updated>2008-03-20T16:25:59Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; H-Bridge &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8137</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8137"/>
		<updated>2008-03-20T16:25:43Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; H-Bridge &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8136</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8136"/>
		<updated>2008-03-20T16:25:13Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; H-Bridge &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; Inverting Schmidt Trigger &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8135</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8135"/>
		<updated>2008-03-20T16:24:34Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* PIC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; PIC &amp;lt;/b&amp;gt;&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
===H-Bridge===&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
===Inverting Schmidt Trigger===&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8134</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8134"/>
		<updated>2008-03-20T16:23:39Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===PIC===&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
===H-Bridge===&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
===Inverting Schmidt Trigger===&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8133</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8133"/>
		<updated>2008-03-20T16:23:28Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===PIC===&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
===H-Bridge===&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
===Inverting Schmidt Trigger===&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8132</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8132"/>
		<updated>2008-03-20T16:21:10Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|450px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===PIC===&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
===H-Bridge===&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
===Inverting Schmidt Trigger===&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8131</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8131"/>
		<updated>2008-03-20T16:20:27Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Reflections */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===PIC===&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
===H-Bridge===&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
===Inverting Schmidt Trigger===&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
===Objectives Met===&lt;br /&gt;
* Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
* Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
* Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
===Problems Encountered===&lt;br /&gt;
* (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
* (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
* (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
* (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
===Future Work===&lt;br /&gt;
* More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
* A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
* With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8130</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8130"/>
		<updated>2008-03-20T16:19:53Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Results */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===PIC===&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
===H-Bridge===&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
===Inverting Schmidt Trigger===&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
===Final Design===&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Video===&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8129</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8129"/>
		<updated>2008-03-20T16:18:53Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===PIC===&lt;br /&gt;
The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
===H-Bridge===&lt;br /&gt;
The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
===Inverting Schmidt Trigger===&lt;br /&gt;
The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
===Outline of Code===&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===main()===&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===runsetup()===&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===encodersave()===&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===positioncount()===&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===ircheck()===&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===equalize()===&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===servotrack()===&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8128</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8128"/>
		<updated>2008-03-20T16:16:31Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
* On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
* Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===PIC===&lt;br /&gt;
* The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
===H-Bridge===&lt;br /&gt;
* The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
===Inverting Schmidt Trigger===&lt;br /&gt;
* The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
* The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
* The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8127</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8127"/>
		<updated>2008-03-20T16:16:03Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
* On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
* Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===PIC===&lt;br /&gt;
* The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
===H-Bridge===&lt;br /&gt;
* The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
===Inverting Schmidt Trigger===&lt;br /&gt;
* The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===IR Detector / Emitter Pairs===&lt;br /&gt;
* The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
===Hall Sensors===&lt;br /&gt;
* The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8126</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8126"/>
		<updated>2008-03-20T16:14:46Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===Primary Components===&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Detecting Component===&lt;br /&gt;
* On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===Rotating Component===&lt;br /&gt;
* Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8125</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8125"/>
		<updated>2008-03-20T16:13:54Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
===The primary components used in this device are:===&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===* Detecting Component:===&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
===* Rotating Component:===&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8124</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8124"/>
		<updated>2008-03-20T16:12:09Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8123</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8123"/>
		<updated>2008-03-20T16:11:47Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8122</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8122"/>
		<updated>2008-03-20T16:11:30Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[image:IR_Tracker_Header.jpg|400px|right]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=ME_333_final_projects&amp;diff=8066</id>
		<title>ME 333 final projects</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=ME_333_final_projects&amp;diff=8066"/>
		<updated>2008-03-20T07:42:26Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* IR Tracker */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;#039;&amp;#039;&amp;#039;[[ME 333 end of course schedule]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== ME 333 Final Projects 2008 ==&lt;br /&gt;
&lt;br /&gt;
=== [[IR Tracker]] ===&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Main.jpg|right|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
The IR Tracker (aka &amp;quot;Spot&amp;quot;) is a device that follows a moving infrared light. It continuously detects the position of an infrared emitter in two axises, and then tracks the emitter with a laser. [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 See Spot Run.]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Robot Snake]] ===&lt;br /&gt;
[[Image:HLSSnakeMain.jpg|right|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
This wirelessly controlled robotic snake uses servo motors with a traveling sine wave to  mimic serpentine motion.  The robotic snake is capable of moving forward, left, right and in reverse.   &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Programmable Stiffness Joint]] === &lt;br /&gt;
&lt;br /&gt;
[[Image:SteelToePic.jpg|thumb|200px|The &amp;#039;Steel Toe&amp;#039; programmable stiffness joint|right]]&lt;br /&gt;
&lt;br /&gt;
The Programmable Stiffness Joint varies rotational stiffness as desired by the user.  It is the first step in modeling the mechanical impedance of the human ankle joint (both stiffness and damping) for the purpose of determining the respective breakdown of the two properties over the gait cycle.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Magnetic based sample purification]] ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== [[Continuously Variable Transmission]] ===&lt;br /&gt;
&lt;br /&gt;
[[image:CVT_setup1.jpg|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
A continuously variable tramsission is intended to provide a transition from low to high gear ratios while keeping the engine input running at the max efficient speed. It is achieved by a system of variable radius pulleys and a v-belt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Granular Flow Rotating Sphere]] ===&lt;br /&gt;
&lt;br /&gt;
This device will be used to study the granular flow of particles within a rotating sphere. The sphere is filled with grains of varying size and then rotated about two different axes according to a series of position and angular velocity inputs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Vibratory Clock]] ===&lt;br /&gt;
&lt;br /&gt;
[[Image:Vibratory_Clock.jpg|right|thumb|Vibratory Clock|200px]]&lt;br /&gt;
&lt;br /&gt;
The Vibratory Clock allows a small object to act as an hour &amp;quot;hand&amp;quot; on a horizontal circular platform that is actuated from underneath by three speakers.  The object slides around the circular platform, impelled by friction forces due to the vibration.  [http://www.youtube.com/watch?v=KhgTNCfdwZw Check it out!]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[WiiMouse]] ===&lt;br /&gt;
&lt;br /&gt;
[[Image:HPIM1027.jpg|right|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
The WiiMouse is a handheld remote that can be used to move a cursor on a windows-based PC, via accelerometer input captured through device movement.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Intelligent Oscillation Controller]] ===&lt;br /&gt;
&lt;br /&gt;
[[image:ME333_learning_oscillator.jpg|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
This device &amp;quot;learns&amp;quot; a forcing function that is applied to a spring and mass system to match an arbitrary, periodic acceleration profile.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Baseball]] ===&lt;br /&gt;
&lt;br /&gt;
[[Image:Baseball_Playfield.jpg|right|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
An interactive baseball game inspired by pinball, featuring pitching, batting, light up bases and a scoreboard to keep track of the game.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=ME_333_final_projects&amp;diff=8064</id>
		<title>ME 333 final projects</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=ME_333_final_projects&amp;diff=8064"/>
		<updated>2008-03-20T07:42:02Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* IR Tracker */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;#039;&amp;#039;&amp;#039;[[ME 333 end of course schedule]]&amp;#039;&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
__TOC__&lt;br /&gt;
&lt;br /&gt;
== ME 333 Final Projects 2008 ==&lt;br /&gt;
&lt;br /&gt;
=== [[IR Tracker]] ===&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Main.jpg|right|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
The IR Tracker (aka &amp;quot;Spot&amp;quot;) is a device that follows a moving infrared light. It continuously detects the position of an infrared emitter in two axises, and then tracks the emitter with a laser. [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 See Spot run.]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Robot Snake]] ===&lt;br /&gt;
[[Image:HLSSnakeMain.jpg|right|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
This wirelessly controlled robotic snake uses servo motors with a traveling sine wave to  mimic serpentine motion.  The robotic snake is capable of moving forward, left, right and in reverse.   &lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Programmable Stiffness Joint]] === &lt;br /&gt;
&lt;br /&gt;
[[Image:SteelToePic.jpg|thumb|200px|The &amp;#039;Steel Toe&amp;#039; programmable stiffness joint|right]]&lt;br /&gt;
&lt;br /&gt;
The Programmable Stiffness Joint varies rotational stiffness as desired by the user.  It is the first step in modeling the mechanical impedance of the human ankle joint (both stiffness and damping) for the purpose of determining the respective breakdown of the two properties over the gait cycle.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Magnetic based sample purification]] ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=== [[Continuously Variable Transmission]] ===&lt;br /&gt;
&lt;br /&gt;
[[image:CVT_setup1.jpg|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
A continuously variable tramsission is intended to provide a transition from low to high gear ratios while keeping the engine input running at the max efficient speed. It is achieved by a system of variable radius pulleys and a v-belt.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Granular Flow Rotating Sphere]] ===&lt;br /&gt;
&lt;br /&gt;
This device will be used to study the granular flow of particles within a rotating sphere. The sphere is filled with grains of varying size and then rotated about two different axes according to a series of position and angular velocity inputs.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Vibratory Clock]] ===&lt;br /&gt;
&lt;br /&gt;
[[Image:Vibratory_Clock.jpg|right|thumb|Vibratory Clock|200px]]&lt;br /&gt;
&lt;br /&gt;
The Vibratory Clock allows a small object to act as an hour &amp;quot;hand&amp;quot; on a horizontal circular platform that is actuated from underneath by three speakers.  The object slides around the circular platform, impelled by friction forces due to the vibration.  [http://www.youtube.com/watch?v=KhgTNCfdwZw Check it out!]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[WiiMouse]] ===&lt;br /&gt;
&lt;br /&gt;
[[Image:HPIM1027.jpg|right|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
The WiiMouse is a handheld remote that can be used to move a cursor on a windows-based PC, via accelerometer input captured through device movement.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Intelligent Oscillation Controller]] ===&lt;br /&gt;
&lt;br /&gt;
[[image:ME333_learning_oscillator.jpg|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
This device &amp;quot;learns&amp;quot; a forcing function that is applied to a spring and mass system to match an arbitrary, periodic acceleration profile.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== [[Baseball]] ===&lt;br /&gt;
&lt;br /&gt;
[[Image:Baseball_Playfield.jpg|right|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
An interactive baseball game inspired by pinball, featuring pitching, batting, light up bases and a scoreboard to keep track of the game.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8062</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8062"/>
		<updated>2008-03-20T07:30:55Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8060</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8060"/>
		<updated>2008-03-20T07:30:02Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Electrical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=File:Hall_Sensors.jpg&amp;diff=8059</id>
		<title>File:Hall Sensors.jpg</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=File:Hall_Sensors.jpg&amp;diff=8059"/>
		<updated>2008-03-20T07:29:01Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=File:IR_Detector_Emitter.jpg&amp;diff=8057</id>
		<title>File:IR Detector Emitter.jpg</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=File:IR_Detector_Emitter.jpg&amp;diff=8057"/>
		<updated>2008-03-20T07:28:33Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8054</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8054"/>
		<updated>2008-03-20T07:22:19Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Results */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=File:Circuit_Top_View.jpg&amp;diff=8053</id>
		<title>File:Circuit Top View.jpg</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=File:Circuit_Top_View.jpg&amp;diff=8053"/>
		<updated>2008-03-20T07:20:12Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8052</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8052"/>
		<updated>2008-03-20T07:09:44Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this device are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8051</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8051"/>
		<updated>2008-03-20T07:09:23Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|700px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8050</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8050"/>
		<updated>2008-03-20T07:08:34Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|center|Mechanical Design|thumb|600px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=File:Rotating_Component.jpg&amp;diff=8049</id>
		<title>File:Rotating Component.jpg</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=File:Rotating_Component.jpg&amp;diff=8049"/>
		<updated>2008-03-20T07:07:46Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8048</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8048"/>
		<updated>2008-03-20T07:06:09Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8047</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8047"/>
		<updated>2008-03-20T07:05:56Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[[image:Mechanical_Design.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8046</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8046"/>
		<updated>2008-03-20T07:00:26Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design_left.jpg|center|Mechanical Design|thumb|300px][image:Mechanical_Design.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8045</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8045"/>
		<updated>2008-03-20T06:59:56Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design_left.jpg|center|Mechanical Design|thumb|300px]]&lt;br /&gt;
[[image:Mechanical_Design.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8044</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8044"/>
		<updated>2008-03-20T06:58:22Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Mechanical Design */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design_left.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
[[image:Mechanical_Design.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=File:Mechanical_Design.jpg&amp;diff=8043</id>
		<title>File:Mechanical Design.jpg</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=File:Mechanical_Design.jpg&amp;diff=8043"/>
		<updated>2008-03-20T06:57:32Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8042</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8042"/>
		<updated>2008-03-20T06:32:45Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* &amp;quot;Spot&amp;quot; the IR Tracker */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8041</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8041"/>
		<updated>2008-03-20T06:29:45Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* &amp;quot;Spot&amp;quot; the IR Tracker */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|400px|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8040</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8040"/>
		<updated>2008-03-20T06:28:31Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Results */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;center&amp;gt; &amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project. &amp;lt;/center&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8039</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8039"/>
		<updated>2008-03-20T06:28:04Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Results */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|300px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=File:Final_Design.jpg&amp;diff=8038</id>
		<title>File:Final Design.jpg</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=File:Final_Design.jpg&amp;diff=8038"/>
		<updated>2008-03-20T06:25:36Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
	<entry>
		<id>https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8037</id>
		<title>IR Tracker</title>
		<link rel="alternate" type="text/html" href="https://hades.mech.northwestern.edu//index.php?title=IR_Tracker&amp;diff=8037"/>
		<updated>2008-03-20T06:21:34Z</updated>

		<summary type="html">&lt;p&gt;AliceZhao: /* Results */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==&amp;quot;Spot&amp;quot; the IR Tracker==&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Tracker_Header.jpg|center]]&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:IR_Tracker_Team.jpg|right|Left to right: Matt, Mark and Alice|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Mark Straccia (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Matt Turpin (Mechanical Engineering, Class of 2009)&lt;br /&gt;
* Alice Zhao (Electrical Engineering, Class of 2008)&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The goal of this project was to create a device that would follow a moving infrared light. The IR Tracker does this by continuously detecting the position of an infrared emitter in two axises, and then tracking the emitter with a laser.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The IR Tracker follows two major steps: (1) Identify the position of the IR emitter (2) Rotate the device to align itself with the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
# The IR Tracker finds the location of the maximum infrared intensity in both the x-axis and y-axis. Two encoders continuously record the position of the IR emitter based on intensity levels detected by two IT detectors.&lt;br /&gt;
# Given the information about the location of the IR emitter, the device rotates itself in two dimensions to adjusts its plane to be normal to the IR emitter. At this point, the laser should point to the IR emitter.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
We will discuss the mechanical design, electrical design, PIC code, resulting project and our team reflections.&lt;br /&gt;
&lt;br /&gt;
==Mechanical Design==&lt;br /&gt;
&lt;br /&gt;
[[image:Mechanical_Design.jpg|right|Mechanical Design|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 2 Pitman Motors&lt;br /&gt;
* 2 RC Servos&lt;br /&gt;
* 1 Turntable (6031K17, McMaster-Carr, $2.20, readily available)&lt;br /&gt;
* 1 Cylindrical Bearing&lt;br /&gt;
* 1 Laser&lt;br /&gt;
* Mirror&lt;br /&gt;
* Plexiglas&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Detecting_Component.jpg|right|Detecting Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Detecting Component:&lt;br /&gt;
** On the main plane of the device, two encoders are placed perpendicular to each other (able to track on two axises). Mirrors are placed on the output shafts of the two encoders at 45 degree angles. Two infrared detectors are placed in line with the encoders. While the encoders are constantly spinning, the mirrors detect light from the IR emitter and bounce the light to the IR detectors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[image:Rotating_Component.jpg|right|Rotating Component|thumb|300px]]&lt;br /&gt;
&lt;br /&gt;
* Rotating Component:&lt;br /&gt;
** Below the main plane of the device, two servos are used to rotate the entire device to align the main plane to be normal to the emitter. The main plane is attached to a block of Plexiglas that surrounds a cylindrical bearing. This bearing allows the main plane to rotate along the y-axis. This block of Plexiglas is mounted on a turntable which allows the main plane to rotate along the x-axis.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Electrical Design==&lt;br /&gt;
&lt;br /&gt;
The primary components used in this circuit are:&lt;br /&gt;
&lt;br /&gt;
* 1 PIC (18F4520)&lt;br /&gt;
* 2 IR Detector / Emitter Pairs&lt;br /&gt;
* 2 Hall Sensors &amp;amp; Small Magnets&lt;br /&gt;
* 1 H-Bridge (L298)&lt;br /&gt;
* 1 Inverting Schmidt Trigger (74HC14)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Circuit_Diagram.jpg|left|IR Tracker Schematic|thumb|500px]]&lt;br /&gt;
[[image:Circuit_Top_View.jpg|right|IR Tracker Top View|thumb|500px]]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* PIC&lt;br /&gt;
** The PIC is the central controller of the IR Tracker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:IR_Detector_Emitter.jpg|left|IR Detector|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* IR Detector / Emitter Pairs&lt;br /&gt;
** The IR Detector detects the intensity of IR light coming from the IR Emitter. Over each revolution of the encoder&amp;#039;s output shaft, the location of the maximum IR intensity is recorded. This information is given to the PIC to determine the servo&amp;#039;s next position.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Hall_Sensors.jpg|left|Hall Sensors|thumb|200px]]&lt;br /&gt;
&lt;br /&gt;
* Hall Sensors&lt;br /&gt;
** The Hall Sensors are used as a position marker for the encoders. They are located just below the output shafts of the encoders. A small magnet is placed on the side of the output shafts. As both encoder shafts spin, the Hall Sensor detects the location of the magnet. The purpose of the Hall Sensors is to use this information to sync the rotations of the two encoder shafts.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;br clear=all&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
* H-Bridge&lt;br /&gt;
** The H-Bridge is an intermediate component between the PIC and the servo motors. It allows the servo motors to run forwards and backwards.&lt;br /&gt;
&lt;br /&gt;
* Inverting Schmidt Trigger&lt;br /&gt;
** The Schmidt Trigger acts as an intermediate between both the PIC to the motors and the PIC to the Hall Sensors. It helps filter out noisy signals and invert signals to be readable for the PIC, respectively.&lt;br /&gt;
&lt;br /&gt;
==Code==&lt;br /&gt;
&lt;br /&gt;
[[media:IR_Tracker_Code.c|See full code here]]&lt;br /&gt;
&lt;br /&gt;
Here is a general outline of what the IR Tracker does:&lt;br /&gt;
&lt;br /&gt;
# Spin encoder shafts&lt;br /&gt;
# Record current position of the servo&lt;br /&gt;
# Determine location of the maximum IR intensity detected&lt;br /&gt;
# Convert encoder position to servo position&lt;br /&gt;
# Rotate the main plane to the new servo position&lt;br /&gt;
# Sync rotations of the two encoder shafts&lt;br /&gt;
# Repeat steps 2-6&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
This is the main function of our code:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void main()&lt;br /&gt;
{&lt;br /&gt;
   runsetup();&lt;br /&gt;
 &lt;br /&gt;
   while (TRUE)&lt;br /&gt;
   {&lt;br /&gt;
      encodersave();&lt;br /&gt;
      positioncount();&lt;br /&gt;
      ircheck();&lt;br /&gt;
      equalize();&lt;br /&gt;
   }&lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The five functions it encompasses are described in detail below.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; runsetup() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The interrupts for both servos and Hall sensors are set, and motors begin to spin at full velocity. This function is only called once at the beginning of the main function.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void runsetup()&lt;br /&gt;
{&lt;br /&gt;
   setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);       //External input from quadrature encoder 0&lt;br /&gt;
   setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);          //External input from quadrature encoder 1&lt;br /&gt;
   setup_timer_2(T2_DIV_BY_4, 255, 5);                //Servo incrememnting timer at 1 kHz&lt;br /&gt;
   setup_timer_3(T3_INTERNAL | T3_DIV_BY_4);          //Servo reseting timer&lt;br /&gt;
 &lt;br /&gt;
   enable_interrupts(INT_TIMER2);                     //Enable ISR2 for servo&lt;br /&gt;
   enable_interrupts(INT_TIMER3);                     //Enable ISR3 for servo&lt;br /&gt;
   enable_interrupts(INT_EXT);                        //Enable Interrupt for Hall sensor X&lt;br /&gt;
   enable_interrupts(INT_EXT1);                       //Enable Interrupt for Hall sensor Y&lt;br /&gt;
   enable_interrupts(global);&lt;br /&gt;
 &lt;br /&gt;
   setup_ccp1(ccp_pwm);                               //Setup PWM1&lt;br /&gt;
   setup_ccp2(ccp_pwm);                               //Setup PWM2&lt;br /&gt;
 &lt;br /&gt;
   set_timer0(0);                                     //Clear starting point of counters&lt;br /&gt;
   set_timer1(0);&lt;br /&gt;
 &lt;br /&gt;
   set_PWM1_duty(0);                                  //Motors off for startup&lt;br /&gt;
   set_PWM2_duty(0);&lt;br /&gt;
 &lt;br /&gt;
   for(ii=0;ii&amp;lt;3;ii++)                                //Start up flashing lights&lt;br /&gt;
   {&lt;br /&gt;
      output_d(0x55);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
      output_d(0);&lt;br /&gt;
      delay_ms(100);&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   set_PWM1_duty(1023);                               //Motors start at full velocity&lt;br /&gt;
   set_PWM2_duty(1023);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; encodersave() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This records the current servo positions in the both the x and y direction.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void encodersave()                                    //Counters will return the number of pulses to the inputs.&lt;br /&gt;
{ &lt;br /&gt;
   PositionCountX = get_timer0();                     //Relative position of the X encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
 &lt;br /&gt;
   PositionCountY = get_timer1();                     //Relative position of the Y encoder&lt;br /&gt;
   delay_us(10);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; positioncount() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This uses LEDs to represent where in the rotation the mirrors are, with one LED for each axis. The LEDs are on for half of the rotation and off for the other half.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void positioncount()                                  //Gives visual information about where in the rotation the mirrors are&lt;br /&gt;
{&lt;br /&gt;
   if (PositionCountX &amp;gt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountX&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D3);&lt;br /&gt;
   }&lt;br /&gt;
   if (PositionCountY &amp;gt; 4875)&lt;br /&gt;
   {&lt;br /&gt;
      output_high(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
   else if(PositionCountY&amp;gt;0)&lt;br /&gt;
   {&lt;br /&gt;
      output_low(pin_D4);&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; ircheck() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This finds the location of maximum infrared light in both the x-axis and y-axis. This information is originally collected as an encoder position count value, is converted into an angle in degrees and is then converted again into servo position count value, so that the servo can move to the appropriate location.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void ircheck()                                        //Check to see where in the rotation&lt;br /&gt;
{&lt;br /&gt;
   if(PositionCountX&amp;lt;4875)                            //First half of rotation, facing up&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C6)==0)                            //If IR detector is detecting encoded signal&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D5);                         //Gives visual representation&lt;br /&gt;
         MomentX+=PositionCountX;                     //Increase moment count by position&lt;br /&gt;
         MomentCountX+=1;                             //Increase count of moments&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D5);                          //Clear B5 if mirror is facing down&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountX&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountX&amp;gt;5)) //Runs when this function is called and IR detector&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleX = (MomentX/MomentCountX);         //Average the moment to give the center of IR in terms of motor encoder counts&lt;br /&gt;
&lt;br /&gt;
      if (((AverageAngleX)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleX)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionX = 3647-23*AverageAngleX/34;   //Convert motor encoder counts to servo values&lt;br /&gt;
       &lt;br /&gt;
         if (ServoCurrent[0] + AnglePositionX &amp;lt; 1950) //Less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[0] + AnglePositionX&amp;gt; 4250) //Greater than 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else                                         //between 90 and 450 degrees&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetX = AnglePositionX + ServoCurrent[0] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentX = 0;                                    //reset moment variables&lt;br /&gt;
      MomentCountX = 0;&lt;br /&gt;
   }&lt;br /&gt;
 &lt;br /&gt;
   if(PositionCountY&amp;lt;4875)&lt;br /&gt;
   {&lt;br /&gt;
      if(input(PIN_C5)==0)&lt;br /&gt;
      {&lt;br /&gt;
         output_high(PIN_D6);&lt;br /&gt;
         MomentY+=PositionCountY;   &lt;br /&gt;
         MomentCountY+=1;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         output_low(PIN_D6);&lt;br /&gt;
      }&lt;br /&gt;
   }&lt;br /&gt;
   else if((PositionCountY&amp;gt;=4875)&amp;amp;&amp;amp;(MomentCountY&amp;gt;5))&lt;br /&gt;
   {&lt;br /&gt;
      AverageAngleY = (MomentY/MomentCountY);&lt;br /&gt;
      &lt;br /&gt;
      if (((AverageAngleY)&amp;gt;650)&amp;amp;&amp;amp;((AverageAngleY)&amp;lt;4800))&lt;br /&gt;
      {&lt;br /&gt;
         AnglePositionY = 3647-23*AverageAngleY/34;&lt;br /&gt;
       &lt;br /&gt;
         // find target location in terms of servo counts&lt;br /&gt;
         if (ServoCurrent[1] + AnglePositionY &amp;lt; 1950) //less than 90 degress&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 400;&lt;br /&gt;
         }&lt;br /&gt;
         else if (ServoCurrent[1] + AnglePositionY&amp;gt; 4250) // greater 450 deg&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = 2700;&lt;br /&gt;
         }&lt;br /&gt;
         else&lt;br /&gt;
         {&lt;br /&gt;
            ServoTargetY = AnglePositionY + ServoCurrent[1] - 1550;&lt;br /&gt;
         }&lt;br /&gt;
      }&lt;br /&gt;
      MomentY = 0;&lt;br /&gt;
      MomentCountY = 0;&lt;br /&gt;
   }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; equalize() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This syncs the speed and direction of the rotation of the two encoder motors.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void equalize()                                       //puts motors in phase with each other&lt;br /&gt;
{&lt;br /&gt;
  if(PositionCountX&amp;gt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM1_duty(850);&lt;br /&gt;
     set_PWM2_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
  else if (PositionCountX&amp;lt;PositionCountY)&lt;br /&gt;
  {&lt;br /&gt;
     set_PWM2_duty(850);&lt;br /&gt;
     set_PWM1_duty(1023);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;b&amp;gt; servotrack() &amp;lt;/b&amp;gt;&lt;br /&gt;
&lt;br /&gt;
While this function isn&amp;#039;t in the main function, it is called in one of the interrupts. Its purpose is to slowly move the servos from their current location to its destination.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
void servotrack() //Slowly adjusts the pulsing of the servo to place the plan in the appropriate location&lt;br /&gt;
{&lt;br /&gt;
if(((SlowServoX&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;5))||((SlowServoX&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;125))&lt;br /&gt;
  ||((SlowServoX&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetX-ServoCurrent[0])&amp;gt;450))||SlowServoX&amp;gt;100)  //Can be used to slow down motion of Servos&lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[0] &amp;lt; ServoTargetX - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] + ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[0] &amp;gt; ServoTargetX + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextX = ServoCurrent[0] - ServoMove;&lt;br /&gt;
         ServoCurrent[0] = ServoNextX;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[0] = ServoTargetX;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoX=0;&lt;br /&gt;
   }&lt;br /&gt;
   if(((SlowServoY&amp;gt;15)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;5))||((SlowServoY&amp;gt;8)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;125))&lt;br /&gt;
     ||((SlowServoY&amp;gt;3)&amp;amp;&amp;amp;(abs(ServoTargetY-ServoCurrent[1])&amp;gt;450))||SlowServoY&amp;gt;100)&lt;br /&gt;
     &lt;br /&gt;
   {&lt;br /&gt;
      if (ServoCurrent[1] &amp;lt; ServoTargetY - ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] + ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else if (ServoCurrent[1] &amp;gt; ServoTargetY + ServoMove)&lt;br /&gt;
      {&lt;br /&gt;
         ServoNextY = ServoCurrent[1] - ServoMove;&lt;br /&gt;
         ServoCurrent[1] = ServoNextY;&lt;br /&gt;
      }&lt;br /&gt;
      else&lt;br /&gt;
      {&lt;br /&gt;
         ServoCurrent[1] = ServoTargetY;&lt;br /&gt;
      }&lt;br /&gt;
      SlowServoY=0;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
   SlowServoX++;&lt;br /&gt;
   SlowServoY++;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Results==&lt;br /&gt;
&lt;br /&gt;
Our final design:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[image:Final_Design.jpg|600px|center]]&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;quot;See Spot Run&amp;quot;: Click [http://depot.northwestern.edu/mht363/public_html/IR%20Track/Mar%2019%20IR%20tracker.mp4 here] to see a movie of our final project.&lt;br /&gt;
&lt;br /&gt;
==Reflections==&lt;br /&gt;
&lt;br /&gt;
Overall, our team is very satisfied with our end result.&lt;br /&gt;
&lt;br /&gt;
* Objectives Met:&lt;br /&gt;
** Ability to locate the position of an IR emitter on two axises.&lt;br /&gt;
** Ability to rotate to align its plane to be normal to an IR emitter.&lt;br /&gt;
** Ability to continuously track an IR signal.&lt;br /&gt;
&lt;br /&gt;
* Problems Encountered:&lt;br /&gt;
** (Mechanical) Initially, the device had very shaky movements. We added additional screws to make the device more stable.&lt;br /&gt;
** (Electrical) We added Schmidt Triggers to clean up the noisy signals from the servo motors to the PICs.&lt;br /&gt;
** (Code) Originally coded to track direction of change of the IR emitter and supposed to move accordingly. We encountered a lot of noise. We changed the code to find the exact position of the IR emitter instead.&lt;br /&gt;
** (Electrical) The Schmidt Triggers ended up heating up the entire circuit. In our final design, we chose to use MOSFETS in their place.&lt;br /&gt;
&lt;br /&gt;
* Future Work:&lt;br /&gt;
** More powerful servos and a more accurately calculated counterweight would help smooth the movement of the device.&lt;br /&gt;
** A more sensitive IR detector could track lower intensities of IR light in a room, instead of specifically an IR emitter.&lt;br /&gt;
** With more precise measurements of the relationship between the encoder and servo values, the IR Tracker could more accurately determine the position of an IR signal.&lt;/div&gt;</summary>
		<author><name>AliceZhao</name></author>
	</entry>
</feed>