Skip to content

Commit aa06be2

Browse files
authored
Add MoveWaypointFromCollisionRandomSampler to FixStateCollisionProcess (#426)
* Add MoveWaypointFromCollisionRandomSampler to FixStateCollisionProcess * Add more generalized way of specifying correction methods * Bug fix * Add assert to catch mismatched sizes * Rebase fixes and a bug fix
1 parent 6535ab9 commit aa06be2

File tree

6 files changed

+457
-62
lines changed

6 files changed

+457
-62
lines changed

tesseract/tesseract_planning/tesseract_process_managers/include/tesseract_process_managers/process_generators/fix_state_collision_process_generator.h

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,22 +48,39 @@ struct FixStateCollisionProfile
4848
DISABLED
4949
};
5050

51+
/** @brief Used to specify method used to correct states in collision */
52+
enum class CorrectionMethod
53+
{
54+
NONE,
55+
TRAJOPT,
56+
RANDOM_SAMPLER
57+
};
58+
5159
FixStateCollisionProfile(Settings mode = Settings::ALL) : mode(mode) {}
5260

5361
/** @brief Sets which terms will be corrected */
5462
Settings mode;
5563

64+
/** @brief Order that correction methods will be applied. These will be attempted in order until one succeeds or all
65+
* have been tried */
66+
std::vector<CorrectionMethod> correction_workflow{ CorrectionMethod::TRAJOPT, CorrectionMethod::RANDOM_SAMPLER };
67+
5668
/** @brief Percent of the total joint range that a joint will be allowed to be adjusted */
5769
double jiggle_factor{ 0.02 };
5870

5971
/** @brief Safety margin applied to collision costs/cnts when using trajopt to correct collisions */
6072
double safety_margin{ 0.025 };
73+
74+
/** @brief Number of sampling attempts if TrajOpt correction fails*/
75+
int sampling_attempts{ 100 };
6176
};
6277
using FixStateCollisionProfileMap = std::unordered_map<std::string, FixStateCollisionProfile::Ptr>;
6378

6479
/**
6580
* @brief This generator modifies the const input instructions in order to push waypoints that are in collision out of
66-
* collision
81+
* collision.
82+
*
83+
* First it uses TrajOpt to correct the waypoint. If that fails, it reverts to random sampling
6784
*/
6885
class FixStateCollisionProcessGenerator : public ProcessGenerator
6986
{
@@ -100,5 +117,47 @@ class FixStateCollisionProcessGenerator : public ProcessGenerator
100117

101118
void process(ProcessInput input) const;
102119
};
120+
121+
/**
122+
* @brief Checks if a joint state is in collision
123+
* @param start_pos Vector that represents a joint state
124+
* @param input Process Input associated with waypoint. Needed for kinematics, etc.
125+
* @return True if in collision
126+
*/
127+
bool StateInCollision(const Eigen::Ref<const Eigen::VectorXd>& start_pos,
128+
const ProcessInput& input,
129+
const FixStateCollisionProfile& profile);
130+
131+
/**
132+
* @brief Checks if a waypoint is in collision
133+
* @param waypoint Must be a waypoint for which getJointPosition will return a position
134+
* @param input Process Input associated with waypoint. Needed for kinematics, etc.
135+
* @return True if in collision
136+
*/
137+
bool WaypointInCollision(const Waypoint& waypoint, const ProcessInput& input, const FixStateCollisionProfile& profile);
138+
139+
/**
140+
* @brief Takes a waypoint and uses a small trajopt problem to push it out of collision if necessary
141+
* @param waypoint Must be a waypoint for which getJointPosition will return a position
142+
* @param input Process Input associated with waypoint. Needed for kinematics, etc.
143+
* @param profile Profile containing needed params
144+
* @return True if successful
145+
*/
146+
bool MoveWaypointFromCollisionTrajopt(Waypoint& waypoint,
147+
const ProcessInput& input,
148+
const FixStateCollisionProfile& profile);
149+
150+
/**
151+
* @brief Takes a waypoint and uses random sampling to find a position that is out of collision
152+
* @param waypoint Must be a waypoint for which getJointPosition will return a position
153+
* @param input Process Input associated with waypoint. Needed for kinematics, etc.
154+
* @param profile Profile containing needed params
155+
* @return True if successful
156+
*/
157+
bool MoveWaypointFromCollisionRandomSampler(Waypoint& waypoint,
158+
const ProcessInput& input,
159+
const FixStateCollisionProfile& profile);
160+
161+
bool ApplyCorrectionWorkflow(Waypoint& waypoint, const ProcessInput& input, const FixStateCollisionProfile& profile);
103162
} // namespace tesseract_planning
104163
#endif // TESSERACT_PROCESS_MANAGERS_FIX_STATE_BOUNDS_PROCESS_GENERATOR_H

