UBNT AirOS 4种无线模式 的区别。

很早之前专门查过,当时路由器有ARP绑定,无线模式设置的是站模式,结果全部上不去网,路由器显示无线后面的设备的MAC地址全是无线设备的MAC地址。解决办法是设置为 站 WDS 模式或接入点WDS模式。这两天又用到了,怕忘了,记一下。

站模式:会通过无线连接到接入点(即无线AP),但是和网线连接有一个区别是会把 AirOS 后面设备的MAC全部改成桥本身的MAC地址,如果网络模式是网桥模式并且路由器等设备做了ARP绑定就会出现上不了网的现象,需要修改为带WDS的无线模式。

站 WDS 模式:会通过无线连接到接入点(即无线AP),相当于一根网线直接直接连接,不会修改 AirOS 后面设备的MAC地址,如果主路由有ARP绑定推荐本模式或无线接入点WDS模式。

接入点模式:即无线AP模式,允许其他设备炼乳本网络。

接入点 WDS 模式:会自动的和同样 ESSID、密码、同样接入点 WDS 模式的设备自动组网,AirOS 之间自动通过通过无线通信,不需要网线连接,而且每个 AirOS 设备都同时提供无线AP功能,手机设备都可以直接连接进来,应该还提供无缝漫游功能(未实测)。 实测过两个 AirOS 都开接入点 WDS模式,双方之间没有网线连接,网络模式都是网桥模式,其中一个 AirOS 设备通过网线和路由器连接,手机可以连入 AirOS

XML-RPC 笔记

XML-RPC 编码是个坑,只支持 ASCII 字符串(或二进制缓冲块)。为了安全,只能用 xmlrpclib.Binary 把数据包装一下了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> from SimpleXMLRPCServer import SimpleXMLRPCServer
>>> import xmlrpclib
>>> def a():
	return xmlrpclib.Binary({'a':1})
 
>>> server = SimpleXMLRPCServer(("localhost", 8000))
>>> print "Listening on port 8000..."
Listening on port 8000...
>>> server.register_function(python_logo, 'python_logo')
 
Traceback (most recent call last):
  File "<pyshell#7>", line 1, in <module>
    server.register_function(python_logo, 'python_logo')
NameError: name 'python_logo' is not defined
>>> server.register_function(a, 'a')
>>> server.serve_forever()
127.0.0.1 - - [22/Apr/2014 22:36:32] "POST / HTTP/1.1" 200 -
 
Traceback (most recent call last):
  File "<pyshell#9>", line 1, in <module>
    server.serve_forever()
  File "C:\Python27\lib\SocketServer.py", line 236, in serve_forever
    poll_interval)
  File "C:\Python27\lib\SocketServer.py", line 155, in _eintr_retry
    return func(*args)
KeyboardInterrupt
 
>>> import json
>>> def a():
	return xmlrpclib.Binary(json.dumps({'a':1}))
 
>>> server.register_function(a, 'a')
>>> server.serve_forever()
127.0.0.1 - - [22/Apr/2014 22:37:27] "POST / HTTP/1.1" 200 -
127.0.0.1 - - [22/Apr/2014 22:37:39] "POST / HTTP/1.1" 200 -
127.0.0.1 - - [22/Apr/2014 22:38:04] "POST / HTTP/1.1" 200 -
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Python 2.7.5 (default, May 15 2013, 22:43:36) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import xmlrpclib
>>> proxy = xmlrpclib.ServerProxy("http://localhost:8000/")
>>> proxy.a()
 
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    proxy.a()
  File "C:\Python27\lib\xmlrpclib.py", line 1224, in __call__
    return self.__send(self.__name, args)
  File "C:\Python27\lib\xmlrpclib.py", line 1578, in __request
    verbose=self.__verbose
  File "C:\Python27\lib\xmlrpclib.py", line 1264, in request
    return self.single_request(host, handler, request_body, verbose)
  File "C:\Python27\lib\xmlrpclib.py", line 1297, in single_request
    return self.parse_response(response)
  File "C:\Python27\lib\xmlrpclib.py", line 1473, in parse_response
    return u.close()
  File "C:\Python27\lib\xmlrpclib.py", line 793, in close
    raise Fault(**self._stack[0])
