首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >树莓派小车进阶:用 Python+Tornado 实现按键控制,电脑端就能操控!

树莓派小车进阶:用 Python+Tornado 实现按键控制,电脑端就能操控!

原创
作者头像
高老师
发布2025-09-17 18:16:05
发布2025-09-17 18:16:05
3200
代码可运行
举报
运行总次数:0
代码可运行

树莓派小车进阶:用Python+Tornado实现按键控制,电脑端就能操控!

玩树莓派小车时,光让它自己跑可不够有意思——上周跟着教程装好了小车底盘、接好电机,这次终于到了“手动操控”的环节。原本以为直接写个GPIO控制脚本就行,结果发现需要实时响应按键指令,最后用Tornado框架搭了个简单的Web服务,实现了电脑键盘和鼠标都能控制小车。今天把完整步骤拆解开,新手也能跟着做出来。

一、先理清楚:按键控制的核心逻辑

要让按键控制小车,本质是“实时接收指令→控制GPIO引脚电平→驱动电机转动”。这里有两个关键问题要解决:

  1. 指令接收:怎么让树莓派实时拿到我们按的键(比如W前进、A左转)?用Web服务最方便,电脑浏览器访问树莓派的IP,就能通过键盘或鼠标发送指令;
  2. 异步响应:按键按下时需要小车持续动作(比如按住W就一直前进),普通脚本容易卡顿,所以用Tornado这个异步IO框架,能高效处理实时请求。

另外,还需要准备这些工具:

  • 已组装好的树莓派小车(带电机驱动模块,比如L298N);
  • 树莓派系统(建议用Raspbian,已预装Python);
  • 电脑和树莓派在同一局域网(方便浏览器访问)。

二、第一步:安装依赖库(Tornado+GPIO控制库)

树莓派默认没有装Tornado,而且控制GPIO需要RPi.GPIO库(如果没装的话),先把这两个库装好。

1. 安装RPi.GPIO(控制GPIO引脚)

如果是官方Raspbian系统,一般自带这个库;如果没有,执行以下命令安装:

代码语言:bash
复制
sudo apt-get update
sudo apt-get install python-rpi.gpio  # Python2版本
# 如果你用Python3,装这个:
# sudo apt-get install python3-rpi.gpio

2. 安装Tornado(异步Web框架)

推荐用pip安装,简单快捷。如果没装pip,先装pip:

代码语言:bash
复制
# 安装pip(Python2)
sudo apt-get install python-pip
# 安装Tornado
sudo pip install tornado

如果pip安装慢,也可以用源码安装(备用方案):

代码语言:bash
复制
# 下载Tornado源码包
wget https://pypi.python.org/packages/source/t/tornado/tornado-4.3.tar.gz
# 解压
tar xvzf tornado-4.3.tar.gz
# 进入目录编译安装
cd tornado-4.3
python setup.py build
sudo python setup.py install

三、第二步:接线!GPIO引脚对应关系要记牢

在写代码前,必须先把树莓派和电机驱动模块(以L298N为例)接好线,不然代码跑起来小车也不会动。这里用的是树莓派的BOARD引脚编号(按物理引脚顺序编号,不容易乱)。

引脚对应表

树莓派BOARD引脚

电机驱动模块(L298N)

作用

11

IN1

控制左电机正转/反转

12

IN2

控制左电机正转/反转

13

IN3

控制右电机正转/反转

15

IN4

控制右电机正转/反转

5V

12V(需外接电源)

给电机供电(树莓派5V不够,必须外接)

GND

GND

共地(必须接,否则会烧模块)

接线注意:电机驱动模块的电源一定要外接(比如12V电池),直接用树莓派的5V引脚供电会导致树莓派死机,甚至烧硬件!

四、第三步:写代码!分Python后端和HTML前端

整个控制逻辑分两部分:Python后端(用Tornado搭Web服务,处理GPIO控制)和HTML前端(提供按键界面,发送指令给后端)。

