Advertisements

Kenmore 158: Linearized Speed Control

Plugging the normalized pedal position into the code that turns position into speed:

case PD_RUN :
	if (PedalPosition > 5) {
		if (MotorDrive.State != RUNNING) {
			EnableMotor();
			MotorDrive.State = RUNNING;
		}

		BaseDAC.setVoltage(0x0fff,false);								// give it a solid pulse
		SampleCurrent(PIN_CURRENT_SENSE);								// sample over a half cycle
		if (DriveOnTime > CurrentSamplingTime) {
			delay(DriveOnTime - CurrentSamplingTime);
		}

// Pedal to the metal?
//   ... if so, stall here with motor drive fully on until the pedal releases

		while ((MotorDrive.SpeedRange == SPEED_HIGH) && (PedalPosition >= 100)) {
			PedalPosition = PedalPercent(ReadAI(PIN_PEDAL));
		}

		BaseDAC.setVoltage(0,false);									//  ... then turn it off

		delay(map(constrain(PedalPosition,0,PedalMaxClamp),
				0,100,
				DriveOffTime,0));
	}
	else {
		if (MotorDrive.State == RUNNING) {
			if (MotorSensor.RPM) {
				printf("Coast: %d\r\n",MotorSensor.RPM);
				delay(100);
			}
			else {
				printf("Parking ");
				ParkNeedle(MotorDrive.ParkPosition);
				MotorDrive.State = STOPPED;
				printf(" stopped\r\n");
			}
		}
	}
	break;

The magic happens in highlighted statement, which flips the sense of the pedal motion and linearly scales the result into a delay() value ranging from 120 ms (pedal barely pressed) down to 0 ms (pedal almost fully pressed). If the pedal is all the way down, then the preceding while() locks up until it’s released a bit, whereafter the delay will be nearly zero.

That sorta-kinda worked, but the user community said that the pedal response required pushing too hard for top speed: it should get faster, sooner. The problem came from the simplistic way I set the speed: it was inversely proportional to the position.

Plotting speed against pedal position shows the problem:

Speed vs pedal - period control

Speed vs pedal – period control

I figured the right approach was to make the speed vary linearly with the pedal position, so the trick was to plot the off-time delay vs. the actual speed:

Off-time delay vs speed - period control

Off-time delay vs speed – period control

The second-order equation bottles up a bunch of nonlinear things. Given that this was using the original code, I made the dubious assumption that more-or-less the same delay in the new code would produce more-or-less the same speed.

The new code discards the current-sampling routine that I was using to get a fixed delay (because I don’t need to know the current in pulse-drive mode), then used that time for the floating-point calculation required to find the off-time delay. That chunk of code took a bit of fiddling to get right:

case PD_RUN :
	if (PedalPosition > 5) {
		if (MotorDrive.State != RUNNING) {
			EnableMotor();
			MotorDrive.State = RUNNING;
		}

		BaseDAC.setVoltage(0x0fff,false);								// give it a solid pulse
		MillisOn = millis() + (unsigned long)DriveOnTime;

		TargetSpeed = (float)map(constrain(PedalPosition,0,PedalMaxClamp),
				0,100,
				0,700);						// quick and dirty 0 to 700 stitch/min range
		OffTime = (int)((1.94e-4*TargetSpeed - 0.286)*TargetSpeed + 106.0);
		OffTime = constrain(OffTime,0,120);								// clamp to reasonable range
		MillisOff = MillisOn + (unsigned long)OffTime;					// compute end of off time

		while (millis() <= MillisOn) {									// wait for end of pulse
			continue;
		}

		if ((PedalPosition >= 100) && (MotorDrive.SpeedRange == SPEED_HIGH)) {	// pedal down in full speed mode?
			printf("Full speed ... ");
			OffTime = 0;
			while (PedalPosition >= 100) {								//  ... full throttle!
				PedalPosition = PedalPercent(ReadAI(PIN_PEDAL));
			}
			BaseDAC.setVoltage(0,false);								//  pedal released, start coasting
			printf(" done\r\n");
		}
		else {															// pedal partially pressed
			BaseDAC.setVoltage(0,false);								// pulse done, turn motor off
			while (millis() <= MillisOff) {								// wait for end of off period
				continue;
			}
		}
	}

But the result looks as pretty as you could possibly expect:

Speed vs pedal - linearized speed control

Speed vs pedal – linearized speed control

The pedal still provides a soft-start transition from not moving to minimum speed, which remains an absolute requirement: having an abrupt transition to that straight line would be a Bad Thing. Fortunately, the nature of the magnet moving toward the Hall effect sensor gives you that for free.

Although we’re still evaluating the results, the user community seems happier…

Advertisements

, ,

  1. #1 by madbodger on 2015-02-18 - 08:11

    Now you’ve gone beyond the realm of most commercial products. They’ll just put in whatever slope is easiest and call it done. However, you’re actually evaluating the user experience and feedback, along with the physics of the process, the math behind it, and the amount of calculations your code has time to accomplish. That is real dedication to Getting It Right, which leads to the difference between a merely adequate product and one that’s really satisfying to use.

    • #2 by Ed on 2015-02-18 - 10:02

      And it definitely secures me a spot near the inner edge of the Seventh Circle of Dweebdom. [grin]

      I must measure the original rheostat control’s transfer function, which I should have done up front. If I’ve devoted all this effort to (unknowingly) duplicating that function, well, cue the Happy Dance…

    • #3 by tantris on 2015-02-18 - 11:19

      because in commercial products the user can’t display the same amount of power.

      • #4 by Ed on 2015-02-18 - 18:31

        As the saying goes: “When Momma ain’t happy, ain’t nobody happy!” [grin]