tesseract/tesseract_planning/tesseract_process_managers/src/process_generators/fix_state_collision_process_generator.cpp

Lines changed: 117 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,50 @@ TESSERACT_COMMON_IGNORE_WARNINGS_POP
3737

3838
namespace tesseract_planning
3939
{
40-
/**
41-
* @brief Checks if a waypoint is in collision
42-
* @param waypoint Must be a waypoint for which getJointPosition will return a position
43-
* @param input Process Input associated with waypoint. Needed for kinematics, etc.
44-
* @return True if in collision
45-
*/
46-
bool WaypointInCollision(const Waypoint& waypoint, const ProcessInput& input)
40+
bool StateInCollision(const Eigen::Ref<const Eigen::VectorXd>& start_pos,
41+
const ProcessInput& input,
42+
const FixStateCollisionProfile& profile)
43+
{
44+
using namespace tesseract_collision;
45+
using namespace tesseract_environment;
46+
47+
auto env = input.tesseract->getEnvironment();
48+
auto kin = input.tesseract->getManipulatorManager()->getFwdKinematicSolver(input.manip_info.manipulator);
49+
50+
std::vector<ContactResultMap> collisions;
51+
tesseract_environment::StateSolver::Ptr state_solver = env->getStateSolver();
52+
DiscreteContactManager::Ptr manager = env->getDiscreteContactManager();
53+
AdjacencyMap::Ptr adjacency_map = std::make_shared<tesseract_environment::AdjacencyMap>(
54+
env->getSceneGraph(), kin->getActiveLinkNames(), env->getCurrentState()->link_transforms);
55+
56+
manager->setActiveCollisionObjects(adjacency_map->getActiveLinkNames());
57+
manager->setContactDistanceThreshold(profile.safety_margin);
58+
collisions.clear();
59+
60+
tesseract_common::TrajArray traj(1, start_pos.size());
61+
traj.row(0) = start_pos.transpose();
62+
63+
// This never returns any collisions
64+
if (!checkTrajectory(collisions, *manager, *state_solver, kin->getJointNames(), traj))
65+
{
66+
CONSOLE_BRIDGE_logDebug("No collisions found");
67+
return false;
68+
}
69+
else
70+
{
71+
CONSOLE_BRIDGE_logDebug("Waypoint is not contact free!");
72+
for (std::size_t i = 0; i < collisions.size(); i++)
73+
for (const auto& contact_vec : collisions[i])
74+
for (const auto& contact : contact_vec.second)
75+
CONSOLE_BRIDGE_logDebug(("timestep: " + std::to_string(i) + " Links: " + contact.link_names[0] + ", " +
76+
contact.link_names[1] + " Dist: " + std::to_string(contact.distance))
77+
.c_str());
78+
}
79+
80+
return true;
81+
}
82+
83+
bool WaypointInCollision(const Waypoint& waypoint, const ProcessInput& input, const FixStateCollisionProfile& profile)
4784
{
4885
// Get position associated with waypoint
4986
Eigen::VectorXd start_pos;
@@ -56,56 +93,9 @@ bool WaypointInCollision(const Waypoint& waypoint, const ProcessInput& input)
5693
CONSOLE_BRIDGE_logError("WaypointInCollision error: %s", e.what());
5794
return false;
5895
}
59-
60-
// Check if there are collisions
61-
{
62-
using namespace tesseract_collision;
63-
using namespace tesseract_environment;
64-
65-
auto env = input.tesseract->getEnvironment();
66-
auto kin = input.tesseract->getManipulatorManager()->getFwdKinematicSolver(input.manip_info.manipulator);
67-
68-
std::vector<ContactResultMap> collisions;
69-
tesseract_environment::StateSolver::Ptr state_solver = env->getStateSolver();
70-
DiscreteContactManager::Ptr manager = env->getDiscreteContactManager();
71-
AdjacencyMap::Ptr adjacency_map = std::make_shared<tesseract_environment::AdjacencyMap>(
72-
env->getSceneGraph(), kin->getActiveLinkNames(), env->getCurrentState()->link_transforms);
73-
74-
manager->setActiveCollisionObjects(adjacency_map->getActiveLinkNames());
75-
manager->setContactDistanceThreshold(0);
76-
collisions.clear();
77-
78-
tesseract_common::TrajArray traj(1, start_pos.size());
79-
traj.row(0) = start_pos.transpose();
80-
81-
// This never returns any collisions
82-
if (!checkTrajectory(collisions, *manager, *state_solver, kin->getJointNames(), traj))
83-
{
84-
CONSOLE_BRIDGE_logDebug("No collisions found");
85-
return false;
86-
}
87-
else
88-
{
89-
CONSOLE_BRIDGE_logDebug("Waypoint is not contact free!");
90-
for (std::size_t i = 0; i < collisions.size(); i++)
91-
for (const auto& contact_vec : collisions[i])
92-
for (const auto& contact : contact_vec.second)
93-
CONSOLE_BRIDGE_logDebug(("timestep: " + std::to_string(i) + " Links: " + contact.link_names[0] + ", " +
94-
contact.link_names[1] + " Dist: " + std::to_string(contact.distance))
95-
.c_str());
96-
}
97-
}
98-
99-
return true;
96+
return StateInCollision(start_pos, input, profile);
10097
}
10198

