这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【chronos】树莓派DIY进程 - 更新【实验番外篇】任务四:基于树莓派的网

共41条 3/5 1 2 3 4 5 跳转至
菜鸟
2014-09-17 23:51:39     打赏
21楼

更新视频演示~~室内搜台数量有限,依然无语中。。。。。。还是河北音乐广播给力啊!超稳定的输出!!

视频传送门 -->


助工
2014-09-18 19:30:20     打赏
22楼
加精前留名

菜鸟
2014-09-21 00:53:45     打赏
23楼
【实验番外篇】任务三:人脸识别~~序 - 摄像头测试 + 图像获取

首先,这次的主角,摄像头(路边摊¥25) ,不知道什么牌子的。。。

Plug~~用lsusb指令查看一下是否能识别,嘿,看来有门儿~~


估计应该属于标准的UVC协议,目前的Raspbian是集成了这部分驱动的,输入ls /dev,查看dev中是否存在一个video0这个设备,如果有了说明系统识别了,如果不能识别,可以试试更新一下系统


安装一个camorama的小工具,可以查看,修改和保存摄像头捕获的图片

sudo apt-get install camorama

然后在远程桌面的开始菜单中找到这个工具

320*240的尺寸,平均每秒11帧,没有期望太高。。。

还试了一下mplayer和camorama,640*480的尺寸,卡得不行,平均每秒3帧,丢帧很严重!

不过摄像头是可以用了,下面安装opencv

sudo apt-get update
sudo apt-get install libopencv-dev
sudo apt-get install python-opencv

利用opencv写一段简单的代码,获取当前摄像头的图像,实时显示出来

# coding=utf-8
import cv2

cv2.namedWindow("video")     # 命名一个窗口
capture = cv2.VideoCapture(0)       # 打开默认摄像头
success, frame = capture.read()     # 读取一帧图像,第一个返回值是成功标志,第二个返回值是图像本身

while True:
    success, frame = capture.read()
    cv2.imshow("video", frame)      # 显示图像到窗口
    key = cv2.waitKey(10)           # 利用按键退出
    c = chr(key & 255)
    if c in ['q', 'Q', chr(27)]:
        break
cv2.destroyWindow("video")          # 停止窗口

160*120的尺寸,效果还是可以接受的,至少没什么卡顿现象了,CPU也只占了80%左右

上传视频,大家看看效果~~

视频传送门 -->


菜鸟
2014-09-21 14:44:45     打赏
24楼

【实验番外篇】任务三:人脸识别~~续 - PYTHON OPENCV2

人脸识别,咋一听上去很高深的样子,其实通过opencv中的一些模块和强大的Haarcascades Repositories实现起来还是很简单的!

这里用的是CV2.X版本,因为CV1.X版本中摄像头获取的帧图像默认是iplimage格式的,在进行人脸识别的时候需要转成numpy array格式,同时识别完成后还需要转换回去才能将结果显示出来,而CV2.X版本则默认就是numpy array格式的图像,应用起来更加快速、方便一些。

1、调用OpenCV2.X及numpy

import cv2
import numpy as np

2、这里我们进行一些初始化的工作,建立一个窗口用于显示图像,打开默认的摄像头,读取一帧图像并取得读取成功标志位,设置人脸框颜色,以及设置分类器的haar特征数据库

tips:cv2.VideoCapture() - 当该函数的第一个参数为数字时表示摄像头摄像头id,当只有一个摄像头时,传入0,OpenCV会打开该摄像头。该功能仅限笔记本、USB摄像头,对于工业的网络摄像头(如海康摄像头、AXIS摄像头等)需要针对SDK进行相应开发

cv2.namedWindow("video")     # 命名一个窗口
capture = cv2.VideoCapture(0)       # 打开默认摄像头
success, frame = capture.read()     # 读取一帧图像,第一个返回值是成功标志,第二个返回值是图像本身
color = (255, 255, 255)             # 设置人脸框的颜色
classfier = cv2.CascadeClassifier("/usr/share/opencv/haarcascades/"
                                  "haarcascade_frontalface_alt_tree.xml")    # 定义分类器:正脸

官方同时还提供了许多特征库供我们使用

而且我们还可以到 http://opencvlibrary.svn.sourceforge.net/viewvc/opencvlibrary/trunk/opencv/data/haarcascades/ 去下载其他的特征库

3、如果图像获取成功,则循环执行人脸识别,并对每一个成功识别的人脸画出矩形框,将最终结果显示到之前建立好的窗口中

