在智能硬件项目中,舵机经常承担“把程序动作变成机械动作”的角色。LED 能展示状态,蜂鸣器能发出提示,而舵机可以让设备产生真实的转动效果。智能垃圾桶自动开盖、门锁拨片切换、摄像头云台转向、小型机械臂摆动,这些看起来像产品功能的动作,本质上都离不开 PWM 信号对角度舵机的控制。

本实验使用 CanMV K210 / MaixPy 环境控制一颗角度舵机。程序将舵机信号线连接到 IO6,通过 TimerPWM 输出 50Hz 控制信号,再把舵机角度抽象成 -90° ~ 90° 的范围。代码中不仅包含固定角度转动和平滑扫描,还封装了“打开”“关闭”“回中”“释放 PWM”等动作方法,并通过智能垃圾桶和门锁拨片两个场景演示舵机在真实项目中的应用思路。

学习目标 说明
理解舵机控制方式 认识角度舵机如何通过 PWM 信号转动到指定位置
掌握 PWM 输出 使用 TimerPWM 在 IO6 输出 50Hz 控制信号
理解角度映射 -90° ~ 90° 的角度范围转换成对应 PWM 占空比
学会动作封装 把打开、关闭、回中、平滑转动等动作封装成类方法
建立执行器思维 将舵机控制扩展到垃圾桶开盖、门锁拨片、云台转向等项目场景

这个实验的重点不是让舵机简单转一下,而是理解“角度参数、PWM 占空比、机械动作”之间的对应关系。屏幕中的角度数值会变成舵机摇臂的真实位置,函数调用会变成开合、扫描、锁定、解锁等动作,这正是智能硬件执行器控制的基础。

理论基础

角度舵机和普通直流电机的控制方式不同。普通直流电机更关注转动方向和速度,通电后会持续旋转;角度舵机更关注目标位置,程序通过 PWM 信号告诉舵机转到某个角度,并保持在对应位置。这个特性让舵机非常适合做开盖、拨片、转向、限位和小型机械结构控制。

PWM 可以理解成一种周期性高低电平信号。当前实验中,程序设置 SERVO_FREQ = 50,表示 PWM 频率为 50Hz,对应周期约为 20ms。舵机并不是根据普通 GPIO 的 0 或 1 判断角度,而是根据每个周期内高电平持续时间的比例来调整转轴位置。代码中使用 MIN_DUTY = 2.5MAX_DUTY = 12.5,把占空比范围映射到 -90° ~ 90° 的教学角度范围。

本实验中的 Servo 类把底层 PWM 输出封装成更容易理解的角度控制方法。程序调用 set_angle(45) 时,并不是直接把数字 45 发送给舵机,而是先通过 angle_to_duty() 把 45° 换算成对应占空比,再由 PWM 对象在 IO6 输出控制信号。舵机内部根据 PWM 信号调整转轴位置,最终表现为真实机械转动。

Python 程序
set_angle / smooth_to / open / close

角度参数
-90° ~ 90°

角度换算
angle_to_duty()

PWM 输出
50Hz / 2.5% ~ 12.5%

CanMV K210
IO6 信号引脚

角度舵机
SIG 接收 PWM

机械动作
转向 / 开合 / 扫描 / 拨片

5V 供电
舵机 VCC

公共地线
舵机 GND 与开发板 GND 相连

这张流程图展示的是舵机实验中的电路与控制链路。程序中的角度命令先被转换成 PWM 占空比,PWM 信号从 IO6 输出到舵机 SIG 引脚。舵机 VCC 负责供电,GND 负责提供公共参考电平,SIG 负责接收控制信号。只有信号线、供电线和公共地同时正确连接,舵机才能稳定执行角度动作。

舵机实验还需要注意供电问题。舵机转动时会产生明显电流需求,如果供电不足,可能出现抖动、复位、角度不准或完全不动等现象。入门实验可以使用稳定 5V 供电,并保证舵机 GND 与 CanMV GND 相连。程序控制的是 SIG 信号线,舵机真正转动所需的能量来自 5V 供电端。

硬件设施

本实验围绕一颗角度舵机展开,核心控制方式是 PWM。代码没有使用 LCD、按键、蜂鸣器、摄像头或传感器,因此这些模块不作为当前内容重点。程序侧主要依赖 machine.Timermachine.PWMtime,分别用于创建 PWM 定时器、输出 PWM 信号和控制动作停顿时间。