Fault: <Fault 1: "<type 'exceptions.TypeError'>:must be string or buffer, not dict">
>>> proxy.a()
<xmlrpclib.Binary instance at 0x02AEC508>
>>> proxy.a().data
'{"a": 1}'
>>> import json
>>> json.loads(proxy.a().data)
{u'a': 1}
>>>

另一种注册方法:

http://soft.zdnet.com.cn/software_zone/2008/0527/887034.shtml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 
import SimpleXMLRPCServer
 
#定义自己的CMS类
class MyCMS:
    def getVersion(self):#向外公开版本的方法
        return "Powerd By python 0.1a"
 
cms = MyCMS()
server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 8888))
server.register_instance(cms)
 
print "Listening on port 8888"
server.serve_forever()#服务器执行,并监听8888端口
 
 
import xmlrpclib
 
server = xmlrpclib.ServerProxy("http://localhost:8888";)
 
version = server.getVersion()
 
print "version:"+version

参考:

https://docs.python.org/2/library/xmlrpclib.html#fault-objects

lxml 笔记

蜘蛛之前用的pyquery做采集,发现对非正常的网页并不适用,正则表达式更是不能用,目标网站网页乱的一团乱麻,打算重新用lxml的迭代解析功能来做吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
Python 2.7.3 (default, Apr 10 2012, 23:31:26) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import lxml
 
Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
    import lxml
ImportError: No module named lxml
>>> import lxml
>>> class EchoTarget(object):
	def start(self, tag, attrib):
		print("start %s %r" % (tag, dict(attrib)))
		print attrib
	def end(self, tag):
		print("end %s" % tag)
	def data(self, data):
		print("data %r" % data)
	def comment(self, text):
		print("comment %s" % text)
	def close(self):
		print("close")
		return "closed!"
 
 
>>> from lxml import etree
>>> from io import StringIO, BytesIO
 
>>> parser = etree.XMLParser(target = EchoTarget())
>>> result = etree.XML("<element a='aaa' b='bbb' c=333 >some<!--comment-->text</element>",parser)
close
 
Traceback (most recent call last):
  File "<pyshell#18>", line 1, in <module>
    result = etree.XML("<element a='aaa' b='bbb' c=333 >some<!--comment-->text</element>",parser)
  File "lxml.etree.pyx", line 2723, in lxml.etree.XML (src/lxml/lxml.etree.c:52448)
  File "parser.pxi", line 1573, in lxml.etree._parseMemoryDocument (src/lxml/lxml.etree.c:79932)
  File "parser.pxi", line 1452, in lxml.etree._parseDoc (src/lxml/lxml.etree.c:78774)
  File "parser.pxi", line 960, in lxml.etree._BaseParser._parseDoc (src/lxml/lxml.etree.c:75389)
  File "parsertarget.pxi", line 149, in lxml.etree._TargetParserContext._handleParseResultDoc (src/lxml/lxml.etree.c:86190)
  File "parser.pxi", line 585, in lxml.etree._raiseParseError (src/lxml/lxml.etree.c:71955)
XMLSyntaxError: AttValue: " or ' expected, line 1, column 15
>>> result = etree.XML("<element a='aaa' b='bbb' >some<!--comment-->text</element>",parser)
start element {'a': u'aaa', 'b': u'bbb'}
{'a': u'aaa', 'b': u'bbb'}
data u'some'
comment comment
data u'text'
end element
close
>>> result
'closed!'
>>> class EchoTarget(object):
	def start(self, tag, attrib):
		print attrib
		print("start %s %r" % (tag, dict(attrib)))
	def end(self, tag):
		print("end %s" % tag)
	def data(self, data):
		print("data %r" % data)
	def comment(self, text):
		print("comment %s" % text)
	def close(self):
		print("close")
		return "closed!"
 
 
>>> result = etree.XML("<element a='aaa' b='bbb' >some<!--comment-->text</element>",parser)
start element {'a': u'aaa', 'b': u'bbb'}
{'a': u'aaa', 'b': u'bbb'}
data u'some'
comment comment
data u'text'
end element
close
>>> class EchoTarget(object):
	def start(self, tag, attrib):
		print attrib
		print("start %s %r" % (tag, dict(attrib)))
	def end(self, tag):
		print("end %s" % tag)
	def data(self, data):
		print("data %r" % data)
	def comment(self, text):
		print("comment %s" % text)
	def close(self):
		print("close")
		return "closed!"
 
 
