用 Linkbot 学习 Python 3 / 调试
- "在我们开始编程后,我们惊讶地发现,编写正确的程序并不像我们想象的那么容易。调试必须被发现。我还记得那一刻,我意识到我余生中很大一部分时间将花费在我自己程序的错误寻找上。"— 莫里斯·威尔克斯发现调试,1949 年
到目前为止,如果你一直在玩弄你的程序,你可能已经发现,有时程序会做一些你不想它做的事情。这是相当常见的。调试是找出计算机在做什么,然后让它做你想让它做的事情的过程。这可能很棘手。令人惊讶的是,花费数周时间追踪和修复一个由某人将 x
放在应该放置 y
的地方而导致的 bug 非常常见。
本章将比之前的章节更抽象。
第一步(这听起来很明显)是弄清楚程序如果正确运行应该在做什么。想出一些测试用例,看看会发生什么。
例如,让我们尝试编写一个程序,该程序让一个带有两个轮子的 Linkbot-I 前进旋转一圈,同时使 LED 变为绿色,然后让 Linkbot 向后滚动,LED 变为红色。以下是该程序的第一个尝试
import barobo
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot('ABCD') # Change 'ABCD' to your Linkbot's Serial ID
myLinkbot.moveNB(360, 0, -360) # Roll Forward
myLinkbot.setLEDColor(0, 255, 0) # Set LED to green
myLinkbot.moveNB(-360, 0, 360) # Roll Backward
myLinkbot.setLEDColor(255, 0, 0) # Set the LED color to red
乍一看,上面的程序似乎是正确的。但是,当您实际尝试它时,您会发现机器人永远不会将 LED 变成绿色,而且它永远不会向前移动。它会立即将 LED 变为红色并向后移动。发生在应该让机器人向前滚动并使 LED 变为绿色的两行代码上的事情是什么?
调试此类问题的一种简单而有效的方法是使用 print()
函数在程序运行时跟踪其进度。让我们尝试将一些 print 语句添加到我们的原始程序中,看看是否可以找到问题
import barobo
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot('ABCD') # Change 'ABCD' to your Linkbot's Serial ID
print("Moving forward...")
myLinkbot.moveNB(360, 0, -360) # Roll Forward
print("Done. Setting LED to green...")
myLinkbot.setLEDColor(0, 255, 0) # Set LED to green
print("Done. Moving backward...")
myLinkbot.moveNB(-360, 0, 360) # Roll Backward
print("Done. Setting LED to red...")
myLinkbot.setLEDColor(255, 0, 0) # Set the LED color to red
print("Done.")
当您运行此程序时,请注意它打印的内容,以及它打印的方式和时间。当您运行程序时,您应该得到的输出是
Moving forward... Done. Setting LED to green... Done. Moving backward... Done. Setting LED to red... Done.
但是,您会注意到所有这些语句都立即打印出来;在告诉机器人向前移动的命令和告诉它向后移动的命令之间没有延迟。这应该立即给我们一个关于错误的指示:程序应该等到机器人完成向前移动后才向后移动并变为红色。您会注意到,我们在程序中使用了非阻塞移动函数,以便我们可以同时移动和更改 LED 颜色。但是,我们也知道 Python 不会停止并等待非阻塞和“设置”函数。从上一章中,我们还有一个使 Python 等待非阻塞运动完成的函数,名为 moveWait()
。让我们尝试修复我们损坏的程序
import barobo
dongle = barobo.Dongle()
dongle.connect()
myLinkbot = dongle.getLinkbot('ABCD') # Change 'ABCD' to your Linkbot's Serial ID
myLinkbot.moveNB(360, 0, -360) # Roll Forward
myLinkbot.setLEDColor(0, 255, 0) # Set LED to green
myLinkbot.moveWait() # Wait until the robot is finished rolling forward
myLinkbot.moveNB(-360, 0, 360) # Roll Backward
myLinkbot.setLEDColor(255, 0, 0) # Set the LED color to red
在两个步骤之间添加了一个 moveWait()
语句。这迫使 Python 等待机器人完成向前移动后,才告诉它向后移动并将 LED 颜色设置为红色。
如果没有 moveWait()
语句,Python 会非常快地执行所有这些语句,以至于机器人向前滚动和绿色 LED 对人眼和耳朵来说是不可察觉的。只有最后一条命令,向后滚动和红色 LED,会“停留在”机器人上,并且可以观察到,这使得它看起来像 Python 完全跳过了向前滚动和绿色 LED。
- 这可能听起来很明显,但首先要确保的是您知道程序应该做什么。
- 使用
print()
语句检查以确保程序流程是正确的。 - 使用
print()
语句检查以确保变量值是正确的。 - 对于可能使用循环和变量的复杂程序,尝试逐行“逐步”执行程序,使用笔和纸跟踪变量值和程序输出。
- 如果您有一个很长的程序,它对一个变量执行许多计算,并且该变量的值在程序结束时是错误的,尝试使用“二分查找”方法来查找 bug:使用
print()
语句来检查程序中间变量的值。如果值是正确的,则您知道 bug 位于程序的后半部分。在程序后半部分的中间放置另一个print()
语句并检查其值。重复此过程,直到找到错误地设置变量值的错误代码行。这种技术对于找到 bug 发生的位置非常有用,并且对于许多类型的 bug 都很有用。