// ******** Main Loop *********
void controllercycle() {

	double pos_current = (double) ENCODER_ReadPos();
	double speed;
	double rc_speed_signal = (double) (rcData[SERVO_ESC] - SERVO_CENTER);

	/*
	 * Servo AUX is handled always, independent from the mode
	 */
	if (rcData[SERVO_AUX] >= SERVO_MIN && rcData[SERVO_AUX] <= SERVO_MAX) {
		double diff_aux = (double) (rcData[SERVO_AUX] - SERVO_CENTER);
		if (diff_aux < -NEUTRAL_RANGE || diff_aux > +NEUTRAL_RANGE) {     // As long as it remains in the neutral zone input values from the serial protocol take precedence, else we overwrite with rc inpput
			servo[SERVO_AUX] = rcData[SERVO_AUX];
			in_neutral_aux = 0;
		} else if (in_neutral_aux == 0) {
			servo[SERVO_AUX] = SERVO_CENTER;
			in_neutral_aux = 1;
		}
	}


	if (activesettings.mode == MODE_PASSTHROUGH) {
		/*
		 * In passthrough mode we send the input values directly to the output
		 */
		PWM_SetPWM(SERVO_1_OUT, rcData[SERVO_ESC]);
		PWM_SetPWM(SERVO_2_OUT, rcData[SERVO_PITCH]);
		PWM_SetPWM(SERVO_3_OUT, rcData[SERVO_YAW]);
		// We need that to make sure when enabling the MODE_ABSOLUTE, we start slowly
		pos_target = pos_current;
		speed = 0;
	} else {

		/*
		 * Step 1 is to check the pitch and yaw values.
		 * There are multiple cases:
		 * 1. the input is outside the allowed range, e.g. no RC connected or the input mapping is wrong
		 * 2. the stick was moved from outside neutral into neutral, hence the value should be neutral
		 * 3. the stick was in neutral all the time and the value was overwritten using the serial protocol - then we should not change anything here
		 *
		 * The neutral ranges of the stick are compensated, e.g. the stick might say
		 * rc=1521us
		 * center=1500us
		 * NEUTRAL_RANGE=20us
		 * ==> output=1501us
		 *
		 */
		if (rcData[SERVO_PITCH] >= SERVO_MIN && rcData[SERVO_PITCH] <= SERVO_MAX) { // maybe we never got an input because no receiver is connected
			double diff_pitch = (double) (rcData[SERVO_PITCH] - SERVO_CENTER);
			if (diff_pitch < -NEUTRAL_RANGE) {     // As long as it remains in the neutral zone input values from the serial protocol take precedence, else we overwrite with rc inpput
				servo[SERVO_PITCH] = rcData[SERVO_PITCH] + (int16_t)NEUTRAL_RANGE; // Pitch below the neutral zone hence we start to give signal slowly
				in_neutral_pitch = 0;
			} else if (diff_pitch > +NEUTRAL_RANGE) {     // As long as it remains in the neutral zone input values from the serial protocol take precedence, else we overwrite with rc inpput
				servo[SERVO_PITCH] = rcData[SERVO_PITCH] - (int16_t)NEUTRAL_RANGE; // Pitch above the neutral zone hence we start to give signal slowly
				in_neutral_pitch = 0;
			} else if (in_neutral_pitch == 0) {
				servo[SERVO_PITCH] = SERVO_CENTER;
				in_neutral_pitch = 1;
			}
		}

		if (rcData[SERVO_YAW] >= SERVO_MIN && rcData[SERVO_YAW] <= SERVO_MAX) { // maybe we never got an input because no receiver is connected
			double diff_yaw = (double) (rcData[SERVO_YAW] - SERVO_CENTER);
			if (diff_yaw < -NEUTRAL_RANGE) {      // As long as it remains in the neutral zone input values from the serial protocol take precedence, else we overwrite with rc inpput
				servo[SERVO_YAW] = rcData[SERVO_YAW] + (int16_t)NEUTRAL_RANGE; // Yaw below the neutral zone hence we start to give signal slowly
				in_neutral_yaw = 0;
			} else if (diff_yaw > +NEUTRAL_RANGE) {      // As long as it remains in the neutral zone input values from the serial protocol take precedence, else we overwrite with rc inpput
				servo[SERVO_YAW] = rcData[SERVO_YAW] - (int16_t)NEUTRAL_RANGE; // Yaw above the neutral zone hence we start to give signal slowly
				in_neutral_yaw = 0;
			} else if (in_neutral_yaw == 0) {
				servo[SERVO_YAW] = SERVO_CENTER;
				in_neutral_yaw = 1;
			}
		}

		/*
		 * ESC speed is similar with the additional requirement that the stick has to be in neutral at start, else we stay in neutral
		 */
		if (rcData[SERVO_ESC] >= SERVO_MIN && rcData[SERVO_ESC] <= SERVO_MAX) {
			if (rc_speed_signal < NEUTRAL_RANGE && rc_speed_signal > -NEUTRAL_RANGE) { // limit the noise -> everything around the zero point is zero as requested speed
				rc_speed_signal = 0.0;
				if (controller_status == CONTROLLER_INIT) {
					controller_status = CONTROLLER_FIND_END_POS;
				}
			} else if (controller_status == CONTROLLER_INIT ) {
				if (is1Hz()) {
					printDebug_string("We have a speed signal at start! ", DEBUG_LEVEL_ALWAYS);
					printlnDebug_double(rc_speed_signal, DEBUG_LEVEL_ALWAYS);
				}
				rc_speed_signal = 0.0;
			} else if (rc_speed_signal >= NEUTRAL_RANGE) {
				rc_speed_signal -= NEUTRAL_RANGE; // speed signal was e.g. 55ms, we consider +-20ms the NEUTRAL_RANGE hence the stick has been 35ms above neutral
			} else if (rc_speed_signal <= -NEUTRAL_RANGE) {
				rc_speed_signal += NEUTRAL_RANGE;
			}
		} else {
			rc_speed_signal = 0.0;
		}



		/*
		 * The speed is the number of sensor signals per cycle. In MODE_ABSOLUTE_POSITION we define the target speed by increasing the target position.
		 * But in MODE_LIMITER the RC speed signal sets the ESC signal, there is no speed as such. All we know is the current speed by checking the current position.
		 */
		if (activesettings.mode == MODE_LIMITER || activesettings.mode == MODE_LIMITER_ENDPOINTS) {
			speed = pos_current - pos_current_alt;
			pos_target = pos_current;
			if (activesettings.mode == MODE_LIMITER) {
				/*
				 * In this mode some hacking takes place. Imagine the motor stands still. Hence the measured speed from the sensor is zero and
				 * the speed signal from the rc stick is zero.
				 * Now the user cranks the speed signal to the max at once. The ESC starts to accelerate the motor as quickly it can, resulting in a higher
				 * acceleration than allowed. Therefore the speed signal needs to be reduced but to what value? In the absolute mode the error is constantly
				 * calculated and the PID loop tries to adjust the speed signal. But in this mode there is no error value.
				 * Hence two limiters are combined. First the speed signal is not allowed to change abruptly, it is limited to up
				 * to rs_speed_signal_max_acceleration. This allows the ESC to reach terminal velocity for the current speed signal. Now, if that
				 * terminal velocity is too high for the acceleration limiter based on the actual speed, the speed signal is kept at that level for another cycle.
				 * Since there is a relationship between those two acceleration limiters, setting the values needs some care. Best would be a low rc speed signal
				 * acceleration so that the actual acceleration limiter never kicks in. The more aggressive the speed signal is allowed to be changed, the less precise
				 * the actual acceleration can be controlled.
				 */


				// For slow accelerations in this mode, the speed signal has its own acceleration limiter
				if (rc_speed_signal > rc_speed_signal_old + activesettings.rs_speed_signal_max_acceleration) {
					rc_speed_signal = rc_speed_signal_old + activesettings.rs_speed_signal_max_acceleration;
				} else if (rc_speed_signal < rc_speed_signal_old - activesettings.rs_speed_signal_max_acceleration) {
					rc_speed_signal = rc_speed_signal_old - activesettings.rs_speed_signal_max_acceleration;
				}

				// In mode limiter all we can do is keeping the speed signal at the same level as in the previous cycle in case we are too fast
				// we have a speed limiter
				if (speed > max_speed && rc_speed_signal > rc_speed_signal_old) {
					rc_speed_signal = rc_speed_signal_old;
				}
				if (speed < -max_speed && rc_speed_signal < rc_speed_signal_old) {
					rc_speed_signal = rc_speed_signal_old;
				}

				// and an acceleration limiter
				if ((speed - speed_old) > max_acceleration && rc_speed_signal > rc_speed_signal_old) {
					rc_speed_signal = rc_speed_signal_old;
				} else if ((speed - speed_old) < -max_acceleration && rc_speed_signal < rc_speed_signal_old) {
					rc_speed_signal = rc_speed_signal_old;
				}
			}

			/*
			 * One thing might happen here: You drive directly into the end point but the speed sensor got disconnected. Hence speed remains zero and
			 * controller believes it is standing still. In this case we do an emergency brake.
			 */
			if (speed == 0.0 && (rc_speed_signal > 200.0 || rc_speed_signal < -200.0)) {
				rc_speed_signal = 0.0;
				if (is5s()) {
					printlnDebug_string("ERMEGENCY: speed=0 and rc signal is high - there is something severly wrong", DEBUG_LEVEL_VERYHIGH);
				}
			}
		} else if (activesettings.mode == MODE_ABSOLUTE_POSITION) {
			speed = rc_speed_signal / 5.0;
			// we have a speed limiter
			if (speed > max_speed) {
				speed = max_speed;
			}
			if (speed < -max_speed) {
				speed = -max_speed;
			}

			// and an acceleration limiter
			if ((speed - speed_old) > max_acceleration) {
				speed = speed_old + max_acceleration;
			} else if ((speed - speed_old) < -max_acceleration) {
				speed = speed_old - max_acceleration;
			}
		} else {
			// Impossible to reach this code but...
			speed = 0.0;
			pos_target = pos_current;
		}


		pos_target += speed;

		double distance_to_stop = abs_d(speed_old) * (abs_d(speed_old)-max_acceleration) / 2.0f / max_acceleration;
		/*
		   We need to work with the average speed, and that is the motor speed and the new speed at max deceleration

		   speed_old	accel	new_speed	distance_to_stop		old_pos_target	    new_pos_target	old_pos_target+distance_to_stop
				 100	1		99	        4950	                  0	                 99	     	    4950
				  99	1		98	        4851	                 99	                197	     	    4950
				  98	1		97	        4753	                197	                294	      	    4950
				  97	1		96	        4656	                294	                390	      	    4950
				  96	1		95	        4560	                390	                485	      	    4950
				  95	1		94	        4465	                485	                579	     	    4950
				  94	1		93	        4371	                579	                672	     	    4950
				  93	1		92	        4278	                672	                764				4950
				  92	1		91	        4186	                764	                855				4950
				  91	1		90	        4095	                855	                945				4950
				  ..
				  ..
				  ..
		 */

		if (pos_end == POS_END_NOT_SET) {
			if (rc_speed_signal == 0.0 || speed == 0.0) { // either the stick is in neutral or the cablecam does not move by itself
				if (pos_target > 1000.0 || pos_target < -1000.0) {
					if (number_of_idle_cycles > 500) {
						// initially the end_pos is not set (very high) but if the stick was in neutral pos for 10 seconds and we traveled at least 1000 steps forward, then we take this position as final end pos
						setPosStartEnd(pos_target, 0.0);
					} else if (is1Hz()) {
						printDebug_string("If we stay there 10 seconds we have an end pos: pos=", DEBUG_LEVEL_ALWAYS);
						printDebug_double(pos_target, DEBUG_LEVEL_ALWAYS);
						printDebug_string(" wait time=", DEBUG_LEVEL_ALWAYS);
						printlnDebug_int((int) (number_of_idle_cycles/50), DEBUG_LEVEL_ALWAYS);
					}
				} else if (pos_target != 0.0) {
					if (is1Hz()) {
						printDebug_string("This is too close to the start position for being a valid end position: pos=", DEBUG_LEVEL_VERYLOW);
						printlnDebug_double(pos_target, DEBUG_LEVEL_VERYLOW);
					}
				}
				number_of_idle_cycles++;
			} else {
				number_of_idle_cycles = 0;
			}
		} else {
			if (pos_target + distance_to_stop >= pos_end && rc_speed_signal >= 0.0) {
				speed = speed_old - max_acceleration;
				if (speed < 0.0) speed = 0.0; // do brake but do not drive reverse
				rc_speed_signal = 0.0; // in MODE_LIMITER activate the brakes
			} else if (pos_target - distance_to_stop <= pos_start && rc_speed_signal <= 0.0) {
				speed = speed_old + max_acceleration;
				if (speed > 0.0) speed = 0.0;
				rc_speed_signal = 0.0; // in MODE_LIMITER activate the brakes
			}
			if (pos_target > pos_end) {
				pos_target = pos_end;
				if (rc_speed_signal > 0.0) {
					rc_speed_signal = 0.0;
				}
			} else if (pos_target < pos_start) {
				pos_target = pos_start;
				if (rc_speed_signal < 0.0) {
					rc_speed_signal = 0.0;
				}
			}
		}

		if (activesettings.mode == MODE_LIMITER || activesettings.mode == MODE_LIMITER_ENDPOINTS) {
			int16_t s = SERVO_CENTER + (int16_t) (rc_speed_signal);
			/* if (rc_speed_signal < 0.0f) {
				s = (int16_t) (rc_speed_signal - NEUTRAL_RANGE);
			} else if (rc_speed_signal > 0.0f) {
				s = (int16_t) (rc_speed_signal + NEUTRAL_RANGE);
			} else {
				s = 0;
			}
			s += SERVO_CENTER; */
			PWM_SetPWM(SERVO_1_OUT, s);

			if (is5s() && activesettings.debuglevel >= DEBUG_LEVEL_LOW) {
				printDebug_string("target pos=", DEBUG_LEVEL_LOW);
				printDebug_double(pos_target, DEBUG_LEVEL_LOW);
				printDebug_string(" current pos=", DEBUG_LEVEL_LOW);
				printDebug_double(pos_current, DEBUG_LEVEL_LOW);

				printDebug_string("    speed=", DEBUG_LEVEL_LOW);
				printDebug_double(speed, DEBUG_LEVEL_LOW);
				printDebug_string(" rc_out=", DEBUG_LEVEL_LOW);
				printlnDebug_int(s, DEBUG_LEVEL_LOW);
			}
		} else {
			/* The target position calculation does not care about the physics, all it said was "In an ideal world the target pos should be here,
				 the requested (filtered with max speed and max accel and end point limiters) speed is that, hence the new target pos = target pos + speed."
				 Example:
				   target_pos = 0m;
				   speed = 0m/s
				   speed as provided by the stick is 400/10 = 40m/s
				   --> kick in the accel limiter, 40m/s² is way too high, limited accel to 2m/s²
				   new_speed = 0m/s + 1s*2m/s² = 2m/s
				   target_pos = target_pos + 1s * 2m/s = 0m + 2m

				 In the code below is the PID logic to keep the motor following the target position value.
			 */
			double e, y = 0.0f;


			e = pos_target - pos_current;     // This is the amount of steps the target pos does not match the reality

		    // let's not be overly precise. If the error is below a threshold we are good enough
			// Not sure that is a good idea though, as it turns the control loop function into an even more unsteady curve around neutral.
			if (e >= -activesettings.tolerated_positional_error_limit && e <= activesettings.tolerated_positional_error_limit) {
				e = 0.0;
			}

			y = yalt + Q0*e + Q1*ealt + Q2*ealt2;         // PID loop calculation

			if (is5s() && activesettings.debuglevel >= DEBUG_LEVEL_VERYLOW) {
				printDebug_string("target pos=", DEBUG_LEVEL_VERYLOW);
				printDebug_double(pos_target, DEBUG_LEVEL_VERYLOW);
				printDebug_string(" current pos=", DEBUG_LEVEL_VERYLOW);
				printDebug_double(pos_current, DEBUG_LEVEL_VERYLOW);

				printDebug_string("    esc=", DEBUG_LEVEL_VERYLOW);
				printDebug_double(y, DEBUG_LEVEL_VERYLOW);
				printDebug_string("=", DEBUG_LEVEL_VERYLOW);
				printDebug_double(yalt, DEBUG_LEVEL_VERYLOW);
				printDebug_string(" + ", DEBUG_LEVEL_VERYLOW);
				printDebug_double(Q0, DEBUG_LEVEL_VERYLOW);
				printDebug_string("*", DEBUG_LEVEL_VERYLOW);
				printDebug_double(e, DEBUG_LEVEL_VERYLOW);
				printDebug_string(" + ", DEBUG_LEVEL_VERYLOW);
				printDebug_double(Q1, DEBUG_LEVEL_VERYLOW);
				printDebug_string("*", DEBUG_LEVEL_VERYLOW);
				printDebug_double(ealt, DEBUG_LEVEL_VERYLOW);
				printDebug_string(" + ", DEBUG_LEVEL_VERYLOW);
				printDebug_double(Q2, DEBUG_LEVEL_VERYLOW);
				printDebug_string("*", DEBUG_LEVEL_VERYLOW);
				printlnDebug_double(ealt2, DEBUG_LEVEL_VERYLOW);
			}



			// remove the ESC neutral range
			int16_t esc_speed = (int16_t) y;

			if (e > activesettings.tolerated_positional_error_exceeded || e < -activesettings.tolerated_positional_error_exceeded) {
				// EMERGENCY there is something severely wrong, variable overflow, hall sensor counter, hall sensor direction, anything...
				// hence we stop. User can change the mode to passthrough to move the cablecam
				if (is5s()) {
					printDebug_string("ERMEGENCY: target pos=", DEBUG_LEVEL_VERYHIGH);
					printDebug_double(pos_target, DEBUG_LEVEL_VERYHIGH);
					printDebug_string(" current pos=", DEBUG_LEVEL_VERYHIGH);
					printlnDebug_double(pos_current, DEBUG_LEVEL_VERYHIGH);
				}
				resetThrottle();

			} else {
				servo[SERVO_ESC] = esc_neutral_range_center + (esc_speed * activesettings.esc_direction);
				if (esc_speed>0) {
					servo[SERVO_ESC]+=(esc_neutral_range_forward * activesettings.esc_direction); // a speed request of +1us means +40+1 as the ESC starts at +40us only -> ESC's neutral range
					if (is5s() && activesettings.debuglevel >= DEBUG_LEVEL_LOW) {
						printDebug_string(" dist=", DEBUG_LEVEL_LOW);
						printDebug_double(e, DEBUG_LEVEL_LOW);
						printDebug_string("target pos=", DEBUG_LEVEL_LOW);
						printDebug_double(pos_target, DEBUG_LEVEL_LOW);
						printDebug_string("current pos=", DEBUG_LEVEL_LOW);
						printDebug_double(pos_current, DEBUG_LEVEL_LOW);
						printDebug_string(" old pos=", DEBUG_LEVEL_LOW);
						printDebug_double(pos_current_alt, DEBUG_LEVEL_LOW);
						printDebug_string("esc=", DEBUG_LEVEL_LOW);
						printlnDebug_int(servo[SERVO_ESC], DEBUG_LEVEL_LOW);
					}
				} else if (esc_speed<0) {
					servo[SERVO_ESC]-= (esc_neutral_range_reverse * activesettings.esc_direction);
					if (is5s() && activesettings.debuglevel >= DEBUG_LEVEL_LOW) {
						printDebug_string(" dist=", DEBUG_LEVEL_HIGH);
						printDebug_double(e, DEBUG_LEVEL_LOW);
						printDebug_string("target pos=", DEBUG_LEVEL_LOW);
						printDebug_double(pos_target, DEBUG_LEVEL_LOW);
						printDebug_string("current pos=", DEBUG_LEVEL_LOW);
						printDebug_double(pos_current, DEBUG_LEVEL_LOW);
						printDebug_string(" old pos=", DEBUG_LEVEL_LOW);
						printDebug_double(pos_current_alt, DEBUG_LEVEL_LOW);
						printDebug_string("esc=", DEBUG_LEVEL_LOW);
						printlnDebug_int(servo[SERVO_ESC], DEBUG_LEVEL_LOW);
					}
				}
				if (servo[SERVO_ESC] < SERVO_MIN) servo[SERVO_ESC] = SERVO_MIN;
				if (servo[SERVO_ESC] > SERVO_MAX) servo[SERVO_ESC] = SERVO_MAX;
			}

			PWM_SetPWM(SERVO_1_OUT, servo[SERVO_ESC]);
			if (resetthrottle == 1) {
				y = 0.0f;
				yalt = 0.0f;
				e = 0.0f;
				ealt = 0.0f;
				ealt2 = 0.0f;
				resetthrottle = 0;
			} else {
				ealt2 = ealt;
				ealt = e;
				yalt = y;
			}
		}
		PWM_SetPWM(SERVO_2_OUT, servo[SERVO_PITCH]);
		PWM_SetPWM(SERVO_3_OUT, servo[SERVO_YAW]);
		PWM_SetPWM(SERVO_5_OUT, servo[SERVO_AUX]);
	}
	rc_speed_signal_old = rc_speed_signal;
	pos_current_alt = pos_current;
	speed_old = speed;
}
Esempio n. 2
0
int equal(double x, double y) {
	return (x - y) < 0.003 * abs_d(y) && (x - y) > -0.003 * abs_d(y);
}
Esempio n. 3
0
/* 
 * ===  FUNCTION  ======================================================================
 *         Name:  m2Degree
 *  Description:  AT SMALL SCALE, a approximately(so also fast) conversion from distance 
 *                in meter to degree on latitude circle.
 *                When wishing to convert to longitude, 
 *                you can fill latitude to longitude parameter,
 *                with some loss of precision because the earth is taken as sphere
 *                WARNING: Here I calculate small circle arc, but since every 
 *                greate circle arc connect between the original boundary and 
 *                extended boundary will be greater than it, 
 *                (maybe) there is no corretness problem.
 *               
 * =====================================================================================
 */
double m2Degree ( double m, double lon ) {
	return abs_d(m / (R * cos(lon * PI / 180)) * 180 / PI);
}		/* -----  end of function m2Degree  ----- */