>>> parser = etree.XMLParser(target = EchoTarget())
>>> 
>>> result = etree.XML("<element a='aaa' b='bbb' >some<!--comment-->text</element>",parser)
{'a': u'aaa', 'b': u'bbb'}
start element {'a': u'aaa', 'b': u'bbb'}
data u'some'
comment comment
data u'text'
end element
close
>>>

使用目标解析器方法
目标解析器方法对于熟悉 SAX 事件驱动代码的开发人员来说应该不陌生。目标解析器是可以实现以下方法的类:
start 在元素打开时触发。数据和元素的子元素仍不可用。
end 在元素关闭时触发。所有元素的子节点,包括文本节点,现在都是可用的。
data 触发文本子节点并访问该文本。
close 在解析完成后触发。
清单 2 演示了如何创建实现所需方法的目标解析器类(这里称为 TitleTarget)。这个解析器在一个内部列表(self.text)中收集 Title 元素的文本节点,并在到达 close() 方法后返回列表。
清单 2. 一个目标解析器,它返回 Title 标记的所有文本子节点的列表
class TitleTarget(object):
def __init__(self):
self.text = []
def start(self, tag, attrib):
self.is_title = True if tag == ‘Title’ else False
def end(self, tag):
pass
def data(self, data):
if self.is_title:
self.text.append(data.encode(‘utf-8′))
def close(self):
return self.text

parser = etree.XMLParser(target = TitleTarget(),recover=True)

# This and most other samples read in the Google copyright data
infile = ‘copyright.xml’

results = etree.parse(infile, parser)

# When iterated over, ‘results’ will contain the output from
# target parser’s close() method

out = open(‘titles.txt’, ‘w’)
out.write(‘\n’.join(results))
out.close()
在运行版权数据时,代码运行时间为 54 秒。目标解析可以实现合理的速度并且不会生成消耗内存的解析树,但是在数据中为所有元素触发事件。对于特别大型的文档,如果只对其中一些元素感兴趣,那么这种方法并不理想,就像在这个例子中一样。能否将处理限制到选择的标记并获得较好的性能呢?

In [7]: f = StringIO.StringIO(r”””
…:
…: Mark
…: http://diveintomark.org/
…:

…: ②
…: ...: href='http://diveintomark.org/archives/2009/03/27/dive-into-history-2009-edition'/>
…: tag:diveintomark.org,2009-03-27:/archives/20090327172042
…: 2009-03-27T21:56:07Z
…: 2009-03-27T17:20:42Z …:
…:
…:
…:

Putting an entire chapter on one page sounds ⑦
…: bloated, but consider this &mdash; my longest chapter so far
…: would be 75 printed pages, and it loads in under 5 seconds&hellip;
…: On dialup.

…: ⑧
…: “””)

http://lxml.de/parsing.html#parsers

The target parser interface

As in ElementTree, and similar to a SAX event handler, you can pass a target object to the parser:
>>> class EchoTarget(object):
… def start(self, tag, attrib):
… print(“start %s %r” % (tag, dict(attrib)))
… def end(self, tag):
… print(“end %s” % tag)
… def data(self, data):
… print(“data %r” % data)
… def comment(self, text):
… print(“comment %s” % text)
… def close(self):
… print(“close”)
… return “closed!”

>>> parser = etree.XMLParser(target = EchoTarget())

>>> result = etree.XML(“sometext“,
… parser)
start element {}
data u’some’
comment comment
data u’text’
end element
close

>>> print(result)
closed!
It is important for the .close() method to reset the parser target to a usable state, so that you can reuse the parser as often as you like:
>>> result = etree.XML(“sometext“,
… parser)
start element {}
data u’some’
comment comment
data u’text’
end element
close

>>> print(result)
closed!
Starting with lxml 2.3, the .close() method will also be called in the error case. This diverges(分歧) from the behaviour of ElementTree, but allows target objects to clean up their state in all situations, so that the parser can reuse them afterwards.
>>> class CollectorTarget(object):
… def __init__(self):
… self.events = []
… def start(self, tag, attrib):
… self.events.append(“start %s %r” % (tag, dict(attrib)))
… def end(self, tag):
… self.events.append(“end %s” % tag)
… def data(self, data):
… self.events.append(“data %r” % data)
… def comment(self, text):
… self.events.append(“comment %s” % text)
… def close(self):
… self.events.append(“close”)
… return “closed!”

