机器人上轮编码器的Python线程编程

1 投票
3 回答
1888 浏览
提问于 2025-04-17 19:56

我正在为一个机器人编写代码,这个机器人是我学校参加比赛用的。目前,我正在尝试使用反射传感器来制作一些轮子编码器。我早些时候意识到,我可能需要使用线程来实现这个功能,因为机器人需要同时监控左右两个编码器。下面是我目前写的代码:

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 个回答

0

你可以从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)

需要注意的是,编码器并不是一个线程——它是一种“拥有”的关系,而不是“是”的关系。

0

run方法就是执行的线程。

如果你想在这个线程里做其他事情,就得从Encoder.run()里调用它。

哦,对了,Encoder(0,0).start()确实会创建一个对象。只是因为你没有把这个对象绑定到一个局部变量上,并不代表它不存在。如果它不存在,你就不能调用它的start方法。

不过,你得非常小心这个对象的生命周期,因为没有局部变量来保持它的存在。

1

Encoder(0,0).start() 是一个启动线程的方法调用。这个方法会调用你自己写的 run 方法,而这个方法里面并没有使用 rotations 方法。如果你想用这个方法,那你需要在 run 的循环里去调用它。

当你写 Thread = Encoder(0,0).start() 时,你其实是在存储这个调用返回的值(这个值是 None),不过无论如何,你还是得先启动新线程才能得到这个值。

撰写回答