Difference between revisions of "Basketball"

From Mech
Jump to navigationJump to search
Line 68: Line 68:


== Code ==
== Code ==
#include <18f4520.h>
#fuses HS,NOLVP,NOWDT,NOPROTECT
#use delay(clock=40000000)
#use rs232(baud=19200, UART1)


We used two PICs for this project. One ran the motor control since we wanted the smallest interrupt possible for the most accurate speed control. The other PIC ran the servo attached to the turntable as well as the infrared and ultrasonic sensors.
int16 EncCount = 9984; //Number of Encoder Counts per Rev
float pGain =1.25; //Kp for proportional control
The motor was controlled using a PD controller which controlled both speed and position. Theory behind the controller was that if accurate speed control could be achieved along with the ability to control the release point by stopping the motor, we could control the distance the ball traveled to a high degree of accuracy having it follow simple kinematic equations. The launch was triggered when the ultrasonic sensor distance was sent to it. We opted against I2C and instead went with RS-232 for simplicity since we were only sending two bytes of data.
float dGain =3; //Kd for derivative control
signed int32 encoderTarget = 0; //Target for encoder counts to reach
signed int32 error = 0; //EncoderTarget - EncoderActual
signed int32 lastError; //Previous error term
signed int32 encoderActual = 0; //Actual position of shaft in total encoder counts
signed int32 initialCount = 0; //Encoder count when system boots
signed int32 speed = 0; //Speed of rotation 250*speed = encoder counts per second
signed int32 targetSpeed = 0; //What speed should be set to
signed int32 Torque; //PWM value
signed int32 derivative = 0; //Derivative term
signed int32 offset = 0; //Value to set theta = 0 from when system starts
int16 theta=0; //Value between 0->EncCount representing angle from 0 to 360
int16 thetaTarget = 0; //Position Control Target
int16 encoderTargetTheta =0 ; //Theta value of encoderTarget after speed has been added
signed int32 thetaError = 0; // == thetaTarget - theta
int16 upCount = 0, downCount = 0, lastUpCount = 0, lastDownCount = 0; //Encoder count variables
int launching = 0; //Indicates whether arm is on up-swing of launch (1), (0) otherwise
int32 milsecs = 0; //Increments at each interrupt
int rsCount = 0; //Used to track characters coming over RS-232
char c_0 = 0;
char c_1 = 0;
int16 distance =0; //Distance sent to PIC over RS-232
float shotSpeed; //Speed determined by CalculateAndLaunch()


Full source code of the motor controller can be found here:[[Media:Motor-Control.c]].
void LaunchBall(int16 speed, int16 angle);
void CalculateAndLaunch(float dist);


#INT_RDA
#INT_RDA
Line 117: Line 90:
if(rsCount >= 2) rsCount = 0;
if(rsCount >= 2) rsCount = 0;
}
}
This interrupt is called when the hardware RS-232 receives a byte of data. Once two bytes of data are received (int16), the interrupt reassembles them into a single int16 and calls CalculateAndLaunch().



#INT_TIMER2 // designates that this is the routine to call when timer3 overflows
#INT_TIMER2 // designates that this is the routine to call when timer3 overflows

Revision as of 01:40, 20 March 2009

Mechatronics2009Bball

<br=clear all>

Team Members

Team Members from right to left: John, Alex, and Meredith
  • John Rula (Mechanical Engineering, Class of 2009)
  • Alex Wojcicki (Mechanical Engineering, Class of 2009)
  • Meredith Chow (Electrical Engineering, Class of 2010)














Introduction

A throwing arm propelled by a Pittman DC brush motor is mounted on a turntable and throws the ball into the "hoop." The hoop is wrapped in reflective tape and an IR emitter, receiver pair is used to sense where the IR is reflected most (the hoop with highly reflective tape). An ultrasonic sensor then pings the hoop for the distance of the hoop. With this information, the arm is able to "make a basket."

Entire system
Front view
Close up



















Mechanical Design

CAD assembly

The major mechanical components were machined out of clear acrylic using the laser cutter in the machine shop. Holes were machined and threaded as required. The base is a square (12" x 12") with threaded holes for attachment to the purchased turntable bearing. The turntable has threaded holes for attachment to the turntable bearing as well. Large holes were added to the turntable for screw access during assembly because once one of the parts is attached to the turntable, the screws would otherwise not be able to be tightened. The turntable features a rectangular cutout for the servo motor and threaded holes for mounting. There are additional through holes for mounting the sensor bracket and the motor supports.

Two motor supports were designed to accept the Pittman motor and hold it six inches above the turntable surface. Fasteners are inserted from below the turntable into threaded holes in the motor supports. Clamps were designed to thread onto the supports and secure the motor in place. The sensors (IR emitter/receiver pair and ultrasonic sensor) are mounted on a machined acrylic bracket that which is attached to the turntable again through fasteners inserted underneath the turntable.

