Mobus应该说一种通讯协议,最初为美国modicon公司为其PLC产品定义的一个基于RS232/422/485的串行通信协议,现在许多PLC、仪表等都支持这个协议。
1、奇偶校验
用户可以配置控制器是奇或偶校验,或无校验。这将决定了每个字符中的奇偶校验位是如何设置的。
如果指定了奇或偶校验,“1”的位数将算到每个字符的位数中(ASCII模式7个数据位,RTU中8个数据位)。例如RTU字符帧中包含以下8个数据位:
1 1 0 0 0 1 0 1
整个“1”的数目是4个。如果便用了偶校验,帧的奇偶校验位将是0,便得整个“1”的个数仍是4个。如果便用了奇校验,帧的奇偶校验位将是1,便得整个“1”的个数是5个。
如果没有指定奇偶校验位,传输时就没有校验位,也不进行校验检测。代替一附加的停止位填充至要传输的字符帧中。
两种传输模式中(ASCII或RTU),传输设备以将Modbus消息转为有起点和终点的帧,这就允许接收的设备在消息起始处开始工作,读地址分配信息,判断哪一个设备被选中(广播方式则传给所有设备),判知何时信息已完成。部分的消息也能侦测到并且错误能设置为返回结果。
1、ASCII帧
使用ASCII模式,消息以冒号(:)字符(ASCII码 3AH)开始,以回车换行符结束(ASCII码 0DH,0AH)。
其它域可以使用的传输字符是十六进制的0...9,A...F。网络上的设备不断侦测“:”字符,当有一个冒号接收到时,每个设备都解码下个域(地址域)来判断是否发给自己的。
消息中字符间发送的时间间隔最长不能超过1秒,否则接收的设备将认为传输错误。一个典型消息帧如下所示:
起始位 设备地址 功能代码 数据 LRC校验 结束符
1个字符 2个字符 2个字符 n个字符 2个字符 2个字符
图2 ASCII消息帧
2、RTU帧
使用RTU模式,消息发送至少要以3.5个字符时间的停顿间隔开始。在网络波特率下多样的字符时间,这是最容易实现的(如下图的T1-T2-T3-T4所示)。传输的第一个域是设备地址。可以使用的传输字符是十六进制的0...9,A...F。网络设备不断侦测网络总线,包括停顿间隔时间内。当第一个域(地址域)接收到,每个设备都进行解码以判断是否发往自己的。在最后一个传输字符之后,一个至少3.5个字符时间的停顿标定了消息的结束。一个新的消息可在此停顿后开始。
整个消息帧必须作为一连续的流转输。如果在帧完成之前有超过1.5个字符时间的停顿时间,接收设备将刷新不完整的消息并假定下一字节是一个新消息的地址域。同样地,如果一个新消息在小于3.5个字符时间内接着前个消息开始,接收的设备将认为它是前一消息的延续。这将导致一个错误,因为在最后的CRC域的值不可能是正确的。一典型的消息帧如下所示:
起始位 设备地址 功能代码 数据 CRC校验 结束符
T1-T2-T3-T4 8Bit 8Bit n个8Bit 16Bit T1-T2-T3-T4
图3 RTU消息帧
3、地址域
消息帧的地址域包含两个字符(ASCII)或8Bit(RTU)。可能的从设备地址是0...247 (十进制)。单个设备的地址范围是1...247。主设备通过将要联络的从设备的地址放入消息中的地址域来选通从设备。当从设备发送回应消息时,它把自己的地址放入回应的地址域中,以便主设备知道是哪一个设备作出回应。
地址0是用作广播地址,以使所有的从设备都能认识。当Modbus协议用于更高水准的网络,广播可能不允许或以其它方式代替。
4、如何处理功能域
消息帧中的功能代码域包含了两个字符(ASCII)或8Bits(RTU)。可能的代码范围是十进制的1...255。当然,有些代码是适用于所有控制器,有此是应用于某种控制器,还有些保留以备后用。
当消息从主设备发往从设备时,功能代码域将告之从设备需要执行哪些行为。例如去读取输入的开关状态,读一组寄存器的数据内容,读从设备的诊断状态,允许调入、记录、校验在从设备中的程序等。
当从设备回应时,它使用功能代码域来指示是正常回应(无误)还是有某种错误发生(称作异议回应)。对正常回应,从设备仅回应相应的功能代码。对异议回应,从设备返回一等同于正常代码的代码,但最重要的位置为逻辑1。
例如:一从主设备发往从设备的消息要求读一组保持寄存器,将产生如下功能代码:
0 0 0 0 0 0 1 1 (十六进制03H)
对正常回应,从设备仅回应同样的功能代码。对异议回应,它返回:
1 0 0 0 0 0 1 1 (十六进制83H)
除功能代码因异议错误作了修改外,从设备将一独特的代码放到回应消息的数据域中,这能告诉主设备发生了什么错误。
主设备应用程序得到异议的回应后,典型的处理过程是重发消息,或者诊断发给从设备的消息并报告给操作员。
5、数据域
数据域是由两个十六进制数集合构成的,范围00...FF。根据网络传输模式,这可以是由一对ASCII字符组成或由一RTU字符组成。
从主设备发给从设备消息的数据域包含附加的信息:从设备必须用于进行执行由功能代码所定义的所为。这包括了象不连续的寄存器地址,要处理项的数目,域中实际数据字节数。
例如,如果主设备需要从设备读取一组保持寄存器(功能代码03),数据域指定了起始寄存器以及要读的寄存器数量。如果主设备写一组从设备的寄存器(功能代码10十六进制),数据域则指明了要写的起始寄存器以及要写的寄存器数量,数据域的数据字节数,要写入寄存器的数据。
如果没有错误发生,从从设备返回的数据域包含请求的数据。如果有错误发生,此域包含一异议代码,主设备应用程序可以用来判断采取下一步行动。
在某种消息中数据域可以是不存在的(0长度)。例如,主设备要求从设备回应通信事件记录(功能代码0B十六进制),从设备不需任何附加的信息。
6、错误检测域
标准的Modbus网络有两种错误检测方法。错误检测域的内容视所选的检测方法而定。
ASCII
当选用ASCII模式作字符帧,错误检测域包含两个ASCII字符。这是使用LRC(纵向冗长检测)方法对消息内容计算得出的,不包括开始的冒号符及回车换行符。LRC字符附加在回车换行符前面。
RTU
当选用RTU模式作字符帧,错误检测域包含一16Bits值(用两个8位的字符来实现)。错误检测域的内容是通过对消息内容进行循环冗长检测方法得出的。CRC域附加在消息的最后,添加时先是低字节然后是高字节。故CRC的高位字节是发送消息的最后一个字节
在样本数量比较少的情况下,可以采用HOG、SVM对样本进行初步的筛选出,正负样本,本文接着上一节二值化出来部分样本后,用pictureRelate做初步筛选出正负样本各500,准确训练。
1、pictureRelate使用http://www.walthelm.net/picture-relate/
可以用来比较图片的相似程度,或找出类似的图片文件的图像处理工具。在同一个视窗里浏览不同文件夹和硬盘驱动器中的图片文件#支持查看,改名,删除,剪贴,拖动,切换至文件管理器等操作#可处理多达50000多张图片!支持JPG, BMP, PNG, TIFF,
PPM, PGM, PBM, RAS等格式#可以通过导入过滤命令行读取任何其它格式。
这里HOG LBP特征可以采用VLfeat(http://www.vlfeat.org/)这个开源库里面的特征,或者用OpenCV里面的也可以,分类器SVM采用的是OpenCV里面的。
2、下面介绍下VLfeat里面的HOG特征
左图是原图,右图是HOG变化后的可视化效果。可以仔细对比,房子的轮廓还是可见的。
3、HOG+SVM训练目标检测的流程(OpenCV自带):
http://blog.csdn.net/carson2005/article/details/7841443
(1)准备训练样本集合;包括正样本集和负样本集;
(2)收集到足够的训练样本之后,你需要手动裁剪样本。例如,你想用Hog+SVM来对商业步行街的监控画面中进行行人检测,那么,你就应该用收集到的训练样本集合,手动裁剪画面中的行人(可以写个简单程序,只需要鼠标框选一下,就将框选区域保存下来--这一步在上一节有介绍)。
(3)裁剪得到训练样本之后,将所有正样本放在一个文件夹中;将所有负样本放在另一个文件夹中;并将所有训练样本缩放到同样的尺寸大小。本文是采用90*24的区域训练的。
(4)提取所有正样本的Hog特征;
(5)提取所有负样本的Hog特征;
(6)对所有正负样本赋予样本标签;例如,所有正样本标记为1,所有负样本标记为0;
(7)将正负样本的Hog特征,正负样本的标签,都输入到SVM中进行训练;Dalal在论文中考虑到速度问题,建议采用线性SVM进行训练。这里,不妨也采用线性SVM;
(8)SVM训练之后,将结果保存为文本文件。
(9)线性SVM进行训练之后得到的文本文件里面,有一个数组,叫做support vector,还有一个数组,叫做alpha,有一个浮点数,叫做rho;将alpha矩阵同support vector相乘,注意,alpha*supportVector,将得到一个列向量。之后,再该列向量的最后添加一个元素rho。如此,变得到了一个分类器,利用该分类器,直接替换opencv中行人检测默认的那个分类器(cv::HOGDescriptor::setSVMDetector()),就可以利用你的训练样本训练出来的分类器进行检测了。
效果显示:
1.问题
你希望在 table view 的顶部显示一个漂亮的刷新控件,让用户可以直观的进行下拉 table
view 以进行内容的更新。刷新控件的两个不同状态如下图 所示:
刷新控件的两个不同状态
2。方案
创建一个 table view 控制器(4.13小节介绍过),然后将其 refreshControl属性设置为一
个新的 UIRefreshControl实例,如下代码:
- (id)initWithStyle:(UITableViewStyle)style{ self = [super initWithStyle:style]; if (self) { /* Create the refresh control */ self.refreshControl = [[UIRefreshControl alloc] init]; self.refreshControl = self.refreshControl; [self.refreshControl addTarget:self action:@selector (handleRefresh:) forControlEvents:UIControlEventValueChanged]; } return self; }
3. 讨论
刷新控件是 iOS 6 SDK 中新增的一个 UI 组建。它在 table view 的顶部简单的显示一个
指示器,告诉用户,有些内容正在更新中。例如,在 iOS 6 之前的版本中,为了刷新 Mail
程序中的收件箱,你必须按一下刷新按钮。而在 iOS 6 中,现在你可以简单的向下拖动 mail
列表以进行刷新了查看新内容了。当 iOS 检测到这样的手势,会触发一个刷新事件。很酷
吧?iPhone 中的 Twitter 程序早已经添加这样的一个刷新控件了,并获得了用户的高度赞
扬!苹果也认识到以这样的方式来更新 table view 非常的棒,因此也决定在新的 SDK 中添
加这样的一个 UI组建。组建的类名叫做 UIRefreshControl。
通过调用这个类的 init 方法即可创建一个实例了。创建好之后,将这个实例添加到 table
viewl 控制器(如本节方案中所述)。
现在,你希望知道用户什么时候在 table view 中触发了一个刷新事件。为了达到这个目
的,可以调用刷新控件的 addTarget:action:forControlEvents:实例方法,并传入一个 target 对
象和一个 selector,然后将 UIControlEventValueChanged 当做 forControlEvents 参数传入。
这里——我想要向你演示这个功能。在这里的示例中,我有一个 table view 控制器,用
来显示日期和时间的格式化字符串。当用户下拉以刷新列表时,我将会把当前的日期和时间
添加到 table view 中。这样,每次用户进行下拉时,都会触发刷新事件,我们也就可以将当
前的日期和时间添加到列表中,以让 table view 显示新的日期和时间。下面我们就在我们的
table view 控制器实现文件中开始,并定义刷新控件和数据源:
#import "ViewController.h" @interface ViewController () @property (nonatomic, strong) NSMutableArray *times; @property (nonatomic, strong) UIRefreshControl *refreshControl; @end @implementation ViewController ...
times 属性是一个简单的可变数组,它将包含所有的 NSDate 实例,以显示在 table view
中。在本节方案中的代码里面,我们可以看到table view controller 的初始化。所以这里我就
不再重复写了。不方案中的代码,我们已经设置了一个 handleRefresh:方法来响应刷新控件
的 UIControlEventValueChanged 事件。在这个方法中,我们所做的事情就是将当前日期而后
事件添加到我们的数组中,然后刷新一下table view:
- (void) handleRefresh:(id)paramSender{ /* Put a bit of delay between when the refresh control is released and when we actually do the refreshing to make the UI look a bit smoother than just doing the update without the animation */ int64_t delayInSeconds = 1.0f; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ /* Add the current date to the list of dates that we have so that when the table view is refreshed, a new item will appear on the screen so that the user will see the difference between the before and the after of the refresh */ [self.times addObject:[NSDate date]]; [self.refreshControl endRefreshing]; [self.tableView reloadData]; }); }
最后,最后,我将通过 table view 的 delegate 和数据源为其ᨀ供数据:
- (id)initWithStyle:(UITableViewStyle)style{ self = [super initWithStyle:style]; if (self) { self.times = [NSMutableArray arrayWithObject:[NSDate date]]; /* Create the refresh control */ self.refreshControl = [[UIRefreshControl alloc] init]; self.refreshControl = self.refreshControl; [self.refreshControl addTarget:self action:@selector (handleRefresh:) forControlEvents:UIControlEventValueChanged]; } return self; } - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView{ return 1; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{ return self.times.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{ static NSString *CellIdentifier = @"Cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (cell == nil) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; } cell.textLabel.text = [NSString stringWithFormat:@"%@", self.times[indexPath.row]]; return cell; }
在模拟器或者设备中运行这个程序。当程序运行时,首先你将看到在列表中只有一个跳
日期/时间数据。通过下拉 table view 来获得更多的数据(如下图)。
-------摘自《iOS 6 Programming Cookbook》