1. Python后端代码(小车控制核心)

新建一个car_control.py文件,代码里包含了GPIO初始化、小车动作函数(前进、后退、转弯)和Tornado服务配置:

代码语言:python
代码运行次数:0
运行
复制
#!/usr/bin/python
# coding: utf8
import RPi.GPIO as GPIO
import time
import tornado.ioloop
import tornado.web
import tornado.httpserver
import tornado.options
from tornado.options import define, options

# 定义Web服务端口(默认80,浏览器访问时不用输端口)
define("port", default=80, type=int)

# 定义GPIO引脚(对应树莓派BOARD引脚)
IN1 = 11  # 左电机控制1
IN2 = 12  # 左电机控制2
IN3 = 13  # 右电机控制1
IN4 = 15  # 右电机控制2

def init():
    """初始化GPIO引脚"""
    GPIO.setmode(GPIO.BOARD)  # 使用BOARD引脚编号
    # 设置4个引脚为输出模式
    GPIO.setup(IN1, GPIO.OUT)
    GPIO.setup(IN2, GPIO.OUT)
    GPIO.setup(IN3, GPIO.OUT)
    GPIO.setup(IN4, GPIO.OUT)

# ------------------- 小车动作函数 -------------------
def forward(tf):
    """前进:左右电机同时正转"""
    init()
    GPIO.output(IN1, GPIO.HIGH)
    GPIO.output(IN2, GPIO.LOW)
    GPIO.output(IN3, GPIO.HIGH)
    GPIO.output(IN4, GPIO.LOW)
    time.sleep(tf)  # 持续tf秒
    GPIO.cleanup()  # 释放GPIO资源

def reverse(tf):
    """后退:左右电机同时反转"""
    init()
    GPIO.output(IN1, GPIO.LOW)
    GPIO.output(IN2, GPIO.HIGH)
    GPIO.output(IN3, GPIO.LOW)
    GPIO.output(IN4, GPIO.HIGH)
    time.sleep(tf)
    GPIO.cleanup()

def left(tf):
    """左转:右电机正转,左电机停转"""
    init()
    GPIO.output(IN1, GPIO.LOW)
    GPIO.output(IN2, GPIO.LOW)
    GPIO.output(IN3, GPIO.HIGH)
    GPIO.output(IN4, GPIO.LOW)
    time.sleep(tf)
    GPIO.cleanup()

def right(tf):
    """右转:左电机正转,右电机停转"""
    init()
    GPIO.output(IN1, GPIO.HIGH)
    GPIO.output(IN2, GPIO.LOW)
    GPIO.output(IN3, GPIO.LOW)
    GPIO.output(IN4, GPIO.LOW)
    time.sleep(tf)
    GPIO.cleanup()

def pivot_left(tf):
    """后左转:左电机反转,右电机停转"""
    init()
    GPIO.output(IN1, GPIO.LOW)
    GPIO.output(IN2, GPIO.HIGH)
    GPIO.output(IN3, GPIO.LOW)
    GPIO.output(IN4, GPIO.LOW)
    time.sleep(tf)
    GPIO.cleanup()

def pivot_right(tf):
    """后右转:右电机反转,左电机停转"""
    init()
    GPIO.output(IN1, GPIO.LOW)
    GPIO.output(IN2, GPIO.LOW)
    GPIO.output(IN3, GPIO.LOW)
    GPIO.output(IN4, GPIO.HIGH)
    time.sleep(tf)
    GPIO.cleanup()

def p_left(tf):
    """原地左转:左电机反转,右电机正转"""
    init()
    GPIO.output(IN1, GPIO.LOW)
    GPIO.output(IN2, GPIO.HIGH)
    GPIO.output(IN3, GPIO.HIGH)
    GPIO.output(IN4, GPIO.LOW)
    time.sleep(tf)
    GPIO.cleanup()