102-
/**
103-
* @brief Takes a waypoint and uses a small trajopt problem to push it out of collision if necessary
104-
* @param waypoint Must be a waypoint for which getJointPosition will return a position
105-
* @param input Process Input associated with waypoint. Needed for kinematics, etc.
106-
* @param profile Profile containing needed params
107-
* @return True if successful
108-
*/
10999
bool MoveWaypointFromCollisionTrajopt(Waypoint& waypoint,
110100
const ProcessInput& input,
111101
const FixStateCollisionProfile& profile)
@@ -199,6 +189,70 @@ bool MoveWaypointFromCollisionTrajopt(Waypoint& waypoint,
199189
return setJointPosition(waypoint, results);
200190
}
201191

192+
bool MoveWaypointFromCollisionRandomSampler(Waypoint& waypoint,
193+
const ProcessInput& input,
194+
const FixStateCollisionProfile& profile)
195+
{
196+
// Get position associated with waypoint
197+
Eigen::VectorXd start_pos;
198+
try
199+
{
200+
start_pos = getJointPosition(waypoint);
201+
}
202+
catch (std::runtime_error& e)
203+
{
204+
CONSOLE_BRIDGE_logError("MoveWaypointFromCollision error: %s", e.what());
205+
return false;
206+
}
207+
208+
const auto kin = input.tesseract->getManipulatorManager()->getFwdKinematicSolver(input.manip_info.manipulator);
209+
Eigen::MatrixXd limits = kin->getLimits().joint_limits;
210+
Eigen::VectorXd range = limits.col(1).array() - limits.col(0).array();
211+
Eigen::VectorXd pos_sampling_limits = range * profile.jiggle_factor;
212+
Eigen::VectorXd neg_sampline_limits = range * -profile.jiggle_factor;
213+
214+
assert(start_pos.size() == range.size());
215+
for (int i = 0; i < profile.sampling_attempts; i++)
216+
{
217+
Eigen::VectorXd start_sampled_pos =
218+
start_pos + Eigen::VectorXd::Random(start_pos.size()).cwiseProduct(range) * profile.jiggle_factor;
219+
220+
// Make sure it doesn't violate joint limits
221+
Eigen::VectorXd sampled_pos = start_sampled_pos;
222+
sampled_pos = sampled_pos.cwiseMax(limits.col(0));
223+
sampled_pos = sampled_pos.cwiseMin(limits.col(1));
224+
225+
if (!StateInCollision(sampled_pos, input, profile))
226+
{
227+
return setJointPosition(waypoint, sampled_pos);
228+
}
229+
}
230+
231+
return false;
232+
}
233+
234+
bool ApplyCorrectionWorkflow(Waypoint& waypoint, const ProcessInput& input, const FixStateCollisionProfile& profile)
235+
{
236+
for (const auto& method : profile.correction_workflow)
237+
{
238+
switch (method)
239+
{
240+
case FixStateCollisionProfile::CorrectionMethod::NONE:
241+
return false; // No correction and in collision, so return false
242+
case FixStateCollisionProfile::CorrectionMethod::TRAJOPT:
243+
if (MoveWaypointFromCollisionTrajopt(waypoint, input, profile))
244+
return true;
245+
break;
246+
case FixStateCollisionProfile::CorrectionMethod::RANDOM_SAMPLER:
247+
if (MoveWaypointFromCollisionRandomSampler(waypoint, input, profile))
248+
return true;
249+
break;
250+
}
251+
}
252+
// If all methods have tried without returning, then correction failed
253+
return false;
254+
}
255+
202256
FixStateCollisionProcessGenerator::FixStateCollisionProcessGenerator(std::string name) : name_(std::move(name))
203257
{
204258
// Register default profile
@@ -268,10 +322,10 @@ int FixStateCollisionProcessGenerator::conditionalProcess(ProcessInput input) co
268322
if (instr_const_ptr)
269323
{
270324
PlanInstruction* mutable_instruction = const_cast<PlanInstruction*>(instr_const_ptr);
271-
if (WaypointInCollision(mutable_instruction->getWaypoint(), input))
325+
if (WaypointInCollision(mutable_instruction->getWaypoint(), input, *cur_composite_profile))
272326
{
273327
CONSOLE_BRIDGE_logInform("FixStateCollisionProcessGenerator is modifying the const input instructions");
274-
if (!MoveWaypointFromCollisionTrajopt(mutable_instruction->getWaypoint(), input, *cur_composite_profile))
328+
if (!ApplyCorrectionWorkflow(mutable_instruction->getWaypoint(), input, *cur_composite_profile))
275329
return 0;
276330
}
277331
}
@@ -283,10 +337,10 @@ int FixStateCollisionProcessGenerator::conditionalProcess(ProcessInput input) co
283337
if (instr_const_ptr)
284338
{
285339
PlanInstruction* mutable_instruction = const_cast<PlanInstruction*>(instr_const_ptr);
286-
if (WaypointInCollision(mutable_instruction->getWaypoint(), input))
340+
if (WaypointInCollision(mutable_instruction->getWaypoint(), input, *cur_composite_profile))
287341
{
288342
CONSOLE_BRIDGE_logInform("FixStateCollisionProcessGenerator is modifying the const input instructions");
289-
if (!MoveWaypointFromCollisionTrajopt(mutable_instruction->getWaypoint(), input, *cur_composite_profile))
343+
if (!ApplyCorrectionWorkflow(mutable_instruction->getWaypoint(), input, *cur_composite_profile))
290344
return 0;
291345
}
292346
}
@@ -304,7 +358,8 @@ int FixStateCollisionProcessGenerator::conditionalProcess(ProcessInput input) co
304358
bool in_collision = false;
305359
for (const auto& instruction : flattened)
306360
{
307-
in_collision |= WaypointInCollision(instruction.get().cast_const<PlanInstruction>()->getWaypoint(), input);
361+
in_collision |= WaypointInCollision(
362+
instruction.get().cast_const<PlanInstruction>()->getWaypoint(), input, *cur_composite_profile);
308363
}
309364
if (!in_collision)
310365
break;
@@ -315,7 +370,8 @@ int FixStateCollisionProcessGenerator::conditionalProcess(ProcessInput input) co
315370
const Instruction* instr_const_ptr = &instruction.get();
316371
Instruction* mutable_instruction = const_cast<Instruction*>(instr_const_ptr);
317372
PlanInstruction* plan = mutable_instruction->cast<PlanInstruction>();
318-
if (!MoveWaypointFromCollisionTrajopt(plan->getWaypoint(), input, *cur_composite_profile))
373+
374+
if (!ApplyCorrectionWorkflow(plan->getWaypoint(), input, *cur_composite_profile))
319375
return 0;
320376
}
321377
}