# 图像获取成功,循环执行
while success:
    success, frame = capture.read()
    size = frame.shape[:2]    # 获取当前帧彩色图像的大小
    image = np.zeros(size, dtype=np.float16)   # 定义一个与当前帧图像大小相同的的灰度图像矩阵
    image = cv2.cvtColor(frame, cv2.cv.CV_BGR2GRAY)     # 将当前帧图像转换成灰度图像
    cv2.equalizeHist(image, image)      # 灰度图像进行直方图等距化
    # 设定最小人脸图像的大小,1/8
    divisor = 8
    h, w = size
    minSize = (w/divisor, h/divisor)
    faceRects = classfier.detectMultiScale(image, 1.2, 2, cv2.CASCADE_SCALE_IMAGE, minSize)    # 人脸检测
    if len(faceRects) > 0:              # 如果人脸数组长度大于0
        for faceRect in faceRects:      # 对每一个人脸画矩形框
            x, y, w, h = faceRect
            cv2.rectangle(frame, (x, y), (x+w, y+h), color)
    cv2.imshow("video", frame)      # 显示图像到窗口

并不是每次都能成功定位到人脸的,这个跟特征数据以及训练的参数有关,也跟测试时候参数的设置、环境光线强度等有关。例如,采用了正脸的特征库,一般侧脸就很难识别出来;同时,如果面部特征不全或有遮挡的话,也很难进行识别

4、通过指定的按键退出循环,并停止窗口响应,退出程序

while success:

    ......

    key = cv2.waitKey(10)           # 等待按键退出
    c = chr(key & 255)
    if c in ['q', 'Q', chr(27)]:
        break
cv2.destroyWindow("video")          # 停止窗口

tips:cv.waitKey() - 该函数等待用户键盘事件,参数为等待时长,单位是ms。注意,窗体显示必须调用一下该函数,否则窗口中无画面,当参数为0时,窗口将无限等待,该函数有返回值,没接受到键盘事件时的返回值为-1

上传完整代码:

# coding=utf-8
import cv2
import numpy as np

cv2.namedWindow("video")     # 命名一个窗口
capture = cv2.VideoCapture(0)       # 打开默认摄像头
success, frame = capture.read()     # 读取一帧图像,第一个返回值是成功标志,第二个返回值是图像本身
color = (255, 255, 255)             # 设置人脸框的颜色
classfier = cv2.CascadeClassifier("/usr/share/opencv/haarcascades/"
                                  "haarcascade_frontalface_alt_tree.xml")    # 定义分类器:正脸
# 图像获取成功,循环执行
while success:
    success, frame = capture.read()
    size = frame.shape[:2]    # 获取当前帧彩色图像的大小
    image = np.zeros(size, dtype=np.float16)   # 定义一个与当前帧图像大小相同的的灰度图像矩阵
    image = cv2.cvtColor(frame, cv2.cv.CV_BGR2GRAY)     # 将当前帧图像转换成灰度图像
    cv2.equalizeHist(image, image)      # 灰度图像进行直方图等距化
    # 设定最小图像的大小,1/8
    divisor = 8
    h, w = size
    minSize = (w/divisor, h/divisor)
    faceRects = classfier.detectMultiScale(image, 1.2, 2, cv2.CASCADE_SCALE_IMAGE, minSize)    # 人脸检测
    if len(faceRects) > 0:              # 如果人脸数组长度大于0
        for faceRect in faceRects:      # 对每一个人脸画矩形框
            x, y, w, h = faceRect
            cv2.rectangle(frame, (x, y), (x+w, y+h), color)
    cv2.imshow("video", frame)      # 显示图像到窗口
    key = cv2.waitKey(10)           # 等待按键退出
    c = chr(key & 255)
    if c in ['q', 'Q', chr(27)]:
        break
cv2.destroyWindow("video")          # 停止窗口

上传视频,大家看看效果~~晚上录的,光线强度差点儿,找了一张高中时候的毕业照,人还算比较多的,识别效果还可以接受。。。¥25的摄像头CMOS实在不敢恭维

视频传送门 -->

tips:

如果程序执行过程中出现:“Xlib:  extension "RANDR" missing on display xxx”的提示信息。一般来说,这个信息是可以忽略的,但是,由于RANDR扩展的不存在,会导致在使用的过程中无法动态改变屏幕的大小。
RandR的wikipedia中说道:“The X Resize, Rotate and Reflect Extension (RandR)[2] allows clients to dynamically change X screens, so as to resize, rotate and reflect the root window of a screen.”


