Python、Webiopi和树莓派
我正在尝试通过无线网络和webiopi控制我的树莓派小车。基本功能运行得很好——我有一个界面,可以点击前进按钮,小车就会向前走,当我松开按钮时,它会停下来。
现在我想把超声波距离传感器整合进来,这样当我向前开的时候,如果前面有东西,小车就会停下来。我已经实现了,当我点击前进按钮时,小车会行驶,并在有东西在范围内时停下,但它只会在有东西的时候停,而不是在我松开按钮时停。我的循环似乎卡住了,无法读取webiopi的松开按钮功能。
有人能帮帮我吗?我已经纠结了好几天,不知道哪里出错了 :-(
这是我Python脚本中的循环代码:
def go_forward(arg):
global motor_stop, motor_forward, motor_backward, get_range
print "Testing"
print mousefwd()
while (arg) == "fwd":
print (arg)
direction = (arg)
dist = get_range()
print "Distance %.1f " % get_range()
if direction == "fwd" and get_range() < 30:
motor_stop()
return
else:
motor_forward()
这是我webiopi函数调用中的代码:
function go_forward() {
var args = "fwd"
webiopi().callMacro("go_forward", args);
}
function stop1() {
var args = "stop"
webiopi().callMacro("go_forward", args);
webiopi().callMacro("stop");
}
现在的代码是这样的,但还是不工作(我完全是个新手 :-) ):
def go_forward(arg):
global motor_stop, motor_forward, motor_backward, get_range
print "Testing"
direction = arg
while direction == "fwd":
print arg
dist = get_range()
print "Distance %.1f " % get_range()
if get_range() < 30:
motor_stop()
elif direction == "fwd":
motor_forward()
else:
motor_stop()
可能有一点进展。看到webiopi使用了自己的“循环”,我添加了循环代码来检查电机GPIO的状态和距离,如果电机在运行且距离太短,就停下来。现在,当我按下前进按钮时,小车会移动,并在我松开时停下,当向前移动且距离小于30厘米时,它也会停下。唯一的问题是,当距离太短时,我快速多次按下前进按钮,就会出现“GPIO.output(Echo,1) _webiopi.GPIO.InvalidDirectionException: GPIO通道不是输出”这个错误 :-( 。
现在代码看起来是这样的:
def go_forward(direction):
motor_forward()
def loop():
if get_range() < 30 and GPIO.digitalRead(M1) == GPIO.HIGH:
stop()
print "stop"
sleep(1)
2 个回答
试试这个 - 你总是停在范围上,但可能想要根据方向来停止
if get_range() < 30:
motor_stop()
elif direction == "fwd":
motor_forward()
else:
motor_stop()
顺便说一下,你在引用时只需要用 arg,不需要加括号 (arg)
打印 arg
函数调用中的括号是用来表示这是一个函数的参数
我马上会回答你主要的问题,但首先我想提一下,作为一个Python初学者,你有些做法让自己变得更复杂了。
在你的函数开头,global
这个声明其实是没必要的。在Python中,只有当你想要修改一个全局变量时,才需要用到global
。如果你只是想读取这个变量,或者调用函数,就不需要这个声明。你在函数中使用的名字,无论是变量名还是函数名,都会按照以下顺序查找:局部、封闭、全局和内置。"局部"指的是在你的函数内部定义的名字,比如你的direction
变量。"封闭"是指当你在一个函数内部定义另一个函数时的规则。这在很多情况下很有用,但现在你不需要担心这个。"全局"指的是在程序顶层定义的名字,比如你的motor_stop
函数等。最后,"内置"指的是Python自带的名字,比如str
、int
或file
。
所以如果你去掉global
声明,Python会按照以下步骤查找motor_stop
这个名字:
- 局部:这个函数里有定义
motor_stop
吗?没有,继续。 - 封闭:这个函数是在另一个函数内部定义的吗?没有,所以"封闭"规则不适用,继续。
- 全局:在程序顶层有定义
motor_stop
吗?有,找到了。 - (内置):这个规则根本不检查,因为名字已经通过"全局"规则找到了。
你所有的函数都是在程序的顶层定义的,所以"全局"规则会找到它们,而不需要global
声明。这应该能让你的代码简单一些。
现在来回答你的主要问题:为什么你的代码会无限循环。这是因为你的while
循环的测试条件永远不会变为假。你是在while direction == "fwd"
的情况下循环,而while
循环内部的代码并没有改变direction
变量。所以每次到达循环的末尾时,它又会回去测试条件。direction
变量依然是字符串"fwd"
,因此while
循环又会执行第二次。然后direction
变量还是"fwd"
,所以又会执行第三次。如此反复。
要在你想要退出while
循环时退出,可以在想停止的时候把direction
变量设置为其他值,比如"stop"
(例如,在你调用motor_stop()
之后)。另一种方法是使用return
语句在那时退出函数。但我建议你使用break
语句,它的意思是“在这里,立即退出我所在的循环。”它适用于while
循环和for
循环,是一个非常有用的语句。看起来你希望你的while
循环在每次调用motor_stop()
时退出,所以你可以在两个地方放置break
语句:
def go_forward(arg):
print "Testing"
direction = arg
while direction == "fwd":
print arg
dist = get_range()
print "Distance %.1f " % get_range()
if get_range() < 30:
motor_stop()
break
elif direction == "fwd":
motor_forward()
# No break statement here, so the while loop continues
else:
motor_stop()
break
还有一些其他的改动我建议你做。首先,你在循环中调用了get_range()
三次,但其实只需要调用一次。你已经把它的结果赋值给了dist
变量,那为什么不直接使用这个变量呢?
def go_forward(arg):
print "Testing"
direction = arg
while direction == "fwd":
print arg
dist = get_range()
print "Distance %.1f " % dist
if dist < 30:
motor_stop()
break
elif direction == "fwd":
motor_forward()
# No break statement here, so the while loop continues
else:
motor_stop()
break
其次,在while
循环内部,direction
不可能不是"fwd",所以最后的else
是多余的,你的函数可以变成:
def go_forward(arg):
print "Testing"
direction = arg
while direction == "fwd":
print arg
dist = get_range()
print "Distance %.1f " % dist
if dist < 30:
motor_stop()
break
else:
motor_forward()
最后,你有一个函数参数叫arg
,这个名字并没有告诉你它的用途。你马上把它赋值给一个叫direction
的变量,这个名字更好。那么为什么不一开始就用这个名字作为参数呢?没有规定函数的参数必须叫"arg";它们可以叫任何你喜欢的名字(除了Python的一些保留字,比如break
或if
)。所以让我们给你的函数一个更有意义的参数名,这样会更简单:
def go_forward(direction):
print "Testing"
while direction == "fwd":
print direction
dist = get_range()
print "Distance %.1f " % dist
if dist < 30:
motor_stop()
break
else:
motor_forward()
这样就更容易阅读和理解了,也应该能解决你遇到的while
循环在车子靠近物体时不退出的问题。
(我还能看到一个可能的问题,就是在车子向前行驶时,while
循环可能不会退出,即使你松开了“向前”按钮——但我需要看看你的motor_forward()
代码才能知道怎么解决这个问题。可能只需在motor_forward()
之后放一个break
语句,这样就真的不需要while
循环了——但我需要知道你的motor_forward()
代码是如何与webiopi交互的,以及webiopi的事件处理代码是如何工作的。现在我对这些都不太了解,所以我会回答我能回答的问题,即使我的回答不完整,也不想让你没有答案。)
Mark的编辑:这是motor_forward
的代码:
def motor_forward():
GPIO.output(M1, GPIO.HIGH)
GPIO.output(M2, GPIO.LOW)
string = "echo 0=130 > /dev/servoblaster"
os.system(string)