MG996R Servo motor calibration or slippage problems

Hi, This is my first post here. I am a (retired) professional electronic engineer, with many years circuit design and programming experience, but I'm having problems with MG996R servos attached to an Uno R3 (made by Elegoo).

I have built a small aluminium robot, which works, but the servos seem to change their characteristics after half an hour or so. I'm using various cheap clones of the original MG996R (which of course might be the problem), but before I splash out for the more expensive originals, I thought I'd ask whether anyone else has the same problem.

Firstly, I note that the servos have an operating range of somewhat more than 180 degrees rather than the advertised 120 degrees (which suits me fine).

Secondly, I note that they are not very linear. I decided to fix this problem, calibrate the servo and implement a centre zero command system with a +/- 90 degree offset, with the following routine:

// TargetAngle is the desired angular position of the servo
// AngleIncr is the size of the step in degrees
// TimeIncr is the time in milliseconds allowed for the servo to reach its target.

void  Left_Foot (int TargetAngle, int AngleIncr, unsigned long TimeIncr)
  if (TargetAngle < 0)
    L_Foot_Target = map(TargetAngle, -90, 0, 155, 88);   //  Calibrate the negative range (see below)
    L_Foot_Target = map(TargetAngle, 0, 90, 88, 10);      //  Calibrate the positive range (see below)
  L_Foot_Angle_Incr = AngleIncr;
  L_Foot_Time_Incr = TimeIncr;
  L_Foot_Ready = false;
  L_Foot_Time = millis();

The above splits the range into two parts: -90 to 0 and 0 to + 90 degrees. (It could be split further if better linearity is needed.)

The strange numbers in there (155, 88 and 10) are calibration numbers that are determined experimentally to set the middle (0 degrees) and the +/- 90 degrees. These need to be set for the particular servo in use. After getting the Arduino to set the servo angle to 0, -90 and then +90 respectively, I edit these numbers so that the servo horn angle matches the relevant command angle.

The actual servo control is then done with the following code:

// L_Foot_Position is the previous position of the servo (updated in the routine)

 if ((L_Foot_Time <= millis()) && (L_Foot_Ready != true))  // Is it time for the next incremental step?
    if (L_Foot_Target > L_Foot_Position)
      L_Foot_Position = min(L_Foot_Position + L_Foot_Angle_Incr, L_Foot_Target);
      L_Foot_Time = millis() + L_Foot_Time_Incr;
    else if (L_Foot_Target < L_Foot_Position)
      L_Foot_Position = max(L_Foot_Position - L_Foot_Angle_Incr, L_Foot_Target);
      L_Foot_Time = millis() + L_Foot_Time_Incr;  // Set the time for the next incremental step
    else if (L_Foot_Position == L_Foot_Target)
      L_Foot_Ready = true;

Everything works fine to start with, but then I notice that the servos seem to have drifted so that when I ask for 0 degrees, I get something like 5% and similarly for +/-90, giving me 95 and -85 degrees respectively. The robot then can't walk smoothly.

It's as if one of the servo gears has slipped on the spindle or maybe the duration of the PWM signal from the UNO has altered in length as the device warmed up. (But if that were the case, letting it cool down overnight would put it back, but it doesn't.)

Has anyone else seen anything similar? If so, how did you solve it?

It's as if one of the servo gears has slipped on the spindle

Or, the internal position feedback pot that the gears are driving has shifted. There is a calibration screw in the servo output shaft to set the zero position.

If you are running the output shaft into internal end stops, you might expect this sort of behavior.

I have had 2 instances of a cheap servo deciding that it had a new center position.

Let me tell you that this makes for an 'exciting' flight on a radio controlled airplane!

I did consider opening up the servo to see what had slipped, but instead decided to just throw it away and install a replacement servo. No problems since (other than my iffy piloting skills)

Edit: I should say that my current practice when purchasing bottom dollar electronics, be they servos or arduino clones, is to get extras. I some times let out the magic smoke. I solder something improperly. I break things. And sometimes they are bad from the factory.
It is much easier to deal with this when I already have a replacement in the house.

Thank you both for your feedback.

I'll try recalibrating the servo and then make sure that it doesn't hit the end stop. I'll report back in a few days or so with any results or conclusions.

Incidentally, I opened one up, but couldn't see a calibration screw anywhere. Maybe you only get that in the more expensive versions.

couldn't see a calibration screw anywhere.

On most servos, the calibration screw is accessed through the screw hole in the output shaft. No need to open the servo.

Oi! being an engineer, you might also be interested in writing to the servos using uS instead of whole degrees.

A servo, generally, has a 0 degree of 500uS and a 180 of 2500uS. That's 2000uS for 180 degrees or 5.55uS, use 5.54uS, per degree. Which will give you a lot more control over servo positioning.

Next neat trick is if you were to limit the servos to +/-45 degrees around 1500uS, that's the 90 degree position. With a limited travel of 90 degrees you still get 2000uS, which should work out to getting 11.11uS per degree.

I find the shoehorns mount at 25uS off of 90, 1500uS. I set the servo to 1500uS, place the shoe, screw in the shoehorn, and then note the servo offset for later use. So, either 1475uS or 1525uS becomes 90 degrees.

For me, yea I know 200ish hours can be a lot, but I wore out those plastic geared servos in 200ish hours of continuous operation. I switched to metal geared servos, I got 3 metal geared servos, continuous running for over 9 months.

I run the servos on their own well filtered and regulated power supply. The servos operate really well only common connected to the Uno, Mega, MiniPro, Due, STM32, or ESP32.

I have had some crazy problems with the MG 996R also. At first I thought it was a power supply issue, but the same problem shows up when using a 6 volt battery to power the servo. Running the basic example servo sweep program generally moves the servo, but the servo can be stopped easily and then stays where it was stopped until the program loops back to the position where it was stopped. Then the servo would start moving again. It is as if the potentiometer is not providing correct positional feedback. The servo can be moved, by hand, beyond the 0 or 180 degree limits, and then it will not start moving because the sweep program never goes into this outside position.

It also shows up as a problem when a simple program to move between 60 and 120 degrees is run. The servo will not move between these two positions unless it is "helped" by hand to get it off of one position and near to then other position.

This action was repeated on very many servos---until, I grabbed one with a slightly more purple than pink label, with the label missing the Tower Pro printing. This servo works just fine.

Inside the cases, there are some differences. The labeled "Tower Pro" has a ball bearing and the pot is wired onto the circuit board. The non labeled servo has a bushing, and the pot is soldered directly onto the board and the two circuit boards have other differences. An interesting example of what happens with cheap parts here in China.

I work at an international school in Shanghai, so I am sure we paid nothing for these servos; and that's what we got.

Hello Newbie
First of all... sorry for my English. I'm from Spain.

I have the same problem. I have bought 6 Dm MG996R servos with an arm robot and the servos rotate more than 180. I think it turns 210 degrees more or less.

In all some datasheets appears 120 degrees, in others 180. I have to limit the turn for don't break the arm.

Anybody knows how do it?


Anybody knows how do it?

Construct your code in such a way that it does not write a value that causes the breakage.
If it were me, I would modify a copy of the knob tutorial so that:

  • it maps the pot reading to valid microsecond values
  • it writes to the servo using writemicroseconds
  • it prints out the value it is sending to the servo.

Then rotate the pot until you are just short of the collision that causes the breakage.
Note the microsecond value on each of the movement that is just short of collision.
Use those microsecond values in the attach command to define the range of movement for the servo.