树莓派Raspberry Pi 系统搭建和智能机器人小车的组装调试
本文的出发点:无意接触到树莓派这款优秀的芯片,比较适合青少年编程,激发孩子的编程思维的培养,最好的学习就是实战,借助于淘宝上现成零件式的智能小车硬件和树莓派实现智能机器人小车的实现,给孩子带来低成本高性能的玩具,重要的是通过个人的行动带动孩子的兴趣达到启蒙作用。
主要分树莓派系统搭建和智能小车组装调试两大部分,内容偏实战,理论知识网络上遍地都是会附带而过,尽量做到步骤简洁易懂,细节力求完整,为您零基础学习树莓派提供捷径,如有不足请指正。
1. 树莓派系统搭建
1.1 什么是树莓派
Raspberry Pi(中文名为“树莓派”,简写为RPi,(或者RasPi / RPI) [1] 是为学习计算机编程教育而设计,只有信用卡大小的微型电脑,其系统基于Linux,由注册于英国的慈善组织“Raspberry Pi 基金会”开发,推广给全世界的青少年电脑爱好者,用于培养计算机程序设计的兴趣和能力。
您可以将树莓派连接电视、显示器、键盘鼠标等设备使用。你可以通过树莓派来DIY以下一些场景应用:
- 打造一款属于自己的手持式游戏机,虽然没手机方便,但也会很酷....
- 借助于一些传感器和执行机构实现智能家居的远程监控,上班也能瞧瞧美丽的小家和可爱的娃 : )
- 扔掉带有各种广告和限制的机顶盒,实现自己的家庭多媒体中心,重温下硬盘里的经典影片,创建专属的家庭相册和视频集,打打儿时的经典游戏,,,
- 打造一款智能小车,可以根据你的指令跑到厨房里问妈妈:今天有红烧肉吃吗?再跑回来告诉你结果
- 设计平面自动作画系统或者简易3D打印机,在家也能体验艺术魅力
树莓派裁剪了桌面计算机的强大应用,简化复杂的硬件构建和编程,更注重于作品的创意和设计,培养青少年的编程思维。
树莓派参数信息不作具体详细介绍,直接上Raspberry Pi 3B有个直观印象。
1.2 树莓派系统镜像下载、烧写和备份
树莓派支持多种系统类型,如 Raspbian、Arch Linux ARM、Debian Squeeze、Firefox OS、Gentoo Linux、Google Chrome OS、Raspberry Pi Fedora Remix、Slackware ARM、QtonPi、Slackware ARM、WebOS、RISC OS、FreeBSD、NetBSD、Android 4.0(Ice Cream Sandwich)等。
树莓派官网下载地址:http://www.raspberrypi.org/downloads,可在官网上下载最新Raspbian系统。
树莓派实际使用micro SD 卡装载系统,系统烧写及时写SD卡的过程
- 电脑插入SD卡(推荐4G以上的Class4以上卡),使用Win32DiskImager(Win32DiskImager-0.9.5-install.exe)烧写Raspbian操作系统镜像文件2017-03-02-raspbian-jessie.img,一顿猛操作:最好关闭系统防火墙;选择加载img镜像系统;点击write;确认yes;喝完茶之后看到write successful界面说明系统安装成功了!细心点你会发现磁盘空间有点奇怪,最大也就几十MB,莫慌!这是正常现象,linux下的磁盘分区在Windows下是看不到的。
1.3 树莓派硬件连接
电源:树莓派正常采用5V, 2A 的Micro USB供电,就是常见的安卓手机接口,但考虑到众多外设、显示屏等诸多负载产生的压降,特地购置了原装树莓派专用官方电源(5.1V, 2.5A)。网上获知因电源不稳定造成的诸多后果太多了:键盘动不了;显示器不显示或者花屏闪屏;系统重启等等。总之而言,电源是无论放在什么场合都是重中之重,必须足够重视,否则很容易陷入各种不可预期的麻烦,让你怀疑人生。
1.4 树莓派连接显示屏
树莓派可以通过各种工具如SSH, TightVnc,Putty,Winscp等等实现文件交互甚至系统画面访问,不过初学者还是强烈建议买块显示屏进行初始的一些配置,所谓眼见为实,这样便于入门。这里我配置了一块微雪的7寸高清电容屏和微型无线键盘鼠标。
7寸显示器HDMI接树莓派的HDMI; 显示器Mico USB供电口接入树莓派的USB供电;无线键盘的的接收器同样插到树莓派USB口上;最后上电启动树莓派系统。
初次启动即发现屏幕显示花屏,无法全屏显示
咨询了微雪显示屏客户得到了解决方案:用于树莓派的Raspbian/ Ubuntn mate系统时,需要手动修改系统根目录下config.txt配置文件,文件末尾添加以下配置行并确保等号左右没有空格即可,亲自有效!
max_usb_current=1
hdmi_group=2
hdmi_mode=87
hdmi_cvt 1024 600 60 6 0 0 0
hdmi_drive=1
成功启动画面如图所示:
- pi账户和root账户
pi账户是树莓派自动分配的用户账户,默认密码为raspberry,建议用户用以下命令自行修改密码。
pi@raspberry:password pi
不过一般的文件系统操作,pi账户没有足够的权限,这时候就需要获取系统的root权限。在pi账户登录情况下,可以临时通过sudo临时获取root权限如下命令:
pi@raspberry:sudo raspi-config
对于官方的初始系统root密码默认是没有密码的,但账户确是锁定的,可在pi账户下开启root密码,如下命令:
pi@raspberry:sudo passwd root
执行此命令后系统会提示输入两遍的root密码,输入你想设的密码即可,然后再执行:
sudo passwd --unlock root
这样root用户解锁成功,如果不想每次输入命令都使用sudo获取临时root权限的话,可以在pi账户下输入su命令并输入root密码即可获取长期root权限。
1.4 登录方式配置
通过显示屏进入系统,发现画面右上角已经显示出当前可连接的wifi路由器,直接点击输入WiFi密码连接。使用默认浏览器看是否能上网。接着打开系统左上角的命令行终端,输入ifconfig命令查看当前系统ip地址为(172.20.1.5),这样电脑端连接同样的Wifi路由,只要能访问到该IP地址就可以使用多种远程工具进行访问树莓派系统,进行文件传送,远程桌面访问等操作了。
pi@raspberry:ifconfigifconfig
- SSH和SSH文件传输
使用SSH,必须先手动开启树莓派的SHH远程服务,操作如下:
a.命令打开raspi配置窗口
pi#sudo raspi-config
然后选择"Interfacing options" 进入二级菜单,选择"P2 SSH" 回车确认Enable即可。
b. 这样我们就可以通过SSH工具软件远程访问树莓派系统了。
默认使用pi账户及默认密码raspberry登录,成功登录后显示如下信息。
c. 点击SSH工具栏上的“New file transfer window”图标可以打开文件系统操作窗口如下,左侧为本地电脑目录系统,右侧即为树莓派可访问的linux目录系统,借助于root权限可以相互移动和修改文件。
注意1:如果出现server responded “algorithm negotiation failed”的操作错误,这时我们需要登录pi之后修改ssh的配置文件/etc/ssh/sshd_config
sudo vi /etc/ssh/sshd_config
并在打开的文件中添加如下代码
Ciphers aes128-cbc,aes192-cbc,aes256-cbc,aes128-ctr,aes192-ctr,aes256-ctr,3des-cbc,arcfour128,arcfour256,arcfour,blowfish-cbc,cast128-cbcMACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160,hmac-sha1-96,hmac-md5-96KexAlgorithms diffie-hellman-group1-sha1,diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha1,diffie-hellman-group-exchange-sha256,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group1-sha1,curve25519-sha256@libssh.org
重启sshd服务后,即可正常登录
sudo service ssh restart
或者
sudo /etc/init.d/ssh restart
- 远程桌面登录(xdrp)
如果你觉得SSH访问工具不够直观的话,还可以通过远程桌面访问的方式,顾名思义就是可以访问到树莓派系统的主画面,相当于直接操作树莓派的linux系统,这样无论从文件操作,程序运行以及命令执行上都显得比较明了,方便初学者使用。
首先需要获取root权限并安装xdrp
pi@raspberry:sudo apt-get install xrdp
或者
pi@raspberry:su
root@raspberry:apt-get install xrdp
安装过程如下图:
安装完成后,可以通过Windows命令框输入mstsc或者remote打开远程桌面访问客户端,并输入树莓派的IP地址访问连接。
pi和root账户皆可登录到树莓派系统中,如下就可以看到正在的树莓派Raspbian操作系统了。
不过如果远程桌面的登录过程中出现如下错误(IP地址,用户名,密码正确的情况下)
解决的办法就需要再树莓派系统中安装tightvnc server服务
- Tight Vnc
root@raspberry: apt-get install tightvncserver
安装完成后,reboot重启后再次远程桌面连接即可成功!
1.5 Vim编辑器配置
Linux自带的编辑器有nano和vi,但vi编辑器使用起来很不方便,通常我们可以使用Vim编辑器。vim 是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。vim的这些优势主要体现在以下几个方面:1、多级撤消我们知道在vi里,按 u只能撤消上次命令,而在vim里可以无限制的撤消。2、易用性vi只能运行于unix中,而vim不仅可以运行于unix,windows ,mac等多操作平台。3、语法加亮vim可以用不同的颜色来加亮你的代码。4、可视化操作就是说vim不仅可以在终端运行,也可以运行于x window、 mac os、 windows。5、对vi的完全兼容某些情况下,你可以把vim当成vi来使用。
安装之前,首先需要更新索引源:
sudo apt-get update
安装vim编辑器:
sudo apt-get install vim
注意:有的系统默认自带安装了Vim文本编辑器,但是树莓派的源是国外版的,默认编辑器并不好用,还无法显示中文,所以最好先卸载一次,再重新安装:
sudo apt-get remove vim-common
sudo apt-get install vim
也可以使用SSH Secure File Transfer工具将windows下的文件与树莓派的文件实现文件跨系统传输,将下载好的vimconfig.tar.gz文件安装包传输到树莓派系统的目录中,如/home/pi/workdir。然后在命令行中输入解压缩命令:
进入vimconfig目录中运行config.sh脚本
会出现如下错误,不用急
我们只需要在/root/目录下新建.vim和.vimrc文件,还需要apt-get install ctags即可 ,确保在root(加sudo),否则没有权限。
配置好的vim编辑器:
具体Vim编辑器的使用方法和相关命令,请自行百度查询。
1.7 树莓派配置成路由器
Raspberry Pi 3板载了无线网络适配器和陶瓷天线,不需要额外增加无线网卡就可以把它打造成一个无线路由器。
以下描述如何开启树莓派无线网络适配器的AP功能,并且共享其有线网络,实现无线路由功能。
- 先下载相关软件hostapd和dnsmasp,前者可以开启无线适配器的AP功能,后者是DHCP和DNS服务器。
sudo apt-get install hostapd dnsmasq
安装完成之后就可以直接安装DHCP服务了
sudo apt-get install isc-dhcp-server
- 下面设置静态IP
需要将无线接口wlan0的IP配置成静态地址,首先让dhcpcd不再管理wlan0,避免设置冲突。修改etc文件夹中的dhcpcd.conf文件,开头增加一行:denyinterfaces wlan0
vi /etc/dhcpcd.conf
然后设置wlan0的静态ip,修改文件:
vi /etc/network/interfaces
将wlan0相关的内容修改成如下内容:
192.168.0.1是给树莓派做路由器分配的网关IP,不能与局域网其他路由器网关IP重复,若重复此处可以修改IP地址为其他网关。
重启服务和wlan0
service dhcpcd restart
ifdown wlan0
ifup wlan0
通过ifconfig可以看到wlan0的ip已经设定好了。
- 安装Hostapd
新建配置文件,并输入如下配置行:
vi /etc/hostapd/hostapd.conf
上面文档中,ssid=RaspberryPi是无线网络的名字,wpa_passphrase=raspberry是密码。
测试配置是否正确:
sudo /usr/sbin/hostapd /etc/hostapd/hostapd.conf
通过手机等设备应该可以搜到名为RaspberryPi的WiFi,还不需要连接。如果没有问题,按Ctrl+C停止测试。使上述设置生效:
vi /etc/default/hostapd
将#DAEMON_CONF=""修改为DAEMON_CONF="/etc/hostapd/hostapd.conf"。
- 配置DHCP服务器
备份配置文件:
cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.bak
编辑新的配置文件,修改成如下内容:
vi /etc/dhcp/dhcpd.conf
重启DHCP服务
service isc-dhcp-server restart
- 开启IPV4转发
修改相应的文件,去掉net.ipv4.ip_forward=1前面的“#”号
vi /etc/sysctl.conf
通过iptables做NAT转发
这些配置重启之后就会失效了,需要保存起来
sh -c “iptables-save > /etc/iptables.ipv4.nat”
并设置为开机自动加载:
vi /etc/rc.local
在exit 0上方增加:
iptables-restore < /etc/iptables.ipv4.nat
启动服务
service hostapd start
接着reboot重启。树莓派重启之后就可以用手机连接到wifi了。名字和密码在上面的文档中体现了,连接之后系统会自动为我们连接的设备分配未使用的ip。如果我们的树莓派通过有线网络连上互联网的话,我们连上的树莓派路由器的话,也是可以上网的。
1.8 树莓派摄像头配置
树莓派摄像头配置需要如下步骤
- 拷贝和解压摄像头驱动
通过SSH拷贝驱动包文件master.zip到home文件夹,并执行解压缩命令
#unzip master.zip
- 安装CMake工具
CMake是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。他能够输出各种各样的makefile或者project文件,能测试编译器所支持的C++特性,类似UNIX下的automake。这里编译摄像头驱动需要用到此工具。执行下列下载安装命令:
#sudo apt-get install cmake
中间若有提示,输入“Y”即可,完成后如下图所示:
- 安装所需要的编译支持库libjpeg8
执行指令,完成后如下图所示:
#sudo apt-get install libjpeg8-dev
- 执行摄像头编译
首先需要知道你手上的摄像头的数据格式(JPEG或者YUYV),驱动中默认为JPEG格式,如需修改请在如下目录文件中修改
# cd /home/pi/mjpg-streamer-master/mjpg-streamer-experimental/plugins/input_uvc/
# sudo vim input_uvc.c
修改135行处的format为V4L2_PIX_FMT_YUYV,然后然后返回到/mjpg-streamer-experimental/下执行如下命令完成编译
#sudo make clean all
- 摄像头画面调用
进入树莓派配置界面,Enable激活Camera摄像头
#sudo raspi-config
然后插上USB摄像头,重启系统
# sudo reboot
重启完毕进入系统,进入mjpg-streamer-experimental目录并使用下列指令启动普通USB摄像头
#./mjpg_streamer -i "./input_uvc.so" -o "./output_http.so -w ./www"
启动树莓派专用摄像头RaspiCamera的指令
#./mjpg_streamer -i "./input_raspicam.so" -o "./output_http.so -w ./www"
执行过程中可能会有报错,可以忽略,只要最后没有退回到命令行提示符,而且显示“Starting ouput”,就应该成功了。
下面在PC端使用浏览器查看摄像头的静止图像
http://<树莓派IP>:8080/?action=snapshot
动态图像查询:
http://<树莓派IP>:8080/?action=stream
或者 http://<树莓派IP>:8080/javascript_simple.html
1.9 树莓派3串口(UART)使用问题和方法解决
串口(UART)的硬件bug如下文引用
““”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“””
今天要说说树莓派3的UART串口的使用问题,该串口问题的官方反馈及回复请参考这两篇官方博文“https://www.raspberrypi.org/forums/viewtopic.php?f=63&t=137932”和“https://www.raspberrypi.org/forums/viewtopic.php?f=107&t=138223”
根据官方的反馈和回复,我们了解到树莓派3上用户目前无法正常是使用GPIO中的UART串口(GPIO14&GPIO15),也就是说用户无论是想用串口来调试树莓派,还是想用GPIO中的串口来连接GPS,蓝牙,XBEE等等串口外设目前都是有问题的。
原因是树莓派CPU内部有两个串口,一个是硬件串口(官方称为PL011 UART),一个是迷你串口(官方成为mini-uart)。在树莓派2B/B+这些老版树莓派上,官方设计时都是将“硬件串口”分配给GPIO中的UART(GPIO14&GPIO15),因此可以独立调整串口的速率和模式。而树莓派3的设计上,官方在设计时将硬件串口分配给了新增的蓝牙模块上,而将一个没有时钟源,必须由内核提供时钟参考源的“迷你串口”分配给了GPIO的串口,这样以来由于内核的频率本身是变化的,就会导致“迷你串口”的速率不稳定,这样就出现了无法正常使用的情况。
目前解决方法就是,关闭蓝牙对硬件串口的使用,将硬件串口重新恢复给GPIO的串口使用,也就意味着树莓派3的板载蓝牙和串口,现在成了鱼和熊掌,两者无法兼得。
”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”“”
下面讲述如何恢复硬件串口的方法
- 下载pi3-miniuart-bt-overlay文件,解压出pi3-miniuart-bt-overlay.dtb文件,并将dtb文件拷贝到/boot/overlays/目录
- 修改/boot目录下的config.txt文件,在最后添加如下两行并保存.
#sudo vim /boot/config.txt
dtoverlay=pi3-miniuart-bt-overlay
force_turbo=1
- 修改/boot目录下的cmdline.txt文件
#sudo vim /boot/cmdline.txt
修改前的截图:
修改为:
dwc_otg.lpm_enable=0 console=serial1,115200 console=tty1 root=/dev/mmcblk0p2 kgdboc=serial1,115200 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
最后保存退出。
下面需要做的就是关闭自带的板载蓝牙功能。
- 输入下面命令关闭hciuart使用uart0
#sudo systemctl disable hciuart
- 修改/lib/systemd/system/hciuart.server 将 “ttyAMA0”修改为“ttyS0”,并保存退出。
sudo vim /lib/systemd/system/hciuart.service
- 最后更新并重启即可
sudo apt-get update
sudo apt-get upgrade
sudo reboot
1.6 Wiring库函数
2. 智能小车组装调试
智能小车简介
这里选用深圳亚博智能科技出品的4WD可兼容多种控制器(树莓派、Arduino、51、STM32)的智能小车。该车支持多个定制的精良传感器,能出色完成巡线、避障、跟随、寻光、灰度识别等功能;支持多种遥控方式,蓝牙4.0控制、微信小程序控制、红外遥控、PC上位机遥控等;支持多种组装方式,单、双层平台结构,平台上多个安装孔选择。
2.1 智能小车组装
具体安装步骤如下:底板安装孔、电机安装、轮胎安装、电池盒安装、循迹探头安装、红外寻光模块安装、风扇安装、蓝牙安装、树莓派3B+与扩展板安装、扩展板安装、舵机,超声波模块、探照灯模块安装、摄像头云台搭建、摄像头云台与二层平台安装、双平台安装、扩展板接线端子、PS2遥控手柄安装、检查各模块插头连接和跳线。详细见下面官方图,在此无需累赘。
2.1 智能小车调试
2.1.1 Wiring库函数
简介:wiringPi是应用于树莓派平台的GPIO控制库函数,wiringPi中的函数类似于Arduino的wiringPi系统,wiringPi库包含了丰富的库函数,如GPIO库,I2C库,SPI库,UART库和软件PWM库。
wiringPi安装
- 使用git工具
首先安装git工具
sudo apt-get install git-core
如果安装时发生错误,可以尝试更新apt库
sudo apt-get update
接着通过 git 在线获得wiringPi的源代码,输入命令
git clone git://git.drogon.net/wiringPi
进入wiringPi目录并安装wiringPi库
cd wiringPi
./build
build脚本将会自动完成wiringPi库的编译和安装,安装完成如下....
若需要更新wiringPi库,输入以下命令:
cd wiringPi
git pull origin
- 离线下载安装wiringPi函数库
直接下载wiringPi源码和解压,并进行本地安装...从以下网址获得wiringPi源码:
https://git.drogon.net/?p=wiringPi;a=summary
点击图中的snapshot便可下载最新版本,将下载到的安装包移动到你需要安装的目录下,然后使用以下命令解压和安装:
tar xfz wiringPi-96344ff.tar.gz
cd wiringPi-96344ff
./build
wiringPi的版本信息查看
安装完成后,打开命令终端,可以通过gpio命令来检查wiringPi的版本信息。
GPIO口的使用
命令行输入gpio readall 可以获得wiringPi和树莓派的GPIO接口之间的对应关系,如下图所示:
上图中的physical列代表的是树莓派物理上接口J8的管脚定义,wPi列即代表在wiringPi中所对应的数值,BCM列即代表在BCM2835的GPIO寄存器中的偏移地址,即在BCM2835 C library中对应的GPIO的数值。我们使用的是wiringPi库函数,进行的树莓派wifi智能小车的开发。
2.1.1 代码功能调试和说明
WiringPi库的具体函数使用说明可以自己网上查看手册,在此不作累赘,具体以智能小车的控制功能和源码进行典型性讲解。
这里以厂家提供的源码程序简单介绍
库文件引用和变量定义
#include <wiringPi.h>
#include <softPwm.h>
#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wiringSerial.h>
#include <errno.h>
#include <time.h>
#include <unistd.h>#define run_car '1'//按键前
#define back_car '2'//按键后
#define left_car '3'//按键左
#define right_car '4'//按键右
#define stop_car '0'//按键停#define front_left_servo '1'
#define front_right_servo '2'
#define up_servo '3'
#define down_servo '4'
#define left_servo '6'
#define right_servo '7'
#define updowninit_servo '5'
#define stop_servo '8'#define ON 1
#define OFF 0#define HIGH 1
#define LOW 0/*小车运行状态枚举*/
enum {enSTOP = 0,enRUN,enBACK,enLEFT,enRIGHT,enTLEFT,enTRIGHT
} enCarState;/*小车舵机状态枚举*/
enum {enFRONTSERVOLEFT = 1,enFRONTSERVORIGHT,enSERVOUP,enSERVODOWN,enSERVOUPDOWNINIT,enSERVOLEFT,enSERVORIGHT,enSERVOSTOP,enSERVOFRONTINIT
} enServoState;//定义引脚
int Left_motor_go = 28; //左电机前进AIN2连接Raspberry的wiringPi编码28口
int Left_motor_back = 29; //左电机后退AIN1连接Raspberry的wiringPi编码29口int Right_motor_go = 24; //右电机前进BIN2连接Raspberry的wiringPi编码24口
int Right_motor_back = 25; //右电机后退BIN1连接Raspberry的wiringPi编码25口int Left_motor_pwm = 27; //左电机控速PWMA连接Raspberry的wiringPi编码27口
int Right_motor_pwm = 23; //右电机控速PWMB连接Raspberry的wiringPi编码23口/*循迹红外传感器引脚及变量设置*/
//TrackSensorLeftPin1 TrackSensorLeftPin2 TrackSensorRightPin1 TrackSensorRightPin2
// 9 21 7 1
const int TrackSensorLeftPin1 = 9; //定义左边第一个循迹红外传感器引脚为wiringPi编码9口
const int TrackSensorLeftPin2 = 21; //定义左边第二个循迹红外传感器引脚为wiringPi编码21口
const int TrackSensorRightPin1 = 7; //定义右边第一个循迹红外传感器引脚为wiringPi编码7口
const int TrackSensorRightPin2 = 1; //定义右边第二个循迹红外传感器引脚为wiringPi编码1口//定义各个循迹红外引脚采集的数据的变量
int TrackSensorLeftValue1;
int TrackSensorLeftValue2;
int TrackSensorRightValue1;
int TrackSensorRightValue2;
char infrared_track_value[5] = {0};/*避障红外传感器引脚及变量设置*/
const int AvoidSensorLeft = 26; //定义左边避障的红外传感器引脚为wiringPi编码26口
const int AvoidSensorRight = 0; //定义右边避障的红外传感器引脚为wiringPi编码0口int LeftSensorValue ; //定义变量来保存红外传感器采集的数据大小
int RightSensorValue ;
char infrared_avoid_value[3] = {0};/*定义光敏电阻引脚及变量设置*/
const int LdrSensorLeft = 11; //定义左边光敏电阻引脚为wiringPi编码11口
const int LdrSensorRight = 22; //定义右边光敏电阻引脚为wiringPi编码22口int LdrSersorLeftValue ; //定义变量来保存光敏电阻采集的数据大小
int LdrSersorRightValue ;
char LDR_value[3] = {0};/*蜂鸣器引脚设置*/
int buzzer = 10; //设置控制蜂鸣器引脚为wiringPi编码10口/*小车初始速度控制*/unsigned int g_CarSpeedControl = 150;/*设置舵机驱动引脚*/
int FrontServoPin = 4;
int ServoUpDownPin = 13;
int ServoLeftRightPin = 14;/*超声波引脚及变量设置*/
int EchoPin = 30; //定义回声脚为连接Raspberry的wiringPi编码30口
int TrigPin = 31; //定义触发脚为连接Raspberry的wiringPi编码31口/*RGBLED引脚设置*/
int LED_R = 3; //LED_R接在Raspberry上的wiringPi编码3口
int LED_G = 2; //LED_G接在Raspberry上的wiringPi编码2口
int LED_B = 5; //LED_B接在Raspberry上的wiringPi编码5口/*灭火电机引脚设置*/
int OutfirePin = 8; //设置灭火电机引脚为wiringPi编码8口/*变量*/
/*摄像头舵机上下和左右两个自由度的变量*/
int ServoUpDownPos = 90;
int ServoLeftRightPos = 90;
/*前舵机左右摇动变量*/
int FrontServoLeftRightPos = 90;
unsigned char g_frontservopos = 90;/*舵机控制标志位*/
int ServoFlags;
int g_ServoState = enSERVOSTOP;
int g_lednum = 0; //led颜色切换变量/*串口设备打开的文件描述符*/
int fd;/*串口长度变量*/
int g_num=0;
int g_packnum=0;/*计时变量*/
int serialtime = 5000;
int count = 20;/*串口数据设置*/
char InputString[512] = {0}; //用来储存接收到的内容
int NewLineReceived = 0; //前一次数据结束标志
int StartBit = 0; //协议开始标志
int g_CarState = enSTOP; //1前2后3左4右0停止
char ReturnTemp[512] = {0}; //存储返回值/*小车模式切换*/
int g_modeSelect = 0; //0是默认状态; //2:巡线模式 3:超声波避障 //4: 七彩探照 5: 寻光模式 6: 红外跟踪
重要功能函数
- main函数:初始化以及模式控制
/**
* Function main
* @brief 对串口发送过来的数据解析,并执行相应的指令
* @param[in] void
* @retval void
* @par History 无
*/
int main()
{g_modeSelect = 1;//wiringPi初始化wiringPiSetup();digitalWrite(OutfirePin, HIGH); //初始化电机驱动IO为输出方式pinMode(Left_motor_go, OUTPUT);pinMode(Left_motor_back, OUTPUT);pinMode(Right_motor_go, OUTPUT);pinMode(Right_motor_back, OUTPUT);//创建两个软件控制的PWM脚softPwmCreate(Left_motor_pwm,0,255); softPwmCreate(Right_motor_pwm,0,255);//定义左右传感器为输入接口pinMode(AvoidSensorLeft, INPUT);pinMode(AvoidSensorRight, INPUT);//定义寻迹红外传感器为输入模式pinMode(TrackSensorLeftPin1, INPUT);pinMode(TrackSensorLeftPin2, INPUT);pinMode(TrackSensorRightPin1, INPUT);pinMode(TrackSensorRightPin2, INPUT);//定义光敏电阻引脚为输入模式pinMode(LdrSensorLeft, INPUT);pinMode(LdrSensorRight, INPUT);//初始化蜂鸣器IO为输出方式pinMode(buzzer, OUTPUT);digitalWrite(buzzer, HIGH);//初始化超声波引脚模式pinMode(EchoPin, INPUT); //定义超声波输入脚pinMode(TrigPin, OUTPUT); //定义超声波输出脚//定义灭火IO口为输出模式并初始化pinMode(OutfirePin, OUTPUT);//初始化RGB三色LED的IO口为输出方式,并初始化pinMode(LED_R, OUTPUT);pinMode(LED_G, OUTPUT);pinMode(LED_B, OUTPUT);softPwmCreate(LED_R,0,255); softPwmCreate(LED_G,0,255); softPwmCreate(LED_B,0,255); //初始化舵机引脚为输出模式pinMode(FrontServoPin, OUTPUT);//初始化舵机引脚为输出模式pinMode(FrontServoPin, OUTPUT);pinMode(ServoUpDownPin, OUTPUT);pinMode(ServoLeftRightPin, OUTPUT);//舵机位置初始化servo_init();//打开串口设备,如若失败则会打印错误信息if ((fd = serialOpen("/dev/ttyAMA0", 9600)) < 0){fprintf(stderr, "Uable to open serial device: %s\n", strerror(errno));return -1;}while(1){//调用串口解包函数serialEvent();if (NewLineReceived){ printf("serialdata:%s\n",InputString);serial_data_parse(); //调用串口解析函数NewLineReceived = 0;}//切换不同功能模式switch (g_modeSelect){case 1: break; //暂时保留case 2: Tracking_Mode(); break; //巡线模式case 3: Ultrasonic_avoidMode(); break; //超声波避障模式case 4: LED_Color_Mode(); break; //七彩颜色模式case 5: LightSeeking_Mode(); break; //寻光模式case 6: Infrared_follow_Mode(); break; //红外跟随模式}//舵机云台的控制Servo_Control_Thread();//让串口平均每秒发送采集的数据给上位机 if(g_modeSelect == 1){serialtime--;if(serialtime ==0){count--;serialtime = 5000;if(count == 0){serial_data_postback(fd);serialtime = 5000;count = 20;}}}usleep(10);}serialClose(fd); //关闭串口return 0;
}
- servo_appointed_detection(n);// 舵机旋转一定的角度
/**
* Function servo_pulse
* @brief 定义一个脉冲函数,用来模拟方式产生PWM值
* 时基脉冲为20ms,该脉冲高电平部分在0.5-2.5ms
* 控制0-180度
* @param[in1] ServPin:舵机控制引脚
* @param[in2] myangle:舵机转动指定的角度
* @param[out] void
* @retval void
* @par History 无
*/
void servo_pulse(int v_iServoPin, int myangle)
{int PulseWidth; //定义脉宽变量PulseWidth = (myangle * 11) + 500; //将角度转化为500-2480 的脉宽值digitalWrite(v_iServoPin, HIGH); //将舵机接口电平置高delayMicroseconds(PulseWidth); //延时脉宽值的微秒数digitalWrite(v_iServoPin, LOW); //将舵机接口电平置低delay(20 - PulseWidth / 1000); //延时周期内剩余时间return;
}
/**
* Function servo_appointed_detection
* @brief 舵机旋转到指定角度
* @param[in] pos:指定的角度
* @param[out] void
* @retval void
* @par History 无
*/
void servo_appointed_detection(int pos)
{int i = 0;for (i = 0; i <= 20; i++) //产生PWM个数,等效延时以保证能转到响应角度{servo_pulse(FrontServoPin, pos); //模拟产生PWM}
}
- 超声波测距函数
/**
* Function Distance
* @brief 超声波测一次前方的距离
* @param[in] void
* @param[out] void
* @retval float:distance返回距离值
* @par History 无
*/
float Distance()
{float distance;struct timeval tv1;struct timeval tv2;struct timeval tv3;struct timeval tv4;long start, stop;digitalWrite(TrigPin, LOW);delayMicroseconds(2);digitalWrite(TrigPin, HIGH); //向Trig脚输入至少10US的高电平delayMicroseconds(15); //10digitalWrite(TrigPin, LOW);//防止程序未检测到电平变化,陷入死循环,加入一个超时重测机制gettimeofday(&tv3, NULL); //超时重测机制开始计时start = tv3.tv_sec * 1000000 + tv3.tv_usec;while(!digitalRead(EchoPin) == 1){gettimeofday(&tv4, NULL); //超时重测机制结束计时stop = tv4.tv_sec * 1000000 + tv4.tv_usec;if ((stop - start) > 30000) //最大测5米时的时间值:10/340=0.03s{return -1; //超时返回-1}} //防止程序未检测到电平变化,陷入死循环,加入一个超时重测机制gettimeofday(&tv1, NULL); //当echo脚电平变高时开始计时start = tv1.tv_sec*1000000+tv1.tv_usec;while(!digitalRead(EchoPin) == 0){gettimeofday(&tv3,NULL); //超时重测机制开始计时stop = tv3.tv_sec*1000000+tv3.tv_usec;if ((stop - start) > 30000){return -1;}} //超时重测机制结束计时gettimeofday(&tv2, NULL); //当echo脚电平变低时结束计时start = tv1.tv_sec * 1000000 + tv1.tv_usec;stop = tv2.tv_sec * 1000000 + tv2.tv_usec;distance = (float)(stop - start)/1000000 * 34000 / 2;printf("distance: %f\n", distance);return distance;
}
- 小车前进、后退、刹车、左拐、右拐等运行函数
/**
* Function run
* @brief 小车前进
* @param[in1] LeftCarSpeedControl:左轮速度
* @param[in2] RightCarSpeedControl:右轮速度
* @param[out] void
* @retval void
* @par History 无
*/
void run(unsigned int LeftCarSpeedControl,unsigned int RightCarSpeedControl)
{//左电机前进digitalWrite(Left_motor_go, HIGH); //左电机前进使能digitalWrite(Left_motor_back, LOW); //左电机后退禁止softPwmWrite(Left_motor_pwm, LeftCarSpeedControl*0.95);//右电机前进digitalWrite(Right_motor_go, HIGH); //右电机前进使能digitalWrite(Right_motor_back, LOW); //右电机后退禁止softPwmWrite(Right_motor_pwm, RightCarSpeedControl);
}/**
* Function brake
* @brief 小车刹车
* @param[in] void
* @param[out] void
* @retval void
* @par History 无
*/
void brake()
{digitalWrite(Left_motor_go, LOW);digitalWrite(Left_motor_back, LOW);digitalWrite(Right_motor_go, LOW);digitalWrite(Right_motor_back, LOW);
}/**
* Function left
* @brief 小车左转(左轮不动,右轮前进)
* @param[in1] LeftCarSpeedControl:左轮速度
* @param[in2] RightCarSpeedControl:右轮速度
* @param[out] void
* @retval void
* @par History 无
*/
void left(unsigned int LeftCarSpeedControl,unsigned int RightCarSpeedControl)
{//左电机停止digitalWrite(Left_motor_go, LOW); //左电机前进禁止digitalWrite(Left_motor_back, LOW); //左电机后退禁止softPwmWrite(Left_motor_pwm, LeftCarSpeedControl);//右电机前进digitalWrite(Right_motor_go, HIGH); //右电机前进使能digitalWrite(Right_motor_back, LOW); //右电机后退禁止softPwmWrite(Right_motor_pwm, RightCarSpeedControl);
}/**
* Function spin_left
* @brief 小车原地左转(左轮后退,右轮前进)
* @param[in1] LeftCarSpeedControl:左轮速度
* @param[in2] RightCarSpeedControl:右轮速度
* @param[out] void
* @retval void
* @par History 无
*/
void spin_left(unsigned int LeftCarSpeedControl,unsigned int RightCarSpeedControl)
{//左电机后退digitalWrite(Left_motor_go, LOW); //左电机前进禁止digitalWrite(Left_motor_back, HIGH); //左电机后退使能softPwmWrite(Left_motor_pwm, LeftCarSpeedControl);//右电机前进digitalWrite(Right_motor_go, HIGH); //右电机前进使能digitalWrite(Right_motor_back, LOW); //右电机后退禁止softPwmWrite(Right_motor_pwm, RightCarSpeedControl);
}/**
* Function right
* @brief 小车右转(左轮前进,右轮不动)
* @param[in1] LeftCarSpeedControl:左轮速度
* @param[in2] RightCarSpeedControl:右轮速度
* @param[out] void
* @retval void
* @par History 无
*/
void right(unsigned int LeftCarSpeedControl,unsigned int RightCarSpeedControl)
{//左电机前进digitalWrite(Left_motor_go, HIGH); //左电机前进使能digitalWrite(Left_motor_back, LOW); //左电机后退禁止softPwmWrite(Left_motor_pwm, LeftCarSpeedControl);//右电机停止digitalWrite(Right_motor_go, LOW); //右电机前进禁止digitalWrite(Right_motor_back, LOW); //右电机后退禁止softPwmWrite(Right_motor_pwm, RightCarSpeedControl);
}/**
* Function spin_right
* @brief 小车原地右转(右轮后退,左轮前进)
* @param[in1] LeftCarSpeedControl:左轮速度
* @param[in2] RightCarSpeedControl:右轮速度
* @param[out] void
* @retval void
* @par History 无
*/
void spin_right(unsigned int LeftCarSpeedControl,unsigned int RightCarSpeedControl)
{//左电机前进digitalWrite(Left_motor_go, HIGH); //左电机前进使能digitalWrite(Left_motor_back, LOW); //左电机后退禁止softPwmWrite(Left_motor_pwm, LeftCarSpeedControl);//右电机后退digitalWrite(Right_motor_go, LOW); //右电机前进禁止digitalWrite(Right_motor_back, HIGH); //右电机后退使能softPwmWrite(Right_motor_pwm, RightCarSpeedControl);
}/**
* Function back
* @brief 小车后退
* @param[in1] LeftCarSpeedControl:左轮速度
* @param[in2] RightCarSpeedControl:右轮速度
* @param[out] void
* @retval void
* @par History 无
*/
void back(unsigned int LeftCarSpeedControl,unsigned int RightCarSpeedControl)
{//左电机后退digitalWrite(Left_motor_go, LOW); //左电机前进禁止digitalWrite(Left_motor_back, HIGH); //左电机后退使能softPwmWrite(Left_motor_pwm, LeftCarSpeedControl);//右电机后退digitalWrite(Right_motor_go, LOW); //右电机前进禁止digitalWrite(Right_motor_back, HIGH); //右电机后退使能softPwmWrite(Right_motor_pwm, RightCarSpeedControl);
}
- 小车巡线模式函数
/***********模式2 巡线模式*************/
/**
* Function Tracking_Mode
* @brief 巡线模式
* @param[in1] void
* @param[out] void
* @retval void
* @par History 无
*/
int g_trackstate = 0;
void Tracking_Mode()
{char *p=ReturnTemp;memset(ReturnTemp,0,sizeof(ReturnTemp));track_test();//在巡线过程中发送巡线传感器效果serialtime--;if(serialtime == 0){count--;serialtime = 5000;if(count == 0){strcat(p,"4WD,CSB0,PV8.4,GS0,LF");strcat(p,infrared_track_value);strcat(p,",HW00,GM00#");serialPrintf(fd, p);serialtime = 5000;count = 20;}}//四路循迹引脚电平状态// 0 0 X 0// 1 0 X 0// 0 1 X 0//以上6种电平状态时小车原地右转,速度为250,延时80ms//处理右锐角和右直角的转动if ( (TrackSensorLeftValue1 == LOW || TrackSensorLeftValue2 == LOW) && TrackSensorRightValue2 == LOW){g_trackstate = 1;spin_right(200, 200); // luz 250, 250delay(80);}//四路循迹引脚电平状态// 0 X 0 0 // 0 X 0 1 // 0 X 1 0 //处理左锐角和左直角的转动else if ( TrackSensorLeftValue1 == LOW && (TrackSensorRightValue1 == LOW || TrackSensorRightValue2 == LOW)){g_trackstate = 2;spin_left(200, 200); // luz 250, 250delay(80);}// 0 X X X//最左边检测到else if ( TrackSensorLeftValue1 == LOW){g_trackstate = 3;spin_left(160, 160); //luz 200, 200//delay(10);}// X X X 0//最右边检测到else if ( TrackSensorRightValue2 == LOW ){g_trackstate = 4;spin_right(160, 160); //luz 200, 200//delay(10);}//四路循迹引脚电平状态// X 0 1 X//处理左小弯else if ( TrackSensorLeftValue2 == LOW && TrackSensorRightValue1 == HIGH){g_trackstate = 5;left(0, 176); //luz 0, 220}//四路循迹引脚电平状态// X 1 0 X //处理右小弯else if (TrackSensorLeftValue2 == HIGH && TrackSensorRightValue1 == LOW){g_trackstate = 6;right(176, 0); //luz 220,0}//四路循迹引脚电平状态// X 0 0 X//处理直线else if (TrackSensorLeftValue2 == LOW && TrackSensorRightValue1 == LOW){g_trackstate = 7;run(204, 204); //luz 255, 255 }else{switch(g_trackstate){case 1:spin_right(200,200);printf("1\n");break; //luz 250,250case 2:spin_left(200,200);printf("2\n");break; //luz 250,250case 3:spin_left(160,160);printf("3\n");break; //luz 200,200case 4:spin_right(160,160);printf("4\n");break; //luz 200,200case 5:printf("5\n");left(0, 176); break;//luz 0, 220 no breakcase 6:printf("6\n");right(176,0); break;//luz 220,0 no breakcase 7:printf("7\n");run(204,204); break;//luz 255, 255 no break}}//当为1 1 1 1时小车保持上一个小车运行状态}
- 摄像头舵机根据超声波测距距离的动作函数
/**
* Function servo_color_carstate
* @brief 舵机转向超声波测距避障行驶,led根据车的状态显示相应的颜色
* @param[in] void
* @param[out] void
* @retval void
* @par History 无
*/void servo_color_carstate()
{float distance;//定义舵机位置变量和小车前方,左侧,右侧距离int iServoPos = 0;int LeftDistance = 0; //左方距离值变量LeftDistanceint RightDistance = 0; //右方距离值变量RightDistanceint FrontDistance = 0; //前方距离值变量FrontDistancecolor_led_pwm(255, 0, 0);//开红灯back(80,80); //避免突然停止,刹不住车delay(80);brake();//舵机旋转到0度,即右侧,测距servo_appointed_detection(0);delay(500);distance = Distance_test(); //测距RightDistance = distance; //所测的右侧距离赋给变量RightDistance//printf("rightdistance :%d\n",RightDistance);//舵机旋转到180度,即左侧,测距servo_appointed_detection(180);delay(500);distance = Distance_test(); //测距LeftDistance = distance;//所测的左侧距离赋给变量LeftDistance// printf("leftdistance :%d\n",LeftDistance);//舵机旋转到90度,即左侧,测距servo_appointed_detection(90);delay(500);distance = Distance_test();FrontDistance = distance;//所测前侧距离付给变量FrontDistance//printf("FrontDistance:%d\n",FrontDistance);if (LeftDistance < 30 && RightDistance < 30 && FrontDistance < 30 ){//亮品红色,掉头color_led_pwm(255, 0, 255);spin_right(200,200);delay(700);}else if ( LeftDistance >= RightDistance) //当发现左侧距离大于右侧,原地左转{//亮蓝色color_led_pwm(0, 0, 255);spin_left(200,200);delay(350);}else if (LeftDistance < RightDistance ) //当发现右侧距离大于左侧,原地右转{//亮品红色,向右转color_led_pwm(255, 0, 255);spin_right(200,200);delay(350);}
}
- 小车根据超声波测距距离的运动控制
/************模式3:超声波避障模式*************/
/**
* Function Ultrasonic_avoidMode
* @brief 超声波避障模式
* @param[in] void
* @param[out] void
* @retval void
* @par History 无
*/
void Ultrasonic_avoidMode()
{float distance = 0;distance = Distance_test(); //测量前方距离if (distance > 50 ) //障碍物距离大于50时,开启左右红外辅助避障{////遇到障碍物,红外避障模块的指示灯亮,端口电平为LOW//未遇到障碍物,红外避障模块的指示灯灭,端口电平为HIGHLeftSensorValue = digitalRead(AvoidSensorLeft);RightSensorValue = digitalRead(AvoidSensorRight);if (LeftSensorValue == HIGH && RightSensorValue == LOW){spin_left(200,200); //右边探测到有障碍物,有信号返回,原地向左转delay(200);}else if (RightSensorValue == HIGH && LeftSensorValue == LOW){spin_right(200,200);//左边探测到有障碍物,有信号返回,原地向右转delay(200);}else if (RightSensorValue == LOW && LeftSensorValue == LOW){spin_right(200,200);//当两侧均检测到障碍物时调用固定方向的避障(原地右转)delay(200);}//距离大于50时前进,亮绿灯run(180, 180); //250, 250color_led_pwm(0, 255, 0);}else if ((distance >= 30 && distance <= 50)){//遇到障碍物,红外避障模块的指示灯亮,端口电平为LOW//未遇到障碍物,红外避障模块的指示灯灭,端口电平为HIGHLeftSensorValue = digitalRead(AvoidSensorLeft);RightSensorValue = digitalRead(AvoidSensorRight);if (LeftSensorValue == HIGH && RightSensorValue == LOW){spin_left(200,200); //右边探测到有障碍物,有信号返回,原地向左转delay(200);}else if (RightSensorValue == HIGH && LeftSensorValue == LOW){spin_right(200,200);//左边探测到有障碍物,有信号返回,原地向右转delay(200);}else if (RightSensorValue == LOW && LeftSensorValue == LOW){spin_right(200,200);//当两侧均检测到障碍物时调用固定方向的避障(原地右转)delay(200);}//距离在30-50之间时慢速前进run(120, 120); //180, 180}else if ( distance < 30 )//当距离小于30时调用舵机颜色控制程序{servo_color_carstate();}
}
- 通过串口通信接收蓝牙微信控制程序的控制指令
/**
* Function serial_data_parse
* @brief 串口数据解析并指定相应的动作
* @param[in] void
* @param[out] void
* @retval void
* @par History 无
*/
void serial_data_parse()
{//解析上位机发过来的模式切换指令if(StringFind((const char *)InputString, (const char *)"MODE", 0) > 0){if(InputString[10] == '0')//停止模式{brake();g_CarState = enSTOP;g_modeSelect = 1;BeepOnOffMode();}else{switch (InputString[9]){case '0': g_modeSelect = 1; ModeBEEP(0); break;case '1': g_modeSelect = 1; ModeBEEP(1); break;//遥控模式case '2': g_modeSelect = 2; ModeBEEP(2); break;//巡线模式case '3': g_modeSelect = 3; ModeBEEP(3); break;//避障模式case '4': g_modeSelect = 4; ModeBEEP(4); break;//七彩模式case '5': g_modeSelect = 5; ModeBEEP(5); break;//寻光模式case '6': g_modeSelect = 6; ModeBEEP(6); break;//跟随模式default: g_modeSelect = 1; break;}delay(1000);BeepOnOffMode();}memset(InputString, 0x00, sizeof(InputString));NewLineReceived = 0;return; }//非apk模式则退出
// if(g_modeSelect != 1)
// {
// memset(InputString, 0x00, sizeof(InputString));
// NewLineReceived = 0;
// return;
// }//解析上位机发来的舵机云台的控制指令并执行舵机旋转//如:$4WD,PTZ180# 舵机转动到180度if (StringFind((const char *)InputString, (const char *)"PTZ", 0) > 0){int m_kp, i, ii;//寻找以PTZ开头,#结束中间的字符i = StringFind((const char *)InputString, (const char *)"PTZ", 0); ii = StringFind((const char *)InputString, (const char *)"#", i);if (ii > i){char m_skp[5] = {0};memcpy(m_skp, InputString + i + 3, ii - i -3);m_kp = atoi(m_skp); //将找到的字符串变成整型servo_appointed_detection(180 - m_kp);//转动到指定角度m_kpNewLineReceived = 0; memset(InputString, 0x00, sizeof(InputString));return;}}//解析上位机发来的七彩探照灯指令并点亮相应的颜色//如:$4WD,CLR255,CLG0,CLB0# 七彩灯亮红色if (StringFind((const char *)InputString, (const char *)"CLR", 0) > 0){int m_kp, i, ii, red, green, blue;char m_skp[5] = {0};i = StringFind((const char *)InputString, (const char *)"CLR", 0);ii = StringFind((const char *)InputString, (const char *)",", i);if (ii > i){ memcpy(m_skp, InputString + i + 3, ii - i -3);m_kp = atoi(m_skp);red = m_kp;}i = StringFind((const char *)InputString, (const char *)"CLG", 0);ii = StringFind((const char *)InputString, (const char *)",", i);if (ii > i){memcpy(m_skp, InputString + i + 3, ii - i -3);m_kp = atoi(m_skp);green = m_kp;}i = StringFind((const char *)InputString, (const char *)"CLB", 0);ii = StringFind((const char *)InputString, (const char *)"#", i);if (ii > i){memcpy(m_skp, InputString + i + 3, ii - i -3);m_kp = atoi(m_skp);blue = m_kp;color_led_pwm(red, green, blue);//点亮相应颜色的灯NewLineReceived = 0; memset(InputString, 0x00, sizeof(InputString));return;}}//解析上位机发来的通用协议指令,并执行相应的动作//如:$1,0,0,0,0,0,0,0,0,0# 小车前进if (StringFind((const char *)InputString, (const char *)"4WD", 0) == -1 &&StringFind((const char *)InputString,(const char *)"#",0) > 0){//puts(InputString);//小车原地左旋右旋判断if (InputString[3] == '1') //小车原地左旋{g_CarState = enTLEFT;}else if (InputString[3] == '2') //小车原地右旋{g_CarState = enTRIGHT;}else{g_CarState = enSTOP;}//小车鸣笛判断if (InputString[5] == '1') //鸣笛{whistle();}//小车加减速判断if (InputString[7] == '1') //加速,每次加50{g_CarSpeedControl += 20;if (g_CarSpeedControl > 200){g_CarSpeedControl = 200;}}if (InputString[7] == '2') //减速,每次减50{g_CarSpeedControl -= 20;if (g_CarSpeedControl < 100){g_CarSpeedControl = 100;}}//舵机左旋右旋判断
/* if (InputString[9] == '1') //舵机旋转到180度{servo_appointed_detection(180);}if (InputString[9] == '2') //舵机旋转到0度{servo_appointed_detection(0);}
*///点灯判断if (InputString[13] == '1')//七彩灯亮白色{g_lednum++;if(g_lednum == 1){color_led_pwm(255, 255, 255);}else if(g_lednum == 2){color_led_pwm(255,0,0);}else if(g_lednum == 3){color_led_pwm(0,255,0);}else if(g_lednum == 4){color_led_pwm(0,0,255);}else if(g_lednum == 5){color_led_pwm(255,255,0);}else if(g_lednum == 6){color_led_pwm(0,255,255);}else if(g_lednum == 7){color_led_pwm(255,0,255);}else{color_led_pwm(0,0,0);g_lednum=0;}}if (InputString[13] == '2')//七彩灯亮红色{color_led_pwm(255, 0, 0);}if (InputString[13] == '3')//七彩灯亮绿灯{color_led_pwm(0, 255, 0);}if (InputString[13] == '4') //七彩灯亮蓝灯{color_led_pwm(0, 0, 255);}if (InputString[13] == '5')//青色{color_led_pwm(0, 255, 255);}if (InputString[13] == '6')//品红色{color_led_pwm(255, 0, 255);}if (InputString[13] == '7')//黄色{color_led_pwm(255, 255, 0);}if (InputString[13] == '8') //七彩灯灭掉{color_led_pwm(0, 0, 0);}//灭火判断if (InputString[15] == '1') //灭火{digitalWrite(OutfirePin, !digitalRead(OutfirePin));}//舵机归为判断if (InputString[17] == '1') //舵机旋转到90度{g_frontservopos = 90; }//前后舵机前后左右转动switch(InputString[9]){case front_left_servo: g_frontservopos = 180 ;break;case front_right_servo: g_frontservopos = 0;break;case up_servo: g_ServoState = enSERVOUP; break;case down_servo: g_ServoState = enSERVODOWN;break;case left_servo: g_ServoState = enSERVOLEFT;break;case right_servo: g_ServoState = enSERVORIGHT;break;case updowninit_servo: g_ServoState = enSERVOUPDOWNINIT;break;case stop_servo: g_ServoState = enSERVOSTOP;break;}//小车的前进,后退,左转,右转,停止动作if (g_CarState != enTLEFT && g_CarState != enTRIGHT){switch (InputString[1]){case run_car: g_CarState = enRUN; break;case back_car: g_CarState = enBACK; break;case left_car: g_CarState = enLEFT; break;case right_car: g_CarState = enRIGHT; break;case stop_car: g_CarState = enSTOP; break;default: g_CarState = enSTOP; break;}}memset(InputString, 0x00, sizeof(InputString)); //清空串口数据NewLineReceived = 0;//根据小车状态做相应的动作switch (g_CarState){case enSTOP: brake(); break;case enRUN: run(g_CarSpeedControl, g_CarSpeedControl); break;case enLEFT: left(0, g_CarSpeedControl); break;case enRIGHT: right(g_CarSpeedControl, 0); break;case enBACK: back(g_CarSpeedControl, g_CarSpeedControl); break;case enTLEFT: spin_left(g_CarSpeedControl, g_CarSpeedControl); break;case enTRIGHT: spin_right(g_CarSpeedControl, g_CarSpeedControl); break;default: brake(); break;}}
}
总结一下:过程很曲折,结果还凑合,也是学习一种有趣新科技,以后慢慢带着孩子们学习探索,附上一些图片,结束收工!
树莓派Raspberry Pi 系统搭建和智能机器人小车的组装调试相关推荐
- 树莓派(raspberry pi)系统开发
[树莓派(raspberry pi)] 01.在linux环境下给树莓派安装系统及入门各种资料 [树莓派(raspberry pi)] 02.PI3安装openCV开发环境做图像识别(详细版) 出处: ...
- 树莓派(Raspberry Pi 3) - 系统烧录及系统使用
转自:https://blog.csdn.net/u012313335/article/details/53405734 树莓派(Raspberry pi)是一块集成度极高的ARM开发板,不仅包含了H ...
- 树莓派要mysql的密码_树莓派raspberry Pi 3B+系统中安装mysql过程中不提示输入密码,安装完后如何设置密码...
树莓派raspberry Pi 3B+安装mysql未提示输入密码,安装后修改mysql密码默认密码 使用mysql -uroot -p 命令连接mysql时,报错 pi@raspberrypi:/ ...
- 树莓派(Raspberry Pi)搭建简单的lamp服务
树莓派(Raspberry Pi)搭建简单的lamp服务: 1. LAMP 的安装 sudo apt-get install apache2 mysql-server mysql-client php ...
- 树莓派 Raspberry Pi 3B+安装官方系统(一)
两年前购买的树莓派(可看当时我的记录树莓派 3B + 重装系统并配置 WiFi | YouForever)已经在角落里吃灰了好长时间,一度以为已经损坏,几天前试着拿出来折腾一下,竟然神奇地复活了,开心 ...
- 树莓派Raspberry Pi的嵌入式QT平台
在树莓派上设计桌面应用一般情况会依赖于X11环境,如果是Windows平台,就目前而言,我们也可以选择Windows 10 IoT环境进行开发.UWP目前也支持在树莓派2上进行部署.至于常见的Linu ...
- 树莓派(raspberry pi)日记1之个人网站的构建(localhost内网穿透实现公网可以访问)
树莓派(raspberry pi)日记1之个人网站的构建(localhost内网穿透实现公网可以访问) 板子:raspberry pi 3B+ // 作者:shawn 2021.2.9 14:10 A ...
- VLOG丨树莓派Raspberry Pi 3安装PLEX并挂载USB硬盘打造最牛的微型家庭影音服务器2018...
视频介绍 树莓派3安装目前最流行的PLEX服务器,实现既能最大限度降低功耗,也能随时随地观看分享影片. 一.在树莓派下安装PLEX媒体服务器 1.在终端,将你的树莓派更新至最新 sudo apt up ...
- 树莓派Raspberry Pi OS开机自启动脚本
树莓派Raspberry Pi OS开机自启动脚本 1. 需求和来源 2. 后台运行Python 3. 运行Terminal 1. 需求和来源 想让设备开机后就要运行Python工程文件 那么需设置l ...
最新文章
- 数据库安装时挂起问题
- jsp页面取整数 和Java页面取整数
- linux系统启动级别
- linux shell ls 列出文件绝对路径
- 一张小票看透支付清结算架构
- 全民大乐斗场服务器状态,看到最近很多人问重修,**说一下这个问题
- 大数据杀熟!用苹果手机怪我咯?
- 卡尔曼滤波——16.新的均值和方差
- erp系统是什么版本
- [洛谷P2384]最短路
- 全局变量在多个进程中不共享
- 常见的HTTP状态码大全
- 脉歌蓝牙耳机线评测_感受蓝牙5.0带来的震撼——脉歌T1000Pro蓝牙耳机轻体验
- 人力资源管理案例选读【2】
- 怎样登陆阿里云mysql_怎样登陆阿里云mysql数据库
- 基于flink的电商用户行为数据分析【1】| 项目整体介绍
- 网站服务器内存满了,云服务器内存满了怎么办
- 使用python抓取并分析数据—链家网(requests+BeautifulSoup)(转)
- 3.4 Docker最新入门教程-Docker入门-共享应用程序
- 苹果a15处理器相当于骁龙多少 苹果a15处理器性能