目录
舵机
分类
结构
工作原理
控制原理
参数
舵机基本控制实验(MG996R舵机)
MG996R舵机规格
硬件电路
TIM4配置
测试环节
按照舵机的控制电路分类:模拟舵机和数字舵机。
模拟舵机和数字舵机的机械结构是完全相同的。
模拟舵机的控制电路为纯模拟电路,需要一直发送目标信号,才能转到指定的位置,响应速度较慢,无反应区较大;
数字舵机内部控制电路则加上了微控制器,只需要发送一次目标信号,即可到达指定位置,响应速度比模拟舵机更快,无反应区更小。
按照使用对象不同分类:航模舵机、车模舵机、船模舵机和机器人舵机。
航模舵机一般要求速度快、精度高。
车模舵机、船模舵机一般要求具有大扭矩和防水性好。
按照内部机械材质分类:塑料齿舵机和金属齿舵机。
塑料齿舵机内部的传动齿轮是塑料的,重量轻价格便宜,但是扭矩一般较小无法做大;
金属齿舵机的扭矩更大且舵机更耐用,但成本较高。
按照外部接口和舵机的控制方式分类:PWM舵机和串行总线舵机。
舵机主要组成:外壳、舵盘、直流电机、减速齿轮组、角度传感器、控制驱动电路和接口线缆等。
角度传感器负责舵机的位置反馈,直接装在舵机的主输出轴上,将轴旋转后产生的角度变化变成电压信号发回控制电路。
控制驱动电路用来接收外部接口传来的信号、接收角度传感器反馈的电压值、驱动直流电机旋转。
减速齿轮组用来降低直流电机的转速和放大扭矩。
常见的廉价舵机通常采用小型的直流有刷电机和塑料材质减速齿轮组,传感器一般使用电位器返回模拟电压。而稍贵的会使用金属齿轮组,高端的舵机内部甚至会采用无刷电机和磁电编码器。
模拟舵机和数字舵机内部电路不同,所以原理上稍有不同。
模拟舵机内部的控制驱动电路板从外界接收控制信号,经过处理后变为一个直流偏置电压(在控制板内部有一个基准电压,这个基准电压由电位器产生并反馈到控制板),将外界获得的直流偏置电压与电位器的基准电压进行比较获得电压差,并输出到电机驱动芯片驱动电机。(电压差的正负决定了电机的正反转,电压差的大小决定了旋转的角度,当电压差为0时,电机停止转动)
舵机内部是闭环控制的,所以这一类电机实际上是一种位置(角度)伺服的简化版伺服电机,将工业伺服电机的三闭环控制简化成了只有一个位置闭环。舵机本质上属于伺服电机,英文叫Servo或RC Servo。
舵机三根线:正极线、负极线、信号线。
舵机的控制通常采用PWM信号,例如需要一个周期为20ms的脉冲宽度调制(PWM),脉冲宽度部分一般为0.5ms~2.5ms范围内的角度控制脉冲部分,总间隔为2ms。当脉冲宽度为1.5ms时,舵机旋转至中间角度;大于1.5ms时舵机旋转角度增大;小于1.5ms时舵机旋转角度减小。舵机分90°、180°、270°、360°。以180°舵机为例看下图。
图中脉冲宽度和舵机旋转角度为线性关系,其它舵机控制脉冲也类似。
舵机旋转速度单位:sec/60°,即舵机旋转60°需要的时间。如果控制脉冲变化宽度大,变化速度快,舵机就有可能在一次脉冲的变化过程中还没有转到目标角度时,脉冲就再次发生了变化。舵机的旋转速度一般为0.16sec/60°、0.12sec/60°等。舵机的选择速度还与工作电压有关,在允许的电压范围内,电压越大速度越快,电压越小速度越慢。
舵机扭矩单位:KG*CM。可以理解为在舵盘上距离舵机轴中心水平距离1cm处,舵机能够带动的物体重量。
通常说的55g舵机、9g舵机等,55g和9g指的是舵机本身的重量。
尺寸:40.5*20*41mm
重量:55g
速度:[email protected]±0.01sec/60°[email protected]±0.01sec/60°
扭力:4.8V@11kg*cm--6.0V@13kg*cm
电压:4.8V~6V
空载工作电流:220±20mA
堵转工作电流:2000±30mA
响应脉宽时间≤5usec
角度偏差:回中差≤1°,左右各45°,误差≤3°
齿轮:5级金属齿轮组
连接线长度:300mm
接口规格:JR/FP通用
电机开发板预留了两个舵机接口,实验只使用其中一个,舵机由TIM4_CH1控制。
TIM_HandleTypeDef htim4;
/* TIM4 init function */
void MX_TIM4_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
htim4.Instance = TIM4;
htim4.Init.Prescaler = 1679;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP;
htim4.Init.Period = 999;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim4.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim4, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim4) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim4, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM2;
sConfigOC.Pulse = 25;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
HAL_TIM_MspPostInit(&htim4);
}
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *tim_baseHandle)
{
if (tim_baseHandle->Instance == TIM4)
{
__HAL_RCC_TIM4_CLK_ENABLE();
}
}
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *timHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if (timHandle->Instance == TIM4)
{
__HAL_RCC_GPIOD_CLK_ENABLE();
/**TIM4 GPIO Configuration
PD12 ------> TIM4_CH1
*/
GPIO_InitStruct.Pin = GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
GPIO_InitStruct.Alternate = GPIO_AF2_TIM4;
HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);
}
}
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *tim_baseHandle)
{
if (tim_baseHandle->Instance == TIM4)
{
__HAL_RCC_TIM4_CLK_DISABLE();
}
}
/**
* @brief 设置舵机占空比
* @param angle: 占空比,(0.5/20.0*PWM_PERIOD_COUNT 到 2.5/20.0*PWM_PERIOD_COUNT)
* @retval 无
*/
void set_steering_gear_dutyfactor(uint16_t dutyfactor)
{
#if 1
{
/* 对超过范围的占空比进行边界处理 */
dutyfactor = 0.5 / 20.0 * 1000 > dutyfactor ? 0.5 / 20.0 * 1000 : dutyfactor;
dutyfactor = 2.5 / 20.0 * 1000 < dutyfactor ? 2.5 / 20.0 * 1000 : dutyfactor;
}
#endif
__HAL_TIM_SET_COMPARE(&TIM_TimeBaseStructure, TIM_CHANNEL_1, dutyfactor);
}
/**
* @brief 设置舵机角度
* @param angle: 角度,(0 到 180(舵机为0°-180°))
* @retval 无
*/
void set_steering_gear_angle(uint16_t angle_temp)
{
angle_temp = (0.5 + angle_temp / 180.0 * (2.5 - 0.5)) / 20.0 * 1000; // 计算角度对应的占空比
set_steering_gear_dutyfactor(angle_temp); // 设置占空比
}
__IO uint16_t ChannelPulse = 0.5 / 20.0 * 1000;
void test(void)
{
初始化
/*开始输出PWM*/
HAL_TIM_PWM_Start(&htim4, PWM_CHANNEL_1);
while(1)
{
/* 处理数据 */
if (Key_Scan(KEY1_GPIO_PORT, KEY1_PIN) == KEY_ON)
{
ChannelPulse -= 10; // 减少占空比
// 检查占空比的合法性
ChannelPulse = 0.5 / 20.0 * 1000 > ChannelPulse ? 0.5 / 20.0 * 1000 : ChannelPulse;
set_steering_gear_dutyfactor(ChannelPulse); // 设置占空比
}
/* 处理数据 */
if (Key_Scan(KEY2_GPIO_PORT, KEY2_PIN) == KEY_ON)
{
ChannelPulse += 10; // 增加占空比
// 检查占空比的合法性
ChannelPulse = 2.5 / 20.0 * 1000 < ChannelPulse ? 2.5 / 20.0 * 1000 : ChannelPulse;
set_steering_gear_dutyfactor(ChannelPulse); // 设置占空比
}
}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
// 串口可接收角度,存到变量angle_temp
if (angle_temp >= 0 && angle_temp <= 180)
{
printf("角度: %d\r\n", angle_temp);
ChannelPulse = (0.5 + angle_temp / 180.0 * (2.5 - 0.5)) / 20.0 * 1000; // 更新按钮控制的占空比
set_steering_gear_angle(angle_temp);
}
}