Improving Actions using Probabilities
In this tutorial we will look at probabilistic specifications of actions and especially at an advance plan to pick up objects. After this tutorial you will know: - Why are probabilities useful for robotics - How to use probabilistic models to specify actions - How to use probabilistic machine learning to improve actions
Let’s start by importing all the necessary modules.
[1]:
import json
import numpy as np
import os
import random
import time
import pandas as pd
import sqlalchemy
import sqlalchemy.orm
import plotly
import plotly.graph_objects as go
import tqdm
from probabilistic_model.learning.jpt.jpt import JPT
from probabilistic_model.learning.jpt.variables import infer_variables_from_dataframe
from probabilistic_model.probabilistic_circuit.probabilistic_circuit import ProbabilisticCircuit
from random_events.events import Event
import pycram.orm.base
from pycram.designators.actions.actions import MoveTorsoActionPerformable
from pycram.plan_failures import PlanFailure
from pycram.designators.object_designator import ObjectDesignatorDescription
from pycram.bullet_world import BulletWorld, Object
from pycram.robot_descriptions import robot_description
from pycram.enums import ObjectType, Arms, Grasp
from pycram.pose import Pose
from pycram.ros.viz_marker_publisher import VizMarkerPublisher
from pycram.process_module import ProcessModule, simulated_robot
from pycram.resolver.probabilistic.probabilistic_action import MoveAndPickUp
from pycram.task import task_tree, reset_tree
ProcessModule.execution_delay = False
np.random.seed(69)
random.seed(69)
Unknown attribute "type" in /robot[@name='pr2']/link[@name='base_laser_link']
Unknown attribute "type" in /robot[@name='pr2']/link[@name='wide_stereo_optical_frame']
Unknown attribute "type" in /robot[@name='pr2']/link[@name='narrow_stereo_optical_frame']
Unknown attribute "type" in /robot[@name='pr2']/link[@name='laser_tilt_link']
[WARN] [1711028821.218958]: Could not import RoboKudo messages, RoboKudo interface could not be initialized
Next, we connect to a database where we can store and load robot experiences.
[2]:
pycrorm_uri = os.getenv('PYCRORM_URI')
pycrorm_uri = "mysql+pymysql://" + pycrorm_uri
engine = sqlalchemy.create_engine('sqlite:///:memory:')
session = sqlalchemy.orm.sessionmaker(bind=engine)()
pycram.orm.base.Base.metadata.create_all(engine)
Now we construct an empty world with just a floating milk, where we can learn about PickUp actions.
[3]:
world = BulletWorld("DIRECT")
robot = Object(robot_description.name, ObjectType.ROBOT, robot_description.name + ".urdf")
milk = Object("milk", ObjectType.MILK, "milk.stl", pose=Pose([1.3, 1, 0.9]))
viz_marker_publisher = VizMarkerPublisher()
milk_description = ObjectDesignatorDescription(types=[ObjectType.MILK]).ground()
Unknown tag "material" in /robot[@name='plane']/link[@name='planeLink']/collision[1]
Unknown tag "contact" in /robot[@name='plane']/link[@name='planeLink']
Unknown tag "material" in /robot[@name='plane']/link[@name='planeLink']/collision[1]
Unknown attribute "type" in /robot[@name='pr2']/link[@name='base_laser_link']
Unknown tag "contact" in /robot[@name='plane']/link[@name='planeLink']
Unknown attribute "type" in /robot[@name='pr2']/link[@name='wide_stereo_optical_frame']
Unknown attribute "type" in /robot[@name='pr2']/link[@name='narrow_stereo_optical_frame']
Unknown attribute "type" in /robot[@name='pr2']/link[@name='laser_tilt_link']
Unknown attribute "type" in /robot[@name='pr2']/link[@name='base_laser_link']
Unknown attribute "type" in /robot[@name='pr2']/link[@name='wide_stereo_optical_frame']
Unknown attribute "type" in /robot[@name='pr2']/link[@name='narrow_stereo_optical_frame']
Unknown attribute "type" in /robot[@name='pr2']/link[@name='laser_tilt_link']
Next, we create a default, probabilistic model that describes how to pick up objects. We visualize the default policy. The default policy tries to pick up the object by standing close to it, but not too close.
[4]:
fpa = MoveAndPickUp(milk_description, arms=[Arms.LEFT.value, Arms.RIGHT.value, Arms.BOTH.value], grasps=[Grasp.FRONT.value, Grasp.LEFT.value, Grasp.RIGHT.value, Grasp.TOP.value])
p_xy = fpa.policy.marginal([fpa.variables.relative_x, fpa.variables.relative_y])
fig = go.Figure(p_xy.root.plot(), p_xy.root.plotly_layout())
fig.update_layout(title="Marginal View of relative x and y position of the robot with respect to the object.")
fig.show()
ProbabilisticCircuit with 15 nodes and 14 edges
Next, we will perform pick up tasks using the default policy and observe the success rate. The robot will now experiment with the behaviour specified by the default policy and observe his success rate in doing so. After finishing the experiments, we insert the results into the database.
[5]:
pycram.orm.base.ProcessMetaData().description = "Experimenting with Pick Up Actions"
fpa.sample_amount = 500
batches = 1
for batch in tqdm.tqdm(range(batches), total=batches, desc="Processing Batches", disable=True):
with simulated_robot:
fpa.batch_rollout()
task_tree.insert(session)
reset_tree()
session.commit()
100%|██████████| 500/500 [00:30<00:00, 16.24it/s, Success Probability=0.228]
Inserting TaskTree into database: 100%|██████████| 5049/5049 [01:15<00:00, 66.72it/s]
Let’s query the data that is needed to learn a pick up action and have a look at it.
[6]:
samples = pd.read_sql(fpa.query_for_database(), engine)
samples
[6]:
arm | grasp | relative_x | relative_y | |
---|---|---|---|---|
0 | left | left | -0.546259 | -0.356499 |
1 | left | right | -0.203552 | -0.605282 |
2 | left | left | -0.230694 | -0.350203 |
3 | left | front | -0.366892 | 0.442297 |
4 | left | right | -0.296431 | 0.054815 |
... | ... | ... | ... | ... |
109 | right | front | -0.014655 | 0.674613 |
110 | right | right | 0.039657 | 0.299975 |
111 | left | front | -0.173449 | 0.398118 |
112 | left | left | -0.155267 | 0.715950 |
113 | left | right | 0.148996 | 0.282944 |
114 rows × 4 columns
We can now learn a probabilistic model from the data. We will use the JPT algorithm to learn a model from the data.
[7]:
variables = infer_variables_from_dataframe(samples, scale_continuous_types=False)
model = JPT(variables, min_samples_leaf=25)
model.fit(samples)
model = model.probabilistic_circuit
[8]:
arm, grasp, relative_x, relative_y = model.variables
Let’s have a look at how the model looks like. We will visualize the model density when we condition on grasping the object from the front with the left arm.
[9]:
event = Event({arm:"left", grasp:"front"})
conditional_model, conditional_probability = model.conditional(event)
p_xy = conditional_model.marginal([relative_x, relative_y])
fig = go.Figure(p_xy.root.plot_2d(), p_xy.root.plotly_layout())
fig.show()
Let’s make a monte carlo estimate on the success probability of the new model.
[10]:
fpa.policy = model
fpa.sample_amount = 100
with simulated_robot:
fpa.batch_rollout()
100%|██████████| 100/100 [00:06<00:00, 14.76it/s, Success Probability=0.31]
We can see, that our new and improved model has a success probability of 60% as opposed to the 30% from the standard policy.
Next, we put the learned model to the test in a complex environment, where the milk is placed in a difficult to access area.
[11]:
kitchen = Object("kitchen", ObjectType.ENVIRONMENT, "apartment.urdf")
milk.set_pose(Pose([0.5, 3.15, 1.04]))
milk_description = ObjectDesignatorDescription(types=[ObjectType.MILK]).ground()
fpa = MoveAndPickUp(milk_description, arms=[Arms.LEFT.value, Arms.RIGHT.value, Arms.BOTH.value], grasps=[Grasp.FRONT.value, Grasp.LEFT.value, Grasp.RIGHT.value, Grasp.TOP.value], policy=model)
fpa.sample_amount = 200
Unknown tag "material" in /robot[@name='apartment']/link[@name='coffe_machine']/collision[1]
Unknown tag "material" in /robot[@name='apartment']/link[@name='coffe_machine']/collision[1]
[12]:
p_xy = model.marginal([relative_x, relative_y])
fig = go.Figure(p_xy.root.plot_2d(), p_xy.root.plotly_layout())
fig.show()
Let’s look at the density of the relative x and y position of the robot with respect to the milk. We can see that he would like to access the object from the right front area.
[13]:
grounded_model = fpa.ground_model()
p_xy = grounded_model.marginal([relative_x, relative_y]).simplify()
fig = go.Figure(p_xy.root.plot_2d(), p_xy.root.plotly_layout())
fig.update_layout(title="Marginal View of relative x and y position with respect to the milk",
xaxis_range=[-1, 1], yaxis_range=[-1, 1])
fig.show()
Finally, we observe our improved plan in action.
[14]:
from pycram.designators.actions.actions import ParkArmsActionPerformable
world.reset_bullet_world()
milk.set_pose(Pose([0.5, 3.15, 1.04]))
with simulated_robot:
MoveTorsoActionPerformable(0.3).perform()
for sample in fpa:
try:
ParkArmsActionPerformable(Arms.BOTH).perform()
sample.perform()
break
except PlanFailure as e:
continue
[15]:
# world.exit()
# viz_marker_publisher._stop_publishing()