Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
doc:pycram:bulletworld [2021/02/11 13:34] – created jdechdoc:pycram:bulletworld [2022/11/07 12:07] (current) jdech
Line 37: Line 37:
 ===== Current Bullet World =====  ===== Current Bullet World ===== 
 The current_bullet_world variable is the default world for all methods. It will always point to the last initialized BulletWorld. To ensure that the current_bullet_world always points on a valid BulletWorld every, instance saves the previous current_bullet_world and resets it once it is finished; making this effectively a single linked list. The current_bullet_world variable is the default world for all methods. It will always point to the last initialized BulletWorld. To ensure that the current_bullet_world always points on a valid BulletWorld every, instance saves the previous current_bullet_world and resets it once it is finished; making this effectively a single linked list.
 +
 +====== Object ======
 +The Object class represents anything inside the simulation regardless of whether it refers to a robot, the environment (like a kitchen) or an item (such as a mug). This makes the working with objects easier because one does not have to specify the na-
 +ture of the given object.
 +On the downside, this approach limits the functionality of the methods because the methods cannot be designed with a special use case in mind. 
 +Below you can see the constructor of the object class, the arguments and what they mean are being explained further down. 
 +
 +<code> Object (name, type, path, position = [ 0, 0, 0], orientation = [ 0, 0, 0, 1], world=None, color = [ 1, 1, 1, 1]) </code>
 +
 +The constructor of the object class requires only the first three arguments, the rest is optional.
 +
 +The first is the name of this object: it may be arbitrary as it is only used to identify the object.  
 +
 +The type of the object specifies the exact type of this object: For example, the type of a kitchen would be "environment". This makes it simple to cluster objects and easily identify a specific group of objects. One instance in which this may be helpful
 +is when excluding environmental objects, such as the kitchen or the floor, from reasoning queries.
 +
 +The path argument is the path to the file, which describes this object. An object can either be generated from an URDF, stl or obj file. If the given file is of stl or obj format an URDF file with one link, that contains this file as mesh, will be generated and spawned. This needs to be performed as PyBullet does not directly support stl or obj files. 
 +In this process it is possible to define an individual color so that meshes can be spawned with different colors: For URDF files this is not necessary as the URDF format supports independent colors for every link.
 +
 +The position and orientation arguments define the position and orientation in which the object should be spawned. These are represented as lists of x, y, z in world coordinate frame for the position and as a list representing a quaternion for the orientation.
 +A quaternion is a list of 4 numeric values that describe the rotation around each axis as well as a normalization value.
 +
 +The world argument specifies a BulletWorld in which the object should be spawned.
 +If no world is given, the current_bullet_world is used. The argument is set to None and not directly to the reference because the standard values will only be evaluated once and then be set. This is problematic when dealing with object references instead of static values. As a workaround for this problem the current_bullet_world will be set in the constructor if the world argument is None.
 +
 +The color argument only works when an obj or stl file is provided. The color is given as a list of RGBA.
 +
 +===== Methods =====
 +The object class provides a number of methods to interact with this object. The current position and orientation of the object or it's links can be queried, it can be attached to other objects or the position, orientation or joint values can be changed. 
 +
 +^ Method ^ Parameter ^ Description ^
 +| attach | Object, link (optional), loose (optional) | Attaches this object to the other given object.|
 +| detach | Object | Detaches this object from the other given object. This method only works if they were previously attached. |
 +| get_position | None | Returns the position of this object in world coordinate frame. |
 +| get_pose | None | An alias for get_position. |
 +| get_orientation | None | Returns the orientation of this object as quanternion. | 
 +| get_position_and_orientation | None | Returns the position and orientation of this object as a list with position in xyz and orientation as a quanternion, both in world coordinate frame. |
 +| set_position_and_orientation | list[float], list [float]  | Sets the position and orientation of this object.|
 +| set_position | list[float] | Sets the base position of this object, the position must be given as a list of xyz.|
 +| set_orientation | list[float] | Sets the orientation of this object, the position must be given as a list representing a quanternion.| 
 +| set_joint_state | String, float | Sets the joint value of the given joint name to the given value. | 
 +| get_joint_id| String | Returns the unique ID of the given joint name. |
 +| get_link_id | String | Returns the unique ID of the given link name. |
 +| get_link_position | String | Returns the position of a given link of this object, the link is specified by its name. |
 +| get_link_orientation | String | Returns the orientation of a given link of this object, the link is specified by its name. |
 +| get_link_position_and_orientation | String | Returns the position and orientation of a given link as a list. |
 +| get_joint_state | String | Returns the joint value of the given joint name. | 
 +
 +
 +===== Attachments ===== 
 +The attachments are mainly used to simulate pick-up actions, because the robot and the object it picks up are two independent objects. As they are also simulated independently, attachments are needed. The attachment method saves the attached object into an internal dictionary which keeps track of all attachments, in addition a virtual joint will be created between the two attached objects. This virtual joints will only be resolved by PyBullet if the simulation is running and is thus only for this purpose. 
 +
 +Because the simulation is rarely run when the robot moves, he is teleported between positions the save time, there is a second method _set_attached_objects which updates the position of all attached objects and objects which are attached to these objects recursively. The _set_attached_objects method is called every time the joint states, position or orientation are changed. 
 +
 +These two methods of attachments ensure that the relative position between objects is maintained at all times. 
 +
 +To keep track of all attachments, every object has a dictionary with the attached object as keys and the unique id of the attachment as value. The id is returned by the PyBullet method which creates the virtual joint. This dictionary is identical for
 +both objects of the attachment, meaning, it is irrelevant on which object the detachment method is called because both know the unique id of the attachment.
 +
 +To attach two objects to another, the user just needs to call the attach method on
 +one of the objects and provide the other object as a parameter.
 +
 +If robot and fork are the names of the objects, the attach method looks like this:
 +<code> robot.attach(fork) </code> 
 +
 +This would be enough to attach the objects, but without further parameters the attachment would be between the base position of the robot and the fork. This is not ideal as a robot would hold a fork with its hands and we want to simulate the attachment this way. For that purpose it is possible to specify the links between which the attachment should be created.
 +This would look like this:
 +<code> robot.attach(fork, link="gripper name", loose=false)</code>
 +
 +Lastly, it is important to mention is that it is not possible to call the attach method again if there is already an attachment between the given objects. The reason for this is that it would create a second joint between the objects and override the entry in the dictionary of all attachments and create an irremovable joint. If the user were to call the attach method with an existing attachment, it would just return without having altered anything.
 +
 +==== Loose Attachments ====
 +Loose attachments are a special case of attachments in which the position of the loose attached object is not updated. This is used to create unidirectional attachments in contrast to the regular attachments which are bidirectional. 
 +This has a use case, for example, if a bottle is placed on a tray, the bottle should move with the tray but the tray should stay at the same position if the bottle is moved.
 +
 +===== Detachments ===== 
 +The detachments can be considered straight-forward because only the virtual joint needs to be removed and the attachment needs to be deleted from the dictionary. With the removal, the two objects become again independent of one another.  
 +
 +To detach two objects, the user just needs to call the detach method on one object and provide the other object as a parameter.
 +
 +<code> robot.detach(fork) </code> 
 +
 +====== Shadow Worlds ======
 +Since some methods especially the geometric reasoning need to modify the position of objects it is necessary to have a second Bullet World, so the second Bullet World can be modified without changing the state of the main Bullet World. This is mainly required to have a consistent belief state. This is what shadow worlds provide. 
 +
 +A shadow world is a second Bullet World that mirrors the main Bullet World, so everything that happens in the main Bullet World will be reflected in the shadow world. This happens completely autonomous by the World_Sync class, this class runs in a separate thread and spawns, removes and changes the location of objects in the shadow world. 
 +
 +===== World Sync ===== 
 +The World Sync synchronizes the shadow world with the main Bullet World, it basically provides the "shadow" functionality. This module runs in a seperate thread and checks every 0.1 seconds if there are objects that need to be added or removed from the shadow world. Checking if anything should be added or removed is done by two queues, anytime an object in the main Bullet World is added or removed there will be a new entry added to the queue with every necessary information. The World Sync then checks if there are any entries in the queue and processes them. 
 +
 +There is also an object mapping which maps the objects of the main Bullet World to the corresponding objects in the shadow world.
 +
 +
 +===== Using the shadow world ===== 
 +To use the shadow world you have to import Use_shadow_world from pycram.bullet_world and use it in a "with" environment, in this environment you can use the BulletWorld.current_bullet_world variable which points at the shadow world. 
 +
 +There are also two methods for retrieving the corresponding object of the other world, these are get_shadow_object and get_bullet_object_for_shadow.
 +
 +<code>
 +from pycram.bullet_world import BulletWorld, Object, Use_shadow_world 
 +
 +world = BulletWorld()
 +milk = Object("milk", "milk", "milk.stl")
 +shadow_milk = world.get_shadow_object(milk)
 +
 +with Use_shadow_world():
 +    shadow_milk.set_position([0,0, 1])
 +    BulletWorld.current_bullet_world.simulate(10)
 +</code>