>>> parser = etree.XMLParser(target = CollectorTarget())

>>> result = etree.XML(“some“,
… parser) # doctest: +ELLIPSIS
Traceback (most recent call last):

lxml.etree.XMLSyntaxError: Opening and ending tag mismatch…

>>> for event in parser.target.events:
… print(event)
start element {}
data u’some’
close
Note that the parser does not build a tree when using a parser target. The result of the parser run is whatever the target object returns from its .close() method. If you want to return an XML tree here, you have to create it programmatically in the target object. An example for a parser target that builds a tree is the TreeBuilder:
>>> parser = etree.XMLParser(target = etree.TreeBuilder())

>>> result = etree.XML(“sometext“,
… parser)

>>> print(result.tag)
element
>>> print(result[0].text)
comment

http://lxml.de/tutorial.html

http://www.ibm.com/developerworks/cn/xml/x-hiperfparse/index.html

http://sebug.net/paper/books/dive-into-python3/xml.html

http://alvinli1991.github.io/python/2013/11/12/python-xml-parser—elementtree/

http://pycoders-weekly-chinese.readthedocs.org/en/latest/issue6/processing-xml-in-python-with-element-tree.html

NRF24L01 无线模块笔记

NRF24L01 收发地址

NRF24L01 模式

引脚      名称      引脚功能     描述
1         CE        数字输入     RX 或 TX 模式选择
2         CSN       数字输入     SPI 片选信号
3         SCK       数字输入     SPI 时钟
4         MOSI      数字输入     从 SPI 数据输入脚
5         MISO      数字输出     从 SPI 数据输出脚
6         IRQ       数字输出     可屏蔽中断脚
7         VDD       电源         电源(+3V)
8         VSS       电源         接地(0V)
 
nRF24L01 可以设置为以下几种主要的模式,
模式            PWR_UP         PRIM_RX              CE            FIFO 寄存器状态 
接收模式         1                1                  1             -
发送模式         1                0                  1             数据在 TX FIFO 寄存器中
发送模式         1                0                 10            停留在发送模式,直至数据发送完
待机模式 II      1                0                  1             TX FIFO 为空
待机模式 I       1                -                  0             无数据传输
掉电模式         0                -                  -             -
 
关于 nRF24L01 I/O 脚更详细的描述请参见下面的表。
nRF24L01 在不同模式下的引脚功能
引脚名称         方向        发送模式       接收模式     待机模式    掉电模式 
CE              输入         高电平>10us     高电平       低电平     -  
CSN             输入         SPI 片选使能,低电平使能
SCK             输入         SPI 时钟
MOSI            输入         SPI 串行输入
MISO            三态输出     SPI 串行输出
IRQ             输出        中断,低电平使能

增强型的ShockBurstTM 模式 比 ShockBurstTM 模式多了个自动应答、自动重发功能。

nRF24L01 在接收模式下可以接收6 路不同通道的数据,每一个数据通道使用不同的地址,但
是共用相同的频道。也就是说6 个不同的nRF24L01 设置为发送模式后可以与同一个设置为接收模式的
nRF24L01 进行通讯,而设置为接收模式的nRF24L01 可以对这6 个发射端进行识别。数据通道0 是唯一
的一个可以配置为40 位自身地址的数据通道。1~5 数据通道都为8 位自身地址和32 位公用地址。所有的
数据通道都可以设置为增强型ShockBurst 模式。
nRF24L01 在确认收到数据后记录地址,并以此地址为目标地址发送应答信号。在发送端,数据通道0
被用做接收应答信号,因此,数据通道0 的接收地址要与发送端地址相等以确保接收到正确的应答信号。
见图5 选择地址举例。

