How do I increase the resolution of a continuous rotation servo motor?

First, some exposition on my setup:

I have a PID algorithm controlling the rotational speed of a quadcopter motor attached to a 1-DOF pivoting arm. I am trying to get the arm-motor assembly to hover in a horizontal position. Feedback for the algorithm comes from the internal IMU of an Uno Wifi Rev2 board mounted at the pivot point so that it rotates with the arm. Acceleration in the X direction is read, and the PID algorithm is given a setpoint of Ax = 0.

The algorithm controls a fractional microsecond pulsewidth value that is rounded and written to an ESC using the writeMicroseconds() function from Servo.h. This allows for higher resolution in the algorithm even though only whole microsecond values can be written. The sketch can be seen below (PID not tuned yet, might try deriving ideal constants from equations of motion).

My issue is in the writing of the pulsewidth value to pin 10. When the value is rounded and written to the pin, it seems to only register values about 4 numbers apart. For example, an output of 1100.24 from the algorithm would be written to pin 10, then I'll read back 1100 from the pin. The output will gradually increase to 1104.00, but the value given by readMicroseconds() remains at 1100 until 1104.00 when the read value becomes 1104. This continues in seemingly regular intervals as the written value increases.

image

I thought this might have something to do with the microsecond range mapping to the angle representation used by the write() function, but that should cause 5.5us gaps. I'm losing resolution in my control with this happening, and every us counts since the point where the motor wants to hover is only around 12%(ish).

Has anyone experienced this before and know the cause or see anything I'm missing in my code/approach?

Thanks.


#include <Servo.h>
#include <Arduino_LSM6DS3.h>

//	create servo objects to control the ESCs
Servo ESC1;

float percentPower = 0; //	initialize servo power
float pwmOut = 0; //	scaled power from percentage to servo signal

//	set PID gain values and setpoint
float kp = 0.1;
float ki = 0;
float kd = 0;
float set = 0;

//	set power limits (percent)
int maxP = 15;
int minP = 2;

//	initialize PID and accel data variables
float p, d, err, errLast, Ax, Ay, Az;
float i = 0;

void setup() {
	Serial.begin(9600);
	IMU.begin();

	//	Attach ESC to pin, calibrate max and min thrust
	ESC1.attach(10, 1000, 2000);
	ESC1.writeMicroseconds(2000);
	delay(10000);
	ESC1.writeMicroseconds(1000);
	delay(10000);
	
	//	Set pwm limits from percentage power limits
	maxPWM = maxP * 10 + 1000;
	minPWM = minP * 10 + 1000;
	IMU.readAcceleration(Ax, Ay, Az); //	get new accel data
	err = set + Ax; //	set initial error (positive bc of IMU orientation)
	errLast = err;	//	initialize last error to current error
}

void loop() {
	//	calculate PID terms and updated pwm value; save last error
	p = kp * err;
	i = i + ki * err;
	d = kd * (err - errLast);
	pwmOut = pwmOut + p + i + d;

	errLast = err;

	//	limit signal output
	if (pwmOut > maxPWM) {
		pwmOut = maxPWM;
	}
	if (pwmOut < minPWM) {
		pwmOut = minPWM;
	}

//	write PID output to ESC
	ESC1.writeMicroseconds(round(pwmOut));

	IMU.readAcceleration(Ax, Ay, Az); //	get new accel data
	err = set + Ax; //	calculate new error
	Serial.print(Ax);
	Serial.print("\t");
	Serial.print(err);
	Serial.print("\t");
	Serial.print(percentPower);
	Serial.print("\t");
	Serial.print(pwmOut);
	Serial.print("\t");
	Serial.println(ESC1.readMicroseconds());
}

Perhaps this is the reason for what you are seeing:
Returns the number of microseconds since the Arduino board began running the current program. This number will overflow (go back to zero), after approximately 70 minutes. On the boards from the Arduino Portenta family this function has a resolution of one microsecond on all cores. On 16 MHz Arduino boards (e.g. Duemilanove and Nano), this function has a resolution of four microseconds (i.e. the value returned is always a multiple of four). On 8 MHz Arduino boards (e.g. the LilyPad), this function has a resolution of eight microseconds.

Use the micro that gives a resolution of one microsecond for your project.

I was hoping it would be this simple. Thank you very much. I'll get a new board and see if it fixes the problem.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.