友情提示:380元/半年,儿童学编程,就上码丁实验室。
EV3: Lesson 8 – Control Sensors
EV3: 第 8 课 – 控制传感器
8.1 Control Sensor by Object Oriented Programs
8.1 使用面向对象程序控制传感器
The program should explain itself, read carefully about the comments.
程序已经很清楚的解释自己了,小心的阅读注释。
Before you run the programs, attach 2 large motors to port A and B, attach a touch sensor to port 1, and attach a Ultrasonic sensor to port 2.
运行程序之前,在端口 A 及 B 安装两个 大马达,在端口 1 安装一个【按键传感器】,在端口 2 安装一个 【超声波传感器】。
Create and copy the following codes to the corresponding programs:
新建并拷贝下面的程序:
from ev3dev.ev3 import *
from time import sleep, time
from importlib import *
import g
import traceback
class SENSORPARM:
intNoOfSensor = 0
# The type of sensor:
# "T" = Touch sensor
# "C" = Color sensor
# "U" = Ultrasonic sensor
# "I" = Infrared sensor
# "G" = Gyro sensor
gastrSType = []
# The port of sensor: Port "1" - "4"
gastrSPort = []
# The Mode of sensor:
# Color Sensor: "COL-COLOR" for Color, "COL-REFLECT" for Reflection, "COL-AMBIENT" for Ambient
# Touch Sensor: "TOUCH"
# Ultrasonic sensor: "US-DIST-CM" for cm
# Infrared sensor: "IR-PROX"
# Gyro sensor: "GYRO-ANG"
gastrSMode = []
class MOTORPARM:
intNoOfMotor = 0
# The type of motor:
# "L" = Large motor,
# "M" = Medium motor
gastrMType = []
# The port of motor: Port "A" - "D"
gastrMPort = []
# The movement direction of motor: either 1 or -1
gaintMDir = []
# The fastest speed of motor: Large Motor: 0 - 900, Medium Motor: 0 - 1500
gaintMDefSpeed = []
# L means robot uses this motor to turn left
# R means robot uses this motor to turn right
# N special motor
gastrLRN = []
# Radius for wheel
gafloRadius = []
class ROBOT:
lcd = Screen()
btn = Button()
gM = []
gS = []
def __init__(self, mtp, ssp):
global module
module = __import__(g.module)
# Define sensors
try:
if (ssp.intNoOfSensor > 0):
i = 1
while i <= ssp.intNoOfSensor:
if (ssp.gastrSType[i - 1] == "T"):
# Define Touch Sensor
self.gS.insert(i - 1, TouchSensor("in" + ssp.gastrSPort[i - 1]))
elif (ssp.gastrSType[i - 1] == "C"):
# Define Color Sensor
self.gS.insert(i - 1, ColorSensor("in" + ssp.gastrSPort[i - 1]))
elif (ssp.gastrSType[i - 1] == "U"):
# Define Ultrasonic Sensor
self.gS.insert(i - 1, UltrasonicSensor("in" + ssp.gastrSPort[i - 1]))
elif (ssp.gastrSType[i - 1] == "I"):
# Define Infrared Sensor
self.gS.insert(i - 1, InfraredSensor("in" + ssp.gastrSPort[i - 1]))
elif (ssp.gastrSType[i - 1] == "G"):
# Define Gyro Sensor
self.gS.insert(i - 1, GyroSensor("in" + ssp.gastrSPort[i - 1]))
intStartTime = time()
bolDefineSensor = False
while not bolDefineSensor:
intEndTime = time()
try:
assert self.gS[i - 1].connected, "Connect a sensor to port " + ssp.gastrSPort[i - 1]
bolDefineSensor = True
if (intEndTime - intStartTime > 1):
self.DefineSErr(ssp.gastrSPort[i - 1])
except:
bolDefineSensor = False
sleep(0.5)
intStartTime = time()
bolDefineSensor = False
while not bolDefineSensor:
intEndTime = time()
try:
self.gS[i - 1].mode = ssp.gastrSMode[i - 1]
bolDefineSensor = True
if (intEndTime - intStartTime > 1):
self.DefineSErr(ssp.gastrSPort[i - 1])
except:
bolDefineSensor = False
sleep(0.1)
# Next Sensor
i = i + 1
sleep(0.1)
except:
# If there is any error, show a log file with prefex e_m + "A/B/C/D" to show which
# port's motor is not connected
self.DefineSErr(ssp.gastrSPort[i - 1])
# Define motors
try:
if (mtp.intNoOfMotor > 0):
i = 1
while i <= mtp.intNoOfMotor:
if (mtp.gastrMType[i - 1] == "L"):
self.gM.insert(i - 1, LargeMotor('out' + mtp.gastrMPort[i - 1]))
else:
self.gM.insert(i - 1, MediumMotor('out' + mtp.gastrMPort[i - 1]))
i = i + 1
sleep(0.1)
i = 1
while i <= mtp.intNoOfMotor:
self.MoveMotor(mtp, i, "F", 1, 100, 0)
sleep(0.01)
self.StopMotor(i)
i = i + 1
except:
# If there is any error, show a log file with prefex e_m + "A/B/C/D" to show which
# port's motor is not connected
logtime = str(time())
f=open("e_m" + mtp.gastrMPort[i - 1] + logtime + ".txt",'a')
traceback.print_exc(file=f)
f.flush()
f.close()
sys.exit()
def DefineSErr(strPort):
logtime = str(time())
f=open("e_s" + strPort + logtime + ".txt",'a')
traceback.print_exc(file=f)
f.flush()
f.close()
sys.exit()
# LCD & Buttons Related Methods
# The following method display a Menu on the lcd screen and wait for user selection
# ShowMenu() Begin
def ShowMenu(self, strMenuTitle, listMenu, listFunction):
global module
intSelect = 1
intNoOfLine = len(listMenu)
bolSelect = False
bolPressed = False
# Continue to loop until the user make a selection
while not bolSelect:
self.lcd.clear()
intX = 5
intY = 5
# Display title
self.lcd.draw.text((intX, intY), strMenuTitle)
# Display a line after the title, to separate the title and the content
self.lcd.draw.line((0, intY + 12, 177, intY + 12), fill = None, width = 0)
intY = intY + 5
# Display all lines
i = 1
while i <= intNoOfLine:
intY = intY + 15
if i == intSelect:
# If the cursor is on this line, reverse the color of this line
self.lcd.draw.rectangle((0, intY, 177, intY + 10), fill = 'black')
self.lcd.draw.text((intX, intY), listMenu[i - 1], fill = 'white')
else:
# If the cursor is not on this line, display the text on this line
self.lcd.draw.text((intX, intY), listMenu[i - 1])
i = i + 1
# Show the screen
self.lcd.update()
if bolPressed:
sleep(0.5)
bolPressed = False
# if bolWait == True, continue waiting for key press event
bolWait = True
while bolWait:
if self.btn.up:
# if Up is pressed
bolPressed = True
bolWait = False
# Cursor go up 1 line
intSelect = intSelect - 1
if intSelect == 0:
# if Cursor at line 0, set Cursor to last line
intSelect = intNoOfLine
elif self.btn.down:
# if Down is pressed
bolPressed = True
bolWait = False
# Cursor go down 1 line
intSelect = intSelect + 1
if intSelect > intNoOfLine:
intSelect = 1
elif self.btn.left:
# if Left is pressed, do nothing
pass
elif self.btn.right:
# if Right is pressed, do nothing
pass
elif self.btn.enter:
# if Enter is pressed, exit loop
bolWait = False
bolSelect = True
else:
# Sleep for 0.05 seconds to detect another key press
sleep(0.05)
# Important to sleep here, otherwise the btn.enter will be catched by another menu
sleep(0.5)
# From here User has made its selection, do the function according to user selection
func = getattr(module, listFunction[intSelect-1])
func()
# ShowMenu() End
# Show a Single String at intX, intY
# bolClear = True means Clear the screen before display string
def DisplaySingleString(self, intX, intY, strMessage, bolClear):
if (bolClear):
self.lcd.clear()
self.lcd.draw.text((intX, intY), strMessage)
# Show the screen
self.lcd.update()
# DisplayLCD() End
# Display Whole String at 0,0 until screen full, clear screen first!
def DisplayWholeString(self, strMessage):
intCharPerLine = 29
self.lcd.clear()
bolFinish = False
strLeft = strMessage
intX = 0
intY = 0
while (not bolFinish):
if (len(strLeft) <= intCharPerLine):
bolFinish = True
strCurLine = strLeft
else:
strCurLine = strLeft[:intCharPerLine]
strLeft = strLeft[-(len(strLeft)-intCharPerLine):]
self.lcd.draw.text((intX,intY), strCurLine)
intY += 12
if (intY >= 116):
bolFinish = True
self.lcd.update()
# Motor Related Methods
# Move a single motor
def MoveMotor(self, mtp, intMotorNo, strType, intDir, intSpeed, intValue):
if (strType == "F"):
# Move a Motor Forever
self.gM[intMotorNo - 1].run_forever(speed_sp=intSpeed * intDir * mtp.gaintMDir[intMotorNo - 1])
elif (strType == "A"):
# Move a Motor a certain Degree intValue
self.gM[intMotorNo - 1].run_to_rel_pos(position_sp=intValue * mtp.gaintMDir[intMotorNo - 1] * intDir, speed_sp=intSpeed, stop_action="hold")
elif (strType == "DA"):
# Move wheel ? Distance cm calculated by movement of motor Angle
self.gM[intMotorNo - 1].run_to_rel_pos(position_sp=intValue * mtp.gaintMDir[intMotorNo - 1] * intDir * 360 / (2 * 3.141592654 * mtp.gafloRadius[intMotorNo - 1]) , speed_sp=intSpeed, stop_action="hold")
# MoveMotor End
# Stop a single motor
def StopMotor(self, intMotorNo):
self.gM[intMotorNo - 1].stop(stop_action="hold")
# StopMotor End
# Move Robot
# floSpeedFL -1.0 to 1.0
def MoveRobot(self, mtp, strType, floSpeedFL, floSpeedFR, intValue):
floMaxSF = 0
if (abs(floSpeedFL) > abs(floSpeedFR)):
floMaxSF = abs(floSpeedFL)
else:
floMaxSF = abs(floSpeedFR)
intMaxSpeed = 0
floMaxRadius = 0
intDir = 0
if (strType == "F"):
# Move Robot Forever
for i in range(1,mtp.intNoOfMotor + 1):
if (mtp.gastrLRN[i - 1] == "L"):
if (floSpeedFL > 0):
self.MoveMotor(mtp, i, strType, 1, mtp.gaintMDefSpeed[i - 1] * floSpeedFL, 0)
else:
self.MoveMotor(mtp, i, strType, -1, mtp.gaintMDefSpeed[i - 1] * floSpeedFL * -1 , 0)
if (mtp.gastrLRN[i - 1] == "R"):
if (floSpeedFR > 0):
self.MoveMotor(mtp, i, strType, 1, mtp.gaintMDefSpeed[i - 1] * floSpeedFR, 0)
else:
self.MoveMotor(mtp, i, strType, -1, mtp.gaintMDefSpeed[i - 1] * floSpeedFR * -1, 0)
elif (strType == "A" or strType == "DA"):
# "A" - Move Robot a Certain Degree,
# "DA" - Move Certain Distance in cm calculated by Degree and Radius "DA"
for i in range(1,mtp.intNoOfMotor + 1):
if (mtp.gaintMDefSpeed[i - 1] > intMaxSpeed):
intMaxSpeed = mtp.gaintMDefSpeed[i - 1]
if (mtp.gafloRadius[i - 1] > floMaxRadius):
floMaxRadius = mtp.gafloRadius[i - 1]
if (mtp.gastrLRN[i - 1] == "L"):
if (floSpeedFL > 0):
self.MoveMotor(mtp, i, strType, 1, mtp.gaintMDefSpeed[i - 1] * floSpeedFL, intValue)
else:
self.MoveMotor(mtp, i, strType, -1, mtp.gaintMDefSpeed[i - 1] * floSpeedFL * -1, intValue)
if (mtp.gastrLRN[i - 1] == "R"):
if (floSpeedFR > 0):
self.MoveMotor(mtp, i, strType, 1, mtp.gaintMDefSpeed[i - 1] * floSpeedFR, intValue)
else:
self.MoveMotor(mtp, i, strType, -1, mtp.gaintMDefSpeed[i - 1] * floSpeedFR * -1, intValue)
intMaxSpeed = intMaxSpeed * floMaxSF
intSleepSecond = 0
if (strType == "A"):
intSleepSecond = intValue/720*900/intMaxSpeed
elif (strType == "DA"):
intSleepSecond = intValue/720*900/intMaxSpeed*360/2/3.141592654/floMaxRadius
sleep(intSleepSecond)
# Move robot End
# Stop Robot
def StopRobot(self):
for i in range(1,mtp.intNoOfMotor + 1):
if (mtp.gastrLRN[i - 1] == "L" or mtp.gastrLRN[i - 1] == "R"):
self.gM[i - 1].stop(stop_action="hold")
# StopRobot End
http://www.zephan.top/ev3pythonlessons/lesson8/robot.py
上面这个是 roboy.py
import robot
def funInitRobot():
global rb, module, mtp, ssp
module = 'fun'
mtp = robot.MOTORPARM()
mtp.intNoOfMotor = 2
ssp = robot.SENSORPARM()
ssp.intNoOfSensor = 2
# Sensor 1
ssp.gastrSType.insert(0, "T")
ssp.gastrSPort.insert(0, "1")
ssp.gastrSMode.insert(0, "TOUCH")
# Sensor 2
ssp.gastrSType.insert(1, "U")
ssp.gastrSPort.insert(1, "2")
ssp.gastrSMode.insert(1, "US-DIST-CM")
# Motor 1
mtp.gastrMType.insert(0, "L")
mtp.gastrMPort.insert(0, "A")
mtp.gaintMDir.insert(0, 1)
mtp.gaintMDefSpeed.insert(0, 900)
mtp.gastrLRN.insert(0, "L")
mtp.gafloRadius.insert(0, 3.0)
# Motor 2
mtp.gastrMType.insert(1, "L")
mtp.gastrMPort.insert(1, "B")
mtp.gaintMDir.insert(1, 1)
mtp.gaintMDefSpeed.insert(1, 900)
mtp.gastrLRN.insert(1, "R")
mtp.gafloRadius.insert(1, 3.0)
rb = robot.ROBOT(mtp, ssp)
http://www.zephan.top/ev3pythonlessons/lesson8/g.py
上面这个是 g.py
from time import sleep, time
import g
def StartProgram():
strMenuTitle = 'Main Menu'
listMenu = ['Press to Move','Move B 20cm','Quit']
listFunction = ['funMove','funB20', 'funQuit']
g.rb.ShowMenu(strMenuTitle, listMenu, listFunction)
def funMove():
g.rb.DisplayWholeString("Program Starts")
bolExit = False
while not bolExit:
if (g.rb.gS[0].value() == 1):
g.rb.MoveRobot(g.mtp, "DA", 1, 1, 10)
if (g.rb.gS[1].value() < 200):
bolExit = True
sleep(0.1)
def funB20():
g.rb.MoveRobot(g.mtp, "DA", -1, -1, 20)
def funQuit():
pass
http://www.zephan.top/ev3pythonlessons/lesson8/fun.py
上面这个是 fun.py
#!/usr/bin/env python3
from time import sleep, time
import traceback
import g
import fun
# Initialize the LCD and Buttons
try:
g.funInitRobot()
# fun.StartProgram()
fun.StartProgram()
except:
# If there is any error, it will be stored in the log file in the same directory
logtime = str(time())
f=open("log" + logtime + ".txt",'a')
traceback.print_exc(file=f)
f.flush()
f.close()
http://www.zephan.top/ev3pythonlessons/lesson8/lesson8_01.py
上面这个是 lesson8_01.py
Upload the above 4 programs and run lesson8_01.py in the EV3 brick, press the Touch Button to move the motors, put something in front of the Ultrasonic Sensor to stop the program.
上传上面这 4 个程序,并且在 EV3 里运行 lesson8_01.py。
按【按键传感器】去启动马达。
在【超声波传感器】前面放点东西去结束程序。
From now on, the robot.py IS COMPLETED, you can now use it in different projects without changing the programs inside robot.py.
从现在开始,robot.py 这个程序已经基本完成里,你可以使用它在不同的项目里,而不需要更改里面的内容。
Declare variables in g.py so that all programs within the same project can access those variables by using:
对于某一个项目里,所有程序都需要用到的全局变量,你可以在 g.py 里去声明它,并且在其他程序里用以下方式去使用这个变量:
g.variablename
Instead of naming the Main Program as lesson8_01.py, in the future, you may name your Main Program as any name you like, for example: “main.py”
在你将来的项目里,你不一定要使用类似 lesson8_01.py 去命名你的主程序,你可以使用任何你喜欢的主程序名称,比方说 main.py。
You may also change the name for “fun.py”, codes inside this program should be modified for different projects.
你也可以改变 fun.py 这个程序名称,这个程序的内容,每一个项目应该是不一样的。
译者后记:
作者关于 ev3dev 的面向对象编程课程,暂时就只有那么多了,帮他翻译了以后,我发现他的内容还是不够丰富,对于程序的解释也不够详细,需要读者自己慢慢的去了解程序,不过对于一个只有 12 岁的作者,能写出这样的课程,还可以了!
作为作者的教练,我会建议他后面再加一些实战篇,图文并茂的解释如何用 ev3dev 编写实用程序,比方说【相扑机器人】之类的。
不过这段时间正在教他如何编写【微信】类的通讯 app,使用了 Cordova 和 Node.js,http://socket.io 等等技术,对他来说还是有一定难度,新的课程可能要稍等了,希望将来有机会和大家分享。
大家可能会问我的学生是外国人吗?为什么写的课程是英文的呢?不是的,是中国人,因为他英文菜,又要参加国际比赛,必须用英语和外国裁判沟通和解释程序,因此我是强迫他平常多用英语。这里我必须提醒一下学生们,想拿某些机器人比赛的世界冠军,英语水平占了挺大的比重的,尤其是创意赛,所有海报要英语,解释要英语,演讲要英语,问答要英语,要是裁判不明白,分数就自然低了哦!
始发于知乎专栏:ken