The arm is also made of laser cut acrylic. It was designed with a close fit on the flat of the motor shaft and with a clamp for tightening using a threaded fastener. For manufacturing reasons, the ball holding portion of the arm was cut out of a separate piece of acrylic and secured to the end of the throwing arm.

The hoop can be anything with the highly reflective tape around it. It is suggested to have a circular profile for better performance detecting its center using the IR emitter/receiver.

Throwing arm attachment close up


Mechanical Components

PartPart No.QtyVendorPrice (Total)
Pittman Motor GM87121Lab-
RC Servo Motor - Futaba S30041Lab-
Acrylic .25" Thick, 24"X 24"8560K3571McMaster$39.63
Corrosion-Resistant Turntable6031K171McMaster$2.42
Fasteners-24Shop supply-
Rubber Feet-4Lab-


Electrical Design

The IR pair, ultrasonic, and servo have a relatively simple circuit. In the diagram below, the IR pair, ultrasonic, and servo are controlled by the program on PIC1. The IR pair and ultrasonic sensor are stacked vertically and inserted into the fitted holes on the laser cut acrylic. Because of the close proximity of the IR pair, to prevent saturation of the IR receiver, the emitter was wrapped in electrical tape. This allowed for the detection of reflected IR rather than from the emitter right below the detector. The RC servo goes through a sweep looking for the hoop. It finds the hoop by storing the location of the highest IR detection. The reflective tape used in Design Challenge 2008 and 2009 was used to wrap the hoop and is used to assist in the finding of the hoop as it reflects more IR radiation than most common objects. After making its sweep, the servo returns to the position where the most IR was detected. The ultrasonic sensor will then ping the hoop to find the distance of the hoop.

The Pittman motor, H-Bridge, and encoder chip circuit is more complex. This circuit is run by PIC2. The information collected about the hoop location is communicated from PIC1 to PIC2. The Pittman is used to launch the ball. After the hoop is found, the arm attached to the Pittman launches the ball into the hoop. The shaft rotates forward and then reverses to its previous position. The device is then ready to find the hoop and launch the ball again.

The two pics communicated with a common ground and wire connecting RC6 and RC7 as shown in the diagram. The power supply was two 12 volt power supplies connected in series to give a total of 24 volts.

Electrical Components

PartPart No.QtyVendorPrice (Total)
PICsPIC18F45202Lab-
EncoderLS70831Lab-
H-BridgeL298N1Lab-
Ping Ultrasonic Sensor280151Lab 5 supply-
IR EmitterQED1231Lab-
IR PhototransistorLTR-4206E1Lab-
Diodes1N41484Lab-
Resistors47.5/150K2Lab-


Circuit Diagram

Circuit diagram of how the the pics, sensors, and motor components are connected.
How the the pics, sensors, and motor components are connected.























Code

We used two PICs for this project. One ran the motor control since we wanted the smallest interrupt possible for the most accurate speed control. The other PIC ran the servo attached to the turntable as well as the infrared and ultrasonic sensors.

The motor was controlled using a PD controller which controlled both speed and position. Theory behind the controller was that if accurate speed control could be achieved along with the ability to control the release point by stopping the motor, we could control the distance the ball traveled to a high degree of accuracy having it follow simple kinematic equations. The launch was triggered when the ultrasonic sensor distance was sent to it. We opted against I2C and instead went with RS-232 for simplicity since we were only sending two bytes of data.

Full source code of the motor controller can be found here:Media:Motor-Control.c.

#INT_RDA
void INTRDAisr() {
 
   if(kbhit()) {
      if(rsCount == 0) c_0 = getc();
      else if(rsCount == 1) {
         c_1 = getc();
         distance = (int16)c_1 + ((int16)c_0<<8);
         CalculateAndLaunch((float)distance);
      }
   }
 
   rsCount++;
   if(rsCount >= 2) rsCount = 0;  
}

This interrupt is called when the hardware RS-232 receives a byte of data. Once two bytes of data are received (int16), the interrupt reassembles them into a single int16 and calls CalculateAndLaunch().