增强型ShockBurstTM 发送模式:
1、 配置寄存器位PRIM_RX 为低
2、 当MCU 有数据要发送时,接收节点地址(TX_ADDR)和有效数据(TX_PLD)通过SPI 接口写入
nRF24L01。发送数据的长度以字节计数从MCU 写入TX FIFO。当CSN 为低时数据被不断的写入。
发送端发送完数据后,将通道0 设置为接收模式来接收应答信号,其接收地址(RX_ADDR_P0)与接
收端地址(TX_ADDR)相同。例:在图5 中数据通道5 的发送端(TX5)及接收端(RX)地址设置如下:
TX5:TX_ADDR=0xB3B4B5B605
TX5:RX_ADDR_P0=0xB3B4B5B605
RX:RX_ADDR_P5=0xB3B4B5B605
3、 设置CE 为高,启动发射。CE 高电平持续时间最小为10 us。
4、 nRF24L01 ShockBurstTM 模式:

限制:
单无线模块只能接受4个设备发送的信息(实际是6个,但是1个为自动应答使用,一个我打算作为默认广播地址使用,就只剩下4个了。其中除了自动应答使用的1个设备地址之外的5个设备的地址非8位部分必须相同。8位地址可以容纳255个设备。),单个设备只能有一个设备地址来接收其他设备发送的信息。
一般200个地址就足够用了,也就是把所有设备除了后8位全部设置成为一样的。

路由设计:

之前被nrf24文档里面的图给坑了,认为为了维持两个设备之间的无线通信必须双方都有一个接收地址设置成为一样的,造成单个nrf24只能和6个设备通信。实际上被那个图给误解了,发数据到另一个设备时只需要把发送地址设置为对方的接收地址(6个中的任意一个)就行,并不需要把自己的接收地址也设置为对方的接收地址(开启自动重发功能时需要将接收地址1设置为发送地址来接受ACK回应),这样设备通信数量的限制就基本上没有了。

文档相关资料:
2、 当MCU 有数据要发送时,接收节点地址(TX_ADDR)和有效数据(TX_PLD)通过SPI 接口写入
nRF24L01。发送数据的长度以字节计数从MCU 写入TX FIFO。当CSN 为低时数据被不断的写入。
发送端发送完数据后,将通道0 设置为接收模式来接收应答信号,其接收地址(RX_ADDR_P0)与接
收端地址(TX_ADDR)相同。
4、 接收到有效的数据包后(地址匹配、CRC 检验正确),数据存储在RX_FIFO 中,同时RX_DR 位
置高,并产生中断。状态寄存器中RX_P_NO 位显示数据是由哪个通道接收到的。

中继设备只需要保存

目前不考虑超过200各设备的情况,真要超过的话以后可以分组的方式来解决。
添加新设备方式:
主控直接和间接(通过已并网的设备中继)的向新设备默认地址广播探测新设备。广播信息如果经过中继的话会记录着中继信息。新设备收到后,会已新设备默认地址按原路径回复信息(包含设备唯一编号)。主控会根据收到的回应来确认找到几个新设备(通过设备唯一编号),各个设备有几条线路可以连接到主控。然后主控为新设备分配设备地址(以后设备地址就固定为这个了)并生成主路由和备用路由信息发给新设备,然后新设备启用收到的新设备地址及路由信息。

个别设备断网时重新入网办法:
< del datetime="2014-01-31T05:43:02+00:00">子设备在主路由信息无法连接主控时会将自身地址设置为本网的新设备默认地址,并循环使用几个主备路由信息来尝试连接到主控。连接到主控则更新路由信息。< /del>

主控在连接不到子设备时会以本网的新设备默认地址为接收地址直接和间接(通过已并网设备中继)的发送探测子设备信息,子设备要是收到则回复,主控等待一段时间根据收到的回复建立新的路由信息并发给子设备,子设备重新并网。

当然,如果主控实在是收不到回应则要么是子设备丢失了本网的地址段(网段非默认网段的情况),要么是之间连接中继或信道环境严重恶化,无法连接,或者子设备掉电或损坏。这些情况只能人工检查和修复了。

python 与 arduino 串口通信

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
>>> import serial
>>> ser = serial.Serial(8)
>>> ser.timeout=1
>>> ser.close()
>>> ser = serial.Serial(8)
>>> ser.timeout=1
>>> ser.readline()
''
>>> ser.write("11111\n")   # 每个命令以 \n 结尾
6L
>>> ser.readline()
'received: 11111\r\n'
>>> ser.write("11111\n")
6L
>>> ser.write("22222\n33333333\n") # 可以一次发送多个命令,同样以 \n 结尾。
15L
>>> ser.readline()
'received: 11111\r\n'
>>> ser.readline()
'received: 22222\r\n'
>>> ser.readline()
'received: 33333333\r\n'
>>> ser.readline()
''
>>>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
char serial_line[100] ="";
int serial_line_length=0;
 
