一、引言
在物联网和智能硬件的时代,远程控制设备变得越来越普遍。本文将介绍如何使用 Python 编写代码来远程控制树莓派小车,实现基本的前进、后退、左转、右转和停止功能。我将详细解释代码的思路,并给出完整的代码示例。
系列文章:树莓派小车舵机控制-CSDN博客
二、整体思路
整个远程控制树莓派小车的系统主要由两部分组成:服务器端(树莓派)和客户端(电脑)。服务器端负责控制小车的电机,接收客户端发送的指令并执行相应的动作;客户端负责向服务器端发送控制指令。
服务器端(server.py)
服务器端的主要任务是初始化树莓派的 GPIO 引脚,设置电机控制引脚,创建一个 TCP 套接字并监听客户端的连接。当有客户端连接时,接收客户端发送的指令,并根据指令调用相应的小车运动函数。
1、导入必要的库:
import RPi.GPIO as GPIO
import socket
import time
RPi.GPIO
用于控制树莓派的 GPIO 引脚,socket
用于创建网络套接字,time
用于控制电机运行的时间。
2、设置 GPIO 模式和电机控制引脚:
# 设置GPIO模式
GPIO.setmode(GPIO.BOARD)
# 定义电机控制引脚
ENA = 32
IN1 = 22
IN2 = 15
ENB = 33
IN3 = 16
IN4 = 18
# 电机引脚初始化为输出模式
GPIO.setup(IN1, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(IN2, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(ENB, GPIO.OUT, initial=GPIO.HIGH)
GPIO.setup(IN3, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(IN4, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(ENA, GPIO.OUT, initial=GPIO.HIGH)
# 设置电机pwm引脚和频率为100hz
pwm_ENA = GPIO.PWM(ENA, 100)
pwm_ENB = GPIO.PWM(ENB, 100)
pwm_ENA.start(0)
pwm_ENB.start(0)
这里我使用 GPIO.BOARD
模式来指定引脚编号,然后将电机控制引脚初始化为输出模式,并设置 PWM(脉冲宽度调制)引脚和频率。
3、定义小车运动函数:
def forward():
GPIO.output(IN1, GPIO.HIGH)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.HIGH)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(20)
pwm_ENB.ChangeDutyCycle(20)
time.sleep(1)
stop()
print("forward")
def backward():
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.HIGH)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.HIGH)
pwm_ENA.ChangeDutyCycle(20)
pwm_ENB.ChangeDutyCycle(20)
time.sleep(1)
stop()
print("backward")
def left():
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.HIGH)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(0)
pwm_ENB.ChangeDutyCycle(15)
time.sleep(1)
stop()
print("left")
def right():
GPIO.output(IN1, GPIO.HIGH)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(15)
pwm_ENB.ChangeDutyCycle(0)
time.sleep(1)
stop()
print("right")
def stop():
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(0)
pwm_ENB.ChangeDutyCycle(0)
这些函数分别控制小车的前进、后退、左转、右转和停止。通过设置 GPIO 引脚的电平状态和 PWM 占空比来控制电机的转动方向和速度。
4、创建网络套接字并监听客户端连接:
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host = "192.168.152.246" #此处为你的树莓派ip地址
port = 12345
# 绑定端口
server_socket.bind((host, port))
# 设置最大连接数,超过后排队
server_socket.listen(5)
while True:
# 建立客户端连接
client_socket, addr = server_socket.accept()
print("连接地址: %s" % str(addr))
try:
while True:
# 接收客户端消息
data = client_socket.recv(1024).decode('utf-8')
if not data:
break
if data == 'forward':
forward()
elif data == 'backward':
backward()
elif data == 'left':
left()
elif data == 'right':
right()
elif data == 'stop':
stop()
except Exception as e:
print(f"发生错误: {e}")
finally:
# 关闭客户端连接
client_socket.close()
# 清理GPIO设置
GPIO.cleanup()
这里我创建了一个 TCP 套接字,绑定到指定的主机和端口,并开始监听客户端的连接。当有客户端连接时,接收客户端发送的指令,并根据指令调用相应的小车运动函数。
客户端(client.py)
客户端的主要任务是创建一个 TCP 套接字,连接到服务器端,然后通过用户输入的指令向服务器端发送控制指令。
1、导入必要的库:
import socket
socket
用于创建网络套接字。
2、创建套接字并连接到服务器:
# 创建socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取服务器的主机名
host = "192.168.152.246" #此处为你的树莓派ip地址
port = 12345
# 连接服务,指定主机和端口
client_socket.connect((host, port))
这里我创建了一个 TCP 套接字,并连接到服务器端的指定主机和端口。
3、接收用户输入并发送指令:
while True:
command = input("请输入控制指令 (forward/backward/left/right/stop/exit): ")
if command == 'exit':
break
# 发送指令到服务器
client_socket.send(command.encode('utf-8'))
# 关闭连接
client_socket.close()
这里我通过一个循环不断接收用户输入的指令,当用户输入 exit
时,退出循环并关闭连接。否则,将指令编码为 UTF-8 格式并发送到服务器端。
三、完整代码
1、server.py
import RPi.GPIO as GPIO
import socket
import time
# 设置GPIO模式
GPIO.setmode(GPIO.BOARD)
# 定义电机控制引脚
ENA = 32
IN1 = 22
IN2 = 15
ENB = 33
IN3 = 16
IN4 = 18
# 电机引脚初始化为输出模式
global pwm_ENA
global pwm_ENB
GPIO.setup(IN1, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(IN2, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(ENB, GPIO.OUT, initial=GPIO.HIGH)
GPIO.setup(IN3, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(IN4, GPIO.OUT, initial=GPIO.LOW)
GPIO.setup(ENA, GPIO.OUT, initial=GPIO.HIGH)
# 设置电机pwm引脚和频率为100hz
pwm_ENA = GPIO.PWM(ENA, 100)
pwm_ENB = GPIO.PWM(ENB, 100)
pwm_ENA.start(0)
pwm_ENB.start(0)
# 定义小车运动函数
def forward():
GPIO.output(IN1, GPIO.HIGH)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.HIGH)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(20)
pwm_ENB.ChangeDutyCycle(20)
time.sleep(1)
stop()
print("forward")
def backward():
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.HIGH)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.HIGH)
pwm_ENA.ChangeDutyCycle(20)
pwm_ENB.ChangeDutyCycle(20)
time.sleep(1)
stop()
print("backward")
def left():
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.HIGH)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(0)
pwm_ENB.ChangeDutyCycle(15)
time.sleep(1)
stop()
print("left")
def right():
GPIO.output(IN1, GPIO.HIGH)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(15)
pwm_ENB.ChangeDutyCycle(0)
time.sleep(1)
stop()
print("right")
def stop():
GPIO.output(IN1, GPIO.LOW)
GPIO.output(IN2, GPIO.LOW)
GPIO.output(IN3, GPIO.LOW)
GPIO.output(IN4, GPIO.LOW)
pwm_ENA.ChangeDutyCycle(0)
pwm_ENB.ChangeDutyCycle(0)
# 创建socket对象
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取本地主机名
host = "192.168.152.246"
port = 12345
# 绑定端口
server_socket.bind((host, port))
# 设置最大连接数,超过后排队
server_socket.listen(5)
while True:
# 建立客户端连接
client_socket, addr = server_socket.accept()
print("连接地址: %s" % str(addr))
try:
while True:
# 接收客户端消息
data = client_socket.recv(1024).decode('utf-8')
if not data:
break
if data == 'forward':
forward()
elif data == 'backward':
backward()
elif data == 'left':
left()
elif data == 'right':
right()
elif data == 'stop':
stop()
except Exception as e:
print(f"发生错误: {e}")
finally:
# 关闭客户端连接
client_socket.close()
# 清理GPIO设置
GPIO.cleanup()
2、client.py
import socket
# 创建socket对象
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 获取服务器的主机名
host = "192.168.152.246"
port = 12345
# 连接服务,指定主机和端口
client_socket.connect((host, port))
while True:
command = input("请输入控制指令 (forward/backward/left/right/stop/exit): ")
if command == 'exit':
break
# 发送指令到服务器
client_socket.send(command.encode('utf-8'))
# 关闭连接
client_socket.close()
四、运行代码
-
在树莓派上运行
server.py
脚本。 -
在客户端设备(如电脑)上运行
client.py
脚本。 -
在客户端输入控制指令,即可控制树莓派小车的运动。
五、注意事项
-
要保证树莓派和客户端设备处于同一网络中。
-
host = "192.168.152.246" 此处为你的树莓派ip地址
六、可能遇到的问题
我在运行client,py时遇到
Traceback (most recent call last): File "C:\Users\25518\Desktop\client.py", line 11, in <module> client_socket.connect((host, port)) ConnectionRefusedError: [WinError 10061] 由于目标计算机积极拒绝,无法连接。
原因是因为windows有防火墙限制
我的解决办法是 在防火墙的入站规则中添加允许 Python 程序通过 12345 端口的规则
打开 “Windows Defender 防火墙高级安全”
在 “Windows Defender 防火墙高级安全” 窗口的左侧面板中,右键单击 “入站规则”,然后选择 “新建规则”。
在弹出的 “新建入站规则向导” 中,选择 “端口”,然后点击 “下一步”。
选择 “特定本地端口”,输入 12345
,然后点击 “下一步”。
选择 “允许连接”,然后点击 “下一步”。
可以根据自己的需求勾选 “域”“专用”“公用” 网络类型,然后点击 “下一步”。
为规则命名,例如 “Python 12345 端口入站规则”,可以添加描述信息以便后续识别,然后点击 “完成”。
七、总结
通过以上步骤,你就能成功搭建并实现树莓派小车的远程控制。实现之后还可以进一步拓展小车功能,如添加pid算法,增加舵机控制等等,让树莓派小车更加智能。