树莓派小车#1 - 硬件采购和简单运动控制系统

raspCar小车

暑假在家清理旧物的时候发现了数不清的杜邦线和曾经冲动买下的「树莓派入门开发套件」,继而发现曾经甚至买过焊枪、热熔胶这种东西。可以说,装备齐全的令人发指。在积满灰尘的角落,这些元器件默默的抱怨着我曾经的「始乱终弃」。于是,重新整装出发。

总之,这个项目意在整个带摄像头的小车然后用内网穿透让我可以在外地远程「撸猫」。(逗逗猫略略略)
后续会慢慢更新,代码地址:raspCar

项目立项概览

硬件介绍

在淘宝买的4个TT直流减速电机 + L298N驱动模块。

1. TT电机

无需多言,参数如图所示,只有正负极两个端口,正接正转,反接反转,电压高低跟转速呈正比。
并没有仔细研究这块,总之我电压应该在14.8V左右也没出现问题。

2. L298N驱动模块

L298N模块
这个知乎专栏描述的很详细:https://zhuanlan.zhihu.com/p/346930154

总之,输入的电源正极的端口是VCC,5V端口如果VCC和GND上面的跳帽没有拔掉是用来输出5V电压的,但是很不稳定;如果跳帽拔掉了,5V端口需要额外的外部电路供电,作为L298N内部逻辑电路的输入。
(PS: 跳帽就是一个黑色的小盖子,其实就是一小段导线啦,短接两个引脚。)

OUT1 & OUT2是由IN1 & IN2的逻辑输入判断供电的(这里的供电额由ENA决定。ENA是PWM引脚,如果PWM占空比是100%,供电额就是VCC。)例如:如果IN1是高电平,IN2是低电平,ENA是高电平占空比100%,那么OUT1输出VCC电压,OUT2输出0V电压。

注1:逻辑输入端 0 - 1.5V视为低电平,1.5V及以上视为高电平。(一般不用管。。)
注2:千万千万记住,GND一定要和逻辑输入电路(比如说单片机)共地,这样L298N才能判断输入的是高电平还是低电平。

3. 控制器

树莓派3B。其实我还有块4B的板子,但是貌似额定功率高很多,为了省电,就用了3B。
没什么好说的,仅放一下GPIO引脚图,以作参考。

4. 电源

18650电池*4

18650单个电池3.7V,4200mAh,可充电,属实好用。这个我用来给L298N供电。

充电宝

本来的打算是用L298N上的5V输出给树莓派供电,但是电压不稳定,导致树莓派疯狂重启。所以我暂时把电源分开了,整个了充电宝单独给树莓派供电。后续准备试试稳压模块,毕竟俩电源还是太冗余了。

注:根据查询树莓派3b最好要有稳定的5V2.1A供电,如果是树莓派4b型号,需要5V3A的稳定供电。

电机控制

1. 电机引脚输出

首先需要导入控制引脚输出的库RPi.GPIO。
如果没有先安装。

sudo apt-get update
sudo apt-get install python3-pip	# 如果已经安装了pip3可以省略这里
pip3 install RPi.GPIO  # pip3是python3.x的库管理工具,这里安装RPi.GPIO

导入库

from RPi.GPIO as GPIO

设置GPIO输出模式。

GPIO.setmode(GPIO.BCM)	# 也可以是GPIO.BOARD,使用板子上写的引脚号。详情见上图

我把轮子和车分别封装了成了类,这样以后添加轮子只需要改Car类就行了。

Wheel类:

# 定义轮子
class Wheel:
    def __init__(self, pin1, pin2, pwm_pin=None, name='New wheel'):
        self._pin1 = pin1	# pin1 pin2是连接tt电机的两个引脚
        self._pin2 = pin2
        # Setup
        print('Setup %s...' % name)
        GPIO.setup(self._pin1, GPIO.OUT)	# 在实际输出信号之前,需要先申明引脚是输出信号的
        GPIO.setup(self._pin2, GPIO.OUT)	# tt电机没有测速,所以没有输入引脚
        if pwm_pin:
            # fq是初始设定频率,频率越高响应速度越快;dc是占空比(调整速度)
            GPIO.setup(pwm_pin, GPIO.OUT)
            self.fq, self.dc = 50, 50 # 初始值设定为50ms一次变化 / 50%占空比
            self.pwm = GPIO.PWM(pwm_pin, self.fq) # self.pwm.ChangeFrequency, self.pwm.ChangeDutyCycle
            self.pwm.start(self.dc)
    
    def forward(self):
        GPIO.output(self._pin1, GPIO.HIGH)
        GPIO.output(self._pin2, GPIO.LOW)

    def backward(self):
        GPIO.output(self._pin1, GPIO.LOW)
        GPIO.output(self._pin2, GPIO.HIGH)
    
    def stop(self):
        GPIO.output(self._pin1, GPIO.LOW)
        GPIO.output(self._pin2, GPIO.LOW)

    def accelerate(self):
        self.dc = min(100, self.dc + 5)
        self.pwm.ChangeDutyCycle(self.dc)
        
    def decelerate(self):
        self.dc = max(0, self.dc - 5)
        self.pwm.ChangeDutyCycle(self.dc)

Car类:

