玩机器人嘛,通讯肯定是少不了的。
驱动板之间的数据处理、ROS系统和驱动板之间的通讯、ROS系统与ROS系统之间的通讯,归根结底其实就是三类——硬件与硬件的通讯、硬件与软件的通讯、软件与软件的通讯。
这里我不讲ROS,这里我会将一些通讯方式和ROS的进行对比,来为大家分享我的一些理念问题。
这里我们先来区分一个概念——通讯方式和通讯协议。
通讯方式呢,就是通讯方式!比如什么串口啊、IIC啊、SPI、RS485、RS232、CAN等等,说的是物理的连接方式!
而通讯协议呢,可能大家听到最多的就是Modbus协议、TCP/UDP协议、MQTT协议(MQTT放到这里不是很准确,我们一会再说)等等,说的是通讯里面Date是怎么样的。
曾经有很多个奇葩,让我给我做个Modbus通讯接口,我问他通讯方式是什么,他告诉我是Modbus!!!唉~
我们先来聊聊硬件与硬件的通讯方式,大家最熟悉的肯定就是串口通讯!话不多说,先上图!
串口通讯由四条线组成,VCC和GND提供供电,TxD(Transmit(x) Data,发送数据x)是发送端,RxD(Receive(x) Data,接收数据x)是接收端。我们在使用串口通讯的时候,需要将硬件1的TxD接硬件2的RxD,硬件1的RxD接硬件2的RxD,实现两个硬件之间的数据接收与发送。
不知道大家在玩单片机的时候有没有听到过“软串口”的概念,在单片机串口不够用的情况下,我们通过调用定时器的方式来模拟时钟来实现时序,做的一个具备串口功能,但是不是串口的方式,我们称之为软串口,即假串口。
这里我们提到了时钟、时序的概念,这两个概念的体系较为庞大,大家可以去借鉴单片机大佬们的文档进行理解。我们先就串口来说,大家在玩串口的时候经常用的就是波特率,大家常用也就是9600、57600、115200等,再者就是大家用到比较少的数据模式,比如我们默认的8N1。
波特率是什么呢?大家指导波特率的单位吗?没错!bps,bits per second,比特/秒,应该是叫比特率(为啥咱们叫波特率,我觉得你可问问你们教这个老师QAQ)。它反应的是每秒进行通讯的位数。
我们再来说说数据模式,这里就要为大家介绍下数据位、停止位、校验位的概念。
数据位是衡量通信中实际数据位的参数。当计算机发送一个信息包,实际的数据不会是8位的,标准的值是5、6、7和8位。如何设置取决于你想传送的信息。比如,标准的ASCII码是0~127(7位)。扩展的ASCII码是0~255(8位)。如果数据使用简单的文本(标准 ASCII码),那么每个数据包使用7位数据。每个包是指一个字节,包括开始/停止位,数据位和奇偶校验位。由于实际数据位取决于通信协议的选取,术语“包”指任何通信的情况。
停止位是用于表示单个包的最后一位。典型的值为1,1.5和2位。由于数据是在传输线上定时的,并且每一个设备有其自己的时钟,很可能在通信中两台设备间出现了小小的不同步。因此停止位不仅仅是表示传输的结束,并且提供计算机校正时钟同步的机会。适用于停止位的位数越多,不同时钟同步的容忍程度越大,但是数据传输率同时也越慢。
检验位是在串口通信中一种简单的检错方式。有四种检错方式:偶、奇、高和低。当然没有校验位也是可以的。对于偶和奇校验的情况,串口会设置校验位(数据位后面的一位),用一个值确保传输的数据有偶个或者奇个逻辑高位。例如,如果数据是011,那么对于偶校验,校验位为0,保证逻辑高的位数是偶数个。如果是奇校验,校验位为1,这样就有3个逻辑高位。高位和低位不是真正的检查数据,简单置位逻辑高或者逻辑低校验。这样使得接收设备能够知道一个位的状态,有机会判断是否有噪声干扰了通信或者是否传输和接收数据是否不同步。
我们在默认的情况下为“8N1”模式,即8位数据位,无(None)校验,1位停止位。
然后是IIC通讯!
IIC的通讯属于是总线挂载的方式,我们通过SCL时钟线上的高低电平变化作为时钟,通过SDA的高低电平变化来传输数据。只是在传输开始和传输结束的时候,都有对应的高低电平响应。
IIC的通讯建议大家对照着相关的模块来进行解读,根据时序图,进行拉高拉低电平操作,及对电平的读取操作,将01二进制转成我们想要的格式,实现数据的读取(未封装,裸原理)。
IIC通讯呢,80%靠Github、CSDN、类库等等,20%靠数据手册!这玩意没办法,作为一个“不精通单片机的单片机开发人员”,我就是那80%!
然后是SPI通讯!
本来不是不打算闲扯SPI的,不过考虑到最后想说的内容,还是多说几句。
SPI通讯由6条线组成Vcc(供电正极)、Gnd(供电负极)、MOSI(Master Output Slave Input,主机输出,从机输入)、MISO(Master Input Slave Output主机输入,从机输出)、SS(Sign Select,信号选择)、SCLK(Sign Clock,时钟信号线)。通讯示意图如下所示。
SS(Sign Select)信号选择线的存在,更类似于一个IO口作为使能端的一个存在。
有的地方,MOSI也叫做SDO(Sign Data Output,数据输出信号),MISO也叫做SDI(Sign Data Input,数据输入信号),
SPI数据的通讯和串口十分相似,都是“接收”对“发送”,“发送”对“接收”,而不同的地方,第一是在时钟方面,串口是自身的通讯频率来作为时钟,而SPI则有一条专门的时钟线(SCLK)来作为时钟,第二则是设备选择,通过SS线,可有效选择从机设备进行通信。由于我们平常使用SPI的时候只有一台从机设备,所以SS也就省略掉。
然后大家听说比较少的单总线和私有!
单总线大家可能听说的比较少,就是通过一根信号线来实现数据的传输,比如我们常用的DS18B20温度传感器、DHT*系列的温湿度传感器、红外接收头和红外发送LED等等,这种都属于是单总线的格式,它们有着之际的数据协议,我们将信号线上读到的数据按照这个协议来解读就可以得到我们想要的数据。
对于私有信号协议方式来说,我更喜欢叫它为自定义时序通讯。在我的理解当中,单片机的通讯实现是在引脚进行高低电平操作,我们根据接收到的高低电平,将这些电平信号传入到我们的解析规范(就是信号的的时序、规则)计算得出我们需要的数据。在我的应用当中,HX711芯片的使用就是这样的功能,这个是芯片在压力传感器变送装置上面使用的较多,比如电子秤的设计与制造。
CAN、RS485、RS232等等
其实在我做的项目当中,用到CAN总线的,一枚TJA1050的IC实现CAN转串口;用到RS232/RS485的,那就更好解决了的,哈哈哈。都是做开发,何必相互为难呢~
我第一次做到对公的项目时候,项目需求是做个超声波驱鸟器,当微波感应雷达或者人体红外热释传感器接收到数据时,LED以50Hz的频率闪烁,超声波模块发出10KHz频率的声音实现驱鸟的效果。当时项目用的51单片机,实在是不熟练51!!!50Hz的频率和10KHz的频率肯定是要用定时器中断的,当时这块还没学,怎么搞?LED闪烁的问题用两个三极管和两个电容实现爆闪灯的效果,10KHz超声波驱动直接上了一块NE555的IC,而我在单片机端所作的任务,就是检测到微波感应雷达和人体红外热释传感器出触发后,给个高低电平来启动闪灯和超声驱鸟的功能即可。
软的不够,硬的来凑!这种方法在急需实现功能的情况下,个人认为还是比较有效的。当然如果考虑成本的话,还是老老实实写代码吧~
我们这里再来聊聊软件与硬件之间的通讯问题!
硬件指的是我们的单片机+传感器的组合,而在软件层次,也就是我们所说的上位机软件、工控机软件、中控软件、终端软件等等,有着多种的叫法。而他们实现的功能都是一样,就是实现软件系统和硬件系统(单片机+传感器)之间的数据交互。
在软件方面的种类就比较多了,电脑软件、手机软件、网页、小程序等等。最常用的一种就是通过USB线+USB转串口的芯片,实现和电脑之间的有线连接通讯,我们电脑端开发一个串口通讯的功能实现通讯,如果是手机端的有线连接的话,我们使用的手机的OTG接口通讯(针对于安卓)。当然也有通讯网线的通讯,这种也是比较简单的,我们将以太网模块接入到单片机的串口,在电脑端进行相关的配置之后,通过网络通讯协议实现数据的交互。
有线的操作实在是不方便,所以在这个基础上,我们需要使用串口的话,我们有无线串口模块;我们需要使用网络的话,我们有WIFI模块;当然其他的蓝牙模块、4G模块等等,很多。如果你玩过这些模块的话,你会发现,这些个模块大多数都是串口,而且大多数执行的数据都是一种叫做AT的数据指令。
通讯方式和通讯协议
在软件与硬件的通讯当中,通讯方式是通讯类型的选择,串口、网络等等;而通讯协议则是对传输数据的格式规范。做通讯会串口都能写,但是数据实现通讯很麻烦,你收到的和你发送的经常出现bug需要你调试(如果你那边有封装的话当我没说,这部分你可以PASS)。所以我们需要通过定义通讯协议的方式来实现数据的交互。以下是我在实际应用当中所用到的一些通讯协议的制定。
信号事件触发与槽函数执行机制
如果说你是做软件领域的,那么你对这个概念一定不会陌生,因为我们在我们的软件当中,前端界面的控件和后台服务的数据,都是通过信号与槽(Signal Slot)的方式触发的。
信号(Signal)就是在特定情况下被发射的事件;槽(Slot)就是对信号响应的函数。
在软件当中,当你点击了一个按键,软件会检测到这个按键被你触发到,在软件里面会产生一个按键触发信号,我们在程序的后端做一个函数进行响应,当按键信号被触发之后,软件来调用这个按键关联的函数做出对应的执行,这一套的过程叫做信号事件触发与槽函数执行。
这些概念是在软件开发常用到的一些概念,在我们的软件硬件通讯对接方面,会比这个简单的很多。
我在接触到的第一个软件硬件通讯交互的项目当中,就是做个电脑软件,来实现控制LED的点亮和熄灭。发送字符“1”,LED点亮;发送字符“2”,LED熄灭。这就是软件硬件最简单的一个通讯方式。
这个也是初学时候最为高效的一种方法,大家可以在单片机上面接几个LED,当串口读到字符“1”,第一个LED点亮;当串口读到字符“2”,第一个LED熄灭,以此类推。我们可以接个蓝牙模块接在串口,通过蓝牙串口助手来实现这个功能。
如果你用的是WiFi模块或者以太网模块的话,可能需要一些比较负载的配置。没关系,万变不离其宗!其实这也是转的一种“思路”,在我们需要蓝牙的情况下,设备自身无法实现,但是有个串口转蓝牙的模块,通过硬件来实现一定的功能。
这里的内容思维可以对照信号与槽,但是这并不是信号与槽的概念。因为我会在后面的内容当中讲到上位机开发的相关代码,而这个信号与槽的概念则是必须的。
Modbus协议
不要怕,Modbus协议很简单。我们在进行数据交互的时候,我们将数据按照一定的规则打包成一格数据包,接收端按照这个规则来对数据进行切片处理(就是从一串数据里面拿出你要的数据)。
Modbus协议的数据格式格式如下:
帧头 地址 指令 长度 数据1 数据2... ...数据N 校验 帧尾
帧头:数据的开始标示。
帧尾:数据的结束标示。
帧头和帧尾之间,就是我们要的数据。在通讯当中,我们不可能刚刚好从帧头开始读取,到帧尾读取结束。如果设备先启动,软件后启动,那个软件读取的第一个数据就有可能不是帧头;同样在设备突然断电的情况下,这个数据也不可能刚刚是帧尾(可能性存在,别杠)。这个时候的数据是残缺的,是无效的数据。
地址:对当前设备的标示。
ModBus在一些总线式的通讯当中使用较为方面,如果有多个通讯的设备需要接入,我们可以为每一个设备的设定一个ID值,我们根据ID值来区分当前的数据来自于哪个设备。
指令:对于的单片机来说,就是读指令(软件从单片机接收数据,比如读取传感器的数值)和写指令(软件给单片机数据,单片机执行,比如软件控制单片机点灯)。
指令的话基本就是读和写,因为这是单片机的,比较简单一些。
长度:当前这一组数据有多少个,即数据长度。
数据:我们传输的数据格式。
校验:在传输的过程当中,数据信号可能收到干扰产生错误的数据,校验来保证数据的准确性。
校验主要是来检验当前发出的数据和收到的数据是否一致的。校验有很多种方式,比如CRC、哈希等等,不过我不用这俩玩意,忒占内存。我的数据比较简单也不涉及加密什么的,我就直接将所有数据位的数据相加,取低八位来作为校验。
在数据接收当中,我们通过帧头和帧尾找到我们要的数据,然后判定地址得知是哪来的数据,判定指令看看这个数据是要读还是要写,判定长度看看传过来的数据有没有丢包的,判定校验来看看数据有没有错误的。虽然繁琐,但是真的很有效,而且很必须。
我之前做到了一个关于航空载荷的项目,其数据格式如下(太长了,懒得复制)。
这个是比较完整得一个通讯指令,由于这个应用只是读取数据,所以我没有加指令位的标示。
以上的两种是我在实际应用的到,第一种对于刚刚接触通讯调试的肯定是方便的,但是涉及庞大一些的数据交互时,Modbus时很高效的。当然不仅仅只有上面的两种,这些只是我经常使用的一些类型。
Modbus协议目前在很多ROS机器人的驱动板的应用方案,通过Modbus读操作获取编码器及陀螺仪等数据。通过Modbus写操作来控制电机转动。因为Modbus协议的方式可以很方便对接到其他得应用场景,而不是仅仅只是在ROS下使用(请自行理解)。
而第一种方式,也是ros_arduino_bridge固件的当时,通过发送对应的指令来对单片机进行操作~
a是AD值读取,b是获取波特率,c是模式设置,d是数字量读取,e是读取编码器数据,m是电机速度控制,p是检测连通性,r是重置编码器数值,s是舵机值写入,t是舵机值读取,u是更新pid值,w是数字量写入,x是模拟量写入~
这里的内容大家可以自行阅读相关开发文档进行理解!
这些的通讯协议在软件与硬件之间通过串口、8266、以太网等方式,均适用!
ROS下的话,也适用!
软件与硬件的方式和协议,基本上都差不多,根据不同的场景来选择适合的方式来设计项目~
我们再来说说软件与软件的方式~
这部分也是和ROS下的通讯也是有着可以类比的方式!
以互联网平台位载体,配合传感器技术实现万物互联,让地球真真正正的变成一个“地球村”,这就是物联网,万“物”互联。
我们可以通过软件来读取传感器的数据,而我们的软件又可以和其他的软件之间进行通讯,那么我们传感器的数据也是可以和他人进行交互的。随着云服务的普及,数据上云、公网透传等技术也愈发的成熟,相关的开发者接口也是比较完善的。
B/S架构的请求应答
B,Browser,浏览者、浏览器;S,Server,服务端、服务器。
B/S是一种基于浏览访问的通讯方式,我们经常用到的网页、网页小程序等前端类的,都是基于B/S架构的开发。我们搭建好一个网站,这个网站有多个用户来进行浏览访问,在这种情况下,我们的网站就是Server,这些个用户就是Browser,它们之间的关系是这样的。
我们Browser向Server发起请求,Server在得到请求后向Browser返回一个应答。从而实现数据的交互。说到这里,我们就不得不说一啊爬虫。
大家都知道爬虫可以从网络爬取大量的数据,而且对于一些抱着目的性的爬虫,这简直就是游走在法律的边缘线(虽然我也经常使用爬虫来爬取数据,咳咳)。
大家在浏览器里面按下F12可以进入到一个控制台,你可以试着点击一下当前页面的跳转按钮之类的,你就会看到关于B/S的数据流动。
这是我在百度的首页按下F12之后,在Console控制台界面看到的招聘。我在想从这里投递简历的话,录取的可能性会不会高一些。
请求/应答的数据传输方式我们会在后面有实例项目,关于网络爬虫及网络安全的内容过于敏感,在此不做过多的学习。
import socket#导入socket模块,用于TCP/IP通讯
serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#初始化一个socket模块的对象
serversocket.bind(('127.0.0.1', 8080))#绑定服务器的IP地址,以及通讯端口
serversocket.listen(5)#设置监听,在这里面的参数(int类型)控制连接当前服务器的最大设备数量
while True:
# 建立客户端连接
conn,addr = serversocket.accept()
print("连接地址: %s" % str(addr))
rec = conn.recv(1024)
print (rec.decode('utf-8'))
msg = input()
conn.send(msg.encode('utf-8'))
connsocket.close()
import socket,sys,time
clientsocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)#初始化一个socket模块的对象
clientsocket.connect(('127.0.0.1', 9999))#建立客户端与服务端的连接
while True:
#发送数据
ms = input()
clientsocket.send(ms.encode('utf-8'))
#接收数据
rev = clientsocket.recv(1024)#接收小于1024字节的数据
easygui.msgbox(msg = rev.decode('utf-8'))
print(rev.decode('utf-8'))
if ms == 'Q!':
break
clientsocket.close()
https://sc.ftqq.com/SCU51176Tfa047ea8f37a0eaeffbe3b0750b5b6db5e42b1423cebe.send?text=主人服务器又挂掉啦~
PS:这里有我的密钥,不要执行攻击我!!!感谢大家不杀之恩!
#coding:utf-8 #定义编码格式为UTF-8
import requests #导入网络请求功能包
import time #导入时间功能包
#通过时间函数,获得今天是周几
def week():
localtime = time.localtime(time.time())
return localtime[6]
#向Server酱油发起网络请求
def push(result):
title = "FormatingClub提示您:注意天气变化保持健康心情\n"
content = 'text='+title+'desp='+result
url="https://sc.ftqq.com/SCU51176Tfa047ea8f37a0eaeffbe3b0750b5b6db5e42b1423cebe.send?%s" % content
r = requests.get(url) #发起请求
r.close()
#根据周几,选择不同的发送内容
def SendData():
if week() == 0:
data = "今天是星期一。\n您的课程有:\n软件工程 曹允斌老师 小多媒体501\njava语言高级 马泽秀老师 信息机房1\nASP.net 李娇老师 信息机房1\n请注意按时上课,再忙都不要忘记陪对象哟!"
elif week() == 1:
data = "今天是星期二。\n您的课程有:\n英语 付玲老师 大多媒体506\nJavaScript 马泽秀老师 信息机房1\n自习课\n请注意按时上课,再忙都不要忘记陪对象哟!"
elif week() == 2:
data = "今天是星期三。\n您的课程有:\nPython 李秋雨老师 小多媒体102\n创新创业(双周) 赵海玲老师 大多媒体202\n自习课\n请注意按时上课,再忙都不要忘记陪对象哟!"
elif week() == 3:
data = "今天是星期四。\n您的课程有:\nASP.net 李娇老师 小多媒体301\njava语言高级 马泽秀老师 小多媒体501\n软件工程 曹允斌老师 信息机房1\n请注意按时上课,再忙都不要忘记陪对象哟!"
elif week() == 4:
data = "今天是星期五。\n您的课程有:\nJavaScript 马泽秀老师 小多媒体502\n体育 卢军锋老师 体育场\nPython 李秋雨老师 机房\n请注意按时上课,再忙都不要忘记陪对象哟!"
elif week() == 5:
data = "今天是星期六。\n该加班了,再忙都不要忘记陪对象哟!"
elif week() == 6:
data = "今天是星期日。\n该加班了,再忙都不要忘记陪对象哟!"
if(data!='0'):
print(data)
push(data) #发送数据
else:
print('GET Data Fail')
if __name__ == '__main__':
while True:
localtime = time.localtime(time.time())
if localtime[3] == 7: #判断当前小时是不是7,也就是早上七点多发到我的微信
SendData()
time.sleep(3600) #延时3600秒,也就是1小时,每小时查询下当前时间是不是早上七点
先到这里吧,突然发现已经写了8000多字了,不想写了~
和ROS对比的话,唔~
Action = Topic + Service,啊哈哈哈!给个图大家自行理解去吧!
这部分内容有视频讲解的,就是我在今年熟悉学校分享的内容的一部分~
大家可以前往古月学院来看视频《如何将ROS用于实际机器人开发》,感谢古月居社区提供的机会!
大道三千,殊途同归~
理念部分的内容,大家可以来相互验证一下。
评论(1)
您还未登录,请登录后发表或查看评论