Skip to content
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

Replanning trigger for the navigation stack. #144

Open
wants to merge 22 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5bc9154
Replanning trigger for the navigation stack.
SimoneMic Apr 12, 2023
9950902
moved all navigation pertinent functions and parameters to a separate…
SimoneMic Apr 17, 2023
2d14446
removed unused dependencies
SimoneMic Apr 17, 2023
2ac5531
bugfix: Removed unused code
SimoneMic Apr 17, 2023
f5a0dbb
removed continuous print
SimoneMic Apr 17, 2023
3d6ec29
removed setStatus. Added comments in header
SimoneMic Apr 17, 2023
04a99e4
added port prefix and publish info config flag
SimoneMic Apr 18, 2023
2dfffb7
Update src/WalkingModule/app/robots/ergoCubGazeboV1/dcm_walking/commo…
SimoneMic Apr 27, 2023
d87e0c6
Update src/NavigationHelper/src/NavigationHelper.cpp
SimoneMic Apr 27, 2023
0a8c6af
removed leftovers variables and include not needed
SimoneMic Apr 27, 2023
012f9c9
Merge branch 'nav_sync' of https://github.com/SimoneMic/walking-contr…
SimoneMic Apr 27, 2023
95b59cf
navigation helper is initialized only if its conf options are found
SimoneMic Apr 27, 2023
a085571
removed Yarp stamp and clock header includes
SimoneMic Apr 27, 2023
c48bd21
updated comments and improved explainations
SimoneMic Apr 28, 2023
6725373
added thread safety for updating feet contacts deques
SimoneMic Apr 28, 2023
56e6997
added config param to switch between system and network clock for nav…
SimoneMic Apr 28, 2023
c78bb24
updated with minor improvements and comment fix
SimoneMic Apr 28, 2023
d9ab019
updateFeetDeques also in advanceReferenceSignals
SimoneMic Apr 28, 2023
8ba752d
Update m_navHelperUsed{false}
SimoneMic Apr 28, 2023
5827344
Formatting
SimoneMic Apr 28, 2023
f947612
moved closeThreads to private, added comments
SimoneMic May 2, 2023
0bd4df7
Merge branch 'robotology:master' into nav_sync
SimoneMic Jun 26, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ add_subdirectory(KinDynWrapper)
add_subdirectory(RetargetingHelper)
add_subdirectory(WalkingModule)
add_subdirectory(JoypadModule)
add_subdirectory(NavigationHelper)
10 changes: 10 additions & 0 deletions src/NavigationHelper/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (C) 2019 Fondazione Istituto Italiano di Tecnologia (IIT)
# All Rights Reserved.
# Authors: Giulio Romualdi <giulio.romualdi@iit.it>

add_walking_controllers_library(
NAME NavigationHelper
SOURCES src/NavigationHelper.cpp
PUBLIC_HEADERS include/WalkingControllers/NavigationHelper/NavigationHelper.h
PUBLIC_LINK_LIBRARIES WalkingControllers::YarpUtilities WalkingControllers::iDynTreeUtilities WalkingControllers::StdUtilities ctrlLib
PRIVATE_LINK_LIBRARIES Eigen3::Eigen)
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/**
* @file NavigationHelper.h
* @authors Simone Micheletti <simone.micheletti@iit.it>
* @copyright 2023 iCub Facility - Istituto Italiano di Tecnologia
* Released under the terms of the LGPLv2.1 or later, see LGPL.TXT
* @date 2023
*/

#ifndef NAVIGATION_HELPER__HPP
#define NAVIGATION_HELPER__HPP

#include <yarp/os/BufferedPort.h>
#include <yarp/os/Bottle.h>
#include <atomic>
#include <thread>
#include <deque>
#include <mutex>

