Tutorial 0: Start on the Right Foot

Start on the Right Foot with ckbot.Logical

You usually won’t need to muck around in ckbot.Logical, but understanding these three things will make your life much less frustrating:

  • The names= parameter
  • The who() method
  • The off() method.

The Beginners Folly

Most of your code will probably begin like so:

import ckbot.logical as L
c = L.Cluster(count=1) 

This isn’t terrible.

L.Cluster() creates a logical.Cluster (i.e., a Cluster)–an object that represents a collection of robot modules connected to a single communication channel (or “bus”). The count= parameter tells the new cluster to call its .populate() method and look for physical modules (usually motors of some sort). The Cluster then receives the numeric node ID stored in the module (something like 0x14) and creates an instance of the appropriate subclass of AbstractModule to represent it.

Please note: The count is important! If you set count to “3” and there are ten clusters, it will find three (more-or-less at random) and quietly exit; you won’t know which three it will find, and it won’t always find the same three. Conversely, if you say “3” and there is only one module, Logical will find that one and then loudly fail after it times out looking for the other two.

This is a good start–but please don’t start out this way.

Learning names=

You can use a module’s numeric ID to control the module. For example, this sets the servo with ID 0x14 to position 1000:

c[0x14].set_pos(1000)

But just as in real life, it’s much better to use names instead of blunt physical descriptions. By default L.Cluster() gives node 0x14 the name Nx14. Therefore, this code could also set that same servo to position 1000:

c.at.Nx14.set_pos(1000)

This is a tiny bit easier to read (no brackets), but still ugly. As you add modules, this strategy tends to grow both cumbersome and confusing.

Story Problem: You are debugging someone else’s code, trying to sort out a problem with motor 0x15. Which of the 11 motors on the robot is that? Once you track it down, you discover motor 0x15 is broken, so you replace it with 0x24–but damage motor 0x09 in the process. Fortunately, motor 0x15 has since been refurbished, and you can use it to replace 0x09. Now you just have to go through and replace all the references to 0x15 with references to 0x24, all the references to 0x09 with 0x15, document those changes, and you’re ready to go!

Question: How many hours will you waste untangling this spaghetti?

Answer: Trick question; time will lose all meaning as you thanklessly slog through this mess.

In real life, relying on these numeric IDs quickly becomes painfully unmanageable.

Let us instead give our modules real, easy-to-read names. You accomplish this with the names= parameter:

c = L.Cluster(count=1, names={0x14:"front"})

Now, you can say:

c.at.front.set_pos(1000)

And the front servo rotates to postion 1000. You can then say:

c.at.front.set_pos(0)

to move it back, and then:

c.at.front.set_pos(500)

to put it halfway in between–and if module 0x14 dies, you’ll only need to fix one line of code after you replace the module. You’ll also find that you can almost always replace a dead “rearRightClawArmElbow” faster than you can figure out which servo is “0x11”.

Even better: multiple modules with different numeric IDs can have the same names.

Example: You have two identical robots, “Alice” and “Bob”. Each has two modules: a servo controlling a pen and a motor driving a wheel. Alice’s pen module is 0x13 and her wheel is 0x35. Bob’s pen is 0x31 and wheel is 0x53.

We can write:

c = L.Cluster(count=1, names={0x13:"pen", 0x31:"pen", 0x35:"wheel", 0x53:"wheel"})

No matter which robot is connected, this code will work, and we’ll have a working a pen module at c.at.pen and working wheel module at c.at.wheel. When you’re about to do your demo and Alice’s wheel motor suddenly stalls, you can quickly grab Bob’s wheel module and put it on Alice. Restart the code, and it will all just work.

Curse of the demo averted–for now!

Discovering who() is out there

What if there is a problem? Something isn’t acting right. Maybe a servo is dead or damaged, maybe some wiring is frayed, maybe a module is simply wired wrong, maybe you reversed two digits in a numerical ID when typing–who knows?

Indeed, who() knows.

Try:

c = L.Cluster()
c.who() 

Logical will query the bus for 10 seconds, building a list of what it finds. Each item in the list will be either: numeric module ID : the name you assigned the module (if it finds a module that you’ve named) or numeric module ID : <?> (if it finds something you haven’t assigned a name). This list will print on the terminal every second for the duration of who(). (This is very handy: Jiggle wires and check modules as who() runs, and see if someone pops into being.)

Want to change the duration of who()? That’s very easy: who() takes one parameter, and it’s the number of seconds to run. who(5) runs for five seconds, who(240) runs for 4 minutes, and so on.

Turning it off()!

You do not want your killbot to go crazy and wreck the lab, tear its parts off, or cut your fingers when you try to grab it. Make your robot’s failure state one of repose.

For this we have off(); it basically cuts power to anything motor-ish, making all modules go slack. For safety’s sake, your code should always include something like:

try:
	# Do something with Cluster c
finally: # Any exception that kills the code (such as hitting ctrl-c on the keyboard) …
	c.off() # … will make the robot go slack

Start Your Code Off Right

Logical gives you several tools to make your life much, much easier. Save your time and sanity by beginning each program like this:

import ckbot.logical as L
c = L.Cluster(count=1, names={0x14:"front"})
try:
	c.at.front.set_pos(1000) # Or whatever else your robot should do
finally: 
	c.off() 

Now go forth and sin no more.


Improve this page