def p_right(tf):
    """原地右转:左电机正转,右电机反转"""
    init()
    GPIO.output(IN1, GPIO.HIGH)
    GPIO.output(IN2, GPIO.LOW)
    GPIO.output(IN3, GPIO.LOW)
    GPIO.output(IN4, GPIO.HIGH)
    time.sleep(tf)
    GPIO.cleanup()

# ------------------- Tornado请求处理 -------------------
class IndexHandler(tornado.web.RequestHandler):
    """处理Web请求:GET显示页面,POST处理按键指令"""
    def get(self):
        # 访问树莓派IP时,返回前端HTML页面
        self.render("index.html")
    
    def post(self):
        # 接收前端发送的按键指令(比如w、a、s、d)
        key = self.get_argument('k')
        sleep_time = 0.1  # 每次动作持续0.1秒,避免动作太猛
        init()  # 初始化GPIO
        
        # 根据按键指令执行对应动作
        if key == 'w':
            forward(sleep_time)
        elif key == 's':
            reverse(sleep_time)
        elif key == 'a':
            left(sleep_time)
        elif key == 'd':
            right(sleep_time)
        elif key == 'q':
            pivot_left(sleep_time)
        elif key == 'e':
            pivot_right(sleep_time)
        elif key == 'z':
            p_left(sleep_time)
        elif key == 'x':
            p_right(sleep_time)
        
        self.write(key)  # 给前端返回确认信息

# ------------------- 启动Web服务 -------------------
if __name__ == '__main__':
    tornado.options.parse_command_line()
    # 配置Tornado路由:访问根路径时,交给IndexHandler处理
    app = tornado.web.Application(handlers=[(r"/", IndexHandler)])
    http_server = tornado.httpserver.HTTPServer(app)
    http_server.listen(options.port)  # 监听配置的端口
    print("小车控制服务已启动,浏览器访问树莓派IP即可控制!")
    tornado.ioloop.IOLoop.instance().start()  # 启动事件循环

2. HTML前端代码(按键界面)

在和car_control.py同一目录下,新建一个templates文件夹(Tornado默认从这个文件夹读取HTML模板),然后在templates里新建index.html文件,代码如下:

代码语言:html
复制
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>树莓派小车控制</title>
    <!-- 引入jQuery,方便发送POST请求 -->
    <script src="http://libs.baidu.com/jquery/1.9.0/jquery.js"></script>
</head>
<body>
<script type="text/javascript">
    // 发送按键指令给后端
    function sendKey(key) {
        $.post('/', {k: key}, function(){});
    }

    $(function(){
        // 1. 键盘控制:按下对应键发送指令
        window.document.onkeydown = function(env) {
            env = env || window.event;
            var keyCode = env.keyCode;
            // W(87)=前进,S(83)=后退,A(65)=左转,D(68)=右转
            if (keyCode == 87) sendKey('w');
            if (keyCode == 83) sendKey('s');
            if (keyCode == 65) sendKey('a');
            if (keyCode == 68) sendKey('d');
            // Q(81)=后左转,E(69)=后右转,Z(90)=原地左转,X(88)=原地右转
            if (keyCode == 81) sendKey('q');
            if (keyCode == 69) sendKey('e');
            if (keyCode == 90) sendKey('z');
            if (keyCode == 88) sendKey('x');
        };

        // 2. 鼠标控制:按住按钮持续发送指令
        var timer = null;
        // 前进按钮(按住持续前进)
        $('.forward').mousedown(function() {
            timer = setInterval(function() {
                sendKey('w');
            }, 100); // 每100毫秒发一次指令,避免动作卡顿
        });
        // 左转按钮
        $('.left').mousedown(function() {
            timer = setInterval(function() {
                sendKey('a');
            }, 100);
        });
        // 右转按钮
        $('.right').mousedown(function() {
            timer = setInterval(function() {
                sendKey('d');
            }, 100);
        });
        // 后退按钮
        $('.back').mousedown(function() {
            timer = setInterval(function() {
                sendKey('s');
            }, 100);
        });
        // 松开鼠标时停止发送指令
        $('#control-panel span').mouseup(function() {
            clearInterval(timer);
        });
    });
