日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

EV3 直接命令 - 第 4 课 用两个驱动轮精确地移动小车

發布時間:2024/4/11 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 EV3 直接命令 - 第 4 课 用两个驱动轮精确地移动小车 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

簡介

上一課,我們編寫了 TwoWheelVehicle 類,一個 EV3 的子類。它的方法是 move 和 stop,但它不僅僅是圍繞操作 opOutput_Speed,opOutput_Start 和 opOutput_Stop 的薄薄的封裝。本節課的最后,類 TwoWheelVehicle 將具有實質的內容。如大多數軟件那樣,它一步步增長,本節課不會是最后一次,我們將繼續使用這個類。

上一課的主題是一個遙控小車。我們編寫了無限移動的代碼,它們由新命令中斷。我們已經看到了這一設計理念的好處,它不阻塞 EV3 設備。精確移動的第一個版本(本節課的結果)將阻塞 EV3 設備,且它將耗費我們進一步的工作來找到一個不阻塞的方案。

同步的電機移動

希望上一節課的車輛仍然存在。我們再次需要它。你還記得,右輪連接到端口 A,左邊連接到端口 D。我們的遙控解決方案存在缺陷:移動越慢,轉向的精度越差。我們需要的是一個操作,兩個電機的速度可以被設置為定義的比率。如你可能期待的那樣,這種操作是存在的。請給你的小車發送如下的直接命令:

------------------------------------------------------------- \ len \ cnt \ty\ hd \op\la\no\sp\ tu \ step \br\op\la\no\ ------------------------------------------------------------- 0x|12:00|2A:00|80|00:00|B0|00|09|3B|81:32|82:D0:02|00|A6|00|09| ------------------------------------------------------------- \ 18 \ 42 \no\ 0,0 \O \0 \A \-5\ 50 \ 720 \0 \O \0 \A \ \ \ \ \ \u \ \+ \ \ \ \ \u \ \+ \ \ \ \ \ \t \ \D \ \ \ \ \t \ \D \ \ \ \ \ \p \ \ \ \ \ \ \p \ \ \ \ \ \ \ \u \ \ \ \ \ \ \u \ \ \ \ \ \ \ \t \ \ \ \ \ \ \t \ \ \ \ \ \ \ \_ \ \ \ \ \ \ \_ \ \ \ \ \ \ \ \S \ \ \ \ \ \ \S \ \ \ \ \ \ \ \t \ \ \ \ \ \ \t \ \ \ \ \ \ \ \e \ \ \ \ \ \ \a \ \ \ \ \ \ \ \p \ \ \ \ \ \ \r \ \ \ \ \ \ \ \_ \ \ \ \ \ \ \t \ \ \ \ \ \ \ \S \ \ \ \ \ \ \ \ \ \ \ \ \ \ \y \ \ \ \ \ \ \ \ \ \ \ \ \ \ \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ \c \ \ \ \ \ \ \ \ \ \ -------------------------------------------------------------

點擊 A 旋轉 720°,點擊 D 移動 360°。小車左轉,兩個電機都以低速轉動且良好的同步。新的操作是:

  • opOutput_Step_Sync = 0xB0,參數是:
    • LAYER
    • NOS:兩個輸出端口,命令不是對稱的,它區別了低端口和高端口。在我們的情況中,低端口是 PORT_A,右輪,高端口是 PORT_D,左輪。
    • SPEED
    • TURN:轉向比率,[-200 - 200]
    • STEP:轉速脈沖以度為單位,值 0 代表無限運動(如果TURN > 0,則 STEP 限制低端口,如果 TURN < 0,則限制較高端口)。正值,參數 SPEED 的符號確定方向。
    • BRAKE

參數 TURN 需要一些解釋。但你處在一個很好的位置,你已經知道了它的含義,因為我們在第 3 課的遠程控制程序中使用了它。如你可能記得的那樣,我們計算兩輪的速度:

if turn > 0:speed_right = speedspeed_left = round(speed * (1 - turn / 100))else:speed_right = round(speed * (1 + turn / 100))speed_left = speed

到整數值的舍入是低速下糟糕的精度的原因!但現在我們很高興,操作 opOutput_Step_Sync 計算時不執行舍入:

if turn > 0:speed_right = speedspeed_left = speed * (1 - turn / 100)else:speed_right = speed * (1 + turn / 100)speed_left = speed

速度和步數是成比例的,我們也可以寫為:

if turn > 0:step_right = math.copysign(1, speed) * stepstep_left = math.copysign(1, speed) * step * (1 - turn / 100)else:step_right = math.copysign(1, speed) * step * (1 + turn / 100)step_left = math.copysign(1, speed) * step

操作 opOutput_Step_Sync 對于具有兩個驅動輪的小車是完美的!就像是專門為它設計的一樣。請通過把兩個 opOutput_Speed 操作替換為一個 opOutput_Step_Sync 來提升你的遠程控制程序。你將看到,它工作的更好,特別是低速下。我把我的 move 函數的代碼修改為了:

def move(self, speed: int, turn: int) -> None:assert self._sync_mode != ev3.SYNC, 'no unlimited operations allowed in sync_mode SYNC'assert isinstance(speed, int), "speed needs to be an integer value"assert -100 <= speed and speed <= 100, "speed needs to be in range [-100 - 100]"assert isinstance(turn, int), "turn needs to be an integer value"assert -200 <= turn and turn <= 200, "turn needs to be in range [-200 - 200]"if self._polarity == -1:speed *= -1if self._port_left < self._port_right:turn *= -1ops = b''.join([ev3.opOutput_Step_Sync,ev3.LCX(0), # LAYERev3.LCX(self._port_left + self._port_right), # NOSev3.LCX(speed),ev3.LCX(turn),ev3.LCX(0), # STEPSev3.LCX(0), # BRAKEev3.opOutput_Start,ev3.LCX(0), # LAYERev3.LCX(self._port_left + self._port_right) # NOS])self.send_direct_cmd(ops)

一些說明:

  • 車輛遵從相同的轉彎,與速度無關。這是 opOutput_Step_Sync 相對于使用兩個 opOutput_Speed 操作的主要提升。
  • 如果你使用了 opOutput_Polarity,你將發現 ,你無法把它和 opOutput_Step_Sync 結合。你不得不手動完成。修改兩個電機的極性很簡單,只是反轉參數 SPEED 即可。
  • 如果你交換了電機的連接,以使你的左邊電機連接在較低端口上,它也很簡單,反轉 TURN。
  • 僅更改一個電機的極性非常棘手。

下表可能有助于理清你已經看到的移動。當給出以下條件時,它描述了依賴于 TURN 的運動類型:

  • 兩個電機具有相同的極性,
  • 右輪連接到更低的端口,左邊的更高。
TURN移動描述
0直行兩個電機以相同的速度朝相同的方向移動
[0 - 100]左轉兩個電機朝相同的方向旋轉,左邊的以更低的速度
100繞左輪左轉只有右邊的電機移動
[100 - 200]向左轉彎兩個電機朝相反的方向旋轉,左邊的以更低的速度旋轉
200向左轉圈兩個電機朝相同的方向旋轉,但速度相同
[0 - -100]右轉兩個電機朝相同的方向旋轉,右邊的以更低的速度
-100繞右輪右轉只有左邊的電機移動
[-100 - -200]向右轉彎兩個電機朝相反的方向旋轉,右邊的以更低的速度旋轉
-200向右轉圈兩個電機朝相同的方向旋轉,但速度相同

當端口連接不同時,左輪為較低端口,右輪為較高端口時,請反映情況。

歡迎你進一步提升遠程控制工程。你可以使用游戲桿而不是你的鍵盤的方向鍵。或者你可以使用智能手機的陀螺儀傳感器。但這是你的項目,我們將遠程控制留在了后面(至少目前為止)。

具有兩個驅動輪的車輛的定義明確且可預測的運動

我們仍然專注于具有兩個驅動輪的車輛及其運動的準確性。遠程控制是一種非常特殊的情況,如果需要,人類的頭腦會監督車輛的運動并立即進行一些修正。這種情況不需要像轉彎半徑那樣的單位。這就像駕駛汽車一樣,沒有必要確切知道轉彎半徑是如何從方向盤的位置決定的。修正是相對和直觀的。機器人在沒有外部控制的情況下移動,它們的算法需要知道參數的確切依賴性。我們將編寫控制車輛運動的程序。我們從最難的變體開始,不存在任何校正機制。這意味著,我們需要能夠預測和精確描述車輛運動的函數。我想到以下幾點:

  • drive_straight(speed:int, distance: float=None),其中

    • speed 的符號描述了方向(向前或向后)
    • speed 的絕對值描述了速度。我們更喜歡每秒 SI 單位計,但它是百分比。
    • distance 為 None 或正的,它以 SI 單位 meter 給出。如果它是 None,則運動是無限的。
  • drive_turn(speed:int, radius_turn:float, angle:float=None, right_turn:bool=False),其中

    • speed 的符號描述了方向(向前或向后)
    • speed 的絕對值描述了速度。
    • radius_turn 是轉彎的半徑,單位為 meter。我們取兩個驅動輪的中間作為我們的參考點。
    • radius_turn 的符號描述了轉彎的方向。正值表示左轉,正轉向,負值代表順時針轉動。
    • angle 為 None 或正的,它是以度為單位的圓弧段 (90° 為四分之一圓,720° 為兩個完整的圓)。如果它是 None,則表示無限移動。
    • right_turn 是一個用于特殊情景的標記。如果我們打開 place,屬性radius_turn 為零并且沒有符號。在這種情況下,左轉是默認值,屬性right_turn 是相反方向的標志。

正如你可能想象的那樣,我們并沒有走太遠。我們將使用操作 opOutput_Ready,opOutput_Start 和 opOutput_Speed_Sync。這是說我們將不使用中斷。從這個角度來看,我們可以回到第二課的知識。

確定車輛的尺寸

們需要車輛的某些尺寸來將上述功能的 SI 單位轉換為參數 turn 和操作opOutput_Speed_Sync 的步驟。 車輛的尺寸為:

  • 驅動輪的半徑:radius_wheel。
  • 車輛 tread。

使用尺子量出(對于我的車輛):

  • radius_wheel = 0.021 m
  • tread = 0.16 m

具有更高精度的替代方案是測量車輛的運動。要獲得驅動輪的半徑,可以使用以下直接命令:

---------------------------------------------------------- \ len \ cnt \ty\ hd \op\la\no\sp\tu\ step \br\op\la\no\ ---------------------------------------------------------- 0x|11:00|2A:00|80|00:00|B0|00|09|14|00|82:10:0E|01|A6|00|09| ---------------------------------------------------------- \ 17 \ 42 \no\ 0,0 \O \0 \A \20\0 \ 3600 \1 \O \0 \A \ \ \ \ \ \u \ \+ \ \ \ \ \u \ \+ \ \ \ \ \ \t \ \D \ \ \ \ \t \ \D \ \ \ \ \ \p \ \ \ \ \ \ \p \ \ \ \ \ \ \ \u \ \ \ \ \ \ \u \ \ \ \ \ \ \ \t \ \ \ \ \ \ \t \ \ \ \ \ \ \ \_ \ \ \ \ \ \ \_ \ \ \ \ \ \ \ \S \ \ \ \ \ \ \S \ \ \ \ \ \ \ \t \ \ \ \ \ \ \t \ \ \ \ \ \ \ \e \ \ \ \ \ \ \a \ \ \ \ \ \ \ \p \ \ \ \ \ \ \r \ \ \ \ \ \ \ \_ \ \ \ \ \ \ \t \ \ \ \ \ \ \ \S \ \ \ \ \ \ \ \ \ \ \ \ \ \ \y \ \ \ \ \ \ \ \ \ \ \ \ \ \ \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ \c \ \ \ \ \ \ \ \ \ \ ----------------------------------------------------------

拿出尺子并測量你的小車移動的距離(如果你的小車沒有直行,尋找輪子的最佳結合,而不是外觀,這似乎是相同的尺寸,真的是)。輪子的一個完整旋轉的距離計算公式為 2 * pi * radius_wheel。3,600 度為 10 個完整旋轉,因此下面的計算給出了你的輪子的 radius_wheel:

radius_wheel = distance / (20 * pi)

這將我的輪子的半徑修正為 radius_wheel = 0.02128 m。接下來,我們讓車輛旋轉,并計數 N,車輛轉數。為此,我們發送直接命令:

---------------------------------------------------------------- \ len \ cnt \ty\ hd \op\la\no\sp\ tu \ step \br\op\la\no\ ---------------------------------------------------------------- 0x|13:00|2A:00|80|00:00|B0|00|09|14|82:C8:00|82:50:46|01|A6|00|09| ---------------------------------------------------------------- \ 19 \ 42 \no\ 0,0 \O \0 \A \20\ 200 \ 18000 \1 \O \0 \A \ \ \ \ \ \u \ \+ \ \ \ \ \u \ \+ \ \ \ \ \ \t \ \D \ \ \ \ \t \ \D \ \ \ \ \ \p \ \ \ \ \ \ \p \ \ \ \ \ \ \ \u \ \ \ \ \ \ \u \ \ \ \ \ \ \ \t \ \ \ \ \ \ \t \ \ \ \ \ \ \ \_ \ \ \ \ \ \ \_ \ \ \ \ \ \ \ \S \ \ \ \ \ \ \S \ \ \ \ \ \ \ \t \ \ \ \ \ \ \t \ \ \ \ \ \ \ \e \ \ \ \ \ \ \a \ \ \ \ \ \ \ \p \ \ \ \ \ \ \r \ \ \ \ \ \ \ \_ \ \ \ \ \ \ \t \ \ \ \ \ \ \ \S \ \ \ \ \ \ \ \ \ \ \ \ \ \ \y \ \ \ \ \ \ \ \ \ \ \ \ \ \ \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ \c \ \ \ \ \ \ \ \ \ \ ----------------------------------------------------------------

我計算了我的車輛 N = 15.2 完整旋轉。兩個輪子都旋轉了 18, 000°,這是50 個完整的旋轉,或者距離 50 * 2 * pi * radius_wheel。旋轉的半徑為 0.5 * tread(我們定義輪子的中間為我們的參考點)。這就是說 N * 2 * pi * 0.5 * tread = 50 * 2 * pi * radius_wheel 或者:

tread = radius_wheel * 100 / N

這修正了 tread 的尺寸(在我的情況中:tread = 0.1346 m)。稍后我們將執行一些額外的運動,也許這將再次修正 tread 的尺寸。

獲取參數 STEP 和 TURN 的數學轉換

OK,我們知道了車的尺寸。接下來我們需要把我們的方法 drive_straight 和 drive_turn 的參數轉為操作 opOutput_Step_Sync 的參數 STEP 和 TURN。這需要一些數學的東西。如果你對細節不感興趣,你可以只看本小節最后的結果。但是大多數人都想知道細節,細節在這里。

給定我們的小車需要旋轉的 angle 和 radius_turn。在轉彎中,兩個輪子移動不同的距離。外面的輪子的距離為:2 * pi * radius_wheel * STEP / 360。可以根據轉彎的幾何形狀和車輛踏板的知識計算相同的距離:2 * pi * (radius_turn + 0.5 * tread) * angle / 360。這兩個相同距離的描述給出了以下等式:

STEP = angle * (radius_turn + 0.5 * tread) / radius_wheel

OK,第一個參數計算出來了,但是我們仍然需要計算 TURN。關鍵的方法來自轉彎的幾何學,是說,兩個車輪之間的速度比 speed_right / speed_left 與外面和里面的車輪的距離比相同:

speed_right / speed_left = (radius_turn + 0.5 * tread) / (radius_turn - 0.5 * tread)

你可能還記得,我們已經知道了兩個輪子的速度:speed_right = SPEED 和 speed_left = SPEED * (1 - TURN / 100)。這給出等式:

1 / (1 - TURN / 100) = (radius_turn + 0.5 * tread) / (radius_turn - 0.5 * tread)

轉換這個等式得到:

TURN = 100 * (1 - (radius_turn - 0.5 * tread) / (radius_turn + 0.5 * tread))

就是這樣,果給出轉彎運動的尺寸 (angle, radius_turn) 和車輛的尺寸 (tread, radius_wheel):

  • 變量 STEP 的計算方法為:
  • STEP = angle * (radius_turn + 0.5 * tread) / radius_wheel
  • 變量 TURN 的計算方法為:
  • TURN = 100 * (1 - (radius_turn - 0.5 * tread) / (radius_turn + 0.5 * tread))

    在 Python 中,這可以寫作:

    rad_outer = radius_turn + 0.5 * self._treadrad_inner = radius_turn - 0.5 * self._treadstep = round(angle*rad_outer / self._radius_wheel)turn = round(100*(1 - rad_inner / rad_outer))if angle < 0:step *= -1turn *= -1if self._polarity == -1:speed *= -1

    數學轉換的控制

    我們做一些合理性檢查:

    • 以半徑 radius_turn = 0.5 * tread 旋轉得到 TURN = 100 或 TURN = -100,這是對的。
    • 以半徑 radius_turn = 0 旋轉得到 TURN = 200 或 TURN = -200,這也是OK 的。

    現在,我們回到真正的測試。以 angle = 90° 和 radius_turn = 0.5 m 旋轉。在我的情況中,由上面的計算給出 STEP = 2358 和 TURN = 23,這將得到命令:

    ---------------------------------------------------------- \ len \ cnt \ty\ hd \op\la\no\sp\tu\ step \br\op\la\no\ ---------------------------------------------------------- 0x|11:00|2A:00|80|00:00|B0|00|09|14|17|82:36:09|01|A6|00|09| ---------------------------------------------------------- \ 17 \ 42 \no\ 0,0 \O \0 \A \20\23\ 2358 \1 \O \0 \A \ \ \ \ \ \u \ \+ \ \ \ \ \u \ \+ \ \ \ \ \ \t \ \D \ \ \ \ \t \ \D \ \ \ \ \ \p \ \ \ \ \ \ \p \ \ \ \ \ \ \ \u \ \ \ \ \ \ \u \ \ \ \ \ \ \ \t \ \ \ \ \ \ \t \ \ \ \ \ \ \ \_ \ \ \ \ \ \ \_ \ \ \ \ \ \ \ \S \ \ \ \ \ \ \S \ \ \ \ \ \ \ \t \ \ \ \ \ \ \t \ \ \ \ \ \ \ \e \ \ \ \ \ \ \a \ \ \ \ \ \ \ \p \ \ \ \ \ \ \r \ \ \ \ \ \ \ \_ \ \ \ \ \ \ \t \ \ \ \ \ \ \ \S \ \ \ \ \ \ \ \ \ \ \ \ \ \ \y \ \ \ \ \ \ \ \ \ \ \ \ \ \ \n \ \ \ \ \ \ \ \ \ \ \ \ \ \ \c \ \ \ \ \ \ \ \ \ \ ----------------------------------------------------------

    確實,我的小車幾乎以半徑 0.5 m 移動了完美的四分之圓。

    增強類 TwoWheelVehicle

    數學已經足夠了,至少在目前,讓我們的編碼吧!作為我們的第一個任務,我們修改類 TwoWheelVehicle 的構造函數。我們添加兩個尺寸 radius_wheel 和 tread,它們被確定為必要的:

    def __init__(self,radius_wheel: float,tread: float,protocol: str=None,host: str=None,ev3_obj: ev3.EV3=None):super().__init__(protocol=protocol, host=host, ev3_obj=ev3_obj)self._radius_wheel = radius_wheelself._tread = treadself._polarity = 1self._port_left = ev3.PORT_Dself._port_right = ev3.PORT_A

    接下來我們編寫方法 _drive,它與方法 move 非常接近。調用者傳入參數 speed,turn 和 step 調用它。外部的世界用 radius_turn 和 angle 來思考,但它們必須被轉為內部的參數 turn 和 step。這是說,方法 drive_straight 和 drive_turn 執行轉換,然后它們調用內部的方法 _drive:

    def _drive(self, speed: int, turn: int, step: int) -> bytes:assert isinstance(speed, int), "speed needs to be an integer value"assert -100 <= speed and speed <= 100, "speed needs to be in range [-100 - 100]"if self._polarity == -1:speed *= -1if self._port_left < self._port_right:turn *= -1ops_ready = b''.join([ev3.opOutput_Ready,ev3.LCX(0), # LAYERev3.LCX(self._port_left + self._port_right) # NOS])ops_start = b''.join([ev3.opOutput_Step_Sync,ev3.LCX(0), # LAYERev3.LCX(self._port_left + self._port_right), # NOSev3.LCX(speed),ev3.LCX(turn),ev3.LCX(step),ev3.LCX(0), # BRAKEev3.opOutput_Start,ev3.LCX(0), # LAYERev3.LCX(self._port_left + self._port_right) # NOS])if self._sync_mode == ev3.SYNC:return self.send_direct_cmd(ops_start + ops_ready)else:return self.send_direct_cmd(ops_ready + ops_start)

    我們區分 SYNC 和 ASYNC 或 STD。在 ASYNC 或 STD 的情況中,我們在移動開始前等待,在 SYNC 的情況中直到它結束。如果你從 ev3-python3 下載了模塊 ev3_vehicle.py,你將找不到方法 _drive。這是一個提示,我們將回到類 TwoWheelVehicle 實現中斷。我們編寫方法 drive_turn 的代碼:

    def drive_turn(self,speed: int,radius_turn: float,angle: float=None,right_turn: bool=False) -> None:assert isinstance(radius_turn, numbers.Number), "radius_turn needs to be a number"assert angle is None or isinstance(angle, numbers.Number), "angle needs to be a number"assert angle is None or angle > 0, "angle needs to be positive"assert isinstance(right_turn, bool), "right_turn needs to be a boolean"rad_right = radius_turn + 0.5 * self._treadrad_left = radius_turn - 0.5 * self._treadif radius_turn >= 0 and not right_turn:turn = round(100*(1 - rad_left / rad_right))else:turn = - round(100*(1 - rad_right / rad_left))if turn == 0:raise ValueError("radius_turn is too large")if angle is None:self.move(speed, turn)else:if turn > 0:step = round(angle*rad_right / self._radius_wheel)else:step = - round(angle*rad_left / self._radius_wheel)self._drive(speed, turn, step)

    非常大的 radius_turn 值將導致一些問題。舍入到整數之后,它們導致直行。在這種情況下我們拋出一個錯誤。

    方法 drive_straight:

    def drive_straight(self, speed: int, distance: float=None) -> None:assert distance is None or isinstance(distance, numbers.Number), \"distance needs to be a number"assert distance is None or distance > 0, \"distance needs to be positive"if distance is None:self.move(speed, 0)else:step = round(distance * 360 / (2 * math.pi * self._radius_wheel))self._drive(speed, 0, step)

    放松,我們已經實現了一種可以預測車輛的工具。 請做一些測試!

    了解車輛的位置和方向

    抱歉,之前的一些內容我寫了 足夠的數學知識,但現在我來到了三角學。但我們處于一種真的值得努力的情境。想象一下,你駕駛你的車并經過了一系列的移動,你需要知道它的位置和方向。我們無需使用傳感器就可以做到。相反,我們使用純數學而不是魔法。

    讓我們做一些假設:

    • 當你創建類 TwoWheelVehicle 時你的車放置的位置將是你的坐標系統的原點 (0, 0)。
    • 此時指向正前方的方向是 x 軸的方向。
    • y 軸指向你的車的左手邊。
    • 你的車的位置由 x 和 y 坐標描述。以米為單位。
    • 你的車的 orientation 是車的原始方向和它的實際方向的差值。以度數為單位。左轉增加 orientation,右轉減小它。

    這次,我不會介紹數學。把它當作原樣,或者把它作為一個謎語,你必須解決。但是請把如下的邏輯添加到你的類 TwoWheelVehicle 中:

    • 構造函數:
    self._orientation = 0.0self._pos_x = 0.0self._pos_y = 0.0
    • drive_straight(speed, distance)
    diff_x = distance * math.cos(math.radians(self._orientation))diff_y = distance * math.sin(math.radians(self._orientation))if speed > 0:self._pos_x += diff_xself._pos_y += diff_yelse:self._pos_x -= diff_xself._pos_y -= diff_y
    • drive_turn(speed, radius_turn, angle)
    angle += 180angle %= 360angle -= 180fact = 2.0 * radius_turn * math.sin(math.radians(0.5 * angle))self._orientation += 0.5 * angleself._pos_x += fact * math.cos(math.radians(self._orientation))self._pos_y += fact * math.sin(math.radians(self._orientation))self._orientation += 0.5 * angleself._orientation += 180self._orientation %= 360self._orientation -= 180

    完成類 TwoWheelVehicle

    我們通過添加更多的功能來完成類 TwoWheelVehicle:

    • 我們添加一個方法 rotate_to(speed: int, o: float),它做如下的事情:

      • 計算實際的和新的方向的距離
      • 以 radius_turn = 0 調用 drive_turn 旋轉小車,以使其獲得新方向。
    • 我們添加一個方法 drive_to(self, speed: int, x: float, y: float),它做如下的事情:

      • 計算實際的位置和新位置的距離:
    diff_x = pos_x - self._pos_xdiff_y = pos_y - self._pos_y

    我們需要坐標和絕對值:

    distance = math.sqrt(diff_x**2 + diff_y**2) - 計算新位置的方向。這很棘手,你需要徹底了解三角學,因為你必須使用 `atan`,`tan` 的反函數。我給你一個提示: if abs(diff_x) > abs(diff_y):direction = math.degrees(math.atan(diff_y/diff_x))else:fract = diff_x / diff_ysign = math.copysign(1.0, fract)direction = sign * 90 - math.degrees(math.atan(fract))if diff_x < 0:direction += 180direction %= 360 - 調用 `rotate_to`,以使 `orientation` 指向 `direction`。 - 調用 `drive_straight` 移動小車到新的位置。

    我們做一些測試:

    • 我們把小車發送到一些循環行程中,并編碼一系列 drive_to,最終在 position = (0,0) 結束。我們最后添加一個以 orientation = 0 對 rotate_to 的調用。請評估,小車是否真的回到了它最初的位置和方向。
    • 我們給循環行程添加一些 drive_turn 并評估,drive_turn 的錯誤是大于還是小于 drive_to 的。

    以大 radius_turn 旋轉將有一個糟糕的精度。這也是舍入的結果。在這種情況中,將 TURN 舍入為整數值創造了錯誤。

    如果你從 ev3-python3 下載了 ev3_vehicle 模塊,當你調用它的方法 drive_straight,drive_turn,rotate_to 或 drive_to 時,你最后需要添加一個對 stop 的方法調用!

    異步和同步運動

    讓我們仔細看看我們的車輛駕駛情況。這是我的循環行程的程序:

    #!/usr/bin/env python3import ev3, ev3_vehiclemy_vehicle = ev3_vehicle.TwoWheelVehicle(0.02128, 0.1346, protocol=ev3.BLUETOOTH, host='00:16:53:42:2B:99') my_vehicle.verbosity = 1 speed = 25 my_vehicle.drive_straight(speed, 0.05) my_vehicle.drive_turn(speed, -0.07, 65) my_vehicle.drive_straight(speed, 0.35) my_vehicle.drive_turn(speed, 0.20, 140) my_vehicle.drive_straight(speed, 0.15) my_vehicle.drive_turn(speed, -1.10, 55) my_vehicle.drive_turn(speed, 0.35, 160) my_vehicle.drive_to(speed, 0.0, 0.0) my_vehicle.rotate_to(speed, 0.0)

    這個程序的輸出:

    15:42:05.989592 Sent 0x|14:00|2A:00|80|00:00|AA:00:09:B0:00:09:19:00:82:87:00:00:A6:00:09| 15:42:05.990443 Sent 0x|15:00|2B:00|80|00:00|AA:00:09:B0:00:09:19:81:9E:82:A3:01:00:A6:00:09| 15:42:05.990925 Sent 0x|14:00|2C:00|80|00:00|AA:00:09:B0:00:09:19:00:82:AE:03:00:A6:00:09| 15:42:05.991453 Sent 0x|15:00|2D:00|80|00:00|AA:00:09:B0:00:09:19:81:32:82:DF:06:00:A6:00:09| 15:42:05.991882 Sent 0x|14:00|2E:00|80|00:00|AA:00:09:B0:00:09:19:00:82:94:01:00:A6:00:09| 15:42:05.992330 Sent 0x|14:00|2F:00|80|00:00|AA:00:09:B0:00:09:19:34:82:C9:0B:00:A6:00:09| 15:42:05.992766 Sent 0x|15:00|30:00|80|00:00|AA:00:09:B0:00:09:19:81:20:82:42:0C:00:A6:00:09| 15:42:05.993306 Sent 0x|16:00|31:00|80|00:00|AA:00:09:B0:00:09:19:82:38:FF:82:D0:01:00:A6:00:09| 15:42:05.993714 Sent 0x|14:00|32:00|80|00:00|AA:00:09:B0:00:09:19:00:82:3F:03:00:A6:00:09| 15:42:05.994202 Sent 0x|15:00|33:00|80|00:00|AA:00:09:B0:00:09:19:82:38:FF:81:69:00:A6:00:09|

    在五毫秒內這個程序給 EV3 設備發送了所有的直接命令,它們被放入隊列并等待執行。這個代碼很簡單,但是會阻塞 EV3 設備直到開始執行最后的命令。請遵循代碼并反映出屬性 pos_x,pos_y 和 orientation 的值,以及它們是否與我們車輛的實際位置和方向相對應。

    這是異步行為! 程序和 EV3 設備在不同的時間尺度上運行。

    現在我們改為同步模式并比較行為:

    #!/usr/bin/env python3import ev3, ev3_vehiclemy_vehicle = ev3_vehicle.TwoWheelVehicle(0.02128, 0.1346, protocol=ev3.BLUETOOTH, host='00:16:53:42:2B:99') my_vehicle.verbosity = 1 speed = 25 my_vehicle.sync_mode = ev3.SYNC my_vehicle.drive_straight(speed, 0.05) my_vehicle.drive_turn(speed, -0.07, 65) my_vehicle.drive_straight(speed, 0.35) my_vehicle.drive_turn(speed, 0.20, 140) my_vehicle.drive_straight(speed, 0.15) my_vehicle.drive_turn(speed, -1.10, 55) my_vehicle.drive_turn(speed, 0.35, 160) my_vehicle.drive_to(speed, 0.0, 0.0) my_vehicle.rotate_to(speed, 0.0)

    此版本產生以下輸出:

    15:46:19.859532 Sent 0x|14:00|2A:00|00|00:00|B0:00:09:19:00:82:87:00:00:A6:00:09:AA:00:09| 15:46:20.307045 Recv 0x|03:00|2A:00|02| 15:46:20.307760 Sent 0x|15:00|2B:00|00|00:00|B0:00:09:19:81:9E:82:A3:01:00:A6:00:09:AA:00:09| 15:46:22.100007 Recv 0x|03:00|2B:00|02| 15:46:22.100612 Sent 0x|14:00|2C:00|00|00:00|B0:00:09:19:00:82:AE:03:00:A6:00:09:AA:00:09| 15:46:24.597018 Recv 0x|03:00|2C:00|02| 15:46:24.597646 Sent 0x|15:00|2D:00|00|00:00|B0:00:09:19:81:32:82:DF:06:00:A6:00:09:AA:00:09| 15:46:29.141999 Recv 0x|03:00|2D:00|02| 15:46:29.142609 Sent 0x|14:00|2E:00|00|00:00|B0:00:09:19:00:82:94:01:00:A6:00:09:AA:00:09| 15:46:30.221994 Recv 0x|03:00|2E:00|02| 15:46:30.222626 Sent 0x|14:00|2F:00|00|00:00|B0:00:09:19:34:82:C9:0B:00:A6:00:09:AA:00:09| 15:46:44.779922 Recv 0x|03:00|2F:00|02| 15:46:44.780364 Sent 0x|15:00|30:00|00|00:00|B0:00:09:19:81:20:82:42:0C:00:A6:00:09:AA:00:09| 15:46:46.938901 Recv 0x|03:00|30:00|02| 15:46:46.939701 Sent 0x|16:00|31:00|00|00:00|B0:00:09:19:82:38:FF:82:D0:01:00:A6:00:09:AA:00:09| 15:46:55.695901 Recv 0x|03:00|31:00|02| 15:46:55.696508 Sent 0x|14:00|32:00|00|00:00|B0:00:09:19:00:82:3F:03:00:A6:00:09:AA:00:09| 15:47:05.061856 Recv 0x|03:00|32:00|02| 15:47:05.062504 Sent 0x|15:00|33:00|00|00:00|B0:00:09:19:82:38:FF:81:69:00:A6:00:09:AA:00:09| 15:47:05.097692 Recv 0x|03:00|33:00|02|

    車輛的運動是相同的,但現在程序發送一個直接命令并等待它完成,然后它發送下一個。sync_mode = SYNC 如它設計那樣工作,它同步程序和EV3 設備的時間尺度。另一個好處是,它控制每個直接命令的成功并直接作出反應,如果出現意外情況。

    這兩個版本(異步和同步)都會阻塞 EV3 設備。

    結論

    類 TwoWheelVehicle 通過兩個驅動輪控制小車的運動。如果我們知道課程的幾何形狀,我們就可以編寫一個程序,通過它來驅動我們的車輛。

    我們已經看到了同步和異步模式之間的區別。目前,我們更傾向于 SYNC。但我們的目標是一個解決方案,它可以同步驅動車輛并且不會阻塞EV3 設備。這將打開多任務的大門。

    我的 EV3TwoWheelVehicle 類實際上有以下狀態:

    Help on module ev3_vehicle:NAMEev3_vehicle - EV3 vehicleCLASSESev3.EV3(builtins.object)TwoWheelVehicleclass TwoWheelVehicle(ev3.EV3)| ev3.EV3 vehicle with two drived Wheels| | Method resolution order:| TwoWheelVehicle| ev3.EV3| builtins.object| | Methods defined here:| | __init__(self, radius_wheel:float, tread:float, protocol:str=None, host:str=None, ev3_obj:ev3.EV3=None)| Establish a connection to a LEGO EV3 device| | Arguments:| radius_wheel: radius of the wheels im meter| tread: the vehicles tread in meter| | Keyword Arguments (either protocol and host or ev3_obj):| protocol| BLUETOOTH == 'Bluetooth'| USB == 'Usb'| WIFI == 'Wifi'| host: mac-address of the LEGO EV3 (f.i. '00:16:53:42:2B:99')| ev3_obj: an existing EV3 object (its connections will be used)| | drive_straight(self, speed:int, distance:float=None) -> None| Drive the vehicle straight forward or backward.| | Attributes:| speed: in percent [-100 - 100] (direction depends on its sign)| positive sign: forwards| negative sign: backwards| | Keyword Attributes:| distance: in meter, needs to be positive| if None, unlimited movement| | drive_to(self, speed:int, pos_x:float, pos_y:float) -> None| Drive the vehicle to the given position.| | Attributes:| speed: in percent [-100 - 100] (direction depends on its sign)| positive sign: forwards| negative sign: backwards| x: x-coordinate of target position| y: y-coordinate of target position| | drive_turn(self, speed:int, radius_turn:float, angle:float=None, right_turn:bool=False) -> None| Drive the vehicle a turn with given radius.| | Attributes:| speed: in percent [-100 - 100] (direction depends on its sign)| positive sign: forwards| negative sign: backwards| radius_turn: in meter| positive sign: turn to the left side| negative sign: turn to the right side| | Keyword Attributes:| angle: absolute angle (needs to be positive)| if None, unlimited movement| right_turn: flag of turn right (only in case of radius_turn == 0)| | move(self, speed:int, turn:int) -> None| Start unlimited movement of the vehicle| | Arguments:| speed: speed in percent [-100 - 100]| > 0: forward| < 0: backward| turn: type of turn [-200 - 200]| -200: circle right on place| -100: turn right with unmoved right wheel| 0 : straight| 100: turn left with unmoved left wheel| 200: circle left on place| | rotate_to(self, speed:int, orientation:float) -> None| Rotate the vehicle to the given orientation.| Chooses the direction with the smaller movement.| | Attributes:| speed: in percent [-100 - 100] (direction depends on its sign)| orientation: in degrees [-180 - 180]| | stop(self, brake:bool=False) -> None| Stop movement of the vehicle| | Arguments:| brake: flag if activating brake| | ----------------------------------------------------------------------| Data descriptors defined here:| | orientation| actual orientation of the vehicle in degree, range [-180 - 180]| | polarity| polarity of motor rotation (values: -1, 1, default: 1)| | port_left| port of left wheel (default: PORT_D)| | port_right| port of right wheel (default: PORT_A)| | pos_x| actual x-component of the position in meter| | pos_y| actual y-component of the position in meter| | ----------------------------------------------------------------------| Methods inherited from ev3.EV3:| | __del__(self)| closes the connection to the LEGO EV3| | send_direct_cmd(self, ops:bytes, local_mem:int=0, global_mem:int=0) -> bytes| Send a direct command to the LEGO EV3| | Arguments:| ops: holds netto data only (operations), the following fields are added:| length: 2 bytes, little endian| counter: 2 bytes, little endian| type: 1 byte, DIRECT_COMMAND_REPLY or DIRECT_COMMAND_NO_REPLY| header: 2 bytes, holds sizes of local and global memory| | Keyword Arguments:| local_mem: size of the local memory| global_mem: size of the global memory| | Returns: | sync_mode is STD: reply (if global_mem > 0) or message counter| sync_mode is ASYNC: message counter| sync_mode is SYNC: reply of the LEGO EV3| | wait_for_reply(self, counter:bytes) -> bytes| Ask the LEGO EV3 for a reply and wait until it is received| | Arguments:| counter: is the message counter of the corresponding send_direct_cmd| | Returns:| reply to the direct command| | ----------------------------------------------------------------------| Data descriptors inherited from ev3.EV3:| | __dict__| dictionary for instance variables (if defined)| | __weakref__| list of weak references to the object (if defined)| | sync_mode| sync mode (standard, asynchronous, synchronous)| | STD: Use DIRECT_COMMAND_REPLY if global_mem > 0,| wait for reply if there is one.| ASYNC: Use DIRECT_COMMAND_REPLY if global_mem > 0,| never wait for reply (it's the task of the calling program).| SYNC: Always use DIRECT_COMMAND_REPLY and wait for reply.| | The general idea is:| ASYNC: Interruption or EV3 device queues direct commands,| control directly comes back.| SYNC: EV3 device is blocked until direct command is finished,| control comes back, when direct command is finished. | STD: NO_REPLY like ASYNC with interruption or EV3 queuing,| REPLY like SYNC, synchronicity of program and EV3 device.| | verbosity| level of verbosity (prints on stdout).

    我希望,你感覺到,我們現在正在做實際的事情。 對我來說,這是一個亮點。 只有在極少數情況下,人們才能用這么少的努力獲得這么多。

    原文

    總結

    以上是生活随笔為你收集整理的EV3 直接命令 - 第 4 课 用两个驱动轮精确地移动小车的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: 亚洲精品aⅴ | 亚洲精品一区二区在线 | 黄色免费网站在线观看 | 亚洲激情视频小说 | 亚洲免费二区 | 黄色视屏软件 | 外国黄色网址 | 香港三级韩国三级日本三级 | 久久精品这里只有精品 | 成人在线一区二区三区 | 公交上高潮的丁芷晴 | 美国一级黄色大片 | 国产精品午夜无码专区 | 中文字幕色 | av男人资源 | 草视频在线 | 不卡日韩| 欧美日韩一区二区三区在线观看 | 亚洲不卡电影 | 秋霞福利视频 | 18在线观看免费入口 | 老女人做爰全过程免费的视频 | 天天插天天狠天天透 | 国产日韩视频在线观看 | 在线观看黄色动漫 | 在线播放国产一区 | 色鬼久久 | 欧美在线播放一区 | 视频一区亚洲 | 丰满人妻熟女aⅴ一区 | 免费毛片网站 | 少妇特黄一区二区三区 | 特黄特色大片免费 | 亚洲精品无码久久久久久久 | 18成人免费观看网站 | 永久免费网站直接看 | 久久99这里只有精品 | 嫩模一区 | 免费a v网站| 伊人久久大香 | 日本在线视频免费 | 久久久精品视频网站 | 欧美性插动态图 | 亚洲高清一区二区三区 | 3d动漫精品啪啪一区二区三区免费 | 日韩 国产 欧美 | 性色一区二区三区 | 欧美综合视频在线 | 教练含着她的乳奶揉搓揉捏动态图 | 亚洲综合情 | 日韩中文在线一区 | 妖精视频在线观看免费 | 一起操网址| 国产夫妻视频 | 欧美偷拍第一页 | 欧美日韩福利视频 | 青青草视频免费观看 | 欧美一区二三区 | 欧美另类tv| 国产亚洲精品aaaaaaa片 | 强行挺进皇后紧窄湿润小说 | 大陆av片| av在线www| 青青操av在线 | 欧美日韩免费做爰视频 | www国产精品内射老熟女 | 1024国产精品| 婷婷成人综合网 | 日韩手机在线视频 | 色婷婷综合五月 | 亚洲精品视频在线 | 久久久视频在线 | 欧美精品黄 | 日韩精品影院 | 性色影院| 黄色视屏免费 | 欧美色偷偷 | 国产在线视频二区 | 特黄a级片 | 西野翔夫の目の前で犯在线 | 国产又白又嫩又爽又黄 | 天堂在线国产 | 交专区videossex农村 | 成人动漫在线观看 | 国产高潮流白浆喷水视频 | 亚洲成人精品 | √天堂8资源中文在线 | 综合亚洲网 | 欧美日韩国产三区 | 黄色日韩 | 无人在线观看高清视频 | 他揉捏她两乳不停呻吟动态图 | 中文在线а√在线 | 日本一区二区免费电影 | 人人舔人人干 | 白丝av | 亚洲乱子伦 | 蜜桃久久av一区 | 在线观看一区二区三区四区 |