As already noted, 100hz too high, should be 50.
https://components101.com/motors/mg996r-servo-motor-datasheet
Any load connected to the servo output arm..?
As already noted, 100hz too high, should be 50.
https://components101.com/motors/mg996r-servo-motor-datasheet
Any load connected to the servo output arm..?
It is a quadruped robot, and each motor is supporting the entire robot. For more info on the design, look up SpotMicro
And exactly what do you mean by the frequency?
No matter how often you call shoulderLB.write(), the servo lib will output a pulse to the servo every 20ms. This is definitely not the reason for the jitter.
You might think not, I am less sure. Which is why I invited the OP to crank up the delay to 333 as an experiment just to see what obtains.
And still no report about to what values the servos are being set… a few print statements would be easy to add just to be sure we aren't talking about GIGO.
a7
My code is completely different now, actually. Here's the new code
/*
Thanks SO much to James Bruton and his MiniDog project, without him this wouldn't be possible.
James, if you read this, I want you to know that your OpenDog project inspired this project.
This is intended for SpotMicro, the open-source robot dog.
*/
#include <Servo.h>
float pitch;
float roll;
float rollFiltered;
int x;
int y;
int z;
int yaw;
int r;
int p;
float multiplierKneesRight = 2;
float multiplierShouldersRight = 2;
float multiplierHipsRight = 0;
float multiplierKneesLeft = 2;
float multiplierShouldersLeft = 2;
float multiplierHipsLeft = 0;
unsigned long currentMillis;
unsigned long previousMillis;
unsigned long previousWalkMillis;
unsigned long previousSafetyMillis;
int filterKneesRight = 30;
int filterShouldersRight = 25;
int filterHipsRight = 30;
int filterKneesLeft = 30;
int filterShouldersLeft = 25;
int filterHipsLeft = 30;
int xAxisPin = 30; // What pins the receiver is connected to
int yAxisPin = 31;
int zAxisPin = 32;
int elevationPin = 33;
int speedPin = 34;
int modePin = 35;
int xAxisPwmValue = 0; // Values from RC receiver
int yAxisPwmValue = 0;
int zAxisPwmValue = 0;
int elevationPwmValue = 0;
int speedPwmValue = 0;
int modePwmValue = 0;
float servo1Pos; // initial value
float servo2Pos;
float servo3Pos;
float servo4Pos;
float servo5Pos;
float servo6Pos;
float servo7Pos;
float servo8Pos;
float servo9Pos;
float servo10Pos;
float servo11Pos;
float servo12Pos;
float servo1PosTrack; // ongoing tracking value
float servo2PosTrack;
float servo3PosTrack;
float servo4PosTrack;
float servo5PosTrack;
float servo6PosTrack;
float servo7PosTrack;
float servo8PosTrack;
float servo9PosTrack;
float servo10PosTrack;
float servo11PosTrack;
float servo12PosTrack;
float servo1PosFiltered; // filtered values
float servo2PosFiltered;
float servo3PosFiltered;
float servo4PosFiltered;
float servo5PosFiltered;
float servo6PosFiltered;
float servo7PosFiltered;
float servo8PosFiltered;
float servo9PosFiltered;
float servo10PosFiltered;
float servo11PosFiltered;
float servo12PosFiltered;
// servo offsets for default position - knees at 45', hips at 0', shoulders at 45'
int servo1Offset = 1680;
int servo2Offset = 1380;
int servo3Offset = 1350;
int servo4Offset = 1550;
int servo5Offset = 1430;
int servo6Offset = 1570;
int servo7Offset = 1550;
int servo8Offset = 1520;
int servo9Offset = 1400;
int servo10Offset = 1700;
int servo11Offset = 1650;
int servo12Offset = 1750;
float xAxisValue = 0;
float yAxisValue = 0;
float zAxisValue = 0;
float elevationValue = 0;
float speedValue = 0;
int mode = 1;
Servo servo1;
Servo servo2;
Servo servo3;
Servo servo4;
Servo servo5;
Servo servo6;
Servo servo7;
Servo servo8;
Servo servo9;
Servo servo10;
Servo servo11;
Servo servo12;
float posX = 0;
float posY = 0;
float posZ = 0;
float rotX = 0;
float rotY = 0;
float rotZ = 0;
float filter(float prevValue, float currentValue, int filter) {
float lengthFiltered = (prevValue + (currentValue * filter)) / (filter + 1);
return lengthFiltered;
}
void kinematics (int leg, int mode, float x, float y, float z, float roll, float pitch, float yaw) {
// *** TRANSLATION AXIS ***
// moving the foot sideways on the end plane
#define hipOffset 76.5
float lengthY;
float hipAngle1a;
float hipAngle1b;
float hipAngle1;
float hipAngle1Degrees;
float hipHyp;
// moving the foot forwards or backwardes in the side plane
float shoulderAngle2;
float shoulderAngle2Degrees;
float z2;
// side plane of individual leg only
#define shinLength 125
#define thighLength 125
float z3;
float shoulderAngle1;
float shoulderAngle1Degrees;
float shoulderAngle1a;
float shoulderAngle1b;
float shoulderAngle1c;
float shoulderAngle1d;
float kneeAngle;
float kneeAngleDegrees;
// *** ROTATION AXIS
// roll axis
#define bodyWidth 40 // half the distance from the middle of the body to the hip pivot
float legDiffRoll; // differnece in height for each leg
float bodyDiffRoll; // how much shorter the 'virtual body' gets
float footDisplacementRoll; // where the foot actually is
float footDisplacementAngleRoll; // smaller angle
float footWholeAngleRoll; // whole leg angle
float hipRollAngle; // angle for hip when roll axis is in use
float rollAngle; // angle in RADIANS that the body rolls
float zz1a; // hypotenuse of final triangle
float zz1; // new height for leg to pass onto the next bit of code
float yy1; // new position for leg to move sideways
// pitch axis
#define bodyLength 163.25 // distance from centre of body to shoulder pivot
float legDiffPitch; // differnece in height for each leg
float bodyDiffPitch; // how much shorter the 'virtual body' gets
float footDisplacementPitch; // where the foot actually is
float footDisplacementAnglePitch; // smaller angle
float footWholeAnglePitch; // whole leg angle
float shoulderPitchAngle; // angle for hip when roll axis is in use
float pitchAngle; // angle in RADIANS that the body rolls
float zz2a; // hypotenuse of final triangle
float zz2; // new height for the leg to pass onto the next bit of code
float xx1; // new position to move the leg fowwards/backwards
// *** yaw calcs ***
float yawAngle; // angle in RADIANs for rotation in yaw
float existingAngle; // existing angle of leg from centre
float radius; // radius of leg from centre of robot based on x and y from sticks
float demandYaw; // demand yaw postion - existing yaw plus the stick yaw
float xx3; // new X coordinate based on demand angle
float yy3; // new Y coordinate based on demand angle
// convert degrees to radians for the calcs
yawAngle = (PI / 180) * yaw;
// put in offsets from robot's parameters so we can work out the radius of the foot from the robot's centre
if (leg == 1) { // front left leg
y = y - (bodyWidth + hipOffset);
x = x - bodyLength;
}
else if (leg == 2) { // front right leg
y = y + (bodyWidth + hipOffset);
x = x - bodyLength;
}
else if (leg == 3) { // back left leg
y = y - (bodyWidth + hipOffset);
x = x + bodyLength;
}
else if (leg == 4) { // back left leg
y = y + (bodyWidth + hipOffset);
x = x + bodyLength;
}
//calc existing angle of leg from cetre
existingAngle = atan(y / x);
// calc radius from centre
radius = y / sin(existingAngle);
//calc demand yaw angle
demandYaw = existingAngle + yawAngle;
// calc new X and Y based on demand yaw angle
xx3 = radius * cos(demandYaw); // calc new X and Y based on new yaw angle
yy3 = radius * sin(demandYaw);
// remove the offsets so we pivot around 0/0 x/y
if (leg == 1) { // front left leg
yy3 = yy3 + (bodyWidth + hipOffset);
xx3 = xx3 + bodyLength;
}
else if (leg == 2) { // front right leg
yy3 = yy3 - (bodyWidth + hipOffset);
xx3 = xx3 + bodyLength;
}
else if (leg == 3) { // back left leg
yy3 = yy3 + (bodyWidth + hipOffset);
xx3 = xx3 - bodyLength;
}
else if (leg == 4) { // back left leg
yy3 = yy3 - (bodyWidth + hipOffset);
xx3 = xx3 - bodyLength;
}
// *** pitch calcs ***
//turn around the pitch for front or back of the robot
if (leg == 1 || leg == 2) {
pitch = 0 - pitch;
}
else if (leg == 3 || leg == 4) {
pitch = 0 + pitch;
xx3 = xx3 * -1; // switch over x for each end of the robot
}
// convert pitch to degrees
pitchAngle = (PI / 180) * pitch;
//calc top triangle sides
legDiffPitch = sin(pitchAngle) * bodyLength;
bodyDiffPitch = cos(pitchAngle) * bodyLength;
// calc actual height from the ground for each side
legDiffPitch = z - legDiffPitch;
// calc foot displacement
footDisplacementPitch = ((bodyDiffPitch - bodyLength) * -1) + xx3;
//calc smaller displacement angle
footDisplacementAnglePitch = atan(footDisplacementPitch / legDiffPitch);
//calc distance from the ground at the displacement angle (the hypotenuse of the final triangle)
zz2a = legDiffPitch / cos(footDisplacementAnglePitch);
// calc the whole angle for the leg
footWholeAnglePitch = footDisplacementAnglePitch + pitchAngle;
//calc actual leg length - the new Z to pass on
zz2 = cos(footWholeAnglePitch) * zz2a;
//calc new Z to pass on
xx1 = sin(footWholeAnglePitch) * zz2a;
if (leg == 3 || leg == 4 ) { // switch back X for the back of the robot
xx1 = xx1 * -1;
}
// *** roll calcs ***
//turn around roll angle for each side of the robot
if (leg == 1 || leg == 3) {
roll = 0 + roll;
yy3 = yy3 * -1;
}
else if (leg == 2 || leg == 4) {
roll = 0 - roll;
}
// convert roll angle to radians
rollAngle = (PI / 180) * roll; //covert degrees from the stick to radians
// calc the top triangle sides
legDiffRoll = sin(rollAngle) * bodyWidth;
bodyDiffRoll = cos(rollAngle) * bodyWidth;
// calc actual height from the ground for each side
legDiffRoll = zz2 - legDiffRoll;
// calc foot displacement
footDisplacementRoll = (((bodyDiffRoll - bodyWidth) * -1) + hipOffset) - yy3;
//calc smaller displacement angle
footDisplacementAngleRoll = atan(footDisplacementRoll / legDiffRoll);
//calc distance from the ground at the displacement angle (the hypotenuse of the final triangle)
zz1a = legDiffRoll / cos(footDisplacementAngleRoll);
// calc the whole angle for the leg
footWholeAngleRoll = footDisplacementAngleRoll + rollAngle;
//calc actual leg length - the new Z to pass on
zz1 = cos(footWholeAngleRoll) * zz1a;
//calc new Y to pass on
yy1 = (sin(footWholeAngleRoll) * zz1a) - hipOffset; // take away the offset so we can pivot around zero
// *** TRANSLATION AXIS ***
// calculate the hip joint and new leg length based on how far the robot moves sideways
// first triangle
lengthY = yy1 + hipOffset; // calc sideways distance of foot from pivot point centre
hipAngle1a = atan(lengthY / zz1); // first angle (that is there at rest due to the offset)
hipHyp = lengthY / (sin(hipAngle1a)); // hypotenuse from joint pivot to foot
// second triangle
hipAngle1b = asin(hipOffset / hipHyp) ; // calc 'the other angle' in the triangle
hipAngle1 = (PI - (PI / 2) - hipAngle1b) + hipAngle1a; // calc total hip angle
hipAngle1 = hipAngle1 - 1.5708; // take away offset for rest position
hipAngle1Degrees = ((hipAngle1 * (180 / PI))); // convert to degrees and take off rest position
// calc new leg length to give to the code below
z2 = hipOffset / tan(hipAngle1b); // new leg length
// ****************
// calculate the shoulder joint offset and new leg length based on now far the foot moves forward/backwards
shoulderAngle2 = atan(xx1 / z2); // calc how much extra to add to the shoulder joint
shoulderAngle2Degrees = shoulderAngle2 * (180 / PI);
z3 = z2 / cos(shoulderAngle2); // calc new leg length to feed to the next bit of code below
// ****************
// calculate leg length based on shin/thigh length and knee and shoulder angle
shoulderAngle1a = sq(thighLength) + sq(z3) - sq(shinLength);
shoulderAngle1b = 2 * thighLength * z3;
shoulderAngle1c = shoulderAngle1a / shoulderAngle1b;
shoulderAngle1 = acos(shoulderAngle1c); // radians
kneeAngle = PI - (shoulderAngle1 * 2); // radians
//calc degrees from angles
shoulderAngle1Degrees = shoulderAngle1 * (180 / PI); // degrees
kneeAngleDegrees = kneeAngle * (180 / PI); // degrees
if (leg == 1) { // *front left leg*
// convert degrees to servo microSeconds
servo11PosTrack = (kneeAngleDegrees - 90) * 25; // positive scaler
servo7PosTrack = ((shoulderAngle1Degrees - 45) * 25) + (shoulderAngle2Degrees * 25) ; // positive scaler
servo3PosTrack = (hipAngle1Degrees * -25);
// no compliance mode
if (mode == 0) {
servo3Pos = servo3PosTrack; // front left hip
servo7Pos = servo7PosTrack; // front left shoulder
servo11Pos = servo11PosTrack; // front right knee
}
// filter motions
servo3PosFiltered = filter(servo3Pos, servo3PosFiltered, filterHipsLeft);
servo3PosFiltered = constrain(servo3PosFiltered, -900, 900);
servo7PosFiltered = filter(servo7Pos, servo7PosFiltered, filterShouldersLeft);
servo7PosFiltered = constrain(servo7PosFiltered, -900, 900);
servo11PosFiltered = filter(servo11Pos, servo11PosFiltered, filterKneesLeft);
servo11PosFiltered = constrain(servo11PosFiltered, -900, 900);
// write out servo positions
servo3.writeMicroseconds(servo3Offset + servo3PosFiltered); // front left hip
servo7.writeMicroseconds(servo7Offset + servo7PosFiltered); // front left shoulder
servo11.writeMicroseconds(servo11Offset + servo11PosFiltered); // front right knee
}
else if (leg == 2) { // *front right leg*
// convert degrees to servo microSeconds
servo12PosTrack = (kneeAngleDegrees - 90) * -25; // inverted scaler
servo8PosTrack = ((shoulderAngle1Degrees - 45) * -25) - (shoulderAngle2Degrees * 25); // inverted scaler
servo4PosTrack = (hipAngle1Degrees * 25);
// no compliance mode
if (mode == 0) {
servo4Pos = servo4PosTrack; // front right hip
servo8Pos = servo8PosTrack; // front right shoulder
servo12Pos = servo12PosTrack; // front right knee
}
// filter motions
servo4PosFiltered = filter(servo4Pos, servo4PosFiltered, filterHipsRight);
servo4PosFiltered = constrain(servo4PosFiltered, -900, 900);
servo8PosFiltered = filter(servo8Pos, servo8PosFiltered, filterShouldersRight);
servo8PosFiltered = constrain(servo8PosFiltered, -900, 900);
servo12PosFiltered = filter(servo12Pos, servo12PosFiltered, filterKneesRight);
servo12PosFiltered = constrain(servo12PosFiltered, -900, 900);
// write out servo potitions
servo4.writeMicroseconds(servo4Offset + servo4PosFiltered); // front right hip
servo8.writeMicroseconds(servo8Offset + servo8PosFiltered); // front right shoulder
servo12.writeMicroseconds(servo12Offset + servo12PosFiltered); // front right knee
}
else if (leg == 3) { // *back left leg*
// convert degrees to servo microSeconds
servo9PosTrack = (kneeAngleDegrees - 90) * -25; // inverted scaler
servo5PosTrack = ((shoulderAngle1Degrees - 45) * -25) + (shoulderAngle2Degrees * 25); // inverted scaler
servo1PosTrack = (hipAngle1Degrees * 25);
// no compliance mode
if (mode == 0) {
servo1Pos = servo1PosTrack; // back left hip
servo5Pos = servo5PosTrack; // back left shoulder
servo9Pos = servo9PosTrack; // back left knee
}
// filter motions
servo1PosFiltered = filter(servo1Pos, servo1PosFiltered, filterHipsLeft);
servo1PosFiltered = constrain(servo1PosFiltered, -900, 900);
servo5PosFiltered = filter(servo5Pos, servo5PosFiltered, filterShouldersLeft);
servo5PosFiltered = constrain(servo5PosFiltered, -900, 900);
servo9PosFiltered = filter(servo9Pos, servo9PosFiltered, filterKneesLeft);
servo9PosFiltered = constrain(servo9PosFiltered, -900, 900);
// write out servo positions
servo1.writeMicroseconds(servo1Offset + servo1PosFiltered); // back left hip
servo5.writeMicroseconds(servo5Offset + servo5PosFiltered); // back left shoulder
servo9.writeMicroseconds(servo9Offset + servo9PosFiltered); // front left knee
}
else if (leg == 4) { // *back right leg*
// convert degrees to servo microSeconds
servo10PosTrack = (kneeAngleDegrees - 90) * 25; // positive scaler
servo6PosTrack = ((shoulderAngle1Degrees - 45) * 25) - (shoulderAngle2Degrees * 25); // positive scaler
servo2PosTrack = (hipAngle1Degrees * -25);
// no compliance mode
if (mode == 0) {
servo2Pos = servo2PosTrack; // back right hip
servo6Pos = servo6PosTrack; // back right shoulder
servo10Pos = servo10PosTrack; // back right knee
}
// filter motions
servo2PosFiltered = filter(servo2Pos, servo2PosFiltered, filterHipsRight);
servo2PosFiltered = constrain(servo2PosFiltered, -900, 900);
servo6PosFiltered = filter(servo6Pos, servo6PosFiltered, filterShouldersRight);
servo6PosFiltered = constrain(servo6PosFiltered, -900, 900);
servo10PosFiltered = filter(servo10Pos, servo10PosFiltered, filterKneesRight);
servo10PosFiltered = constrain(servo10PosFiltered, -900, 900);
// write out servo positions
servo2.writeMicroseconds(servo2Offset + servo2PosFiltered); // back right hip
servo6.writeMicroseconds(servo6Offset + servo6PosFiltered); // back left shoulder
servo10.writeMicroseconds(servo10Offset + servo10PosFiltered); // front left knee
}
} // end of kinematics
void setup() {
// write default positions with delays
servo1.attach(2); // hips
servo2.attach(3);
servo3.attach(4);
servo4.attach(5);
servo1.writeMicroseconds(servo1Offset); // back left hip | higher value moves up/outwards
servo2.writeMicroseconds(servo2Offset); // back right hip | lower value moves up/outwards
servo3.writeMicroseconds(servo3Offset); // front left hip | lower value moves up/outwards
servo4.writeMicroseconds(servo4Offset); // front right hip | higher value moves up/outwards
delay(1000);
servo5.attach(6); // shoulders
servo6.attach(7);
servo7.attach(8);
servo8.attach(9);
servo5.writeMicroseconds(servo5Offset); // back left shoulder | higher value moves leg inwards
servo6.writeMicroseconds(servo6Offset); // back right shoulder | lower value moves leg inwards
servo7.writeMicroseconds(servo7Offset); // front left shoulder | higher value moves leg inwards
servo8.writeMicroseconds(servo8Offset); // front right shoulder | lower value moves leg inwards
delay(1000);
servo9.attach(10); // knees
servo10.attach(11);
servo11.attach(12);
servo12.attach(13);
servo9.writeMicroseconds(servo9Offset); // back left knee | higher value bends knee inwards
servo10.writeMicroseconds(servo10Offset); // back right knee | lower value bends knee inwards
servo11.writeMicroseconds(servo11Offset); // front left knee | lower value bends knee inwards
servo12.writeMicroseconds(servo12Offset); // front right knee | higher value bends knee inwards
pinMode(xAxisPin, INPUT);
pinMode(yAxisPin, INPUT);
pinMode(zAxisPin, INPUT);
pinMode(elevationPin, INPUT);
pinMode(speedPin, INPUT);
pinMode(modePin, INPUT);
delay(5000);
}
void loop() {
// put your main code here, to run repeatedly:
xAxisPwmValue = pulseIn(elevationPin, HIGH);
yAxisPwmValue = pulseIn(elevationPin, HIGH);
zAxisPwmValue = pulseIn(zAxisPin, HIGH);
elevationPwmValue = pulseIn(elevationPin, HIGH);
speedPwmValue = pulseIn(speedPin, HIGH);
modePwmValue = pulseIn(modePin, HIGH);
// Select which mode using the PWM switch
if (modePwmValue <= 1250) {
mode = 1;
}
else if (modePwmValue > 1250 and modePwmValue < 1750) {
mode = 2;
}
else if (modePwmValue >= 1750) {
mode = 3;
}
// The X Axis of the right joystick on the receiver has an issue where it drifts to either side, this code compensates for that.
xAxisValue = xAxisPwmValue - 1500;
xAxisValue = map(xAxisValue, -550, 300, -100, 100);
if (xAxisValue >= -10 and xAxisValue <= 10) {
xAxisValue = 0;
}
// Map the other PWM values to their respective places
yAxisValue = map(yAxisPwmValue, 990, 1990, -100, 100);
zAxisValue = map(zAxisPwmValue, 990, 1990, -100, 100);
elevationValue = map(elevationPwmValue, 990, 1990, 0, 100);
speedValue = map(speedPwmValue, 990, 1990, 0, 100);
if (mode == 1) { // Translation mode
posX = xAxisValue;
posY = yAxisValue;
posZ = elevationValue;
kinematics(1, 0, posX, posY, posZ, rotX, rotY, rotZ);
kinematics(2, 0, posX, posY, posZ, rotX, rotY, rotZ);
kinematics(3, 0, posX, posY, posZ, rotX, rotY, rotZ);
kinematics(4, 0, posX, posY, posZ, rotX, rotY, rotZ);
} else if (mode == 2) { // Rotation mode
rotX = xAxisValue;
rotY = yAxisValue;
rotZ = zAxisValue;
posZ = elevationValue;
kinematics(1, 0, posX, posY, posZ, rotX, rotY, rotZ);
kinematics(2, 0, posX, posY, posZ, rotX, rotY, rotZ);
kinematics(3, 0, posX, posY, posZ, rotX, rotY, rotZ);
kinematics(4, 0, posX, posY, posZ, rotX, rotY, rotZ);
} else if (mode == 3) { // Walk mode
// To be added
}
delay(20);
}
Nice to see you got it working! ![]()
a7
I haven't gotten it working. Could I have some high-current wire branching off into lower-current wire going to each motor?
Then how about a description of exactly what this version does? And a clear detailed photo or two that show exactly how it is now wired? And if you now have a sensible power supply, details of that too please.
In setup() if you write to the servos just BEFORE the attach() commands it will save them going to the default 1500 position and then jerking back to where you actually want them.
Steve
There are far too many wires to explain them all, but here's the idea:
The two PSU wires get split into three high-current wires each, which then fan out to four motors each. My lab bench supply can't source enough current (30v 5a supply, Eventek KPS-305D). I have not yet had it work yet.
What is a "breadboard power supply"? Most certainly, nothing whatsoever to do with servos. ![]()
Hi,
As you have all the servos supporting the robot, the servos will be demanding power ALL the time, not just to move.
Do you have a DMM to measure your 6V supply for the servos.
Thanks.. Tom...
![]()
Yes, I do have a DMM. I am buying a 20A buck converter to run off of a 2S 5500mAh battery.
As for @Paul_B, here's a breadboard power supply:https://cdn.tindiemedia.com/images/resize/TatKshy8G2HRNyjFBImxndFbbd8=/p/fit-in/1032x688/filters:fill(fff)/i/95826/products/2017-05-01T01%3A28%3A56.197Z-面包板.png?1606306133
Hi,
Its worth connecting your DMM across the servo power supply to check if it becomes overloaded.
Tom...
![]()
This site works by your supplying the info, not a job for us to go looking on some obscure site.
For testing purposes write a constant value into the parameters of your servo.write commands. if the don't jitter then, the reason is the value from the pulseIn. If they still jitter, you should check your power source.
That breadboard power supply is inadequate for all but the most basic of circuits as the regulators are very light duty and don't even have heatsinks !
This particular design actually does not but suffers the same severe limitation as the on-board regulator of the UNO/ Mega 2560/ Leonardo with no significant heatsink limiting it to a hundred milliamps or so depending on input voltage, before thermal shutdown.
The "USB" jack is however interesting, it would have that current limitation as an output, but connected by a "male to male"(!) USB cable to a "phone charger" it could provide as much power to the breadboard as the supply was rated. ![]()
I switched to a 2S LiPo with plenty of current, no more jittering.
This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.