接线关系可以先通过下面这张图建立整体印象。舵机通常有 VCC、GND、SIG 三条线,其中 VCC 接 5V,GND 接公共地,SIG 接 CanMV K210 的 IO6。程序真正控制的是 SIG 信号线,电源线本身不由程序直接控制。

在这里插入图片描述

硬件 / 软件 作用 说明
CanMV K210 / MaixPy 开发板 实验运行平台 负责执行 Python 程序,并通过 IO6 输出 PWM 控制信号
角度舵机 执行动作的外设 根据 PWM 占空比变化转动到不同角度
IO6 PWM 信号输出引脚 代码中通过 SERVO_PIN = 6 指定为舵机信号控制引脚
5V 电源 舵机供电 舵机 VCC 接 5V,提供转动所需电源
GND 公共地 舵机 GND 与开发板 GND 相连,保证信号参考一致
machine.Timer PWM 定时器基础 创建 PWM 所需的定时器通道
machine.PWM PWM 输出模块 设置频率、占空比和输出引脚
time 延时控制模块 通过 time.sleep_ms() 控制舵机动作节奏

实验中用到的舵机和连接线如下。实际接线时需要重点区分三条线的功能,不同舵机线序和颜色可能不完全一致,应以模块标识或舵机说明为准。VCC 和 GND 接错可能导致舵机不工作,SIG 接错则会导致程序有输出但舵机没有动作。

角度舵机与连接线实物图

舵机引脚 连接位置 代码变量 对应功能 说明
VCC 5V 舵机供电 为舵机转动提供电源
GND GND 公共地 保证开发板与舵机拥有相同电平参考
SIG IO6 SERVO_PIN = 6 PWM 信号输入 根据代码输出的 PWM 占空比控制角度

完成接线后的整体效果如下。程序运行后,舵机会依次执行固定角度转动、平滑扫描、智能垃圾桶开合和门锁拨片演示。观察时重点关注舵机是否能平稳转到对应位置,是否存在明显抖动、卡顿、角度偏差或供电不足现象。

CanMV K210 角度舵机控制实验成品图

实验现象 正常表现 异常提示
程序启动 终端输出角度和 PWM 占空比信息 没有输出时检查脚本是否运行和串口连接
舵机回中 舵机转到 0° 附近 完全不动时检查供电、GND 和 SIG 接线
固定角度演示 舵机依次转到 -90°、-45°、0°、45°、90°、0° 角度明显不准时需要校准占空比范围
平滑扫描演示 舵机从一侧平滑转到另一侧,再回中 抖动明显时检查供电和机械负载
智能垃圾桶演示 舵机模拟关闭、打开、等待、再次关闭 开合方向相反时调整打开角度和关闭角度
门锁拨片演示 舵机模拟锁定、解锁和复位 卡住或异响时缩小角度范围

软件代码

本实验代码围绕角度舵机的 PWM 控制展开。程序先定义舵机引脚、PWM 频率、角度范围和占空比范围,再通过 Servo 类封装角度限制、角度换算、直接转动、平滑转动、开合动作和 PWM 释放。演示部分包含固定角度、平滑扫描、智能垃圾桶开合和门锁拨片四类效果。

软件环境 作用 检查重点
CanMV IDE 编辑、运行和调试 K210 程序 能识别开发板串口,并能运行基础 print() 测试
CanMV / MaixPy 固件 提供 machine.Timermachine.PWM 等模块 固件环境需要支持当前 PWM 写法
USB 串口驱动 让电脑识别开发板串口 串口工具中能看到对应端口
稳定 5V 供电 为舵机提供转动电源 供电不足会导致舵机抖动、卡顿或开发板复位
串口终端 查看舵机角度和占空比输出 能看到“舵机角度”“固定角度演示”等日志
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# ----湖南创乐博智能科技有限公司----
# 文件名:39_servo_optimized.py
# 版本:V3.0
# 说明:角度舵机实验 - 优化版 Demo
#
# 适用环境:CanMV K210 / MaixPy
#
# 接线说明:
# 舵机 VCC  -> 5V
# 舵机 GND  -> GND
# 舵机 SIG  -> IO6
#
# 说明:
# 舵机通过 PWM 信号控制角度。
# 常见舵机频率为 50Hz,控制周期约 20ms。
# 本 Demo 将舵机角度抽象为 -90° ~ 90°,
# 便于教学时理解“向左、居中、向右”的控制逻辑。

from machine import Timer, PWM
import time


