一起打造自己的自动驾驶小车mycar - 3.手机控制小车移动

Car Assembled

小车组装好后,我们开始编写在小车上运行的python程序!

本次我们实现用手机来控制小车的移动。效果如下:


Remote Control on Phone

1. 功能

控制小车移动是通过一个网页实现的,这样通过手机或者电脑上的浏览器就能控制小车的移动。

网页上部是监控区域,可以看到小车摄像头的实时画面。

网页中部是控制区域,有两个摇杆,左摇杆控制小车左右转向,右摇杆控制小车前进和后退。

网页下部是功能选择,用于后面扩展功能,Autonomous Mode用于在手动驾驶和自动驾驶模式间切换,Record Video用于录制小车行进的视频,后面可以使用视频和控制输入来训练自动驾驶模型。

2. 代码概述

本次我们实现的功能虽然看起来不太复杂,但部分基础代码对以后的扩展是非常重要的,因此有必要设计一个良好的架构。

2.1 消息总线

在真实的汽车上有一个通信总线 - CAN,Controller Area Network,控制器局域网。汽车上需要通信、交换数据的部件都连接到该总线上。它可以实现点对点、一对多、广播的通信方式。CAN有行业的国际标准。

在我们的项目里用消息总线(message bus)模拟CAN,各组件通过发布/订阅(publish/subscribe)的方式进行通信。redis、kafka、rabbitmq、zeromq等都能实现所需功能,其中redis和zeromq比较轻量,在对比了redis和zeromq的性能后,我选择了zeromq作为mycar的消息中间件。

2.2 组件

组件是指小车上实现特定功能,可以独立运行的部件。mycar项目的组件可能有:运动控制、摄像头、蓝牙手柄、激光雷达、IMU等。

在这篇文章里,我们需要实现的功能模块是:

  • 运动控制,模块名:actuator
  • 摄像头,模块名:camera
  • 网页控制,模块名:web_controller

各具体组件的UML类图如下:


Class Diagram
  • Car
    代表一辆小车,是项目的主类,负责加载配置文件和启动各组件(Component)。
    创建该类需要传入配置文件和小车的运行时间。

  • Component
    是小车组件的抽象接口,组件主要的行为方法是runon_messagepublish_messagestartshutdown方法在小车启动和停止时会自动调用。on_messagepublish_message用来收发消息。run方法在start返回True时有作用,用于在单独线程执行组件长时间运行的业务逻辑,例子是摄像头不断产生图像数据。

  • CAN和ZmqCAN
    代表消息总线的抽象类和zeromq实现,它也是一种组件Component。主要方法是向指定的channel发送消息和订阅消息,分别是publish(channel, message)subscribe(channels, listener)

  • PWMSteering和PWMThrottle
    actuator模块,分别是控制小车方向和速度的类。依赖PCA9685类来发出PWM信号。

  • Camera
    是小车的摄像头类,用于产生实时图像。

  • WebController
    WebController组件用于接收页面输出,发送控制消息到消息总线。

2.3 mycar代码目录结构

├── bin
│   └── run.sh
├── config
│   └── web_drive.yml
├── README.md
├── requirements.txt
└── src
    ├── car.py
    ├── components
    │   ├── __init__.py
    │   ├── actuator.py
    │   ├── camera.py
    │   ├── can.py
    │   ├── component.py
    │   ├── templates
    │   │   └── index.html
    │   ├── web_controller.py
    │   └── zmq_can.py
    └── utils
        ├── __init__.py
        ├── map_range.py
        └── pca9685.py

下面分章节详细介绍一些重要的类和组件。

3. 运动控制

3.1 I2C、PWM与PCA9685

PWM,是指脉宽调制Pulse Width Modulation,遥控车一般使用PWM来控制转向和速度,所以2通道的遥控器一路输出到转向舵机,一路输出到电子调速器ESC。

I2C,IC间的通信协议,Inter-Integrated Circuit,包含一条双向串行数据线SDA,一条串行时钟线SCL。

PCA9685,是一个通用的16路舵机控制板,有16个输出端口,我们需要用到其中2个。

我们不需要了解如何产生I2C和PWM信号,在我们的项目里,由软件控制jetson nano的GPIO口产生I2C信号给PCA9685,PCA9685再生成PWM信号去驱动小车运动。

接线方式上一篇文章有讲到。

3.2 PCA9685类

