pip install opencv-python adafruit-circuitpython-pca9685 RPi.GPIO numpy

代码说明
-
PWM 控制:
- 通过 PCA9685 控制 L298N 的 ENA/ENB,调整 PWM 占空比 来控制电机速度。
- 速度范围 0-100%(代码中自动转换为 16-bit)。
-
电机方向控制:
- 通过树莓派 GPIO 输出 控制 L298N 的 IN1 ~ IN8。
- GPIO.HIGH / GPIO.LOW 设置 前进/后退/停止。
-
四种运动模式:
move_forward(speed): 让 4 个电机 前进。move_backward(speed): 让 4 个电机 后退。turn_left(speed): 让 左侧电机后退,右侧电机前进。turn_right(speed): 让 右侧电机后退,左侧电机前进。stop(): 停止所有电机。
-
运行测试:
- 前进 2 秒
- 后退 2 秒
- 左转 2 秒
- 右转 2 秒
- 停止
硬件连接


main.py
import cv2
import socket
import struct
import pickle
import threading
import time
from API_CAR import *
# 视频流服务器
def video_stream_server():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('0.0.0.0', 5002))
server.listen(5)
print("视频服务器启动,等待连接...")
client, addr = server.accept()
print(f"视频客户端连接:{addr}")
cap = cv2.VideoCapture(0)
while True:
success, frame = cap.read()
if not success:
break
frame = cv2.resize(frame, (640, 480)) # 降低分辨率
_, buffer = cv2.imencode('.jpg', frame, [cv2.IMWRITE_JPEG_QUALITY, 50]) # JPEG 压缩
data = pickle.dumps(buffer)
client.sendall(struct.pack("Q", len(data)) + data) # 发送数据大小+压缩图像
time.sleep(0.05) # 控制帧率 (20FPS)
cap.release()
client.close()
# 控制指令服务器
def control_server():
global current_speed
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('0.0.0.0', 5003))
server.listen(5)
print("控制服务器启动,等待连接...")
while True:
client, addr = server.accept()
print(f"控制客户端连接:{addr}")
while True:
try:
data = client.recv(1024).decode().strip()
if not data:
break
print("Rec data:",data)
if data.startswith("FORWARD"):
_, speed = data.split()
current_speed = int(speed)
print(f"FORWARD 速度设置为 {current_speed}%")
Car_qian(current_speed)
time.sleep(3)
Car_stop()
elif data.startswith("BACKWARD"):
_, speed = data.split()
current_speed = int(speed)
print(f"BACKWARD 速度设置为 {current_speed}%")
Car_hou(current_speed)
time.sleep(3)
Car_stop()
elif data.startswith("RIGHTMOVE"):
_, speed = data.split()
current_speed = int(speed)
print(f"RIGHTMOVE 速度设置为 {current_speed}%")
Car_move_right(current_speed)
time.sleep(1)
Car_stop()
elif data.startswith("LEFTMOVE"):
_, speed = data.split()
current_speed = int(speed)
print(f"LEFTMOVE 速度设置为 {current_speed}%")
Car_move_left(current_speed)
time.sleep(1)
Car_stop()
elif data.startswith("LEFT"):
_, speed = data.split()
current_speed = int(speed)
print(f"LEFT 速度设置为 {current_speed}%")
Car_turn_left(current_speed)
time.sleep(3)
Car_stop()
elif data.startswith("RIGHT"):
_, speed = data.split()
current_speed = int(speed)
print(f"RIGHT 速度设置为 {current_speed}%")
Car_turn_right(current_speed)
time.sleep(3)
Car_stop()
elif data.startswith("ALLSPEED"):
_, speed = data.split()
current_speed = int(speed)
print(f"速度设置为 {current_speed}%")
set_motor_allspeed(current_speed)
time.sleep(3)
Car_stop()
elif data == "STOP":
#time.sleep(3)
Car_stop()
pass
elif data.startswith("yaw"):
_, speed = data.split()
print(f"yaw 设置为 {speed}%")
set_servo_angle(Dgree_pin['yaw'], int(speed))
elif data.startswith("roll"):
_, speed = data.split()
print(f"roll 设置为 {speed}%")
set_servo_angle(Dgree_pin['roll'], int(speed))
elif data.startswith("pitch"):
_, speed = data.split()
print(f"pitch 设置为 {speed}%")
set_servo_angle(Dgree_pin['pitch'], int(speed))
#stop()
client.send(b"OK")
except:
break
client.close()
if __name__ == "__main__":
threading.Thread(target=video_stream_server, daemon=True).start()
threading.Thread(target=control_server, daemon=True).start()
while True:
time.sleep(3)
API_CAR.py
import time
import board
import busio
import RPi.GPIO as GPIO
from Adafruit_PCA9685 import PCA9685
# 树莓派 GPIO 设置
GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)
#SCL (时钟线):GPIO 3(物理引脚 5)
#SDA (数据线):GPIO 2(物理引脚 3)
#=================1==========================
# 初始化 I2C 连接
i2c = busio.I2C(board.SCL, board.SDA)
if not i2c.try_lock():
print("I2C 未能成功初始化!")
else:
try:
print("I2C 初始化成功!")
finally:
i2c.unlock() # 释放 I2C 锁
Dgree_pin ={'yaw':4,'roll':5,'pitch':6}
# 设置 PWM 频率为 50Hz(舵机标准)
#使用 树莓派 + PCA9685 模拟四旋翼无人机 遥控器(RC)PWM 信号。
#输出 四个通道的 PWM 信号,分别对应 油门、偏航、俯仰、横滚(Throttle, Yaw, Pitch, Roll)。
#PWM 范围:1000us(最小油门)~ 2000us(最大油门)(典型遥控器信号)。
#更新 PWM 频率为 50Hz(标准 RC 频率)。
pca = PCA9685()
pca.set_pwm_freq(50)
pca.frequency = 50 # 设置PWM频率(1000Hz 适用于L298N)
# 4️⃣ 计算 PWM 幅度
def set_servo_angle(channel, angle):
#信号是自己程序伪造的飞行器接收机,因此实际度数映射有偏差
#这个度数只是个参考值 每个轴旋转范围不一样 需要实际重新角度映射
#默认45时候都在水平中心,偏航上电前需要手动把头朝着正前才是默认基准位置。
#偏航大概 20-160 俯仰大概35-60 横滚 40-50 实际具体测试边界
# 举个例子 假设默认上电 是45度 给了35度 实际旋转了30度(理论上应该是10度),这里需要重新映射,【30-45】-【10-0】
print(channel, channel)
# 3️⃣ 设置舵机的 PWM 范围
SERVO_MIN_PULSE = 1000 # 1000us → 0° 不能改
SERVO_MAX_PULSE = 2000 # 2000us → 180° 不能改
SERVO_RANGE = 180.0 # 舵机角度范围(0° - 180°) 能改
#
"""使用 set_pwm 设置舵机角度"""
pulse_length = 1000000 / pca.frequency # 计算 PWM 总周期(微秒)
pulse_length //= 4096 # PCA9685 12-bit 分辨率,每步 = 总周期 / 4096
# 计算脉冲宽度(单位:4096 级)
pulse = int((angle / SERVO_RANGE) * (SERVO_MAX_PULSE - SERVO_MIN_PULSE) + SERVO_MIN_PULSE)
pulse //= pulse_length
# 使用 set_pwm 设置 PWM (0=起始点, pulse=结束点)
pca.set_pwm(channel, 0, int(pulse))
#speed_percentage = max(-100, min(100, angle)) # 限制速度范围 0% 到 100%
#duty_cycle = int(abs(speed_percentage) / 100 * 4095) # PCA9685 使用 12-bit (0-4095)
#pca.set_pwm(channel, 0, int(pulse))
#set_servo_angle(6,60)
#================2===========================
# L298N 连接设置
motor_pins = {
"motor1_zuoqian": {"in1": 16, "in2": 12, "pwm": 0},
"motor2_youqian": {"in1": 21, "in2": 20, "pwm": 1},
"motor3_zuohou": {"in1": 26, "in2": 19, "pwm": 2},
"motor4_youhou": {"in1": 13, "in2": 6, "pwm": 3}
}
current_speed = 50
# 初始化 GPIO
for motor, pins in motor_pins.items():
GPIO.setup(pins["in1"], GPIO.OUT)
GPIO.setup(pins["in2"], GPIO.OUT)
def set_motor_allspeed(speed_percentage):
print("设置所有速度")
for i_name in motor_pins:
pins = motor_pins[i_name]
speed_percentage = max(-100, min(100, speed_percentage)) # 限制速度范围 0% 到 100%
duty_cycle = int(abs(speed_percentage) / 100 * 4095) # PCA9685 使用 12-bit (0-4095)
pca.set_pwm(pins["pwm"], 0, duty_cycle)
def set_motor_speed(motor_name, speed_percentage):
"""设置电机速度 (0% 到 100%),负值表示反转"""
if motor_name not in motor_pins:
print(f"Invalid motor name: {motor_name}")
return
pins = motor_pins[motor_name]
speed_percentage = max(-100, min(100, speed_percentage)) # 限制速度范围 0% 到 100%
duty_cycle = int(abs(speed_percentage) / 100 * 4095) # PCA9685 使用 12-bit (0-4095)
if speed_percentage > 0:
GPIO.output(pins["in1"], GPIO.HIGH)
GPIO.output(pins["in2"], GPIO.LOW)
elif speed_percentage < 0:
GPIO.output(pins["in1"], GPIO.LOW)
GPIO.output(pins["in2"], GPIO.HIGH)
else:
GPIO.output(pins["in1"], GPIO.LOW)
GPIO.output(pins["in2"], GPIO.LOW)
pca.set_pwm(pins["pwm"], 0, duty_cycle)
def Car_qian(speed=50):
print('前进')
set_motor_speed("motor1_zuoqian", speed)
set_motor_speed("motor2_youqian", speed)
set_motor_speed("motor3_zuohou", speed)
set_motor_speed("motor4_youhou", speed)
def Car_hou(speed=50):
print('后退')
set_motor_speed("motor1_zuoqian", -speed)
set_motor_speed("motor2_youqian", -speed)
set_motor_speed("motor3_zuohou", -speed)
set_motor_speed("motor4_youhou", -speed)
# 左转
def Car_turn_left(speed=50):
print('左转')
set_motor_speed("motor1_zuoqian", -speed)
set_motor_speed("motor3_zuohou", -speed)
set_motor_speed("motor2_youqian", speed)
set_motor_speed("motor4_youhou", speed)
# 右转
def Car_turn_right(speed=50):
print('右转')
set_motor_speed("motor1_zuoqian", speed)
set_motor_speed("motor3_zuohou", speed)
set_motor_speed("motor2_youqian", -speed)
set_motor_speed("motor4_youhou", -speed)
# 水平左移
def Car_move_left(speed=50):
print('水平左移')
set_motor_speed("motor1_zuoqian", -speed)
set_motor_speed("motor2_youqian", speed)
set_motor_speed("motor3_zuohou", speed)
set_motor_speed("motor4_youhou", -speed)
# 水平右移
def Car_move_right(speed=50):
print('水平右移')
set_motor_speed("motor1_zuoqian", speed)
set_motor_speed("motor2_youqian", -speed)
set_motor_speed("motor3_zuohou", -speed)
set_motor_speed("motor4_youhou", speed)
def Car_stop(speed=0):
#for m in motor_pins.keys():
#set_motor_speed(m, 0)
print('停止')
set_motor_speed("motor1_zuoqian", 0)
set_motor_speed("motor2_youqian", 0)
set_motor_speed("motor3_zuohou", 0)
set_motor_speed("motor4_youhou", 0)
# 测试代码
'''
if __name__ == "__main__":
try:
set_servo_angle(Dgree_pin['pitch'], 45)
set_servo_angle(Dgree_pin['roll'], 45)
set_servo_angle(Dgree_pin['yaw'], 45)
#Car_qian(50)
#Car_hou(50)
#Car_turn_left(50)
#Car_turn_right(50)
#Car_move_right(50)
#Car_move_left(50)
time.sleep(3)
#set_motor_allspeed(30)
#time.sleep(3)
Car_stop()
except KeyboardInterrupt:
print("手动停止")
Car_stop()
GPIO.cleanup()
finally:
GPIO.cleanup()
'''
浙公网安备 33010602011771号