# =========================
# 舵机基础配置
# =========================

SERVO_PIN = 6
SERVO_FREQ = 50

MIN_ANGLE = -90
MAX_ANGLE = 90

# 常见舵机在 50Hz 下使用 2.5% ~ 12.5% 占空比控制角度
MIN_DUTY = 2.5
MAX_DUTY = 12.5


# =========================
# 舵机控制类
# =========================

class Servo:
    def __init__(self, pin, timer_id=Timer.TIMER0, channel=Timer.CHANNEL0):
        self.pin = pin
        self.timer = Timer(timer_id, channel, mode=Timer.MODE_PWM)
        self.pwm = PWM(self.timer, freq=SERVO_FREQ, duty=0, pin=pin)

        self.current_angle = 0
        self.open_angle = -90
        self.close_angle = 0

        self.set_angle(0, delay_ms=500)

    def limit_angle(self, angle):
        """限制角度范围,避免舵机超出安全转动区间"""
        if angle < MIN_ANGLE:
            return MIN_ANGLE
        if angle > MAX_ANGLE:
            return MAX_ANGLE
        return angle

    def angle_to_duty(self, angle):
        """
        将角度转换为 PWM 占空比

        -90° 对应 MIN_DUTY
         90° 对应 MAX_DUTY
          0° 位于中间位置
        """
        angle = self.limit_angle(angle)
        duty = (angle - MIN_ANGLE) / (MAX_ANGLE - MIN_ANGLE) * (MAX_DUTY - MIN_DUTY) + MIN_DUTY
        return duty

    def set_angle(self, angle, delay_ms=300):
        """直接转动到指定角度"""
        angle = self.limit_angle(angle)
        duty = self.angle_to_duty(angle)

        self.pwm.duty(duty)
        self.current_angle = angle

        print("舵机角度:{}°,PWM占空比:{:.2f}%".format(angle, duty))
        time.sleep_ms(delay_ms)

    def smooth_to(self, target_angle, step=3, delay_ms=20):
        """平滑转动到指定角度,减少舵机突然抖动"""
        target_angle = self.limit_angle(target_angle)

        if target_angle > self.current_angle:
            angle_range = range(self.current_angle, target_angle + 1, step)
        else:
            angle_range = range(self.current_angle, target_angle - 1, -step)

        for angle in angle_range:
            duty = self.angle_to_duty(angle)
            self.pwm.duty(duty)
            self.current_angle = angle
            time.sleep_ms(delay_ms)

        self.set_angle(target_angle, delay_ms=200)

    def set_open_angle(self, angle):
        """设置打开角度"""
        self.open_angle = self.limit_angle(angle)

    def set_close_angle(self, angle):
        """设置关闭角度"""
        self.close_angle = self.limit_angle(angle)

    def open(self):
        """场景动作:打开"""
        print("执行打开动作")
        self.smooth_to(self.open_angle)

    def close(self):
        """场景动作:关闭"""
        print("执行关闭动作")
        self.smooth_to(self.close_angle)

    def center(self):
        """回到中间位置"""
        print("舵机回中")
        self.smooth_to(0)

    def release(self):
        """
        释放 PWM 输出

        注意:
        duty 设置为 0 后,舵机不再持续保持力矩。
        适合实验结束时使用。
        """
        self.pwm.duty(0)
        print("PWM 已释放")


# =========================
# Demo 演示函数
# =========================

def demo_fixed_angles(servo):
    """固定角度演示"""
    angles = [-90, -45, 0, 45, 90, 0]

    for angle in angles:
        servo.set_angle(angle, delay_ms=600)


def demo_smooth_sweep(servo):
    """平滑扫描演示"""
    servo.smooth_to(-90)
    time.sleep_ms(500)

    servo.smooth_to(90)
    time.sleep_ms(500)

    servo.smooth_to(0)
    time.sleep_ms(500)


def demo_smart_trash_bin(servo):
    """
    智能垃圾桶开合演示

    真实项目中可以把 open() 绑定到人体检测、距离传感器、按键检测等事件。
    """
    servo.set_open_angle(-75)
    servo.set_close_angle(0)

    print("垃圾桶关闭")
    servo.close()
    time.sleep_ms(1000)

    print("检测到靠近,垃圾桶打开")
    servo.open()
    time.sleep_ms(2000)

    print("等待结束,垃圾桶关闭")
    servo.close()
    time.sleep_ms(1000)


