Difference between revisions of "Basketball"

From Mech
Jump to navigationJump to search
Line 83: Line 83:
//update counters and encoder
//update counters and encoder
upCount = get_timer0();
upCount = get_timer0();
encoderActual += upCount - lastUpCount; //NEW HERE (REMOVED (SIGNED INT16 CAST)
encoderActual += upCount - lastUpCount;
downCount = get_timer1();
downCount = get_timer1();
encoderActual -= downCount - lastDownCount; //NEW HERE SAME AS ABOVE
encoderActual -= downCount - lastDownCount;
lastUpCount = upCount;
lastUpCount = upCount;
lastDownCount = downCount;
lastDownCount = downCount;
We used hardware encoders due to the speed of rotation of the shaft.

theta = (int16)(encoderActual - offset) % EncCount;
theta = (int16)(encoderActual - offset) % EncCount;
thetaError = (signed int32)thetaTarget- (signed int32)theta;
thetaError = (signed int32)thetaTarget- (signed int32)theta;
Theta was used for position control. Due to the way we implemented it, the motor was only was only allowed 359.9 degrees of rotation which was suitable for our launching arm.

//speed controls whether it is in forward or reverse
//speed controls whether it is in forward or reverse
if(thetaError > 0) speed = targetSpeed;
if(thetaError > 0) speed = targetSpeed;
else if(thetaError < 0) speed = -targetSpeed;
else if(thetaError < 0) speed = -targetSpeed;
A positive thetaError indicated that the motor needed to move CCW to reach its destination A negative thetaError indicated that the motor needed to move CW to reach its destination.

encoderTarget += speed; //need to add accuracy to speed
encoderTarget += speed; //need to add accuracy to speed
encoderTargetTheta = (int16)(encoderTarget) % EncCount;
encoderTargetTheta = (int16)(encoderTarget) % EncCount;

if(encoderTargetTheta > thetaTarget && speed > 0) {
if(encoderTargetTheta > thetaTarget && speed > 0) {
encoderTarget -= encoderTargetTheta - thetaTarget;
encoderTarget -= encoderTargetTheta - thetaTarget;
Line 106: Line 106:
targetSpeed = 0;
targetSpeed = 0;
}
}
The encoderTargetTheta was used to handle the case of overshoots and was the theta value of the encoderTarget after the speed was added. If that theta value was above/below (depending on direction) the target theta value, it represented that the input had reached its destination, so the speed drops to zero, and the encoderTarget is set to the correct target value.

//do calculations for PD controller with new error
//do calculations for PD controller with new error
error = encoderTarget - (encoderActual - initialCount);
error = encoderTarget - (encoderActual - initialCount);
Line 123: Line 123:
Torque = pGain * error + dGain * derivative;
Torque = pGain * error + dGain * derivative;
}
}
In tuning the PD controller, it was found that different speeds needed different tunings in order to receive a smooth and accurate response. These values represent the best values we were able to obtain in tuning.

if(error > 0) { //going in the CCW direction, Torque positive
if(error > 0) { //going in the CCW direction, Torque positive
output_low(PIN_C4);
output_low(PIN_C4);
Line 137: Line 137:
}
}
}
}
Set PWM values to make motor go forward or reverse. Also, the case where 'launching==1' is to return the arm to a loading position after the arm has shot a ball.

if(Torque < 0) Torque = 0;
if(Torque < 0) Torque = 0;
if(Torque > 624) Torque = 624;
if(Torque > 624) Torque = 624;
Line 144: Line 144:
}
}
}
}
Set the motor's PWM and check for maximum and minimum values. 624 was used instead of 78 for PWM values based on how we set up our timer2.

== PIC to PIC communication with RS-232 ==
We choose to use RS-232 to communicate between PICs because of the small amount of data we were sending (a single int16) and to avoid the overhead required by I2C. We accomplished this by breaking our values into 8-bit chunks cast as chars and sending them one at a time.

sendDistance = avgdistance*100; //distance in cm\
//Break distance into two 8-bit ints and cast as chars to send over RS-232
//Will be reassembled on other pic
putc((char)(sendDistance>>8));
putc((char)(sendDistance & 255));
Code to send value.


#INT_RDA
#INT_RDA
Line 196: Line 208:
}
}
}
}
== Distance Calculation ==

Shot distance was based on a second order polynomial fit we obtained experimentally. The data we collected is shown in the image on the right. [[Image:Distance-Calibration-basketball.png]]
void LaunchBall(int16 speed, int16 angle) {
void LaunchBall(int16 speed, int16 angle) {
targetSpeed = speed;
targetSpeed = speed;
Line 202: Line 215:
launching=1;
launching=1;
}
}

void CalculateAndLaunch(float dist) {
void CalculateAndLaunch(float dist) {
dist = dist+30; //adjust for distance between ranger and motor
dist = dist+30; //adjust for distance between ranger and motor

Revision as of 02:14, 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.

PD Controller

#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; 
    downCount = get_timer1();
    encoderActual -= downCount - lastDownCount;
    lastUpCount = upCount;
    lastDownCount = downCount;

We used hardware encoders due to the speed of rotation of the shaft.

    theta = (int16)(encoderActual - offset) % EncCount;
    thetaError = (signed int32)thetaTarget- (signed int32)theta;

Theta was used for position control. Due to the way we implemented it, the motor was only was only allowed 359.9 degrees of rotation which was suitable for our launching arm.

    //speed controls whether it is in forward or reverse
    if(thetaError > 0) speed = targetSpeed;
    else if(thetaError < 0) speed = -targetSpeed;

A positive thetaError indicated that the motor needed to move CCW to reach its destination A negative thetaError indicated that the motor needed to move CW to reach its destination.

    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;
    }

The encoderTargetTheta was used to handle the case of overshoots and was the theta value of the encoderTarget after the speed was added. If that theta value was above/below (depending on direction) the target theta value, it represented that the input had reached its destination, so the speed drops to zero, and the encoderTarget is set to the correct target value.

    //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;
    }

In tuning the PD controller, it was found that different speeds needed different tunings in order to receive a smooth and accurate response. These values represent the best values we were able to obtain in tuning.

    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;
        }
    }

Set PWM values to make motor go forward or reverse. Also, the case where 'launching==1' is to return the arm to a loading position after the arm has shot a ball.

    if(Torque < 0) Torque = 0;
    if(Torque > 624) Torque = 624;
    set_pwm1_duty(Torque);
   
  }
}

Set the motor's PWM and check for maximum and minimum values. 624 was used instead of 78 for PWM values based on how we set up our timer2.

PIC to PIC communication with RS-232

We choose to use RS-232 to communicate between PICs because of the small amount of data we were sending (a single int16) and to avoid the overhead required by I2C. We accomplished this by breaking our values into 8-bit chunks cast as chars and sending them one at a time.

sendDistance = avgdistance*100; //distance in cm\

//Break distance into two 8-bit ints and cast as chars to send over RS-232
//Will be reassembled on other pic
putc((char)(sendDistance>>8));
putc((char)(sendDistance & 255));

Code to send value.

#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().


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);
  }
}

Distance Calculation

Shot distance was based on a second order polynomial fit we obtained experimentally. The data we collected is shown in the image on the right. Distance-Calibration-basketball.png

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

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.