void setup()
{
  Serial.begin(9600);
  Serial.setTimeout(1000); //串口超时 1000 毫秒
}
 
void loop()
{
  if (Serial.available() > 0)
  {
    // 读取,读到\n或100字符或超时
    serial_line_length = Serial.readBytesUntil('\n', serial_line, 100);
    serial_line[serial_line_length ]='\0';    // 截断字符串
    Serial.print("received: ");
    Serial.println(serial_line);
  }
}

参考:

http://www.geek-workshop.com/thread-5733-1-1.html

http://wiki.geek-workshop.com/doku.php?id=arduino:arduino_language_reference:serial

http://pythonhosted.org//pyserial/pyserial_api.html

ARDUINO 最小系统

atmeg328 内置复位电路,不需要外接复位电路。虽然也有内置晶振,但是 ARDUINO 只支持 16M 或 8M 的频率,所以最小系统还得用 16M 晶振接到9、10口(XTAL1、XTAL2),然后再分别接2个22p的电容到地就可以了。

全新的 atmeg328 没有 ARDUINO 的bootloader(引导程序) ,得用 IPS 接口写进去。之后就可以用串口写程序了。
熔丝位之类的设置不用管,ARDUINO IDE 写bootloader时会自动设置。对了,ARDUINO 的“使用编程器下载”功能会清除 ARDUINO 的bootloader(引导程序),除非资源太紧张,不建议使用。

如果需要复位按钮,可以用一个10K上拉电阻接到1(RESET)脚,然后弄个按钮,一端接到地,一段也接到1(RESET)脚。

还有一个问题,atmeg328 有两个 VCC 口(一个是 A/D 转换器的电源,这个必须接,不然芯片跑不起来,各个脚都是0.5V电压),建议都接到电源上面,如果只接7脚的 VCC ,会悲剧的跑不起来… 今天一上午都弄这个问题了。

附参考:
ARDUINO_V2

atmega328w

Arduino_Uno_Rev3-schematic

cubieboard cubie debian 下用 PocketSphinx 实现智能家居语音控制。

先照着前面几篇文章安装配置好声卡和PocketSphinx ,接着弄好语言模型和词典然后建立下面的03.py文件并执行就可以了。
03.py里用自己路的语音对官方的tdt_sc_8k语音模型做了适应,具体做法可以参考 http://cmusphinx.sourceforge.net/wiki/tutorialadapt 下面附的a.py和b.py是为做适应写的脚本。

实测发现安静环境下语音识别准确率能达到90%以上,但是如果出现其他噪音(其他人的说话声音、电视声音等)识别率就很悲剧。而为了做到全屋的语音识别,却需要把增益开到很大,这就很悲剧了…如果想解决这个问题,只能上麦克风阵列,然后配合傅里叶变换把各个声源的声音独立出来在做语音识别,这样做难度太大,暂时不做这一块。

语言识别相关文章:http://www.chenwang.net/category/%E8%AF%AD%E9%9F%B3%E8%AF%86%E5%88%AB/

03.py

https://gist.github.com/GameXG/647b6c3606a405a47462

#coding:utf-8
import pygtk,gtk,gobject
gobject.threads_init()
import gst
import os
import time
 
 
 
#pipeline=gst.parse_launch('gconfaudiosrc ! audioconvert ! audioresample  ! vader name=vad auto-threshold=true ! pocketsphinx name=asr ! appsink sync=false name=appsink')
pipeline=gst.parse_launch('alsasrc ! audioconvert ! audioresample ! vader name=vad auto-threshold=true   !  pocketsphinx name=asr ! appsink sync=false name=appsink')
#pipeline=gst.parse_launch('pulsesrc ! audioconvert ! audioresample  ! vader name=vad auto-threshold=true ! pocketsphinx name=asr ! appsink sync=false name=appsink')
#pipeline=gst.parse_launch('pulsesrc  ! tee  name=t ! queue ! audioconvert ! audioresample ! vader name=vad auto-threshold=true ! pocketsphinx name=asr ! appsink    sync=false name=appsink  t. queue ! audioconvert ! audioresample ! wavenc ! filesink location=o.wav')
 