def demo_door_lock(servo):
    """
    门锁拨片演示

    通过两个角度模拟锁定和解锁状态。
    """
    lock_angle = -60
    unlock_angle = 60

    print("门锁锁定")
    servo.smooth_to(lock_angle)
    time.sleep_ms(1000)

    print("门锁解锁")
    servo.smooth_to(unlock_angle)
    time.sleep_ms(1000)

    print("门锁复位")
    servo.center()
    time.sleep_ms(1000)


# =========================
# 主循环
# =========================

def loop():
    servo = Servo(SERVO_PIN)

    while True:
        print("\n========== 固定角度演示 ==========")
        demo_fixed_angles(servo)

        print("\n========== 平滑转动演示 ==========")
        demo_smooth_sweep(servo)

        print("\n========== 智能垃圾桶演示 ==========")
        demo_smart_trash_bin(servo)

        print("\n========== 门锁拨片演示 ==========")
        demo_door_lock(servo)

        time.sleep_ms(2000)


# =========================
# 程序入口
# =========================

if __name__ == '__main__':
    try:
        loop()
    except KeyboardInterrupt:
        print("程序停止")

这段程序可以分成舵机参数配置、PWM 对象初始化、角度安全限制、角度到占空比换算、基础动作封装、场景动作演示和主循环调度几个部分。SERVO_PIN = 6 明确了 PWM 信号输出引脚,SERVO_FREQ = 50 对应常见舵机 50Hz 控制频率,MIN_ANGLEMAX_ANGLE 把教学角度限制在 -90° ~ 90°MIN_DUTYMAX_DUTY 负责描述角度范围对应的 PWM 占空比范围。

Servo 类是整段程序的核心。初始化方法中创建 Timer 对象,并把定时器设置为 PWM 模式。PWM(self.timer, freq=SERVO_FREQ, duty=0, pin=pin) 会在指定引脚上准备 PWM 输出。初始化完成后,程序默认调用 self.set_angle(0, delay_ms=500),让舵机回到中间位置,避免刚运行时停在不确定角度。

limit_angle() 用于限制舵机转动范围,避免角度超出机械安全区间。angle_to_duty() 用于把角度转换成 PWM 占空比。set_angle() 适合直接观察指定角度位置,smooth_to() 则把一次大角度变化拆成多个小步进,让舵机逐渐接近目标角度。场景函数进一步把角度动作封装成智能垃圾桶开合和门锁拨片动作。

函数名 功能 对应现象
__init__() 初始化舵机对象、定时器和 PWM 输出 舵机准备接收 50Hz PWM 控制信号,并回到中间位置
limit_angle(angle) 限制角度范围 超出 -90° ~ 90° 的角度会被压到安全范围内
angle_to_duty(angle) 将角度换算为 PWM 占空比 不同角度会转换成不同占空比,舵机转到对应位置
set_angle(angle, delay_ms) 直接转动到指定角度 舵机快速移动到指定角度并停留一段时间
smooth_to(target_angle, step, delay_ms) 平滑转动到目标角度 舵机按小步进逐渐转动,动作更柔和
set_open_angle(angle) 设置打开角度 修改 open() 动作对应的舵机位置
set_close_angle(angle) 设置关闭角度 修改 close() 动作对应的舵机位置
open() 执行打开动作 舵机平滑转动到打开角度
close() 执行关闭动作 舵机平滑转动到关闭角度
center() 回到中间位置 舵机平滑转回
release() 释放 PWM 输出 PWM 占空比变为 0,舵机不再持续保持力矩
demo_fixed_angles(servo) 固定角度演示 舵机依次转到 -90°、-45°、0°、45°、90°、0°
demo_smooth_sweep(servo) 平滑扫描演示 舵机从左侧平滑扫到右侧,再回到中间
demo_smart_trash_bin(servo) 智能垃圾桶开合演示 舵机模拟关闭、打开、等待、再次关闭
demo_door_lock(servo) 门锁拨片演示 舵机模拟锁定、解锁和复位
loop() 主循环调度 不断播放固定角度、平滑扫描和场景演示

主程序通过 loop() 创建舵机对象,并在 while True 中循环执行四组演示。固定角度演示适合理解角度和位置的对应关系;平滑扫描适合观察 smooth_to() 的渐变控制效果;智能垃圾桶演示把舵机动作抽象成开合逻辑;门锁拨片演示则把两个角度映射成锁定和解锁状态。

扩展应用

