# Python script to generate robot model produces wrong result

Platform = Ubuntu 14.04 64 bit, Blender 2.77a Here's the image:

The following simple script produces an incorrect model is from the book "Learning Robotics Using Python"; so I did not create it and I'm a complete newbie to Blender so I'm totally lost concerning fixing it.

There is no errata mentioning bug corrections on the script, so this forum is my best practical hope. The script runs, but the model generated has a bottom base plate missing and one of the wheels is a cube; the rest is okay.

The script is supposed to produce a robot model comprised of 3 vertically stacked circular plates with 4 poles in between them for support; a cube on the middle plate, two wheels with shafts and motors.

Would someone please show me what is wrong?

Thankfully,

Sam

Here is the script:

=========================

import bpy # contains all the functionalities of Blender and it can only be
# accessed from inside the Blender application

# This function will draw base plate
def Draw_Base_Plate():

#Added two cubes for cutting sides of base plate

#############################################################################################

#Adding booleab difference modifier from first cube

bpy.context.object.modifiers["Boolean"].operation = 'DIFFERENCE'
bpy.context.object.modifiers["Boolean"].object =      bpy.data.objects["Cube"]
bpy.ops.object.modifier_apply(modifier="Boolean")

#############################################################################################

#Adding booleab difference modifier from second cube

bpy.context.object.modifiers["Boolean"].operation = 'DIFFERENCE'
bpy.context.object.modifiers["Boolean"].object =     bpy.data.objects["Cube.001"]
bpy.ops.object.modifier_apply(modifier="Boolean")

#############################################################################################

#Deselect cylinder and delete cubes
bpy.ops.object.select_pattern(pattern="Cube")
bpy.ops.object.select_pattern(pattern="Cube.001")
bpy.data.objects['Cylinder'].select = False
bpy.ops.object.delete(use_global=False)

#This function will draw motors and wheels
def Draw_Motors_Wheels():

#Create first Wheel

#Rotate
bpy.context.object.rotation_euler[1] = 1.5708
#Transalation
bpy.context.object.location[0] = 0.135

#Create second wheel
#Rotate
bpy.context.object.rotation_euler[1] = 1.5708
#Transalation
bpy.context.object.location[0] = -0.135

bpy.context.object.rotation_euler[1] = 1.5708

bpy.context.object.rotation_euler[1] = 1.5708

bpy.context.object.rotation_euler[1] = 1.5708

bpy.context.object.rotation_euler[1] = 1.5708

#############################################################################################

#Draw middle plate
def Draw_Middle_Plate():

def Draw_Top_Plate():

def Draw_Support_Tubes():
#Cylinders

#Exporting into STL
def Save_to_STL():
bpy.ops.object.select_all(action='SELECT')
#    bpy.ops.mesh.select_all(action='TOGGLE')
bpy.ops.export_mesh.stl(check_existing=True,
filepath="/home/sam/Desktop/exported.stl",
filter_glob="*.stl", ascii=False, use_mesh_modifiers=True,
axis_forward='Y', axis_up='Z', global_scale=1.0)

#Main code

if __name__ == "__main__":
Draw_Base_Plate()
Draw_Motors_Wheels()
Draw_Middle_Plate()
Draw_Top_Plate()
Draw_Support_Tubes()
Save_to_STL()

• Please post an image of your results that will help us understand what's wrong. I ran this and got the expected result: pasteall.org/pic/show.php?id=102183 – TLousky Apr 19 '16 at 10:05
• Okay, I want to send an image and I have a .png file on my computer. I don't understand how to create a link like you did to have my image accessible on this page. Could you show me how? – Sam Quintanar Apr 19 '16 at 20:41
• Also, what version of blender are you using? Your rendering is MUCH better than mine! and THANK YOU for responding to quickly. – Sam Quintanar Apr 19 '16 at 20:46
• I solved the image link problem. Th image is added to my original question – Sam Quintanar Apr 19 '16 at 22:18

The issue is in lines such as