# 定义Car 类
class Car(object):
    def __init__(self):
        print("Initialize the car...")
        self.leftWheel = Wheel(*WHEELS[0])
        self.rightWheel = Wheel(*WHEELS[1])

    # 小车前进
    def forward(self):
        print('Moving forward...')
        self.leftWheel.forward()
        self.rightWheel.forward()

    #  小车左拐
    def leftTurn(self):
        print("Turn left...")
        self.leftWheel.backward()
        self.rightWheel.forward()

    # 小车右拐
    def rightTurn(self):
        print('Turn right...')
        self.leftWheel.forward()
        self.rightWheel.backward()

    # 小车后退
    def backward(self):
        print('Moving backward...')
        self.leftWheel.backward()
        self.rightWheel.backward()
    
    # 小车停止
    def stop(self):
        print('Stop...')
        self.leftWheel.stop()
        self.rightWheel.stop()
    
    # 小车加速
    def accelerate(self):
        print('Accelerating...')
        self.leftWheel.accelerate()
        self.rightWheel.accelerate()

    # 小车减速
    def decelerate(self):
        print('Deccelerating...')
        self.leftWheel.decelerate()
        self.rightWheel.decelerate()
    
    # 执行具体命令
    def move(self, status):
        options = {
            'forward': self.forward,
            'leftTurn': self.leftTurn,
            'rightTurn': self.rightTurn,
            'backward': self.backward,
            'stop': self.stop,
            'accelerate': self.accelerate,
            'decelerate': self.decelerate
        }
        options[status]() 

2. 网页及交互

先是后端代码,处理交互信息。导入bottle库,如没有先pip安装:

pip3 install bottle
from bottle import get,post,run,request,template

我暂时没有分开成两个.py文件,直接写在了main函数里。

# 直接测试
if __name__ == '__main__':
	
	# 实例化车辆
    car = Car()
	# 返回主页,访问的网页根目录
    @get("/")
    def index():
        return template("index")
    # 读取网页上的按钮输入
    @post("/cmd")
    def cmd():
        adss=request.body.read().decode()
        print("按下了按钮:"+adss)
        car.move(adss)
        return "OK"
    # "0.0.0.0" 等于 localhost,你的树莓派本机ip地址,端口可以随便改
    run(host="0.0.0.0", port=12345)

网页显示 - html代码,保存为index.html。不做过多解释了,暂时是很简朴的页面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, 
                                   initial-scale=1.0,
                                   maximum-scale=1.0,
                                   user-scalable=no">
    <title>raspCar Controller</title>
    <link href="http://cdn.bootcss.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" media="screen">
    <script src="http://code.jquery.com/jquery.js"></script>
    <style type="text/css">
        #front {
            margin-left: 55px;
            margin-bottom: 3px;
        }
        #rear{
            margin-top: 3px;
            margin-left: 55px;
        }
        .btn{
             background: #bb0f5f;
            }
        .container{
            /* padding-right: 15px;
            padding-left: 15px;
            margin-right: auto;
            margin-left: auto; */
            text-align: center;
            background-color: transparent;
            border-radius: 20px;
            width: 300px;
            height: 350px;
            position: absolute;
            left: 50%;
            top: 50%;
            transform: translate(-50%,-50%);

        }
    </style>
    <script>
        $(function(){
            $("button").click(function(){
                $.post("/cmd", this.id, function(data,status){});
            });
        });
    </script>
</head>
<body>
<div id="container" class="container">

   	<!-- <div>
		    <img id="streamimage" class="xform" src="./?action=stream" />
    </div> -->
    
    <div>
        <button id="forward" class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-up"></button>
    </div>
    <div>

        <button id='leftTurn' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-left"></button>
        <button id='stop' class="btn btn-lg btn-primary glyphicon glyphicon-stop"></button>
        <button id='rightTurn' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-right"></button>
    </div>
    <div>
        <button id='backward' class="btn btn-lg btn-primary glyphicon glyphicon-circle-arrow-down"></button>
    </div>

    <div>
        <button id='accelerate' class="btn btn-lg btn-primary glyphicon">加速</button>
        <button id='decelerate' class="btn btn-lg btn-primary glyphicon">减速</button>
    <div>
</div>

<script src="//cdn.bootcss.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
</body>
</html>

现在的目录结构应该是:

./
|- start.py
|- index.html

运行代码:

python ./start.py

运行后,在局域网任意计算机上用浏览器打开192.168.x.y:12345就可以访问了。
(x和y由树莓派ip地址决定,比如我的是192.168.3.27。代码中的"0.0.0.0"默认就是树莓派的ip地址。可以指定不同ip的原因是,每个机器可能有多个ip地址(多网卡\网线),所以可以指定监听不同的ip的访问请求。)网页控制页面如下图所示。
控制界面

至此,一个简单的由网页控制的树莓派小车就做好啦。

总结

获得了一个通过网页控制的小车。

缺点:

  • 控制方式单一
  • 通过网页点击的控制体验并不好
  • 依赖wifi,控制距离短
  • 树莓派电源过于臃肿沉重

优点:

  • 可以使用任意上网设备打开控制页面(如手机)
  • 前端代码相对简单,易于更新

下一步:

  • 添加键盘控制,(1 - 通过网页代码检测键盘 2 - ssh输入+python多线程监听)
  • 整合两个电源,用电源模块分别输出合适的电压。
  • 添加zigbee模块,远程通讯,用串口输入命令。

8.25补充:
文中涉及代码已经变动,详细查看文章 - 树莓派小车#2
完整项目代码可以在 raspCar获取。


END

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值