这个类使用了adafruit公司的python包adafruit-circuitpython-servokit来控制舵机角度与ESC速度。它已经封装好了ServoKit这个类。

使用上一讲的接线方式,ServoKit的使用方法如下:

servokit = ServoKit(channels=16, address=0x40)
# 控制PCA9685第一个口的舵机的转动角度
servokit.servo[0].angle = 135  # 角度范围 0 ~ 180度
# 控制PCA9685第二个口的ESC的速度
servokit.continuous_servo[1].throttle = 0.3  # 速度的范围是 -1 ~ 1,大于0表示向前移动,如果输入负数代表后退,注意从前进切换到后退要输入两次同样的负数

3.3 PWMSteering和PWMThrottle类

这两个类实现了Component接口,调用PCA9685类来实现功能。

  • PWMSteering
    支持定义正前方的前进角度(straight_angle),以防小车有偏差;也支持自定义可转的角度范围(full_left_angle, full_right_angle)。默认的是小车舵机支持的90度的转向范围。

  • PWMThrottle
    支持自定义控制速度的范围(min_throttle, max_throttle),如(-0.3, 0.3),会把(-1, 1)的值映射到这个范围内,因为小车速度较快,调小点可以更方便控制小车,以防撞坏。

4. 摄像头

Camera这个类比较简单,它使用cv2.VideoCapture()来捕捉摄像头图像。

如果连接的是CSI摄像头,那么使用gstreamer pipline + Nvidia的nvarguscamerasrc,参数可以参考官方文档。

如果是在其他平台上做测试,可以使用cv2.VideoCapture(0)来使用默认的摄像头设备。

5. 网页控制

WebController类用Flask来启动了一个本地web server,端口为8080

实时图像展示部分使用的是返回帧图片的HTTP报文的方式,使用<img>HTML标签就可以让浏览器会不断加载新的帧。帧的格式为:

--frame
Content-Type: image/jpeg

[图像字符串]

这种方式实现比较简单。

摇杆部分使用的是一个开源的js库做的,其实用一个摇杆就可以实现方向和速度的调节的,这里我选择分开来控制。

两个功能控制选项目前还没有用到,先忽略。

6. 配置文件

最后是小车运行的配置文件,用来配置小车需要加载的所有组件Component,一个例子如下:

components:
  zmq_can:  # 模块名
    server_mode: True  # 创建实例的参数

  actuator:  # 该模块有两个类
    PWMSteering:  # 第一个类
      subscription: ['web_steering']  # 接收来自这个channel的消息
      channel: 0

    PWMThrottle:  # 第二个类
      subscription: ['web_throttle']
      channel: 1
      min_throttle: -0.3
      max_throttle: 0.3

  camera:  #该模块只有一个类
    publication: ['cam/image']  # 把图像发送到这个channel
    device: '/device/video0'

  web_controller:
    subscription: ['cam/image']
    publication: ['web_steering', 'web_throttle', 'web_record', 'web_autonomous']  # 发送消息到多个channel
  • 顶层,使用components作为键,代表是小车的组件
  • 次层缩进,以组件的模块(文件)作为键,如果该模块只有一个类,那么第三层缩进可以直接写该类的创建时传入的参数,如camera
  • 最后层缩进,配置组件类创建时传入的参数

6.1 subscription和publication属性

这两个属性是每个Component都有的属性,默认为空([]),分别代表组件要订阅的消息和要发出的消息。

7. 安装与运行

介绍完项目的代码后,我们看看怎么跑起来。

7.1 clone代码

代码已开源到github,首先clone一下:

git clone https://github.com/evan-wu/mycar.git --branch blog-3 --single-branch

7.2 安装依赖

注意jetson nano的Jet Pack已经自带了python3和编译好的opencv,切勿自己手动安装opencv。安装以下4个依赖就可以了:

pip3 instal pyyaml adafruit-circuitpython-servokit flask pyzmq

7.3 运行

cd mycar
chmod +x bin/run.sh
bin/run.sh config/web_drive.yml 120

用浏览器(手机浏览器)访问: http://<jetson nano ip>:8080 就可以了。

欢迎github/blog点赞、留言、讨论!

后续预告:手柄控制小车移动,实现PID寻线小车算法...

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,273评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,349评论 3 398
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,709评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,520评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,515评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,158评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,755评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,660评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,203评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,287评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,427评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,122评论 5 349
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,801评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,272评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,393评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,808评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,440评论 2 359