Python、Webiopi和树莓派

0 投票
2 回答
2310 浏览
提问于 2025-04-18 15:05

我正在尝试通过无线网络和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 个回答

1

试试这个 - 你总是停在范围上,但可能想要根据方向来停止

  if get_range() < 30:
      motor_stop()
  elif direction == "fwd":
      motor_forward()
  else:
      motor_stop()

顺便说一下,你在引用时只需要用 arg,不需要加括号 (arg)

打印 arg

函数调用中的括号是用来表示这是一个函数的参数

1

我马上会回答你主要的问题,但首先我想提一下,作为一个Python初学者,你有些做法让自己变得更复杂了。

在你的函数开头,global这个声明其实是没必要的。在Python中,只有当你想要修改一个全局变量时,才需要用到global。如果你只是想读取这个变量,或者调用函数,就不需要这个声明。你在函数中使用的名字,无论是变量名还是函数名,都会按照以下顺序查找:局部、封闭、全局和内置。"局部"指的是在你的函数内部定义的名字,比如你的direction变量。"封闭"是指当你在一个函数内部定义另一个函数时的规则。这在很多情况下很有用,但现在你不需要担心这个。"全局"指的是在程序顶层定义的名字,比如你的motor_stop函数等。最后,"内置"指的是Python自带的名字,比如strintfile

所以如果你去掉global声明,Python会按照以下步骤查找motor_stop这个名字:

  1. 局部:这个函数里有定义motor_stop吗?没有,继续。
  2. 封闭:这个函数是在另一个函数内部定义的吗?没有,所以"封闭"规则不适用,继续。
  3. 全局:在程序顶层有定义motor_stop吗?有,找到了。
  4. (内置):这个规则根本不检查,因为名字已经通过"全局"规则找到了。

你所有的函数都是在程序的顶层定义的,所以"全局"规则会找到它们,而不需要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的一些保留字,比如breakif)。所以让我们给你的函数一个更有意义的参数名,这样会更简单:

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)

撰写回答