def result(asr, text, uttid):
    pipeline.set_state(gst.STATE_PAUSED)
    print "================== "+text+" ========================="
    if text == "朵朵 打开 灯":
            os.system("echo 1 > /sys/class/gpio/gpio17_pg9/value")
            os.system('mplayer "http://translate.google.cn/translate_tts?ie=UTF-8&q=已打开灯&tl=zh-CN"')
    elif text == "朵朵 关闭 灯":
            os.system("echo 0 > /sys/class/gpio/gpio17_pg9/value")
            os.system('mplayer "http://translate.google.cn/translate_tts?ie=UTF-8&q=已关闭灯&tl=zh-CN"')
    else:
        #os.system('mplayer "http://translate.google.cn/translate_tts?ie=UTF-8&q=未知命令,'+text+'&tl=zh-CN"')
        pass
    pipeline.set_state(gst.STATE_PLAYING)
 
 
os.system("echo 17 > /sys/class/gpio/export")
time.sleep(1)
os.system("echo out > /sys/class/gpio/gpio17_pg9/direction")
asr=pipeline.get_by_name('asr')
asr.connect('result', result)
asr.set_property('hmm', 'tdt_sc_8kadapt')
asr.set_property('lm', 'TAR3487/3487.lm')
asr.set_property('dict', 'TAR3487/3487.dic')
asr.set_property('configured', True)
pipeline.set_state(gst.STATE_PLAYING)
gtk.main()
 
# cubie@Cubian:~/yysb/yysb/XueXi$ pocketsphinx_continuous -hmm tdt_sc_8k -lm TAR3487/3487.lm -dict TAR3487/3487.dic

a.py

https://gist.github.com/GameXG/a7e7d1a426ab60a9d79f

#coding:utf-8
# 根据文本文件生成训练语音模型所需的文件
# gamexg@gmail.com
 
import sys
import codecs
 
file_name=None
file_gname=None
 
if len(sys.argv)==0:
    print u'请提供需要处理的文件'
elif len(sys.argv)==2:
    file_name = sys.argv[1]
else:
    file_name = sys.argv[0]
 
if file_name[-4]=='.':
    file_gname = file_name[:-4]
else:
    file_gname = file_name
 
 
 
#f=codecs.open(file_name,'rb','utf-8')
f=open(file_name,'rb')
s=f.readlines()
f.close()
 
transcription=[]
listoffiles=[]
for i in range(len(s)):
    s[i]=s[i].replace(',',' ').replace('.',' ').replace('?',' ').replace(',',' ').replace('。',' ').replace('?',' ').strip()
    listoffiles.append('arctic_'+str(i+1))
    transcription.append('<s> %s </s> (arctic_%s)'%(s[i].strip(),str(i+1)))
 
listoffiles_file = open(file_gname+'.listoffiles','wb')
listoffiles_file.write('\r\n'.join(listoffiles))  #.encode('utf8'))
listoffiles_file.close()
 
transcription_file = open(file_gname+'.transcription','wb')
transcription_file.write('\r\n'.join(transcription))  #.encode('utf8'))
transcription_file.close()

b.py

https://gist.github.com/GameXG/397851c979f8dd626edf

#coding:utf-8 
# 根据 zh_broadcastnews_utf8.dic 为当前字典补全音标
# gamexg@gmail.com
 
import sys
import os
 
file_name=None
 
 
if len(sys.argv)==0:
    print '请提供需要处理的文件'
elif len(sys.argv)==2:
    file_name = sys.argv[1]
else: 
    file_name = sys.argv[0]
 
 
f=open(r'D:\Dropbox\yysb\zh_broadcastnews_utf8.dic','rb')
d={}
i=f.readline()
while(i):
	kv=i.split(' ',1)
	if len(kv)==2 :d[kv[0].strip()]=kv[1].strip()
	i=f.readline()
 
os.rename(file_name,file_name+'.bak')
 
def get_dict(name):
    if not name:
        return ''
    for i in range(len(name)):
        if d.has_key(name[:len(name)-i]):
    	    return d[name[:len(name)-i]] + ' ' + get_dict(name[len(name)-i:])
    return '======= ERR ======='
 
 
 
 
