机器人上轮编码器的Python线程编程
我正在为一个机器人编写代码,这个机器人是我学校参加比赛用的。目前,我正在尝试使用反射传感器来制作一些轮子编码器。我早些时候意识到,我可能需要使用线程来实现这个功能,因为机器人需要同时监控左右两个编码器。下面是我目前写的代码:
from __future__ import division
import threading
import time
from sr import *
R = Robot()
class Encoder(threading.Thread):
def __init__(self, motor, pin, div=16):
self.motor = motor
self.pin = pin
self.div = div
self.count = 0
threading.Thread.__init__(self)
def run(self):
while True:
wait_for(R.io[0].input[self.pin].query.d)
self.count += 1
def rotations(self, angle, start_speed=50):
seg = 360/self.div
startcount = self.count
current_dist = angle #Distance away from target
R.motors[self.motor].target = start_speed
while current_dist > 360:
newcount = self.count - startcount
current_dist = angle - newcount*seg
R.motors[self.motor].target = 50
while abs(current_dist) > seg/2:
newcount = self.count - startcount
current_dist = angle - newcount*seg
current_speed = start_speed * current_dist / 360
if current_speed < 5:
R.motors[self.motor].target = 5
else:
R.motors[self.motor].target = current_speed
R.motors[self.motor].target = 0
WheelLeft = Encoder(0,0)
WheelLeft.start()
WheelRight = Encoder(1,3)
WheelRight.start()
WheelRight.rotations(720)
WheelLeft.rotations(720)
这个 sr 模块是南安普顿大学提供的,他们正在举办这个比赛。这个模块让我们可以与机器人的硬件进行互动。
现在,创建的线程似乎可以让两个反射传感器分别被监控。这段代码:R.io[0].input[self.pin].query.d
用来判断从反射传感器传来的值是否发生了变化。'rotations' 方法通过不断检查轮子已经转过多少度来让轮子转动到一定的角度,并在接近终点时减速。我希望在运行程序时,两个轮子都能开始转动,然后在转过两圈后减速并停止。不过,目前运行程序时,一个轮子先开始转动,减速并停止,然后另一个轮子才开始。这让我觉得 'rotations' 方法没有像 'run' 方法那样在一个线程中运行。难道只有 'run' 方法下的代码才会在一个线程中运行,还是整个类都是这样?
如果有帮助的话,我一直在跟着这个教程:http://www.devshed.com/c/a/Python/Basic-Threading-in-Python/1/
另外,我想知道为什么可以仅用 Encoder(0,0).start()
来启动一个线程。为什么不需要像这样创建一个对象(例如 Thread = Encoder(0,0).start()
)才能创建一个新的线程呢?
抱歉如果我用的术语不太准确,正如你们可能看到的,我对线程和编程整体上都还很陌生。
3 个回答
你可以从SR的Poll
类进行扩展,这样就可以在wait_for
中使用它:
import poll
class Encoder(poll.Poll):
def __init__(self, motor, pin, div=16):
self.motor = motor
self.pin = pin
self.div = div
self.count = 0
self.target_reached = False
# kick off a thread to count the encoder ticks
self.counter_thread = threading.Thread(target=self._update_count)
self.counter_thread.start()
def _update_count(self):
while True:
wait_for(R.io[0].input[self.pin].query.d)
self.count += 1
def rotations(self, angle, start_speed=50):
if not self.target_reached:
raise Exception("Last motion still in progress!")
self.target_reached = False
# kick off a thread to control the speed
self.angle_thread = threading.Thread(
target=self._update_speeds,
args=(angle, start_speed)
)
self.angle_thread.start()
def _update_speeds(self, angle, start_speed):
# control the motor speed as before
...
# let things know we're done
self.target_reached = True
# implement poll methods
def eval(self):
return (self.target_reached, None)
这样你就可以做:
wheelLeft = Encoder(0,0)
wheelRight = Encoder(1,3)
wheelRight.rotations(720)
wheelLeft.rotations(720)
wait_for(wheelRight & wheelLeft)
需要注意的是,编码器并不是一个线程——它是一种“拥有”的关系,而不是“是”的关系。
run方法就是执行的线程。
如果你想在这个线程里做其他事情,就得从Encoder.run()
里调用它。
哦,对了,Encoder(0,0).start()
确实会创建一个对象。只是因为你没有把这个对象绑定到一个局部变量上,并不代表它不存在。如果它不存在,你就不能调用它的start
方法。
不过,你得非常小心这个对象的生命周期,因为没有局部变量来保持它的存在。
Encoder(0,0).start()
是一个启动线程的方法调用。这个方法会调用你自己写的 run
方法,而这个方法里面并没有使用 rotations
方法。如果你想用这个方法,那你需要在 run
的循环里去调用它。
当你写 Thread = Encoder(0,0).start()
时,你其实是在存储这个调用返回的值(这个值是 None),不过无论如何,你还是得先启动新线程才能得到这个值。