bpy.ops.mesh.primitive_cube_add(radius=0.05, location=(0.175,0,0.09))


in the script assumes the two cubes created will be named "Cube" and "Cube.001", as they are later referenced for the boolean modifiers

bpy.context.object.modifiers["Boolean"].object = bpy.data.objects["Cube"]


which is assuming that you don't already have an object in your file named Cube or Cube.001 before running, in which case the newly created cubes will have unique names "Cube.nnn" where nnn is the first available unused name.

When I pasted and ran in a test file with both "Cube" and "Cube.001", already existing in another scene, ended up with a busted base plate and cubes for wheels.

My suggestion would be to a)

make a ref when creating the primitives if they are going to be used as modifier targets etc scene.objects.active gives a reference to the newly created primitive, as would context.active_object, but I've passed the scene to the method. Could pass the context I suppose, which in way is good practice as so much code Ops and Panels etc use method(self, context):

def Draw_Base_Plate(scene=bpy.context.scene):
#Add cube for cutting sides of base plate

cube1 = scene.objects.active
# copy cube1 and link to scene (not required as example howto)
'''
cube2 = cube1.copy()
cube2.location.x = -0.175
'''

cyl = scene.objects.active

for ob in [cube1, cube1]:
mod = cyl.modifiers.new("FacePlateBool", type='BOOLEAN')
mod.operation = 'DIFFERENCE'
mod.object =  ob
bpy.ops.object.modifier_apply(modifier=mod.name)
ob.location.x = -ob.location.x

#Deselect cylinder and delete cube
cyl.select = False
bpy.data.objects.remove(cube1)


or b)

make sure you have no "Cube" or "Cube.nnn" (by renaming) in your file before running script.

• I tried what you recommended and both ways ended up with only a base plate and two cubes for wheels showing. I believe I must also modify the lines: that delete the cubes from the base plate: bpy.ops.object.select_pattern(pattern="cube1") bpy.ops.object.select_pattern(pattern="cube2") bpy.ops.object.delete(use_global=False) but I don't know what syntax to use when dealing with the reference vars cube1 and cube2 instead of file names. How do I change these deletion lines? – Sam Quintanar Apr 19 '16 at 20:29

When Blender is first invoked via the Unity launcher icon in Ubuntu linux, there is a "cube" object automatically present. (This can be verified by mouse clicking on the "Choose Screen layout" icon to the left of the text box showing "Default" at the top bar of the Blender app; then selecting "Scripting" from the drop-down menu; then typing bpy.context.selected_objects in the python console window at the bottom of the scripting layout and pressing return key on keyboard; the result is [bpy.data.objects['Cube']]).

The example code from the book mentioned above inadvertently uses the object name "Cube" when creating the first cube and then uses the object name "Cube.001" when creating the second cube. Apparently, two different objects with the name "Cube" are then in the objects list. The first "Cube" object in the object list is the automatically displayed cube upon startup of Blender, and the second "Cube" object in the list is the one we create. When deleting the cubes from the base plate cylinder to provide space for wheels, the code line bpy.ops.object.select_pattern(pattern="Cube") deletes the automatically displayed startup cube, and leaves the cube we created as part of the model, so one wheel looks like a cube.

The base plate fails to display due to the conflicts caused by having two "Cube" objects.

There are two cures to this problem:

1. Delete the startup Cube object before executing the book script (which isn't obvious that this would be necessary) by doing: bpy.ops.object.select_pattern(pattern="Cube") and bpy.ops.object.delete(use_global=False) in the Python console of the Scripting layout

or

1. Submit an errata to the book that changes the two cube names created by script to: "Cube.001" and "Cube.002" so the while hassle is avoided (unless objects with names if "Cube.001" and "Cube.002" already exist!); or perhaps an errata that explains the potential problem.
• Added a different take on Add_Base_Plate in answer to show non operator ways of doing things. All the operators could be replaced with low level API calls. There is a happy medium (somewhere) between using bpy.ops or otherwise. – batFINGER Apr 20 '16 at 4:44