菜鸟
2014-09-21 14:48:55     打赏
25楼
谢谢捧场~~借您吉言~~

助工
2014-09-21 20:22:53     打赏
26楼

这也太高大上了。。


菜鸟
2014-09-25 01:15:50     打赏
27楼

【实验番外篇】任务四:基于树莓派的网络考勤系统及刷卡门禁 - 第一阶段

背景:公司的打卡机目前还是比较低端的,虽然是指纹识别的,但是每月统计打卡或临时查看记录的时候都要摘下来连接到电脑上,非常的不方便,而且刷卡门禁与考勤系统是分开的两套系统,又不方便统一管理。

虽然手中没有指纹识别模组,但是我们有物美价廉的RFID模块啊~~抱着把手中的RFID模块和树莓派充分利用起来以及Impossible is nothing的信念,决定自己做一套基于RFID模块和树莓派的网络版考勤系统,同时将刷卡门禁功能集成进来,方便统一管理。


任务阶段划分

第一阶段:驱动RFID模块(RC522

第二阶段:搭建RPi网络服务器

第三阶段:完善刷卡门禁功能及终端显示功能

第四阶段:搭建

第五阶段:完善后台管理系统口的衔接

软、能系统


~~本次任务不同于之前几次(任务做完,过来发帖),由于内容比较多,所以需要分阶段完成~~

第一阶段:驱动RFID模块(RC522

工具:RFID模块 -- RC522(通讯接口:SPI)

            非接触式IC卡 -- Mifare One卡


1、启用SPI接口

从黑名单中将SPI接口释放出来,之前在I2C实验中做过类似的操作

sudo nano /etc/modprobe.d/raspi-blacklist.conf

# blacklist spi and i2c by default (many users don't need them)

#blacklist spi-bcm2708
#blacklist i2c-bcm2708

确认内核支持

pi@raspberrypi ~ $ ls -la /dev/spi*
crw-rw---T 1 root spi 153, 0 Jan  1  1970 /dev/spidev0.0
crw-rw---T 1 root spi 153, 1 Jan  1  1970 /dev/spidev0.1

2、这次使用的编程语言是比较熟悉的C,安装wiringPi,方便调用SPI接口

使用GIT工具下载wiringPi,如果你的RPi还没有GIT,可以使用下面的指令获取

sudo apt-get install git-core
通过GIT获得wiringPi的源代码

git clone git://git.drogon.net/wiringPi

进入wiringPi目录并安装wiringPi

cd wiringPi
./build
测试wiringPi是否安装成功
gpio -v
gpio readall


用下面的指令SPI

gpio load spi
3、接线方式

RC522 Pin Name
RPi Header Pin Num
wiringPi Pin Num
VCC
#17(3.3v)

RST
#12(GPIO.1)
1
GND
#25(0v)

MISO
#21(MISO)
13
MOSI
#19(MOSI)
12
SCK(CLK)
#23(SCLK)
14
SDA(NSS)
#24(CE0)
10
4、驱动编写

(1)首先,需要初始化SPI接口,并了解其函数的使用方法,下面是一些比较重要的设置与代码举例: 

    #include <wiringPi.h>
    #include <wiringPiSPI.h>

#define RST           1
#define channel       0
#define speed         1000000

unsigned char ucAddr;
unsigned char data[2] = {0, 0};

wiringPiSetup();			//wiringPi初始化
wiringPiSPISetup(channel, speed);	//设置SPI通道 0,速率 1000000 - 1Mbit/s
wiringPiSPIDataRW(channel, &ucAddr, 1);
wiringPiSPIDataRW(channel, data, 2);

我将RC522的片选(NSS)接到了CE0上,所以这里要设置为通道0,从RC522的DATASHEET中我们可以了解到,模块的SPI 接口可处理高达10Mbit/s的数据速率,这里我仅使用了1Mbit/s的数据速率,已经足够我的需求了。

根据官网的介绍RPi的SPI接口是全双工的工作方式,通过wiringPiSPI接口进行数据读写的时候,SPI总线会自动将返回的数据覆盖到之前发送的变量上。

"This performs a simultaneous write/read transaction over the selected SPI bus. Data that was in your buffer is overwritten by data returned from the SPI bus."

(2)解决通讯基础函数

这里其实遇到了一些问题,导致很多次实验没有成功。首先,我们来看一下DATASHEET中的介绍。

地址字节格式:

读数据字节顺序:

写数据字节顺序:

从中我们可以发现读写的命令是包含在地址字节格式中的,而地址是通过主机发送出去的字节0。

读数据过程中字节0是没有真正返回寄存器的实际值的,而真正返回是从下一个字节开始的,这里我们直接发送0x00地址,获取真实数据。

另外,我在调试的过程中,在运行到if(!(ReadRawRC(ErrorReg)&0x1B))这句时,读ErrorReg寄存器返回值是0x45,然后直接跳转到MI_ERR,正是由于读取寄存器方式错误引起的。

/////////////////////////////////////////////////////////////////////
//功    能:读RC522寄存器
//参数说明:Address[IN]:寄存器地址
//返    回:读出的值
/////////////////////////////////////////////////////////////////////
unsigned char ReadRawRC(unsigned char Address)
{
	unsigned char ucAddr;
	unsigned char data = 0x00;
	ucAddr = ((Address<<1)&0x7E)|0x80;	//address format:1XXXXXX0
	wiringPiSPIDataRW(channel, &ucAddr, 1);
	wiringPiSPIDataRW(channel, &data, 1);	//读两次才能拿到真正的RC522的返回值,非常重要
	return data;
}
/////////////////////////////////////////////////////////////////////
//功    能:写RC522寄存器
//参数说明:Address[IN]:寄存器地址
//          value[IN]:写入的值
/////////////////////////////////////////////////////////////////////
void WriteRawRC(unsigned char Address, unsigned char value)
{  
	unsigned char ucAddr;
	unsigned char data[2] = {0, 0};
	ucAddr = ((Address<<1)&0x7E);	//address format:0XXXXXX0
	data[0] = ucAddr;
	data[1] = value;
	wiringPiSPIDataRW(channel, data, 2);
}

当我们建立起通讯基础函数后,就可以放心的根据程序流程开始编写功能函数了。

上传完整代码:

https://github.com/chronosMaker/RPiRFID.git

看一下实际运行效果,试了一张MF1 S50的空白卡,读写都正常

下面是刚才读的扇区0的具体信息

Mifare 1 卡内部有 1 个 EEPROM,分成 0~15 共 16 个扇区,每个扇区分成 0~3 共 4 块,每块 16 字节。

1、扇区 0 的块 0 是厂商标志字节,保存着只读的卡信息及厂商信息。

22  8B  BE  B5  A2  08  04  00  69  73  73  69  35  36  34  30

前面四个字节 22  8B  BE  B5 是卡序列号,08 是卡容量,04  00 是卡类型,后面是厂商自定义的一些信息。

2、每个扇区的块 0 保存着该块的密钥 A 与密钥 B 及该块的访问条件,每个扇区都有自己的一套密钥及访问条件,其中,4 个字节的访问条件是对每个扇区 4 个块的读写定义。比如块 3 的 16 字节如下:

00  00  00  00  00  00  FF  07  80  69  FF  FF  FF  FF  FF  FF

前面 6 个字节是密钥 A,因为 Read 永远为 Never,所以读到的都是 0x00,最后的 6 字节是密钥 B,其值为 0xFF  0xFF  0xFF  0xFF  0xFF  0xFF,中间的 4 个字节是访问条对应DATASHEET相关表格件。

北京地铁车票

又试一下公司的门禁卡

以上两张卡都由于密钥错误导致无法读取内部数据,如果能够获取对应密钥,那么。。。。。。^_^

tips原装的 Philps S50 芯片在出厂时设置每个分区的的第四块 A 密钥是“FFFFFFFFFFFF”,控制字是:“FF078069”,B 密钥是:“FFFFFFFFFFFF”,A 密钥是供用户读写操作的,利用 A 密钥可对对除 0 区外其它所有扇区块进行读写操作。 B 密钥不可操作,这些用的都是逻辑加密算法加密,而且密钥都是不可见的,我们在读的时候能看到的 A 密钥都是显示为“000000000000”,B 密钥显示:“FFFFFFFFFFFF”,这些都是出厂时厂家设定的默认值。


院士
2014-09-25 07:44:04     打赏
28楼

楼主的进程好丰富啊~~

能学到不少的知识


菜鸟
2014-09-25 08:41:32     打赏
29楼
嘿嘿,不敢当啊~~共同学习的过程嘛~~

助工
2014-09-25 12:16:13     打赏
30楼
前几天也想用用RC522,水平太低,没搞成,前来拜读

共41条 3/5 1 2 3 4 5 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]