舵机实验比 LED 实验更容易受到供电、PWM 参数和机械结构影响。排查时需要同时关注程序输出、舵机供电、信号线连接和舵机动作范围,避免只从代码角度判断问题。

问题现象 可能原因 处理思路
舵机完全不动 舵机未供电、GND 未共地、SIG 未接 IO6、程序没有运行 检查 VCC 是否接 5V,GND 是否与开发板共地,SIG 是否接到 IO6,并确认终端是否有角度打印
舵机抖动明显 供电电流不足、PWM 信号不稳定、机械负载过大 使用稳定 5V 电源,减少舵机负载,确认 SERVO_FREQ = 50 未被改动
舵机角度不准 不同舵机的占空比范围存在差异 根据实际舵机调整 MIN_DUTYMAX_DUTY,逐步校准左右极限
舵机转向与预期相反 舵机安装方向或机械结构方向不同 交换打开角度和关闭角度,例如把 open_angle 从负角度改成正角度
舵机卡住或发出异响 角度范围超出机械限制,或外部结构阻挡 缩小 MIN_ANGLEMAX_ANGLE,检查舵机摇臂是否被卡住
平滑转动太慢 step 太小或 delay_ms 太大 适当增大 step,或减小 delay_ms
平滑转动太快 step 太大或 delay_ms 太小 适当减小 step,或增大 delay_ms
停止后舵机仍有保持力 PWM 仍在输出有效占空比 在实验结束逻辑中调用 release(),让 duty 变为 0
开发板运行中复位 舵机启动瞬间供电波动较大 舵机使用稳定外部 5V 供电,并与开发板共地

舵机实验的价值在于让程序具备“动作执行能力”。LED 适合表达状态,蜂鸣器适合声音提示,舵机适合改变结构位置。当代码可以控制角度、速度和动作顺序时,就可以把很多生活中的开合、拨动、转向和复位动作抽象成函数。当前程序已经封装出 open()close()center()smooth_to(),这些函数可以直接迁移到更完整的智能硬件项目中。

应用场景 实现思路 可扩展能力
智能垃圾桶开盖 使用 open() 控制舵机转到打开角度,使用 close() 回到关闭角度 后续可接入人体检测、距离传感器或按键触发开合
小型门锁拨片 使用两个角度模拟锁定和解锁状态 可扩展密码输入、刷卡识别或远程控制逻辑
摄像头云台转向 通过 set_angle()smooth_to() 控制摄像头方向 后续可结合 AI 摄像头识别结果自动跟随目标
自动挡板控制 使用舵机改变挡板位置,实现开关、限流或分拣动作 可结合传感器数据判断挡板开合时机
教学演示平台 通过角度变化观察 PWM、占空比、函数封装和类封装 适合作为硬件编程入门课程中的执行器案例
机械臂关节基础 把一个舵机看作一个关节,用角度控制模拟动作 后续可扩展多舵机协同控制,实现更复杂运动
设备复位机构 使用 center() 让机构回到默认状态 可在任务结束、异常恢复或设备启动时自动执行

从工程角度看,当前代码已经把“角度控制”和“场景动作”拆开。底层函数负责 PWM 和角度换算,上层函数负责表达打开、关闭、锁定、解锁这些业务动作。后续如果接入按键、传感器、摄像头识别或网络指令,不需要重写舵机控制逻辑,只需要在触发条件满足时调用对应动作函数。这样的结构比直接在主循环里堆 PWM 数值更容易维护,也更接近真实项目开发方式。

总结

本实验通过 CanMV K210 / MaixPy 控制角度舵机,核心能力包括 PWM 输出、定时器配置、占空比控制、角度映射、范围限制、平滑运动、类封装和场景动作封装。程序从 SERVO_PINSERVO_FREQMIN_DUTYMAX_DUTY 等基础参数开始,把舵机控制抽象成可读性更强的角度操作,再通过 Servo 类统一管理 PWM 输出和动作状态,完整展示了代码如何驱动真实机械结构运动。

这类实验非常适合作为执行器控制入门案例。屏幕中的角度数值会变成舵机摇臂的真实转动,PWM 占空比会变成可观察的机械位置,函数调用会变成打开、关闭、锁定、解锁等动作。后续课程可以继续扩展按键触发、传感器检测、AI 摄像头识别、LCD 状态显示、多舵机机械臂和远程控制等内容。理解了 PWM 与角度舵机之间的关系,智能硬件项目中的很多自动开合、自动转向和机械执行逻辑都可以建立在同一套思路之上。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