</script>

<!-- 控制按钮样式:3x3网格,中间空着,四周是功能键 -->
<style type="text/css">
    #control-panel {
        width: 150px;
        height: 150px;
        background: #eee;
        margin: 50px auto;
    }
    #control-panel span {
        width: 50px;
        height: 50px;
        float: left;
        text-align: center;
        line-height: 50px;
        cursor: pointer;
    }
    #control-panel span.active {
        background: #ff6699;
        color: white;
    }
</style>

<!-- 控制按钮面板 -->
<div id="control-panel">
    <span></span>
    <span class="active forward">前进(W)</span>
    <span></span>
    <span class="active left">左转(A)</span>
    <span></span>
    <span class="active right">右转(D)</span>
    <span></span>
    <span class="active back">后退(S)</span>
    <span></span>
</div>
</body>
</html>

五、第四步:运行代码,开始控制小车!

1. 启动Python服务

在树莓派终端进入代码所在目录,执行以下命令启动服务(需要sudo权限,因为控制GPIO需要管理员权限):

代码语言:bash
复制
sudo python car_control.py

如果看到“小车控制服务已启动,浏览器访问树莓派IP即可控制!”,说明服务正常启动了。

2. 查找树莓派IP

在树莓派终端执行ifconfig(或ip addr),找到局域网IP(比如192.168.1.105)。

3. 电脑端控制

打开电脑浏览器,在地址栏输入树莓派的IP(比如http://192.168.1.105),会看到一个3x3的控制面板:

  • 键盘控制:按W(前进)、S(后退)、A(左转)、D(右转),Q/E/Z/X对应其他动作;
  • 鼠标控制:按住面板上的“前进”“左转”等按钮,小车会持续动作,松开就停。

六、常见问题:小车不动?先查这3点

  1. 接线错了:再核对一遍GPIO引脚编号,特别是IN1~IN4有没有接反,GND有没有共地;
  2. 电机没供电:电机驱动模块一定要外接电源(比如12V电池),树莓派的5V带不动电机;
  3. 权限不够:启动Python脚本时必须加sudo,否则无法控制GPIO,会报“Permission denied”错误。

七、后续进阶:还能玩出这些花样

如果觉得基础控制不够过瘾,还可以加这些功能:

  1. 手机控制:把前端HTML改成响应式布局,手机浏览器访问树莓派IP,就能用手机控制;
  2. 遥控距离:如果想在户外控制,给树莓派加个WiFi模块(或4G模块),就能突破局域网限制;
  3. 自动避障:加个超声波传感器(HC-SR04),在代码里加避障逻辑,小车就能自己绕开障碍物。

总之,树莓派小车的乐趣就在于不断折腾——从接线到写代码,再到调试动作,每一步成功都特别有成就感。如果跟着做的时候遇到问题,欢迎评论区交流,一起踩坑一起进步!

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 树莓派小车进阶:用Python+Tornado实现按键控制,电脑端就能操控!
    • 一、先理清楚:按键控制的核心逻辑
    • 二、第一步:安装依赖库(Tornado+GPIO控制库)
      • 1. 安装RPi.GPIO(控制GPIO引脚)
      • 2. 安装Tornado(异步Web框架)
    • 三、第二步:接线!GPIO引脚对应关系要记牢
      • 引脚对应表
    • 四、第三步:写代码!分Python后端和HTML前端
      • 1. Python后端代码(小车控制核心)
      • 2. HTML前端代码(按键界面)
    • 五、第四步:运行代码,开始控制小车!
      • 1. 启动Python服务
      • 2. 查找树莓派IP
      • 3. 电脑端控制
    • 六、常见问题:小车不动?先查这3点
    • 七、后续进阶:还能玩出这些花样
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档