namespace WalkingControllers
{
class NavigationHelper
{
private:
yarp::os::BufferedPort<yarp::os::Bottle> m_replanningTriggerPort; /**< Publishes the flag triggering the navigation's global planner. */
std::atomic<bool> m_runThreads{false}; /**< Global flag that allows the looping of the threads. */
std::deque<bool> m_leftInContact; /**< Copy of the deques in Module of the left feet contacts status. */
std::deque<bool> m_rightInContact; /**< Copy of the deques in Module of the left feet contacts status. */
double m_navigationReplanningDelay; /**< Delay in seconds of how much to wait before sending the trigger to the navigation stack after exiting double support. */
int m_navigationTriggerLoopRate; /**< Loop rate for the thread computing the navigation trigger*/
bool m_publishInfo; /**< Flag to whether publish information. */
std::thread m_navigationTriggerThread; /**< Thread for publishing the flag triggering the navigation's global planner. */
std::mutex m_updateFeetMutex;
bool m_simulationMode{false}; /**< Flag that syncs the trigger delay with the external clock if in simulation. */

const std::string m_portPrefix = "/navigation_helper";

/**
* Function launched by the looping thread
*/
void computeNavigationTrigger();

public:
NavigationHelper();
~NavigationHelper();

/**
* Close the Navigation Helper Threads and ports
* @return true/false in case of success/failure.
*/
bool closeThreads();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably also this can be private.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes and I would also add in the destructor (the same is valid for closeHelper)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have moved it to private and added/fixed some comments/descriptions.
The destructor should never be called inside closeThreads(), since the ports could still be open.
In closeHelper() I don't see the reason to call the destructor inside a class method, since we are not dealing with special manual memory management. I would avoid that and let the owner handle it.


/**
* Close the Navigation Helper Threads and ports
* @return true/false in case of success/failure.
*/
bool closeHelper();

/**
* Initialize the Navigation Helper
* @param config yarp searchable object of the configuration.
* @return true/false in case of success/failure.
*/
bool init(const yarp::os::Searchable& config);

bool updateFeetDeques(const std::deque<bool> &left, const std::deque<bool> &right);
};
}

#endif
183 changes: 183 additions & 0 deletions src/NavigationHelper/src/NavigationHelper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/**
* @file NavigationHelper.cpp
* @authors Simone Micheletti <simone.micheletti@iit.it>
* @copyright 2023 iCub Facility - Istituto Italiano di Tecnologia
* Released under the terms of the LGPLv2.1 or later, see LGPL.TXT
* @date 2023
*/

#include <iostream>
#include <thread>
#include <WalkingControllers/NavigationHelper/NavigationHelper.h>

#include <WalkingControllers/YarpUtilities/Helper.h>


using namespace WalkingControllers;

NavigationHelper::NavigationHelper()
{
}

NavigationHelper::~NavigationHelper()
{
}

bool NavigationHelper::closeThreads()
{
//Close parallel threads
if (m_runThreads)
{
m_runThreads = false;

yInfo() << "Closing m_navigationTriggerThread";
if(m_navigationTriggerThread.joinable())
{
m_navigationTriggerThread.join();
m_navigationTriggerThread = std::thread();
}
}
return true;
}

bool NavigationHelper::closeHelper()
{
try
{
//close threads first
if (m_runThreads)
{
closeThreads();
}
//close ports
if(!m_replanningTriggerPort.isClosed())
m_replanningTriggerPort.close();
}
catch(const std::exception& e)
{
std::cerr << e.what() << std::endl;
return false;
}
return true;
}

bool NavigationHelper::init(const yarp::os::Searchable& config)
{
m_navigationReplanningDelay = config.check("navigationReplanningDelay", yarp::os::Value(0.9)).asFloat64();
m_navigationTriggerLoopRate = config.check("navigationTriggerLoopRate", yarp::os::Value(100)).asInt32();
m_publishInfo = config.check("publishNavigationInfo", yarp::os::Value(false)).asBool();
m_simulationMode = config.check("simulationMode", yarp::os::Value(false)).asBool();
if (config.check("plannerMode", yarp::os::Value("manual")).asString() == "navigation")
{
//if in navigation mode we need this parameter to be true
m_publishInfo = true;
}
if (!m_publishInfo)
{ //exit the funnction if the infos are not needed
yInfo() << "[NavigationHelper::init] Configuring NavigationHelper without publishing infos on ports ";
return true;
}

// format check
if (m_navigationTriggerLoopRate<=0)
{
yError() << "[configure] navigationTriggerLoopRate must be strictly positive, instead is: " << m_navigationTriggerLoopRate;
return false;
}
if (m_navigationReplanningDelay<0)
{
yError() << "[configure] navigationTriggerLoopRate must be positive, instead is: " << m_navigationReplanningDelay;
return false;
}

std::string replanningTriggerPortPortName = m_portPrefix + "/replanning_trigger:o";
if (!m_replanningTriggerPort.open(replanningTriggerPortPortName))
{
yError() << "[NavigationHelper::init] Could not open" << replanningTriggerPortPortName << " port.";
return false;
}

m_runThreads = true;
m_navigationTriggerThread = std::thread(&NavigationHelper::computeNavigationTrigger, this);
return true;
}