tesseract/tesseract_planning/tesseract_process_managers/test/CMakeLists.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,14 @@ target_code_coverage(${PROJECT_NAME}_unit ALL EXCLUDE ${COVERAGE_EXCLUDE} ENABLE
4343
add_gtest_discover_tests(${PROJECT_NAME}_unit)
4444
add_dependencies(${PROJECT_NAME}_unit ${PROJECT_NAME})
4545
add_dependencies(run_tests ${PROJECT_NAME}_unit)
46+
47+
add_executable(${PROJECT_NAME}_fix_state_collision_process_generator_unit fix_state_collision_process_generator_unit.cpp)
48+
target_link_libraries(${PROJECT_NAME}_fix_state_collision_process_generator_unit PRIVATE GTest::GTest GTest::Main tesseract::tesseract_support ${PROJECT_NAME})
49+
target_include_directories(${PROJECT_NAME}_fix_state_collision_process_generator_unit PUBLIC
50+
"$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/examples>")
51+
target_compile_options(${PROJECT_NAME}_fix_state_collision_process_generator_unit PRIVATE ${TESSERACT_COMPILE_OPTIONS})
52+
target_clang_tidy(${PROJECT_NAME}_fix_state_collision_process_generator_unit ARGUMENTS ${TESSERACT_CLANG_TIDY_ARGS} ENABLE ${TESSERACT_ENABLE_CLANG_TIDY})
53+
target_cxx_version(${PROJECT_NAME}_fix_state_collision_process_generator_unit PRIVATE VERSION ${TESSERACT_CXX_VERSION})
54+
target_code_coverage(${PROJECT_NAME}_fix_state_collision_process_generator_unit ALL EXCLUDE ${COVERAGE_EXCLUDE} ENABLE ${TESSERACT_ENABLE_TESTING})
55+
add_gtest_discover_tests(${PROJECT_NAME}_fix_state_collision_process_generator_unit)
56+
add_dependencies(run_tests ${PROJECT_NAME}_fix_state_collision_process_generator_unit)

0 commit comments

Comments
 (0)