#INT_TIMER2       // designates that this is the routine to call when timer3 overflows
void Timer2isr() {
  milsecs++;
  if ((milsecs & 7) == 0) {      // servo routine every 4ms is plenty.  250x/sec
    //update counters and encoder
    upCount = get_timer0();
    encoderActual += upCount - lastUpCount; //NEW HERE (REMOVED (SIGNED INT16 CAST)
    downCount = get_timer1();
    encoderActual -= downCount - lastDownCount; //NEW HERE SAME AS ABOVE
    lastUpCount = upCount;
    lastDownCount = downCount;
    theta = (int16)(encoderActual - offset) % EncCount;
    thetaError = (signed int32)thetaTarget- (signed int32)theta;
    //speed controls whether it is in forward or reverse
    if(thetaError > 0) speed = targetSpeed;
    else if(thetaError < 0) speed = -targetSpeed;
    encoderTarget += speed; //need to add accuracy to speed
    encoderTargetTheta = (int16)(encoderTarget) % EncCount;
    if(encoderTargetTheta > thetaTarget && speed > 0) {
       encoderTarget -= encoderTargetTheta - thetaTarget;
       targetSpeed = 0;
    } else if(encoderTargetTheta < thetaTarget && speed < 0) {
       encoderTarget += thetaTarget - encoderTargetTheta;
       targetSpeed = 0;
    }
    //do calculations for PD controller with new error
    error = encoderTarget - (encoderActual - initialCount);
    derivative = error - lastError;
    lastError = error;
   
    //At different speeds, need different gains, higher kps and kds for higher speeds ect...
    if(speed <= 5) {
        Torque = 3*error + 2*derivative;
    } else
    if (speed <= 75) {
        Torque = 1.25 * error + 3 * derivative;     
    } else if (speed <= 100) {
        Torque = 2 * error + 6 * derivative;     
    } else {  //speed > 100
        Torque = pGain * error + dGain * derivative;
    }
    if(error > 0) {   //going in the CCW direction, Torque positive
        output_low(PIN_C4);
    }
    else if(error < 0) {
        //invert for PWM
        Torque = 624 + Torque;
        output_high(PIN_C4);
        if(launching == 1 && thetaError < 0) { //used to tune when launching
            launching = 0;
            thetaTarget = 300;
            targetSpeed = 10;
        }
    }
    if(Torque < 0) Torque = 0;
    if(Torque > 624) Torque = 624;
    set_pwm1_duty(Torque);
   
  }
}
void main() {
  setup_timer_2(T2_DIV_BY_4, 78, 16);
  setup_timer_0(RTCC_EXT_L_TO_H | RTCC_DIV_1);     //Counter used for Encoder A -- Up Count
  setup_timer_1(T1_EXTERNAL | T1_DIV_BY_1);     //Counter used for Encoder B -- Down Count
  //setup offsets and intialize encoders
  upCount = get_timer0();
  downCount = get_timer1();
  lastUpCount = upCount;
  lastDownCount = downCount;
 
  encoderActual += (upCount - downCount);

  initialCount = encoderActual;

  offset = encoderActual%EncCount;
  if(encoderActual < 0) offset = -offset;
  enable_interrupts(INT_TIMER2);
  enable_interrupts(INT_RDA);
  enable_interrupts(GLOBAL);
  ext_int_edge(0, H_TO_L);      // interrupt on INT0/RB0 pin, low to high transition
  setup_ccp1(CCP_PWM);
  set_pwm1_duty(0);
  output_low(PIN_C4);
  delay_us(10);

  while(TRUE) {
     delay_ms(5000);
  }
}
void LaunchBall(int16 speed, int16 angle) {
   targetSpeed = speed;
   thetaTarget = angle;
   launching=1;
}
void CalculateAndLaunch(float dist) {
   dist = dist+30; //adjust for distance between ranger and motor
   shotSpeed = -0.0012*(dist*dist) + 0.5884*dist + 89.678;
   LaunchBall((int16)(shotSpeed/2), 3000);
}

Results

In the end, the project ultimately succeeded -- baskets could be made. However, better tuning for the motor control and needs to be developed for more consistent results.

LINK TO VIDEO HERE

Problems Encountered

  • Attempting to integrate multiple pics and circuits onto one circuit board did not give good results. Ultimately, we had to use separate boards for each pic. This could be due to the effect of motor noise on our control circuitry.
  • Motor control was an intense programming effort -- almost a project in its own right. Trying to combine the motor control and calibrate our throwing distance was difficult. Designing a functioning throwing device along with getting the rest of the project implemented was difficult to complete with the given time constraints.
  • Many components burned out. Fly back diodes were implemented however, we still went through quite a few H-bridges. Encoder chips also burned out. The PING sensor burned out as well. We are not sure of the cause of some of these part failures.
  • The length of wires for the Encoder A and B should be limited. When using long wires running from the encoder, we were not able to get our encoder chip to function properly. Replacing them with shorter wires fixed the issue. Because the signal from the encoder is switching at such high speeds, the loss in the long wires played a significant effect resulting in a faulty encoder count. Larger diameter wires may be an alternate solution to this problem.

Notes

A few things we might change if we did it again:

  • Simplify the motor control portion of the project. Trying to implement a speed control using the encoder count was difficult to implement. Simply using PWM to drive the motor at different speeds may have been sufficient.
  • Test different wires for length and diameter for optimal performance for the Encoder A and B connections.
  • Combine the PICs on one circuit board, solving the issues we encountered when attempting this.
  • Perform a more detailed calibration, adjusting starting position, final launching position, and speed to get improved control over throwing distance.