// This thread synchronizes the walking-controller with the navigation stack.
// Writes on a port a boolean value when to replan the path
void NavigationHelper::computeNavigationTrigger()
{
if (!m_publishInfo)
{ //exit the function if the infos are not needed
return;
}
bool trigger = false; //flag used to fire the wait for sending the navigation replanning trigger
yInfo() << "[NavigationHelper::computeNavigationTrigger] Starting Thread";
yarp::os::NetworkClock myClock;
if (m_simulationMode)
{
myClock.open("/clock", "/navigationTriggerClock");
}
bool enteredDoubleSupport = false, exitDoubleSupport = true;
while (m_runThreads)
{
{
std::unique_lock<std::mutex> lock(m_updateFeetMutex);
if (m_leftInContact.size()>0 && m_rightInContact.size()>0) //external consistency check
{
if (m_leftInContact.at(0) && m_rightInContact.at(0)) //double support check
{
if (exitDoubleSupport)
{
enteredDoubleSupport = true;
exitDoubleSupport = false;
}
}
else
{
if (! exitDoubleSupport)
SimoneMic marked this conversation as resolved.
Show resolved Hide resolved
{
trigger = true; //in this way we have only one trigger each exit of double support
}
exitDoubleSupport = true;
}
}
else
{
trigger = false;
}
}

//send the replanning trigger after a certain amount of seconds
if (trigger)
{
trigger = false;
//waiting -> could make it dependant by the current swing step duration
//if in simulation we need to sync the clock, we can't use the system clock
if (m_simulationMode)
{
myClock.delay(m_navigationReplanningDelay);
}
else
{
std::this_thread::sleep_for(std::chrono::duration<double, std::milli>(m_navigationReplanningDelay*1000));
S-Dafarra marked this conversation as resolved.
Show resolved Hide resolved
}
yInfo() << "[NavigationHelper::computeNavigationTrigger] Triggering navigation replanning";
auto& b = m_replanningTriggerPort.prepare();
b.clear();
b.add((yarp::os::Value)true); //send the planning trigger
m_replanningTriggerPort.write();
}

std::this_thread::sleep_for(std::chrono::milliseconds(1000/m_navigationTriggerLoopRate));
}
yInfo() << "[NavigationHelper::computeNavigationTrigger] Terminated thread";
}


bool NavigationHelper::updateFeetDeques(const std::deque<bool> &left, const std::deque<bool> &right)
{
std::unique_lock<std::mutex> lock(m_updateFeetMutex);
m_leftInContact = left;
m_rightInContact = right;
return true;
}
2 changes: 1 addition & 1 deletion src/WalkingModule/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ add_walking_controllers_application(
NAME WalkingModule
SOURCES src/main.cpp src/Module.cpp ${WalkingModule_THRIFT_GEN_FILES}
HEADERS include/WalkingControllers/WalkingModule/Module.h
LINK_LIBRARIES WalkingControllers::YarpUtilities WalkingControllers::iDynTreeUtilities WalkingControllers::StdUtilities WalkingControllers::TimeProfiler WalkingControllers::RobotInterface WalkingControllers::KinDynWrapper WalkingControllers::TrajectoryPlanner WalkingControllers::SimplifiedModelControllers WalkingControllers::WholeBodyControllers WalkingControllers::RetargetingHelper BipedalLocomotion::VectorsCollection BipedalLocomotion::ParametersHandlerYarpImplementation ctrlLib
LINK_LIBRARIES WalkingControllers::YarpUtilities WalkingControllers::iDynTreeUtilities WalkingControllers::NavigationHelper WalkingControllers::StdUtilities WalkingControllers::TimeProfiler WalkingControllers::RobotInterface WalkingControllers::KinDynWrapper WalkingControllers::TrajectoryPlanner WalkingControllers::SimplifiedModelControllers WalkingControllers::WholeBodyControllers WalkingControllers::RetargetingHelper BipedalLocomotion::VectorsCollection BipedalLocomotion::ParametersHandlerYarpImplementation ctrlLib
SUBDIRECTORIES app)

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Navigation trigger delay in seconds. Time to wait before sending the replanning command after exiting the double support stance.
navigationReplanningDelay 0.9
# Loop rate in hertz for the thread computing the navigation trigger
navigationTriggerLoopRate 100 #Hz

# Planner Mode: manual i.e. typical utilization of the planner for joystick use - navigation i.e. path following behavior
# If in NAVIGATION mode (1), the type of the controller also specifies the behaviour of the planner:
# A- in direct mode the planner behaves like an omnidirectional floating base that interpolates the path.
# B- in personFollowing it tries to follow the path like an unicycle using state feedback linearization
plannerMode navigation

