麦克纳姆轮小车
- 车身搭建,简单的电机控制。
- 测速
- pid控制
开一个坑,借助ChatGPT完成一个小玩具。
不是0基础,还是要有一定单片机电子模块基础的。
1.硬件要求
目前手里有:
1个车架,
4个直流减速电机(淘宝上常见的tt电机),
4个对射光电传感器,
L9110S四路电机驱动板(不太好用,我替换成了一对l298n),
2对麦克纳姆轮(大家买轮子要注意,别买错方向了,亲身踩的坑),
1块esp32+拓展版,
4节18650。
2.避坑经验:
组装小车
1.电机方向一致,供电的两个铜极朝向同一边,能给你后续调试代码节省很多时间。
2.组装小车的时候,轮子的方向别错了,不懂怎么装就按x形(从上往下看)。
3.我的车底盘是金属的,如果你的也是,切记要把模块背面贴上绝缘胶布(或者干脆架起来)。否则模块一上电碰火,啥都烧了。
4.l298n的5V接口可以当做电源给单片机供电的。前提是!!!L298n的外接电源是12v以下的。过了12那个脚测出来就不止5V,容易给esp32干废了。
![](https://i-blog.csdnimg.cn/direct/64337d479fb042f893164cc8a91152e3.jpeg)
![](https://i-blog.csdnimg.cn/direct/50e718f5f08b46abac31923ff770de14.jpeg)
![](https://i-blog.csdnimg.cn/direct/1c7e8dc5c8f648e3b6866b430e372feb.jpeg)
已经21:37了,今天试着做一下电机控制。
3.chatGPT解答,问题1
给GPT第一个问题:
写一个esp32 micropython pwm 控制直流电机程序
GPT回复了我2段源码
代码说明
引脚初始化:
in1
和 in2
用于控制电机的旋转方向。
enable
是一个 PWM 对象,用于控制电机的速度。
PWM 设置:
使用 enable.freq(1000)
设置 PWM 的频率为 1kHz。这个频率是大多数电机的常用选择,可以根据需要进行调整。
电机控制函数:
motor_forward(speed):使电机按指定速度向前旋转。
motor_backward(speed):使电机按指定速度向后旋转。
motor_stop():停止电机。
速度测试函数:
motor_speed_test() 函数通过一系列速度变化测试电机在不同速度下的运行情况。
错误处理:
使用 try-except 块捕获键盘中断,确保在程序结束时电机被正确停止。
from machine import Pin, PWM
from time import sleep
# 初始化电机引脚
in1 = Pin(15, Pin.OUT) # IN1 引脚
in2 = Pin(2, Pin.OUT) # IN2 引脚
enable = PWM(Pin(4)) # ENA 引脚,用于 PWM 控制速度
enable.freq(1000) # 设置 PWM 频率为 1kHz
def motor_forward(speed):
"""使电机向前运行"""
in1.value(1) # IN1 设置为高电平
in2.value(0) # IN2 设置为低电平
# 将速度转换为 0-1023 范围内的 PWM 占空比
duty_cycle = int(speed * 1023 / 100)
enable.duty(duty_cycle)
print(f"Motor forward at {speed}% speed")
def motor_backward(speed):
"""使电机向后运行"""
in1.value(0) # IN1 设置为低电平
in2.value(1) # IN2 设置为高电平
duty_cycle = int(speed * 1023 / 100)
enable.duty(duty_cycle)
print(f"Motor backward at {speed}% speed")
def motor_stop():
"""停止电机"""
in1.value(0)
in2.value(0)
enable.duty(0)
print("Motor stopped")
def motor_speed_test():
"""测试电机在不同速度下的运行"""
try:
# 前进并逐渐增加速度
for speed in range(0, 101, 20): # 从0%到100%,步长为20%
motor_forward(speed)
sleep(2)
motor_stop() # 停止电机
sleep(2)
# 后退并逐渐增加速度
for speed in range(0, 101, 20):
motor_backward(speed)
sleep(2)
motor_stop() # 停止电机
except KeyboardInterrupt:
motor_stop()
print("Program interrupted and motor stopped")
# 主程序
if __name__ == "__main__":
print("Starting motor control test")
motor_speed_test()
GPT的代码规范是真规范,表扬一波。
4.最终电机控制测试代码
这里和我的需求有些差异,我手里的模块没有enable(使能口)
稍稍调整一下端口和函数:
上传后的效果是小车前进1秒停1秒。
from machine import Pin, PWM
from time import sleep
# 初始化电机引脚,别再问怎么接线啦!!!
in1 = PWM(Pin(13)) # IN1 引脚用于 PWM 控制速度
in2 = PWM(Pin(12)) # IN2 引脚用于 PWM 控制速度
in3 = PWM(Pin(16))
in4 = PWM(Pin(27))
in5 = PWM(Pin(26))
in6 = PWM(Pin(25))
in7 = PWM(Pin(33))
in8 = PWM(Pin(32))
inNlist = [in1,in2,in3,in4,in5,in6,in7,in8]
for i in inNlist:
i.freq(1000) # 设置 PWM 频率为 1kHz
i.duty(0) # IN1 设置占空比为0,初始速度设为0
# 我太懒了,不想写引脚,干脆封装成轮子,后面直接操作轮子对象
class Wheel:
def __init__(self,ina,inb):
self.ina = ina
self.inb = inb
# 后左轮 后右 前左 前右 分别为wa,wb,wc,wd
wa = Wheel(in1,in2)
wb = Wheel(in3,in4)
wc = Wheel(in5,in6)
wd = Wheel(in7,in8)
def motor_forward(wheel,speed):
"""使电机运行,范围[-100,100]"""
duty_cycle = int(abs(speed) * 1023 / 100) # 将速度转换为 0-1023 范围内的 PWM 占空比
if(speed>0):
wheel.ina.duty(duty_cycle) # 所需速度
wheel.inb.duty(0) # 占空比0
elif(speed<0):
wheel.inb.duty(duty_cycle) # 速度反向
wheel.ina.duty(0)
else:
wheel.ina.duty(0) # 没输入速度就停止,避免有奇奇怪怪bug
wheel.inb.duty(0)
print(f"Motor forward at {speed}% speed")
# 主程序
if __name__ == "__main__":
print("Starting motor control test")
while(1):
motor_forward(wa,30)
motor_forward(wb,30)
motor_forward(wc,30)
motor_forward(wd,30)
sleep(1)
motor_forward(wa,0)
motor_forward(wb,0)
motor_forward(wc,0)
motor_forward(wd,0)
sleep(1)
GPT代码分析
大家遇到看不懂的代码,也可以扔给GPT,比如我把我改过的代码扔回去了
她甚至给我意见怎么改,泪目
下面是gpt回复内容:
这段代码是用来控制四个电机的简单程序,可能用于一个四轮小车或机器人。代码使用了 PWM(脉宽调制)来控制每个电机的速度和方向。
5.ChatGPT代码分析
1. 引入库和初始化电机引脚
from machine import Pin, PWM
from time import sleep
# 初始化电机引脚
in1 = PWM(Pin(13)) # IN1 引脚用于 PWM 控制速度
in2 = PWM(Pin(12)) # IN2 引脚用于 PWM 控制速度
in3 = PWM(Pin(16))
in4 = PWM(Pin(27))
in5 = PWM(Pin(26))
in6 = PWM(Pin(25))
in7 = PWM(Pin(33))
in8 = PWM(Pin(32))
inNlist = [in1,in2,in3,in4,in5,in6,in7,in8]
for i in inNlist:
i.freq(1000) # 设置 PWM 频率为 1kHz
i.duty(0) # IN1 设置占空比为0
- 这里导入了两个模块:
machine
用于控制引脚,time
用于处理延时。 - 使用
Pin
和PWM
初始化了八个引脚(in1
到in8
),用于控制四个电机(每个电机需要两个引脚)。 - 频率被设置为 1kHz,这个频率适合多数电机的 PWM 控制。
duty
方法被设置为 0,这表示电机初始状态是停止的,因为占空比为 0 时,电机没有接收到电压。
2. 封装成 Wheel 类
# 我太懒了,不想写引脚,干脆封装成轮子,后面直接操作轮子对象
class Wheel:
def __init__(self,ina,inb):
self.ina = ina
self.inb = inb
# 后左轮 后右 前左 前右 分别为 wa, wb, wc, wd
wa = Wheel(in1,in2)
wb = Wheel(in3,in4)
wc = Wheel(in5,in6)
wd = Wheel(in7,in8)
- 创建了一个
Wheel
类,用于封装每个轮子所需的两个引脚。 - 初始化四个
Wheel
对象wa
,wb
,wc
, 和wd
,分别代表后左轮、后右轮、前左轮和前右轮。 - 封装类的做法使得后续代码更简洁,易于管理和修改。
3. 电机控制函数
def motor_forward(wheel, speed):
"""使电机运行,范围[-100,100]"""
duty_cycle = int(abs(speed) * 1023 / 100) # 将速度转换为 0-1023 范围内的 PWM 占空比
if(speed>0):
wheel.ina.duty(duty_cycle) # 所需速度
wheel.inb.duty(0) # 占空比0
elif(speed<0):
wheel.inb.duty(duty_cycle) # 速度反向
wheel.ina.duty(0)
else:
wheel.ina.duty(0) # 没输入速度就停止,避免有奇奇怪怪bug
wheel.inb.duty(0)
print(f"Motor forward at {speed}% speed")
- 函数
motor_forward
用于控制电机的运动方向和速度。 - 参数:
wheel
:Wheel
类的一个实例。speed
: 控制速度的整数,范围为 -100 到 100。
- 逻辑:
- 速度转换为 PWM 的占空比(
duty_cycle
)。这里假设最大占空比为 1023(常用于 10 位 PWM)。 - 当
speed
大于 0 时,表示正转,ina
设置为duty_cycle
,inb
设置为 0。 - 当
speed
小于 0 时,表示反转,inb
设置为duty_cycle
,ina
设置为 0。 - 当
speed
等于 0 时,停止电机。
- 速度转换为 PWM 的占空比(
- 注意事项:
duty_cycle
计算部分使用了abs
函数,因此speed
的符号用于确定电机方向,而不影响duty_cycle
的计算。
4. 主程序
if __name__ == "__main__":
print("Starting motor control test")
while(1):
motor_forward(wa, 30)
motor_forward(wb, 30)
motor_forward(wc, 30)
motor_forward(wd, 30)
sleep(1)
motor_forward(wa, 0)
motor_forward(wb, 0)
motor_forward(wc, 0)
motor_forward(wd, 0)
sleep(1)
- 主程序中包含了一个无限循环,用于测试电机的前进和停止。
- 电机被设置为 30% 的速度运行 1 秒,然后停止 1 秒。
sleep
函数用于实现简单的时间控制。- 代码中的
while(1)
是一个无限循环,可以根据需要修改成其他逻辑,如使用条件退出循环或者中断信号。
可能的改进
-
引入更多控制功能:
- 可以添加反转、转弯等更多控制功能。通过增加新的函数来实现不同的运动模式,比如:
def motor_reverse(wheel, speed): """反转电机""" motor_forward(wheel, -speed)
- 可以添加反转、转弯等更多控制功能。通过增加新的函数来实现不同的运动模式,比如:
-
PWM频率和占空比的调整:
- 可以根据具体电机和应用调整 PWM 频率和占空比,以便实现更好的控制效果。
- 对于不同类型的电机,可能需要使用不同的 PWM 频率来优化性能和减少噪音。
-
异常处理:
- 增加异常处理来捕获可能的错误,比如引脚初始化错误、参数值超出范围等。
- 使用
try...except
结构来处理硬件异常:try: in1 = PWM(Pin(13)) # 初始化其他引脚 except Exception as e: print("Error initializing PWM:", e)
-
增加对PWM值的范围检查:
- 确保
speed
参数在合适范围内,例如限制在 [-100, 100],以防止误操作。 - 在
motor_forward
中可以增加范围检查:if speed < -100 or speed > 100: raise ValueError("Speed must be between -100 and 100")
- 确保
-
改进结构设计:
- 可以考虑用继承和多态来设计更加模块化和可扩展的结构,例如将电机的前进和停止封装成方法。
- 通过继承基础
Wheel
类,创建特定行为的类:class Car: def __init__(self, wheels): self.wheels = wheels def move_forward(self, speed): for wheel in self.wheels: motor_forward(wheel, speed) def stop(self): for wheel in self.wheels: motor_forward(wheel, 0) car = Car([wa, wb, wc, wd])
总结
这段代码是一个简单的电机控制例程,适合初学者学习如何使用 PWM 控制电机。通过以上的分析和建议,你可以更好地理解代码,并进行改进以满足更复杂的需求。请根据具体应用场景进行调整和扩展,以便获得最佳性能和稳定性。如果有任何其他问题或需要进一步的帮助,请随时告诉我!