f=open(file_name+'.bak','rb')
wf = open(file_name,'wb')
ws=[]
for i in f.readlines():
    i=i.strip()
    if i:
        kv = i.split(' ',1)
        ws.append('%s %s'%(kv[0],get_dict(kv[0]).strip()))
wf.write('\r\n'.join(ws))
wf.close()

cubieboard cubie debian 下安装 PocketSphinx with GStreamer and Python 实现实时语音识别

参考之前在ubuntu下安装的记录 http://www.chenwang.net/?p=460 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 
sudo apt-get upgrade
sudo aptitude install python-gst0.10
 
sudo aptitude install libgstreamer0.10-dev
echo sudo aptitude install python2.7-dev
sudo aptitude install python-dev
sudo aptitude  install gstreamer0.10-alsa gstreamer0.10-tools
sudo aptitude install libsamplerate0-dev
sudo aptitude install libsamplerate0-devexit
sudo aptitude install gstreamer0.10-plugins-base  gstreamer0.10-plugins-good
sudo  aptitude install libgstreamer-plugins-base0.10-dev
 
wget http://downloads.sourceforge.net/project/cmusphinx/pocketsphinx/0.8/pocketsphinx-0.8.tar.gz?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fcmusphinx%2Ffiles%2Fpocketsphinx%2F0.8%2F&ts=1385189168&use_mirror=softlayer-dal
mv pocketsphinx-0.8.tar.gz\?r\=http\:%2F%2Fsourceforge.net%2Fprojects%2Fcmusphinx%2Ffiles%2Fpocketsphinx%2F0.8%2F  pocketsphinx-0.8.tar.gz
wget http://downloads.sourceforge.net/project/cmusphinx/sphinxbase/0.8/sphinxbase-0.8.tar.gz?r=http%3A%2F%2Fsourceforge.net%2Fprojects%2Fcmusphinx%2Ffiles%2Fsphinxbase%2F0.8%2F&ts=1385189298&use_mirror=softlayer-dal
mv  sphinxbase-0.8.tar.gz\?r\=http\:%2F%2Fsourceforge.net%2Fprojects%2Fcmusphinx%2Ffiles%2Fsphinxbase%2F0.8%2F   sphinxbase-0.8.tar.gz
tar xzf sphinxbase-0.8.tar.gz
cd sphinxbase-0.8/
./configure
apt-cache search python-dev
./configure
make
sudo make install
cd ..
tar xzf pocketsphinx-0.8.tar.gz
cd pocketsphinx-0.8
./configure
make
sudo make install
pocketsphinx_continuous
export  LD_LIBRARY_PATH=/usr/local/lib
export  PKG_CONFIG_PATH=/usr/local/lib/pkgconfig
pocketsphinx_continuous

Changing the `location’ property on filesink when a file is open is not supported. 错误解决办法

程序运行中动态修改filesink.location来修改文件保存位置会提示 Changing the `location’ property on filesink when a file is open is not supported. 错误,需要将filesink的状态设置为NULL,然后才能修改,例子:

1
2
3
4
5
        save = pipeline.get_by_name('save')
        save.set_state(gst.STATE_NULL)
        i+=1
        save.set_property('location','save'+str(i)+'.wav')
        save.set_state(gst.STATE_PAUSED)

参考:http://comments.gmane.org/gmane.comp.video.gstreamer.devel/30074

对了,这样修改就像参考里面说的,由于是直接将wav文件截断的,第二个文件没有格式信息,会无法播放。如果想生成的多个wav文件都能正常播放需要将 wavenc 也设置为 gst.STATE_NULL 一次。

cubieboard ubuntu 开启bash增强自动补全

cubieboard ubuntu 默认没有增强自动补全,用起来真难受。

sudo apt-get install bash-completion

修改 /etc/bash.bashrc文件,去掉下面内容的注释

1
2
3
4
5
6
7
8
# enable bash completion in interactive shells
if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi

然后 source /etc/profile 一下就可以了。

看了一下,发现 ~/.bashrc 下面就有启动增强自动补全的代码,也就是只用安装 bash-completion 就可以了。

无觅相关文章插件,快速提升流量