# Flag to publish Navigation info anyway (indipendently by the plannerMode)
publishNavigationInfo 1

# Set to 0 if on the real robot - to 1 if in simulation on Gazebo
simulationMode 1
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ sampling_time 0.01
# Specify the frame to use to control the robot height. Currently, we support only the following options: com, root_link
height_reference_frame root_link

# include navigation parameters
[include NAVIGATION "./dcm_walking/common/navigation.ini"]

# include robot control parameters
[include ROBOT_CONTROL "./dcm_walking/joypad_control/robotControl.ini"]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@

#include <WalkingControllers/TimeProfiler/TimeProfiler.h>

#include <WalkingControllers/NavigationHelper/NavigationHelper.h>

// iCub-ctrl
#include <iCub/ctrl/filters.h>

Expand Down Expand Up @@ -155,6 +157,9 @@ namespace WalkingControllers
size_t m_feedbackAttempts;
double m_feedbackAttemptDelay;

NavigationHelper m_navHelper;
bool m_navHelperUsed;
SimoneMic marked this conversation as resolved.
Show resolved Hide resolved

// debug
std::unique_ptr<iCub::ctrl::Integrator> m_velocityIntegral{nullptr};

Expand Down
30 changes: 30 additions & 0 deletions src/WalkingModule/src/Module.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include <yarp/sig/Matrix.h>
#include <yarp/sig/Vector.h>
#include <yarp/os/LogStream.h>
#include <yarp/os/Stamp.h>
#include <yarp/os/NetworkClock.h>
S-Dafarra marked this conversation as resolved.
Show resolved Hide resolved

// iDynTree
#include <iDynTree/Core/VectorFixSize.h>
Expand Down Expand Up @@ -78,6 +80,9 @@ bool WalkingModule::advanceReferenceSignals()

m_leftInContact.pop_front();
m_leftInContact.push_back(m_leftInContact.back());

if (m_navHelperUsed)
m_navHelper.updateFeetDeques(m_leftInContact, m_rightInContact);

m_isLeftFixedFrame.pop_front();
m_isLeftFixedFrame.push_back(m_isLeftFixedFrame.back());
Expand Down Expand Up @@ -264,6 +269,7 @@ bool WalkingModule::configure(yarp::os::ResourceFinder& rf)
yarp::os::Bottle ellipseMangerOptions = rf.findGroup("FREE_SPACE_ELLIPSE_MANAGER");
trajectoryPlannerOptions.append(generalOptions);
trajectoryPlannerOptions.append(ellipseMangerOptions);

if(!m_trajectoryGenerator->initialize(trajectoryPlannerOptions))
{
yError() << "[configure] Unable to initialize the planner.";
Expand Down Expand Up @@ -442,6 +448,24 @@ bool WalkingModule::configure(yarp::os::ResourceFinder& rf)
m_qDesired.resize(m_robotControlHelper->getActuatedDoFs());
m_dqDesired.resize(m_robotControlHelper->getActuatedDoFs());

yarp::os::Bottle navigationOptions = rf.findGroup("NAVIGATION");

// start the threads used for computing navigation needed infos
if (!navigationOptions.isNull())
{
m_navHelperUsed = true;
if(!m_navHelper.init(navigationOptions, m_leftInContact, m_rightInContact))
{
yError() << "[WalkingModule::configure] Could not initialize the Navigation Helper";
}
yInfo() << "[WalkingModule::configure] Configured Navigation Helper.";
}
else
{
m_navHelperUsed = false;
}


yInfo() << "[WalkingModule::configure] Ready to play! Please prepare the robot.";

return true;
Expand Down Expand Up @@ -470,6 +494,9 @@ bool WalkingModule::close()
{
if(m_dumpData)
m_loggerPort.close();

if(m_navHelperUsed)
m_navHelper.closeHelper();

// restore PID
m_robotControlHelper->getPIDHandler().restorePIDs();
Expand Down Expand Up @@ -1546,6 +1573,9 @@ bool WalkingModule::updateTrajectories(const size_t& mergePoint)
StdUtilities::appendVectorToDeque(leftInContact, m_leftInContact, mergePoint);
StdUtilities::appendVectorToDeque(rightInContact, m_rightInContact, mergePoint);

if (m_navHelperUsed)
m_navHelper.updateFeetDeques(m_leftInContact, m_rightInContact);

StdUtilities::appendVectorToDeque(comHeightTrajectory, m_comHeightTrajectory, mergePoint);
StdUtilities::appendVectorToDeque(comHeightVelocity, m_comHeightVelocity, mergePoint);

Expand Down