Raspberry Pi Pico(11): Control Servo Motor

This article will continue to control the servo motors of the motor type. I have two types of servo motors (or "steering gears") on hand: MG90S and MG996R. The wiring method is the same. Unlike a motor, the main function of a motor is to rotate, and what needs to be controlled is the speed and direction. The servo motor cares about the angle of rotation and does not need to rotate a full circle. The general angle of rotation ranges from 0 degrees to 180 degrees.

To control the speed of the servo motor or the dimming of the LED, PWM (Pulse Width Modulation) counting is usually used, which can control the speed of the DC motor by changing its input voltage. PWM is a technology that uses a series of ON-OFF pulses to adjust the average value of the input voltage. In easy-to-understand terms, it is a switch that frequently changes the voltage. The program is switched at a high speed within a fixed time. The voltage is switched many times. When the on time is longer, the motor speed will be faster; if the time is off, the motor will be turned off. If the time is longer, the speed of the motor will be slower. We call the proportion of time that the voltage is given as the duty cycle (Duty Cycle). The higher the duty cycle, the higher the average voltage applied to the DC motor (high speed), and the lower the duty cycle, the lower the average voltage applied to the DC motor (low speed). For example: light brightness control, motor speed control, screen brightness control, loudspeaker loudness/sound frequency control, etc.

Take the MG996R in this implementation as an example. Generally speaking, the reference signal cycle of PWM control servo motor is 20ms, and the reference pulse width is 1.5ms (the median). Theoretically, the pulse width should be between 1ms and 2ms. However, in practice, The upper pulse width can be between 0.5ms and 2.5ms, and the pulse width corresponds to the steering angle of 0°~180°. Let's use the following table to see how to calculate the duty cycle.
SIGNAL PULSE WIDTHSERVO MOTOR ROTATION ANGLEDUTY CYCLE
0.5ms0 degree2.5% (0.5ms high potential + 19.5 low potential) /td>
1ms45 degree5% (1ms high potential + 19.0 low potential)
1.5ms90 degree7.5% (1.5ms high potential + 18.5 low potential)
2.0ms135 degree10% (2ms high potential + 18.0 low potential)
2.5ms180 degree12.5% (2.5ms high potential + 17.5 low potential)

The angle corresponding to the pulse width of the above signal is converted into a duty cycle, the formula is: 2.5+angle/180*10, and the PWM pin resolution of Pi Pico is 2^16 = 65535, when converted to 0 degrees, its duty cycle The ratio is 65535 * 2.5% = 1638.375. When the angle is 180 degrees, the duty cycle value is 65535 * 12.5% ​​= 8191.875. These two values ​​are related to the program. Taking into account the error and rotation angle, I set the duty cycle Between 1000 and 9000, the steering gear can be rotated smoothly from 0 to 180 degrees.

When using PWM to control the servo motor, it is found that the duty cycle value is different from that of the LED. The reason is that the maximum rotation angle of the servo motor is only 180 degrees, and the brightness of the LED, the duty cycle can range from 0 to 65025. I checked the relevant documents and did not explain how 65025 is calculated? But this value is just 255 * 255. The guess is that the maximum value of 8-bit PWM is 255, and pwm.duty_u16 is the maximum value obtained by multiplying two (2^8-1=255). This means that 65525 will be 100% of the time, keeping the LED or DC motor on or at full value.

The specifications of MG996R servo motor are as follows:
  • Dimensions: 54 x 38 x 20 mm
  • Weight: 55g
  • Working voltage: 4.8V~7.2V
  • No-load working current: 120mA
  • Locked-rotor working current: 1450mA
  • Running speed: 0.13 seconds/60 degrees (6.0V no load)
  • Servo torque: 11kg*cm(6.0V)
  • Swivel angle: the controllable angle is about 180 degrees/270 degrees (maximum)
  • Gear: 5-level metal gear set
  • Weight: 55g

[Material]

  • Raspberry Pi Pico x1
  • MG996 Servo Motor x1
  • Variable resistance 10K x1
  • Breadboard x1
  • Breadboard power module x1
  • Wires x N

[Wiring diagram]

Since Pi Pico can only provide 3.3V power, the servo motor needs an external power supply and connect Pi Pico GND to the power ground wire. The outer two pins of the variable resistor are connected to the positive and negative electrodes provided by the breadboard power supply, and the middle Pin is connected to Pico's ADC0. The following chart:

Pi PicoMG996R Servo Motor10K Resistance
Pin38(GND)GNDGND
Pin31(GP26/ADC0)-Data(Middle Pin)
Pin1(GP0)Data-

[Code:one]

The frequency pwm.freq tells Pi Pico how long to switch on and off, and the program is set to the commonly used 50. The following formula divides 9000 by the maximum value of 65025 to calculate a fixed constant, and divides the value read by the variable resistor by this constant to convert it into the duty cycle of the servo motor. The following is the complete program. Although there are only a few lines, the resistance can be used to control the rotation of the servo motor:
from machine import Pin, PWM

analogvalue = machine.ADC(26)

pwm = PWM(Pin(0))
pwm.freq(50)
scale = 9000/65025

while True:
    reading = analogvalue.read_u16()
    pwm.duty_u16(int(reading*scale))

[Code:two]

This program uses a conversion program to map the input angle to a duty ratio of 1000 ~ 9000, and then let the servo motor rotate to that angle. The loop in the program makes the servo motor continue to rotate forward and backward 180 degrees.
from utime import sleep
from machine import Pin
from machine import PWM

pwm = PWM(Pin(0))
pwm.freq(50)

# Set the rotation angle of the servo motor 
def setServoCycle (position):
    pwm.duty_u16(position)
    sleep(0.01)

# Convert the rotation angle to the duty cycle
def convert(x, i_m, i_M, o_m, o_M):
    return max(min(o_M, (x - i_m) * (o_M - o_m) // (i_M - i_m) + o_m), o_m)

while True:
    for degree in range(0, 180, 1):
        pos = convert(degree, 0, 180, 1000, 9000)
        setServoCycle(pos)

    for degree in range(180, 0, -1):
        pos = convert(degree, 0, 180, 1000, 9000)
        setServoCycle(pos)


[Result]

The execution result of program one:

The execution result of program two:

[Reference]


Post a Comment

Previous Post Next Post