-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add new Advanceable exposing UnicyclePlanner
#320
Add new Advanceable exposing UnicyclePlanner
#320
Conversation
ce00957
to
27defa4
Compare
This is something I did not realize at first. The contacts planned by the unicycle only define the impact time, i.e. the instant in which the foot touches the ground. For computing when the foot lifts from the ground, you need to define the ratio between the single and double support in a step. This logic is included in the
This is an internal detail of the |
Ok, thanks for the pointer! I'll check how to use these methods to finalize the contact conversion.
I quickly skimmed the paper but I couldn't find many details. Is this a mandatory feature or we can ignore it for the moment? |
It is the position of the point F. This point is the one for which we provide a reference. This point has to be away from the wheel axis. In the walking we set it 10 cm forward in the x direction with respect to the center of the feet, and we never touched it (https://github.com/robotology/walking-controllers/blob/master/src/WalkingModule/app/robots/iCubGazeboV2_5/dcm_walking/common/plannerParams.ini#L6). This has a role only when the size of the robot changes consistently (if the robot is 50cm tall, those 10cm make a difference in the walking "style"). |
Thanks for the clarification! It was not straightforward to link the point F to the Btw, I wanted to visualize the current implementation to check that everything is in order, and it seems ok: Codefrom typing import Tuple
import bipedal_locomotion_framework.bindings as blf
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import numpy as np
from scipy.spatial.transform import Rotation as R
def get_unicycle_planner() -> blf.planners.UnicyclePlanner:
parameters_handler = blf.parameters_handler.StdParametersHandler()
# dt
parameters_handler.set_parameter_float("sampling_time_planner", 0.010)
parameters_handler.set_parameter_float("sampling_time_integrator", 0.010)
# gains
parameters_handler.set_parameter_float("gain_unicycle", 10.0)
parameters_handler.set_parameter_float("gain_turning_speed", 5.0)
# weights
parameters_handler.set_parameter_float("weight_time", 2.5)
parameters_handler.set_parameter_float("weight_position", 1.0)
# duration
parameters_handler.set_parameter_float("duration_min", 0.8)
parameters_handler.set_parameter_float("duration_max", 1.0)
parameters_handler.set_parameter_float("duration_nominal", 0.9)
# step length
parameters_handler.set_parameter_float("step_length_min", 0.05)
parameters_handler.set_parameter_float("step_length_max", 0.30)
# feet distance
parameters_handler.set_parameter_float("feet_distance_min", 0.14)
parameters_handler.set_parameter_float("feet_distance_nominal", 0.16)
# angle variation
parameters_handler.set_parameter_float("foot_angle_variation_min", np.deg2rad(8.0))
parameters_handler.set_parameter_float("foot_angle_variation_max", np.deg2rad(15.0))
# Create the planner
unicycle = blf.planners.UnicyclePlanner()
if not unicycle.initialize(handler=parameters_handler):
raise RuntimeError
return unicycle
class FootStep:
def __init__(
self,
xy: Tuple[float, float],
x_size: float,
y_size: float,
color: str,
angle: float = 0.0,
):
self.xy = xy
self.angle = angle
self.color = color
self.x_size = x_size
self.y_size = y_size
def patch(self) -> patches.Rectangle:
anchor = (self.xy[0] - self.x_size / 2, self.xy[1] - self.y_size / 2)
return patches.Rectangle(
xy=anchor,
width=self.x_size,
height=self.y_size,
facecolor=self.color,
edgecolor="k",
angle=np.rad2deg(self.angle),
)
def main():
# Create the unicycle planner
unicycle = get_unicycle_planner()
# Create the input
unicycle_input = blf.planners.UnicyclePlannerInput(
t0=0.0,
tf=20.0,
knots=[
blf.planners.UnicycleKnot(
position=(0.1, 0.0), velocity=(0.0, 0.0), time=0.0
),
blf.planners.UnicycleKnot(
position=(0.5, 0.0), velocity=(0.1, 0.0), time=7.0
),
blf.planners.UnicycleKnot(
position=(1.0, 0.25), velocity=(0.1, 0.1), time=13.0
),
blf.planners.UnicycleKnot(
position=(1.1, 0.25), velocity=(0.0, 0.0), time=15.0
),
],
)
# Set the input
assert unicycle.set_input(input=unicycle_input)
# Compute the steps
assert unicycle.advance()
# Get the output
unicycle_output = unicycle.get_output()
contact_patches = []
for contact in unicycle_output.left:
contact_patches += [
FootStep(
xy=contact.pose.translation(),
x_size=0.1,
y_size=0.05,
color="r",
angle=R.from_quat(contact.pose.quat()).as_euler(seq="zxy")[0],
).patch()
]
for contact in unicycle_output.right:
contact_patches += [
FootStep(
xy=contact.pose.translation(),
x_size=0.1,
y_size=0.05,
color="g",
angle=R.from_quat(contact.pose.quat()).as_euler(seq="zxy")[0],
).patch()
]
fig, ax = plt.subplots()
ax.set_title("Planned Footsteps")
ax.set_xlabel(r"$x$")
ax.set_ylabel(r"$y$")
ax.autoscale(True)
ax.set_aspect("equal", "box")
ax.grid(True, linestyle="--")
ax.set_axisbelow(1.0)
_ = [ax.add_patch(p) for p in contact_patches]
plt.show()
main() |
It seems to me that the logic to compute the step timing is here. It is not 100% crystal clear, but in the most simple case it seems that:
This does not guarantee that there is always a long enough double stance phase, am I right @S-Dafarra? |
Yes. The distance between two consecutive impact times for a given foot contains first a stance phase and then a swing phase.
Exactly. This determines the ratio between the stance and the swing phase between two impact times.
Not directly, no. There is a minimum time between two impact times, and the ratio cannot be such that there is no stance time, but there is not a parameter to directly control the length of each phase. In any case, the logic is a bit more involved to consider the first step and pause conditions. The latter are when the robot simply stops because the references are moving too slow. |
Perfect, this means that running the interpolator is not necessary then. This logic can be easily re-implemented in the current PR when the contact types conversion is performed.
Yes, there are some cases covered that at first seem advance usage. I'll proceed with a simple implementation, we can iterate on that in the future, if needed. |
As quickly mentioned to the meeting, I would avoid duplicating the logic and exploit the one in the So for a first iteration, it makes sense to use something simple, and eventually change later on. Also, from the UnicyclePlanner side, there could be easier ways to retrieve the duration of each phase. |
679166e
to
a8e0fb7
Compare
I guess the PR now is ready for review. Currently there's a minor overlap in logic that is just a couple of lines long: I guess we can get a better behaviour in the future when the unicycle gets vendored in blf. |
for contact in unicycle_output.left: | ||
print(contact) | ||
|
||
print() | ||
|
||
for contact in unicycle_output.right: | ||
print(contact) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe I can comment these out
Windows CI fails with:
Is C++17 enabled on a per-target base on Windows? |
4832f91
to
0ceaa75
Compare
src/Planners/include/BipedalLocomotion/Planners/UnicyclePlanner.h
Outdated
Show resolved
Hide resolved
src/Planners/include/BipedalLocomotion/Planners/UnicyclePlanner.h
Outdated
Show resolved
Hide resolved
src/Planners/include/BipedalLocomotion/Planners/UnicyclePlanner.h
Outdated
Show resolved
Hide resolved
src/Planners/include/BipedalLocomotion/Planners/UnicyclePlanner.h
Outdated
Show resolved
Hide resolved
4839913
to
4e90f6d
Compare
Rebased on top of |
Can you print the activation/deactivation times? I suspect it may be because it does not end with a double support |
Here below you can find the timings generated by the first planning of the script posted in #320 (comment):
|
They look good to me. For me we can tackle the DCM planner problem in a separate issue then! @GiulioRomualdi what do you think? |
As an extra comment, beyond the current problem of the DCM planner that does not like infinite contact deactivation, also appending new contacts to a pre-existing list in the case the last one has infinite deactivation will not work. This is the case of the last Python script posted above, but this case is more a visualization / storage caveat, not really a problem / bug. |
You would need to edit the last planned steps. If you think about this makes sense. You don't know when the last steps are going to be deactivated, until you know better, i.e. you have some new steps to add 🤔 |
a8fad53
to
f0209f2
Compare
f0209f2
to
6285b8c
Compare
Yes, this is what I did in the Python example above. If you think this need is ok, this PR is finally ready. As I mentioned, there might be some edge cases I didn't cover in my simple examples, but we can fix them as soon as this class will start being integrated in more complex pipelines. |
I'm on it :) I check the PR |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As written here https://github.com/ami-iit/bipedal-locomotion-framework/pull/320/files#r716716753 the only thing I'm scared is
contacts.back().deactivationTime = std::numeric_limits<float>().max();
We should understand if the planner (DCM) is compatible with this choice.
Apart from that, the PR is good. Thank you @diegoferigo for the effort
This PR is the first step towards including in BLF the unicycle planner currently stored in robotology/unicycle-footstep-planner.
In order to simplify the review process, I split the porting in two steps:
UnicyclePlanner
from the external repository and defines a new Advanceable with its own parameter handler, input, state, and Python bindings (and tests).UnicyclePlanner
files and perform a small refactoring. I try to minimize the changes to parameters, inputs, and outputs wrt to this PR.Open questions:
::FootPrint
objects are converted toBipedalLocomotion::Contacts::PlannedContacts
. However, it's not clear to me how to handle the duration of the contact.UnicyclePlanner::setDesiredPersonDistance
does.I based the porting on the usage implemented in the following MWE:
MWE
cc @S-Dafarra @GiulioRomualdi @traversaro
Update: the Python script to test the final version of this PR is included in #320 (comment).