Samples
Setup
Generate Planning Data
This example shows how to generate planning data for your robot cell, which is necessary to do before you can start the motion planner.
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Generation.h>
3
4#include <iostream>
5
6namespace
7{
8 void printException(const std::exception &e, const int level = 0)
9 {
10 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
11 try
12 {
13 std::rethrow_if_nested(e);
14 }
15 catch(const std::exception &nestedException)
16 {
17 printException(nestedException, level + 1);
18 }
19 catch(...)
20 {}
21 }
22} // namespace
23
24int main()
25{
26 try
27 {
28 const Zivid::Motion::Application app;
29
30 // Specify which cell you want to generate data for. This is referenced later
31 // when using the data to instantiate a planner.
32 constexpr auto cellName = "demo_cell";
33
34 // Specify which profile you want to generate data for (testing or production)
35 constexpr auto profile = Zivid::Motion::Profile::testing;
36
37 std::cout << "Generating data for " << cellName << "...\n";
38 Zivid::Motion::generate(
39 app,
40 Zivid::Motion::PlannerSettings{ cellName, profile },
41 [](const double progress, const std::string &step) {
42 std::cout << "Generation progress on step " << step << ": " << progress << "% complete\n";
43 });
44 std::cout << "Generation complete. Results stored on drive under the specified cell name.\n";
45 }
46 catch(const std::exception &exception)
47 {
48 printException(exception);
49 std::cout << "Press enter to exit." << std::endl;
50 std::cin.get();
51 return EXIT_FAILURE;
52 }
53 return EXIT_SUCCESS;
54}
1from zividmotion import Application, PlannerSettings, Profile, generate
2
3
4def _main() -> None:
5 app = Application()
6
7 # Specify which cell you want to generate data for. This is referenced later
8 # when using the data to instantiate a planner.
9 cell_name = "demo_cell"
10
11 # Specify which profile you want to generate data for (testing or production)
12 profile = Profile.testing
13
14 print(f"Generating {profile} data for {cell_name}...")
15 generate(
16 app,
17 PlannerSettings(cell_name, profile),
18 progress_callback=lambda progress, step: print(f"Generation progress on step {step}: {progress:.2f}% complete"),
19 )
20 print("Generation complete. Results stored on drive under the specified cell name.")
21
22
23if __name__ == "__main__":
24 _main()
Package Planner Setup
This example shows how to package all the files necessary for running the motion planner into a zip archive, which can be easily distributed and unpacked on other machines.
Run this snippet on the workstation where you have completed the planner setup:
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Packaging.h>
3
4#include <filesystem>
5#include <iostream>
6
7namespace
8{
9 void printException(const std::exception &e, const int level = 0)
10 {
11 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
12 try
13 {
14 std::rethrow_if_nested(e);
15 }
16 catch(const std::exception &nestedException)
17 {
18 printException(nestedException, level + 1);
19 }
20 catch(...)
21 {}
22 }
23} // namespace
24
25int main()
26{
27 try
28 {
29 const Zivid::Motion::Application app;
30 const std::filesystem::path outputPath = "desired/path/output.zip";
31
32 // In this example, we package not only the configuration files for the cell, but also the generated data
33 // for the testing setup.
34 std::cout << "Packaging data...\n";
35 Zivid::Motion::packageCell(app, "demo_cell", outputPath, { Zivid::Motion::Profile::testing });
36
37 std::cout << "Data package stored at: " << std::filesystem::canonical(outputPath) << "\n";
38 }
39 catch(const std::exception &exception)
40 {
41 printException(exception);
42 std::cout << "Press enter to exit." << std::endl;
43 std::cin.get();
44 return EXIT_FAILURE;
45 }
46 return EXIT_SUCCESS;
47}
1from pathlib import Path
2
3from zividmotion import Application, Profile, package_cell
4
5
6def _main() -> None:
7 app = Application()
8
9 output_path = Path("desired/path/output.zip")
10
11 # In this example, we package not only the configuration files for the cell, but also the generated data
12 # for the testing setup.
13 print("Packaging data...")
14 package_cell(app, cell_name="demo_cell", output_path=output_path, include_generated_data=[Profile.testing])
15 print(f"Data package stored at: {output_path.resolve()}")
16
17
18if __name__ == "__main__":
19 _main()
The recipients of the zip archive can then run this snippet to unpack the setup:
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Packaging.h>
3
4#include <filesystem>
5#include <iostream>
6
7namespace
8{
9 void printException(const std::exception &e, const int level = 0)
10 {
11 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
12 try
13 {
14 std::rethrow_if_nested(e);
15 }
16 catch(const std::exception &nestedException)
17 {
18 printException(nestedException, level + 1);
19 }
20 catch(...)
21 {}
22 }
23} // namespace
24
25int main()
26{
27 try
28 {
29 const Zivid::Motion::Application app;
30 const std::filesystem::path packagePath = "path/to/packaged/data.zip";
31
32 std::cout << "Installing package from " << std::filesystem::canonical(packagePath) << "...\n";
33 Zivid::Motion::installPackage(app, packagePath);
34
35 std::cout << "Package installed.\n";
36 }
37 catch(const std::exception &exception)
38 {
39 printException(exception);
40 std::cout << "Press enter to exit." << std::endl;
41 std::cin.get();
42 return EXIT_FAILURE;
43 }
44 return EXIT_SUCCESS;
45}
1from pathlib import Path
2
3from zividmotion import Application, install_package
4
5
6def _main() -> None:
7 app = Application()
8
9 package_path = Path("path/to/packaged/data.zip")
10
11 print(f"Installing package from {package_path.resolve()}...")
12 install_package(app, package_path)
13
14 print("Package installed.")
15
16
17if __name__ == "__main__":
18 _main()
Visualize Cell
This example shows how to open a 3D visualization window for a configured robot cell. This is useful for inspecting your cell setup before generating or running the motion planner.
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Visualizer.h>
3
4#include <iostream>
5
6namespace
7{
8 void printException(const std::exception &e, const int level = 0)
9 {
10 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
11 try
12 {
13 std::rethrow_if_nested(e);
14 }
15 catch(const std::exception &nestedException)
16 {
17 printException(nestedException, level + 1);
18 }
19 catch(...)
20 {}
21 }
22} // namespace
23
24int main()
25{
26 try
27 {
28 const Zivid::Motion::Application app;
29
30 // Specify the cell you want to visualize.
31 constexpr auto cellName = "demo_cell";
32
33 std::cout << "Opening visualizer for cell: " << cellName << "\n";
34 std::cout << "Close the window to exit.\n";
35
36 auto visualizer = Zivid::Motion::Visualizer::viewCell(app, cellName);
37 visualizer.wait();
38 }
39 catch(const std::exception &exception)
40 {
41 printException(exception);
42 std::cout << "Press enter to exit." << std::endl;
43 std::cin.get();
44 return EXIT_FAILURE;
45 }
46 return EXIT_SUCCESS;
47}
1from zividmotion import Application, Visualizer
2
3
4def _main() -> None:
5 app = Application()
6
7 # Specify the cell you want to visualize.
8 cell_name = "demo_cell"
9
10 print(f"Opening visualizer for cell: {cell_name}")
11 print("Close the window to exit.")
12
13 visualizer = Visualizer.view_cell(app, cell_name)
14 visualizer.wait()
15
16
17if __name__ == "__main__":
18 _main()
Planner
Basic Path Calls
Joint Goal
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Planner.h>
3#include <Zivid/Motion/Visualizer.h>
4
5#include <iostream>
6#include <stdexcept>
7
8namespace
9{
10 constexpr auto useVisualizer = true;
11
12 void printException(const std::exception &e, const int level = 0)
13 {
14 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
15 try
16 {
17 std::rethrow_if_nested(e);
18 }
19 catch(const std::exception &nestedException)
20 {
21 printException(nestedException, level + 1);
22 }
23 catch(...)
24 {}
25 }
26
27 Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
28 {
29 return app.createPlanner(
30 Zivid::Motion::PlannerSettings{
31 "demo_cell",
32 Zivid::Motion::Profile::testing,
33 });
34 }
35} // namespace
36
37int main()
38{
39 try
40 {
41 const Zivid::Motion::Application app;
42
43 std::cout << "Starting planner\n";
44 auto planner = startPlanner(app);
45 auto visualizer =
46 useVisualizer ? std::optional{ Zivid::Motion::Visualizer::viewPlanner(planner) } : std::nullopt;
47
48 const Zivid::Motion::Configuration startConfiguration = { 0.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
49 const Zivid::Motion::Configuration goalConfiguration = { 1.57f, 0.f, 0.f, 0.f, 1.57f, 0.f };
50
51 const auto result = planner.path(
52 Zivid::Motion::InitialState{ startConfiguration },
53 Zivid::Motion::PathRequest{ .goalConfigurations = { goalConfiguration },
54 .description = "Path with joint goal" });
55 if(!result)
56 {
57 throw std::runtime_error("Planning failed with result: " + result.toString());
58 }
59
60 std::cout << "Path result:\n" << result << "\n";
61
62 if(useVisualizer)
63 {
64 std::cout << "Close the window to exit.\n";
65 visualizer->wait();
66 }
67 }
68 catch(const std::exception &exception)
69 {
70 printException(exception);
71 std::cout << "Press enter to exit." << std::endl;
72 std::cin.get();
73 return EXIT_FAILURE;
74 }
75 return EXIT_SUCCESS;
76}
1from typing import Optional
2
3from zividmotion import Application, InitialState, PathRequest, Planner, PlannerSettings, Profile, Visualizer
4
5USE_VISUALIZER = True
6
7
8def _start_planner(app: Application) -> Planner:
9 planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
10 return app.create_planner(planner_settings)
11
12
13def _main() -> None:
14 app = Application()
15
16 print("Starting planner")
17 planner = _start_planner(app)
18 visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
19
20 start_configuration = [0.0, 0.0, 0.0, 0.0, 1.57, 0.0]
21 goal_configuration = [1.57, 0.0, 0.0, 0.0, 1.57, 0.0]
22
23 result = planner.path(
24 InitialState(start_configuration),
25 PathRequest(
26 goal_configurations=[goal_configuration],
27 description="Path with joint goal",
28 ),
29 )
30 if not result:
31 raise RuntimeError(f"Planning failed with error: {result.error}")
32
33 print(f"Path result: \n{result}\n")
34
35 if visualizer is not None:
36 print("Close the window to exit.")
37 visualizer.wait()
38
39
40if __name__ == "__main__":
41 _main()
Pose Goal
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Planner.h>
3#include <Zivid/Motion/Visualizer.h>
4
5#include <iostream>
6
7namespace
8{
9 constexpr auto useVisualizer = true;
10
11 void printException(const std::exception &e, const int level = 0)
12 {
13 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
14 try
15 {
16 std::rethrow_if_nested(e);
17 }
18 catch(const std::exception &nestedException)
19 {
20 printException(nestedException, level + 1);
21 }
22 catch(...)
23 {}
24 }
25
26 Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
27 {
28 return app.createPlanner(
29 Zivid::Motion::PlannerSettings{
30 "demo_cell",
31 Zivid::Motion::Profile::testing,
32 });
33 }
34} // namespace
35
36int main()
37{
38 try
39 {
40 const Zivid::Motion::Application app;
41
42 std::cout << "Starting planner\n";
43 auto planner = startPlanner(app);
44 auto visualizer =
45 useVisualizer ? std::optional{ Zivid::Motion::Visualizer::viewPlanner(planner) } : std::nullopt;
46
47 const Zivid::Motion::Configuration startConfiguration = { 0.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
48 constexpr Zivid::Motion::Pose poseGoal = { {
49 { 0.f, -1.f, 0.f, 0.f },
50 { -1.f, 0.f, 0.f, 1.45f },
51 { 0.f, 0.f, -1.f, 1.63f },
52 { 0.f, 0.f, 0.f, 1.f },
53 } };
54
55 const auto poseGoalConfiguration = planner.computeInverseKinematics(poseGoal, startConfiguration);
56 if(!poseGoalConfiguration.has_value())
57 {
58 throw std::runtime_error("No valid inverse kinematics solution found");
59 }
60
61 const auto result = planner.path(
62 Zivid::Motion::InitialState{ startConfiguration },
63 Zivid::Motion::PathRequest{ .goalConfigurations = { poseGoalConfiguration.value() },
64 .description = "Path with goal from pose" });
65 if(!result)
66 {
67 throw std::runtime_error("Planning failed with result: " + result.toString());
68 }
69
70 std::cout << "Path result:\n" << result << "\n";
71
72 if(useVisualizer)
73 {
74 std::cout << "Close the window to exit.\n";
75 visualizer->wait();
76 }
77 }
78 catch(const std::exception &exception)
79 {
80 printException(exception);
81 std::cout << "Press enter to exit." << std::endl;
82 std::cin.get();
83 return EXIT_FAILURE;
84 }
85 return EXIT_SUCCESS;
86}
1from typing import Optional
2
3import numpy as np
4from zividmotion import Application, InitialState, PathRequest, Planner, PlannerSettings, Profile, Visualizer
5
6USE_VISUALIZER = True
7
8
9def _start_planner(app: Application) -> Planner:
10 planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
11 return app.create_planner(planner_settings)
12
13
14def _main() -> None:
15 app = Application()
16
17 print("Starting planner")
18 planner = _start_planner(app)
19 visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
20
21 start_configuration = [0.0, 0.0, 0.0, 0.0, 1.57, 0.0]
22 pose_goal = np.array(
23 [
24 [0.0, -1.0, 0.0, 0.0],
25 [-1.0, 0.0, 0.0, 1.45],
26 [0.0, 0.0, -1.0, 1.63],
27 [0.0, 0.0, 0.0, 1.0],
28 ]
29 )
30
31 pose_goal_configuration = planner.compute_inverse_kinematics(
32 pose=pose_goal,
33 reference_configuration=start_configuration,
34 )
35 if pose_goal_configuration is None:
36 raise RuntimeError("No valid inverse kinematics solution found")
37
38 result = planner.path(
39 InitialState(start_configuration),
40 PathRequest(
41 goal_configurations=[pose_goal_configuration],
42 description="Path with goal from pose",
43 ),
44 )
45 if not result:
46 raise RuntimeError(f"Planning failed with error: {result.error}")
47
48 print(f"Path result: \n{result}\n")
49
50 if visualizer is not None:
51 print("Close the window to exit.")
52 visualizer.wait()
53
54
55if __name__ == "__main__":
56 _main()
Multiple Goals and Prioritization
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Planner.h>
3#include <Zivid/Motion/Visualizer.h>
4
5#include <cassert>
6#include <iostream>
7
8namespace
9{
10 constexpr auto useVisualizer = true;
11
12 void printException(const std::exception &e, const int level = 0)
13 {
14 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
15 try
16 {
17 std::rethrow_if_nested(e);
18 }
19 catch(const std::exception &nestedException)
20 {
21 printException(nestedException, level + 1);
22 }
23 catch(...)
24 {}
25 }
26
27 Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
28 {
29 return app.createPlanner(
30 Zivid::Motion::PlannerSettings{
31 "demo_cell",
32 Zivid::Motion::Profile::testing,
33 });
34 }
35} // namespace
36
37int main()
38{
39 try
40 {
41 const Zivid::Motion::Application app;
42
43 std::cout << "Starting planner\n";
44 auto planner = startPlanner(app);
45 auto visualizer =
46 useVisualizer ? std::optional{ Zivid::Motion::Visualizer::viewPlanner(planner) } : std::nullopt;
47
48 const Zivid::Motion::Configuration startConfiguration = { 0.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
49 const Zivid::Motion::InitialState initialState{ startConfiguration };
50
51 const Zivid::Motion::Configuration goalFarAway = { 3.14f, 0.f, 0.f, 0.f, 1.57f, 0.f };
52 const Zivid::Motion::Configuration goalCloser = { 1.57f, 0.1f, -0.1f, 0.f, 1.57f, 0.f };
53 const Zivid::Motion::Configuration goalClosest = { 1.57f, 0.f, 0.f, 0.f, 1.57f, 0.f };
54 const std::vector<Zivid::Motion::Configuration> goals = { goalFarAway, goalCloser, goalClosest };
55
56 const auto resultShortestPath = planner.path(
57 initialState,
58 Zivid::Motion::PathRequest{ .goalConfigurations = goals,
59 .description = "Multiple goals - Default (ShortestPath) prioritization" });
60 if(!resultShortestPath)
61 {
62 throw std::runtime_error("Planning with ShortestPath failed with result: " + resultShortestPath.toString());
63 }
64 assert(resultShortestPath.selectedGoalIdx == 2u);
65 std::cout << "ShortestPath selected goal index: " << resultShortestPath.selectedGoalIdx.value() << "\n";
66
67 const auto resultListOrder = planner.path(
68 initialState,
69 Zivid::Motion::PathRequest{ .goalPrioritizationMethod =
70 Zivid::Motion::PathRequest::GoalPrioritizationMethod::listOrder,
71 .goalConfigurations = goals,
72 .description = "Multiple goals - ListOrder prioritization" });
73 if(!resultListOrder)
74 {
75 throw std::runtime_error("Planning with ListOrder failed with result: " + resultListOrder.toString());
76 }
77 assert(resultListOrder.selectedGoalIdx == 0u);
78 std::cout << "ListOrder selected goal index: " << resultListOrder.selectedGoalIdx.value() << "\n";
79
80 if(useVisualizer)
81 {
82 std::cout << "Close the window to exit.\n";
83 visualizer->wait();
84 }
85 }
86 catch(const std::exception &exception)
87 {
88 printException(exception);
89 std::cout << "Press enter to exit." << std::endl;
90 std::cin.get();
91 return EXIT_FAILURE;
92 }
93 return EXIT_SUCCESS;
94}
1from typing import Optional
2
3from zividmotion import Application, InitialState, PathRequest, Planner, PlannerSettings, Profile, Visualizer
4
5USE_VISUALIZER = True
6
7
8def _start_planner(app: Application) -> Planner:
9 planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
10 return app.create_planner(planner_settings)
11
12
13def _main() -> None:
14 app = Application()
15
16 print("Starting planner")
17 planner = _start_planner(app)
18 visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
19
20 start_configuration = [0.0, 0.0, 0.0, 0.0, 1.57, 0.0]
21 initial_state = InitialState(start_configuration)
22
23 goal_far_away = [3.14, 0.0, 0.0, 0.0, 1.57, 0.0]
24 goal_closer = [1.57, 0.1, -0.1, 0.0, 1.57, 0.0]
25 goal_closest = [1.57, 0.0, 0.0, 0.0, 1.57, 0.0]
26 goals = [goal_far_away, goal_closer, goal_closest]
27
28 result_shortest_path = planner.path(
29 initial_state,
30 PathRequest(
31 goal_configurations=goals,
32 description="Multiple goals - Default (ShortestPath) prioritization",
33 ),
34 )
35 if not result_shortest_path:
36 raise RuntimeError(f"Planning with ShortestPath failed with error: {result_shortest_path.error}")
37 assert result_shortest_path.selected_goal_idx == 2
38 print(f"ShortestPath selected goal index: {result_shortest_path.selected_goal_idx}")
39
40 result_list_order = planner.path(
41 initial_state,
42 PathRequest(
43 goal_prioritization_method=PathRequest.GoalPrioritizationMethod.listOrder,
44 goal_configurations=goals,
45 description="Multiple goals - ListOrder prioritization",
46 ),
47 )
48 if not result_list_order:
49 raise RuntimeError(f"Planning with ListOrder failed with error: {result_list_order.error}")
50 assert result_list_order.selected_goal_idx == 0
51 print(f"ListOrder selected goal index: {result_list_order.selected_goal_idx}")
52
53 if visualizer is not None:
54 print("Close the window to exit.")
55 visualizer.wait()
56
57
58if __name__ == "__main__":
59 _main()
Setting Runtime Obstacles
Simple Obstacle Geometries
This example shows how you can add simple obstacles such as boxes to the motion planner’s dynamic environment model.
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Planner.h>
3#include <Zivid/Motion/Visualizer.h>
4
5#include <cassert>
6#include <iostream>
7
8namespace
9{
10 constexpr auto useVisualizer = true;
11
12 void printException(const std::exception &e, const int level = 0)
13 {
14 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
15 try
16 {
17 std::rethrow_if_nested(e);
18 }
19 catch(const std::exception &nestedException)
20 {
21 printException(nestedException, level + 1);
22 }
23 catch(...)
24 {}
25 }
26
27 Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
28 {
29 return app.createPlanner(
30 Zivid::Motion::PlannerSettings{
31 "demo_cell",
32 Zivid::Motion::Profile::testing,
33 });
34 }
35
36 Zivid::Motion::Triangles createBoxMesh(
37 const Zivid::Motion::PointXYZ ¢er,
38 const Zivid::Motion::VectorXYZ &dimensions)
39 {
40 const float hx = dimensions.x / 2.f;
41 const float hy = dimensions.y / 2.f;
42 const float hz = dimensions.z / 2.f;
43
44 // 8 vertices of the box
45 const Zivid::Motion::PointXYZ v000{ center.x - hx, center.y - hy, center.z - hz };
46 const Zivid::Motion::PointXYZ v100{ center.x + hx, center.y - hy, center.z - hz };
47 const Zivid::Motion::PointXYZ v010{ center.x - hx, center.y + hy, center.z - hz };
48 const Zivid::Motion::PointXYZ v110{ center.x + hx, center.y + hy, center.z - hz };
49 const Zivid::Motion::PointXYZ v001{ center.x - hx, center.y - hy, center.z + hz };
50 const Zivid::Motion::PointXYZ v101{ center.x + hx, center.y - hy, center.z + hz };
51 const Zivid::Motion::PointXYZ v011{ center.x - hx, center.y + hy, center.z + hz };
52 const Zivid::Motion::PointXYZ v111{ center.x + hx, center.y + hy, center.z + hz };
53
54 // 6 faces × 2 triangles each = 12 triangles
55 return {
56 // -Z face
57 { v000, v100, v110 },
58 { v000, v110, v010 },
59 // +Z face
60 { v001, v111, v101 },
61 { v001, v011, v111 },
62 // -Y face
63 { v000, v101, v100 },
64 { v000, v001, v101 },
65 // +Y face
66 { v010, v110, v111 },
67 { v010, v111, v011 },
68 // -X face
69 { v000, v010, v011 },
70 { v000, v011, v001 },
71 // +X face
72 { v100, v101, v111 },
73 { v100, v111, v110 },
74 };
75 }
76} // namespace
77
78int main()
79{
80 try
81 {
82 const Zivid::Motion::Application app;
83
84 std::cout << "Starting planner\n";
85 auto planner = startPlanner(app);
86 auto visualizer =
87 useVisualizer ? std::optional{ Zivid::Motion::Visualizer::viewPlanner(planner) } : std::nullopt;
88
89 const Zivid::Motion::Configuration startConfiguration = { 0.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
90 const Zivid::Motion::Configuration goalConfiguration = { 1.57f, 0.f, 0.f, 0.f, 1.57f, 0.f };
91 const Zivid::Motion::InitialState initialState{ startConfiguration };
92
93 const auto resultWithoutObstacle = planner.path(
94 initialState,
95 Zivid::Motion::PathRequest{ .goalConfigurations = { goalConfiguration },
96 .description = "Without obstacle" });
97 if(!resultWithoutObstacle)
98 {
99 throw std::runtime_error(
100 "Planning before obstacle is set failed with result: " + resultWithoutObstacle.toString());
101 }
102 assert(resultWithoutObstacle.path().size() == 1);
103 std::cout << "Path before obstacle is set: " << resultWithoutObstacle.path().size() << " waypoint\n";
104
105 // Set an obstacle that obstructs the direct path
106 const auto boxTriangles = createBoxMesh({ 1.1f, 1.0f, 1.6f }, { 0.2f, 0.2f, 0.2f });
107 planner.setObstacles({ Zivid::Motion::Obstacle::fromMesh("box_obstacle", boxTriangles) });
108
109 const auto resultWithObstacle = planner.path(
110 initialState,
111 Zivid::Motion::PathRequest{ .goalConfigurations = { goalConfiguration }, .description = "With obstacle" });
112 if(!resultWithObstacle)
113 {
114 throw std::runtime_error(
115 "Planning after obstacle is set failed with result: " + resultWithObstacle.toString());
116 }
117 assert(resultWithObstacle.path().size() > 1);
118 std::cout << "Path after obstacle is set: " << resultWithObstacle.path().size() << " waypoints\n";
119
120 if(useVisualizer)
121 {
122 std::cout << "Close the window to exit.\n";
123 visualizer->wait();
124 }
125 }
126 catch(const std::exception &exception)
127 {
128 printException(exception);
129 std::cout << "Press enter to exit." << std::endl;
130 std::cin.get();
131 return EXIT_FAILURE;
132 }
133 return EXIT_SUCCESS;
134}
Install additional dependencies with:
pip install trimesh
1from typing import Optional
2
3import trimesh
4from zividmotion import Application, InitialState, Obstacle, PathRequest, Planner, PlannerSettings, Profile, Visualizer
5
6USE_VISUALIZER = True
7
8
9def _start_planner(app: Application) -> Planner:
10 planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
11 return app.create_planner(planner_settings)
12
13
14def _create_box_mesh(center_point, size):
15 box = trimesh.creation.box(extents=size)
16 box.apply_translation(center_point)
17 return box.triangles.tolist()
18
19
20def _main() -> None:
21 app = Application()
22
23 print("Starting planner")
24 planner = _start_planner(app)
25 visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
26
27 start_configuration = [0.0, 0.0, 0.0, 0.0, 1.57, 0.0]
28 goal_configuration = [1.57, 0.0, 0.0, 0.0, 1.57, 0.0]
29 initial_state = InitialState(start_configuration=start_configuration)
30
31 result_without_obstacle = planner.path(
32 initial_state=initial_state,
33 request=PathRequest(
34 goal_configurations=[goal_configuration],
35 description="Without obstacle",
36 ),
37 )
38 if not result_without_obstacle:
39 raise RuntimeError(f"Planning before obstacle is set failed with error: {result_without_obstacle.error}")
40 assert len(result_without_obstacle.path) == 1
41 print(f"Path before obstacle is set: {len(result_without_obstacle.path)} waypoint")
42
43 # Set an obstacle that obstructs the direct path
44 box_triangles = _create_box_mesh(center_point=[1.1, 1.0, 1.6], size=[0.2, 0.2, 0.2])
45 planner.set_obstacles(
46 obstacles=[
47 Obstacle.from_mesh(name="box_obstacle", mesh=box_triangles),
48 ]
49 )
50
51 result_with_obstacle = planner.path(
52 initial_state=initial_state,
53 request=PathRequest(
54 goal_configurations=[goal_configuration],
55 description="With obstacle",
56 ),
57 )
58 if not result_with_obstacle:
59 raise RuntimeError(f"Planning after obstacle is set failed with error: {result_with_obstacle.error}")
60 assert len(result_with_obstacle.path) > 1
61 print(f"Path after obstacle is set: {len(result_with_obstacle.path)} waypoints")
62
63 if visualizer is not None:
64 print("Close the window to exit.")
65 visualizer.wait()
66
67
68if __name__ == "__main__":
69 _main()
Obstacle from CAD File
This example shows how you can add custom cad files to the motion planner’s dynamic environment model. Note that cad files that represent static obstacles are more efficiently handled if added as static obstacles in the urdf files, rather than as dynamic obstacles in runtime.
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Planner.h>
3#include <Zivid/Motion/Visualizer.h>
4
5#include <assimp/Importer.hpp>
6#include <assimp/postprocess.h>
7#include <assimp/scene.h>
8
9#include <filesystem>
10#include <iostream>
11
12namespace
13{
14 constexpr auto useVisualizer = true;
15
16 void printException(const std::exception &e, const int level = 0)
17 {
18 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
19 try
20 {
21 std::rethrow_if_nested(e);
22 }
23 catch(const std::exception &nestedException)
24 {
25 printException(nestedException, level + 1);
26 }
27 catch(...)
28 {}
29 }
30
31 Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
32 {
33 return app.createPlanner(
34 Zivid::Motion::PlannerSettings{
35 "demo_cell",
36 Zivid::Motion::Profile::testing,
37 });
38 }
39
40 Zivid::Motion::Triangles loadTrianglesFromCad(const std::filesystem::path &cadFilePath)
41 {
42 Assimp::Importer importer;
43 const aiScene *scene = importer.ReadFile(cadFilePath, aiProcess_Triangulate | aiProcess_JoinIdenticalVertices);
44
45 if(!scene || !scene->HasMeshes())
46 {
47 throw std::runtime_error("Failed to load CAD file: " + cadFilePath.string());
48 }
49
50 Zivid::Motion::Triangles triangles;
51 for(unsigned int m = 0; m < scene->mNumMeshes; ++m)
52 {
53 const aiMesh *mesh = scene->mMeshes[m];
54 for(unsigned int f = 0; f < mesh->mNumFaces; ++f)
55 {
56 const aiFace &face = mesh->mFaces[f];
57 if(face.mNumIndices != 3)
58 {
59 continue; // skip non-triangles (shouldn't happen with aiProcess_Triangulate)
60 }
61
62 const auto toPoint = [&](unsigned int idx) {
63 const aiVector3D &v = mesh->mVertices[face.mIndices[idx]];
64 return Zivid::Motion::PointXYZ{ v.x, v.y, v.z };
65 };
66
67 triangles.push_back({ toPoint(0), toPoint(1), toPoint(2) });
68 }
69 }
70
71 if(triangles.empty())
72 {
73 throw std::runtime_error("No triangles found in CAD file: " + cadFilePath.filename().string());
74 }
75 return triangles;
76 }
77} // namespace
78
79int main()
80{
81 try
82 {
83 const Zivid::Motion::Application app;
84
85 std::cout << "Starting planner\n";
86 auto planner = startPlanner(app);
87 auto visualizer =
88 useVisualizer ? std::optional{ Zivid::Motion::Visualizer::viewPlanner(planner) } : std::nullopt;
89
90 // Make sure the CAD uses meters as unit, not millimeters
91 const std::filesystem::path cadFilePath = "path/to/obstacle.stl";
92
93 std::cout << "Loading CAD file from: " << cadFilePath << "\n";
94 const auto triangles = loadTrianglesFromCad(cadFilePath);
95
96 planner.setObstacles({ Zivid::Motion::Obstacle::fromMesh("cad_obstacle", triangles) });
97
98 if(useVisualizer)
99 {
100 std::cout << "Close the window to exit.\n";
101 visualizer->wait();
102 }
103 }
104 catch(const std::exception &exception)
105 {
106 printException(exception);
107 std::cout << "Press enter to exit." << std::endl;
108 std::cin.get();
109 return EXIT_FAILURE;
110 }
111 return EXIT_SUCCESS;
112}
Install additional dependencies with:
pip install trimesh
1from pathlib import Path
2from typing import Optional
3
4import trimesh
5from zividmotion import Application, Obstacle, Planner, PlannerSettings, Profile, Visualizer
6
7USE_VISUALIZER = True
8
9
10def _start_planner(app: Application) -> Planner:
11 planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
12 return app.create_planner(planner_settings)
13
14
15def _load_triangles_from_cad_file(cad_file_path: Path) -> list:
16 mesh = trimesh.load(cad_file_path)
17 return mesh.triangles.tolist()
18
19
20def _main() -> None:
21 app = Application()
22
23 print("Starting planner")
24 planner = _start_planner(app)
25 visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
26
27 # Make sure the CAD uses meters as unit, not millimeters
28 cad_file_path = Path("path/to/obstacle.stl")
29
30 print("Loading CAD file from:", cad_file_path)
31 triangles = _load_triangles_from_cad_file(cad_file_path)
32
33 planner.set_obstacles(
34 obstacles=[
35 Obstacle.from_mesh(name="cad_obstacle", mesh=triangles),
36 ]
37 )
38
39 if visualizer is not None:
40 print("Close the window to exit.")
41 visualizer.wait()
42
43
44if __name__ == "__main__":
45 _main()
Obstacle from Zivid Point Cloud
This example shows how to add a point cloud from a Zivid camera to the motion planner’s dynamic environment model. This requires having the Zivid SDK installed, and being connected to a Zivid camera.
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Planner.h>
3#include <Zivid/Motion/Visualizer.h>
4#include <Zivid/Zivid.h>
5
6#include <iostream>
7
8namespace
9{
10 constexpr auto useVisualizer = true;
11 constexpr auto useFileCamera = true;
12
13 Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
14 {
15 return app.createPlanner(
16 Zivid::Motion::PlannerSettings{
17 "demo_cell",
18 Zivid::Motion::Profile::testing,
19 });
20 }
21} // namespace
22
23int main()
24{
25 try
26 {
27 Zivid::Application cameraApp;
28 const Zivid::Motion::Application app;
29
30 std::cout << "Connecting to camera\n";
31 auto camera = useFileCamera ? cameraApp.createFileCamera("/path/to/FileCamera.zfc") : cameraApp.connectCamera();
32
33 std::cout << "Starting planner\n";
34 auto planner = startPlanner(app);
35 auto visualizer =
36 useVisualizer ? std::optional{ Zivid::Motion::Visualizer::viewPlanner(planner) } : std::nullopt;
37
38 // Retrieved from hand-eye calibration of your setup.
39 // In an eye-in-hand setup, this would be the handeye_transform * robot_capture_pose.
40 // In an eye-to-hand setup, this would simply be the handeye_transform.
41 const Zivid::Matrix4x4 cameraToBaseTransform{ {
42 { 1.f, 0.f, 0.f, 0.f },
43 { 0.f, 1.f, 0.f, 0.f },
44 { 0.f, 0.f, 1.f, 0.f },
45 { 0.f, 0.f, 0.f, 1.f },
46 } };
47
48 std::cout << "Capturing with default capture settings\n";
49 const auto settings =
50 Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} },
51 Zivid::Settings::Color{ Zivid::Settings2D{
52 Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{} } } } };
53 const auto frame = camera.capture(settings);
54
55 // Downsampling improves speed, if you don't need the extra resolution
56 frame.pointCloud().downsample(Zivid::PointCloud::Downsampling::by4x4);
57
58 // Transform the point cloud from the camera frame to the base frame of the planner
59 frame.pointCloud().transform(cameraToBaseTransform);
60
61 const auto xyzData = frame.pointCloud().toUnorganizedPointCloud().copyPointsXYZ();
62 std::vector<Zivid::Motion::PointXYZ> points;
63 points.reserve(xyzData.size());
64 for(const auto &p : xyzData)
65 {
66 constexpr auto mmToM = 1.f / 1000.f; // Convert from millimeters to meters
67 points.emplace_back(Zivid::Motion::PointXYZ{ p.x * mmToM, p.y * mmToM, p.z * mmToM });
68 }
69 planner.setObstacles({ Zivid::Motion::Obstacle::fromPointCloud("point_cloud_obstacle", points) });
70
71 if(useVisualizer)
72 {
73 std::cout << "Close the window to exit.\n";
74 visualizer->wait();
75 }
76 }
77 catch(const std::exception &e)
78 {
79 std::cerr << "Error: " << e.what() << std::endl;
80 std::cout << "Press enter to exit." << std::endl;
81 std::cin.get();
82 return EXIT_FAILURE;
83 }
84 return EXIT_SUCCESS;
85}
1from typing import Optional
2
3import numpy as np
4import zivid
5from zividmotion import Application, Obstacle, Planner, PlannerSettings, Profile, Visualizer
6
7USE_VISUALIZER = True
8USE_FILE_CAMERA = True
9
10
11def _start_planner(app: Application) -> Planner:
12 planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
13 return app.create_planner(planner_settings)
14
15
16def _main() -> None:
17 camera_app = zivid.Application()
18 motion_app = Application()
19
20 print("Connecting to camera")
21 camera = (
22 camera_app.create_file_camera("/path/to/FileCamera.zfc") if USE_FILE_CAMERA else camera_app.connect_camera()
23 )
24
25 print("Starting planner")
26 planner = _start_planner(motion_app)
27 visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
28
29 # Retrieved from hand-eye calibration of your setup.
30 # In an eye-in-hand setup, this would be the handeye_transform * robot_capture_pose.
31 # In an eye-to-hand setup, this would simply be the handeye_transform.
32 camera_to_base_transform = np.eye(4)
33
34 print("Capturing with default capture settings")
35 # Replace with your own capture settings for better results
36 capture_settings = zivid.Settings(
37 acquisitions=[zivid.Settings.Acquisition()],
38 color=zivid.Settings2D(acquisitions=[zivid.Settings2D.Acquisition()]),
39 )
40
41 frame = camera.capture(capture_settings)
42
43 # Downsampling improves speed, if you don't need the extra resolution
44 frame.point_cloud().downsample(zivid.PointCloud.Downsampling.by4x4)
45
46 # Transform the point cloud from the camera frame to the base frame of the planner
47 frame.point_cloud().transform(camera_to_base_transform)
48
49 point_cloud = frame.point_cloud().to_unorganized_point_cloud()
50 points = point_cloud.copy_data("xyz") / 1000.0 # Convert from millimeters to meters
51 obstacle = Obstacle.from_point_cloud(name="point_cloud_obstacle", points=points)
52 planner.set_obstacles(obstacles=[obstacle])
53
54 if USE_VISUALIZER:
55 input("Press enter to continue:")
56
57 # Set the obstacle again, but this time with color.
58 # Note that this has a slight performance impact, not recommended in production code.
59 planner.clear_obstacles()
60
61 rgba = point_cloud.copy_data("rgba")
62 colored_obstacle = Obstacle.from_colored_point_cloud(name="colored_obstacle", points=points, colors=rgba)
63 planner.set_obstacles(obstacles=[colored_obstacle])
64
65 if visualizer is not None:
66 print("Close the window to exit.")
67 visualizer.wait()
68
69
70if __name__ == "__main__":
71 _main()
Interaction Planning
Simple Touch Approach
This example shows how to use the Touch functionality to plan an approach to an object to be picked in a simplified setup.
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Planner.h>
3#include <Zivid/Motion/Visualizer.h>
4
5#include <cassert>
6#include <cmath>
7#include <iostream>
8#include <random>
9
10namespace
11{
12 constexpr auto useVisualizer = true;
13
14 void printException(const std::exception &e, const int level = 0)
15 {
16 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
17 try
18 {
19 std::rethrow_if_nested(e);
20 }
21 catch(const std::exception &nestedException)
22 {
23 printException(nestedException, level + 1);
24 }
25 catch(...)
26 {}
27 }
28
29 Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
30 {
31 return app.createPlanner(
32 Zivid::Motion::PlannerSettings{
33 "demo_cell",
34 Zivid::Motion::Profile::testing,
35 });
36 }
37
38 // Utility function for creating a dummy point-cloud obstacle
39 std::vector<Zivid::Motion::PointXYZ>
40 generateSpherePoints(const Zivid::Motion::PointXYZ ¢er, const float radius, const int numPoints)
41 {
42 // Fixed seed for determinism
43 std::mt19937 rng(42);
44 std::uniform_real_distribution<float> thetaDist(0, 2 * M_PI);
45 std::uniform_real_distribution<float> phiDist(0, M_PI);
46
47 std::vector<Zivid::Motion::PointXYZ> points;
48 points.reserve(numPoints);
49 for(int i = 0; i < numPoints; ++i)
50 {
51 const float theta = thetaDist(rng);
52 const float phi = phiDist(rng);
53 points.push_back(
54 {
55 center.x + radius * std::sin(phi) * std::cos(theta),
56 center.y + radius * std::sin(phi) * std::sin(theta),
57 center.z + radius * std::cos(phi),
58 });
59 }
60 return points;
61 }
62
63 // Create a pick pose with the end-effector pointing downwards at the given point
64 // (180-degree rotation around Y-axis)
65 Zivid::Motion::Pose getPickPose(const Zivid::Motion::PointXYZ &pickPoint)
66 {
67 return { {
68 { -1.f, 0.f, 0.f, pickPoint.x },
69 { 0.f, 1.f, 0.f, pickPoint.y },
70 { 0.f, 0.f, -1.f, pickPoint.z },
71 { 0.f, 0.f, 0.f, 1.f },
72 } };
73 }
74} // namespace
75
76int main()
77{
78 try
79 {
80 const Zivid::Motion::Application app;
81
82 std::cout << "Starting planner\n";
83 auto planner = startPlanner(app);
84 auto visualizer =
85 useVisualizer ? std::optional{ Zivid::Motion::Visualizer::viewPlanner(planner) } : std::nullopt;
86
87 const Zivid::Motion::Configuration startConfiguration = { 0.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
88
89 // Set a dummy obstacle to be interacted with
90 constexpr Zivid::Motion::PointXYZ obstacleCenter{ 1.1f, 1.0f, 0.9f };
91 constexpr float obstacleRadius = 0.2f;
92 planner.setObstacles(
93 { Zivid::Motion::Obstacle::fromPointCloud(
94 "interaction_object",
95 Zivid::Motion::Obstacle::PointCloud{ generateSpherePoints(obstacleCenter, obstacleRadius, 1000) }) });
96
97 // Find the pick joint configuration
98 constexpr Zivid::Motion::PointXYZ pickPoint{
99 obstacleCenter.x,
100 obstacleCenter.y,
101 obstacleCenter.z + obstacleRadius
102 - 0.01f, // Put goal 1cm into the object for good contact with gripper compliance
103 };
104 const auto pickConfiguration = planner.computeInverseKinematics(getPickPose(pickPoint), startConfiguration);
105 if(!pickConfiguration.has_value())
106 {
107 throw std::runtime_error("No valid inverse kinematics solution found for pick pose");
108 }
109
110 const Zivid::Motion::InitialState initialState{ startConfiguration };
111
112 // Path type defaults to Free
113 const auto resultFree = planner.path(
114 initialState,
115 Zivid::Motion::PathRequest{ .goalConfigurations = { pickConfiguration.value() },
116 .description = "Free path to pick goal" });
117 // Expect blocked end with path type Free, since the goal is inside the obstacle
118 assert(resultFree.status == Zivid::Motion::PlanResult::Status::blockedEnd);
119
120 const auto resultTouch = planner.path(
121 initialState,
122 Zivid::Motion::PathRequest{ .type = Zivid::Motion::PathRequest::Type::touch,
123 .goalConfigurations = { pickConfiguration.value() },
124 .description = "Touch path to pick goal" });
125 if(!resultTouch)
126 {
127 throw std::runtime_error("Planning with path type Touch failed with result: " + resultTouch.toString());
128 }
129
130 std::cout << "\nSuccessful touch path: " << resultTouch << "\n";
131
132 if(useVisualizer)
133 {
134 std::cout << "Close the window to exit.\n";
135 visualizer->wait();
136 }
137 }
138 catch(const std::exception &exception)
139 {
140 printException(exception);
141 std::cout << "Press enter to exit." << std::endl;
142 std::cin.get();
143 return EXIT_FAILURE;
144 }
145 return EXIT_SUCCESS;
146}
Install additional dependencies with:
pip install scipy
1from typing import Optional
2
3import numpy as np
4from scipy.spatial.transform import Rotation
5from zividmotion import (
6 Application,
7 InitialState,
8 Obstacle,
9 PathRequest,
10 PathResult,
11 Planner,
12 PlannerSettings,
13 Profile,
14 Visualizer,
15)
16
17USE_VISUALIZER = True
18
19
20def _start_planner(app: Application) -> Planner:
21 planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
22 return app.create_planner(planner_settings)
23
24
25# Utility method for creating a dummy obstacle
26def _generate_sphere_points(center_point: list[float], radius: float, num_points: int) -> list[list[float]]:
27 np.random.seed(42) # Set a fixed random seed for determinism
28 points = []
29 for _ in range(num_points):
30 theta = np.random.uniform(0, 2 * np.pi)
31 phi = np.random.uniform(0, np.pi)
32 x = center_point[0] + radius * np.sin(phi) * np.cos(theta)
33 y = center_point[1] + radius * np.sin(phi) * np.sin(theta)
34 z = center_point[2] + radius * np.cos(phi)
35 points.append([x, y, z])
36 return points
37
38
39def _get_pick_pose(pick_point: list[float]) -> np.ndarray:
40 # Create a simple pick pose with the end-effector pointing downwards at the pick point
41 pick_pose = np.eye(4, dtype=np.float32)
42 pick_pose[:3, 3] = pick_point
43 # Rotate 180 degrees around Y-axis to point downwards
44 pick_pose[:3, :3] = Rotation.from_rotvec([0, np.pi, 0]).as_matrix()
45 return pick_pose
46
47
48def _main() -> None:
49 app = Application()
50
51 print("Starting planner")
52 planner = _start_planner(app)
53 visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
54
55 start_configuration = [0.0, 0.0, 0.0, 0.0, 1.57, 0.0]
56
57 # Set a dummy obstacle to be interacted with
58 center_point = [1.1, 1.0, 0.9]
59 radius = 0.2
60 planner.set_obstacles(
61 obstacles=[
62 Obstacle.from_point_cloud(
63 name="interaction_object",
64 points=_generate_sphere_points(center_point=center_point, radius=radius, num_points=1000),
65 )
66 ]
67 )
68
69 # Find the pick joint configuration
70 pick_point = [center_point[0], center_point[1], center_point[2] + radius]
71 pick_point[2] -= 0.01 # Put goal 1cm into the object, for instance for good contact with gripper compliance
72 pick_pose = _get_pick_pose(pick_point)
73 pick_configuration = planner.compute_inverse_kinematics(pose=pick_pose, reference_configuration=start_configuration)
74 if pick_configuration is None:
75 raise RuntimeError("No valid inverse kinematics solution found for pick pose")
76
77 initial_state = InitialState(start_configuration=start_configuration)
78 # Path type defaults to Free
79 result_free = planner.path(
80 initial_state=initial_state,
81 request=PathRequest(
82 goal_configurations=[pick_configuration],
83 description="Free path to pick goal",
84 ),
85 )
86 # Expect blocked end with path type Free, since the goal is inside the obstacle
87 assert result_free.error == PathResult.Error.blockedEnd
88
89 # Specify type touch
90 result_touch = planner.path(
91 initial_state=initial_state,
92 request=PathRequest(
93 type=PathRequest.Type.touch,
94 goal_configurations=[pick_configuration],
95 description="Touch path to pick goal",
96 ),
97 )
98 if not result_touch:
99 raise RuntimeError(f"Planning with path type Touch failed with error: {result_touch.error}")
100
101 print("\nSuccessful touch path:", result_touch.path)
102
103 if visualizer is not None:
104 print("Close the window to exit.")
105 visualizer.wait()
106
107
108if __name__ == "__main__":
109 _main()
Pick Approach and Retract with Replaceable Tool
This example shows how to do a simplified object pick, including approach and retract paths, with a replaceable tool and carried object.
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Planner.h>
3#include <Zivid/Motion/Visualizer.h>
4
5#include <cmath>
6#include <iostream>
7#include <random>
8
9namespace
10{
11 constexpr auto useVisualizer = true;
12
13 void printException(const std::exception &e, const int level = 0)
14 {
15 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
16 try
17 {
18 std::rethrow_if_nested(e);
19 }
20 catch(const std::exception &nestedException)
21 {
22 printException(nestedException, level + 1);
23 }
24 catch(...)
25 {}
26 }
27
28 Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
29 {
30 return app.createPlanner(
31 Zivid::Motion::PlannerSettings{
32 "demo_cell",
33 Zivid::Motion::Profile::testing,
34 });
35 }
36
37 // Utility function for creating a dummy point-cloud obstacle
38 std::vector<Zivid::Motion::PointXYZ>
39 generateSpherePoints(const Zivid::Motion::PointXYZ ¢er, const float radius, const int numPoints)
40 {
41 // Fixed seed for determinism
42 std::mt19937 rng(42);
43 std::uniform_real_distribution<float> thetaDist(0, 2 * M_PI);
44 std::uniform_real_distribution<float> phiDist(0, M_PI);
45
46 std::vector<Zivid::Motion::PointXYZ> points;
47 points.reserve(numPoints);
48 for(int i = 0; i < numPoints; ++i)
49 {
50 const float theta = thetaDist(rng);
51 const float phi = phiDist(rng);
52 points.push_back(
53 {
54 center.x + radius * std::sin(phi) * std::cos(theta),
55 center.y + radius * std::sin(phi) * std::sin(theta),
56 center.z + radius * std::cos(phi),
57 });
58 }
59 return points;
60 }
61
62 std::vector<Zivid::Motion::Obstacle> getDummyObstacles(const Zivid::Motion::PointXYZ ¢er, const float radius)
63 {
64 std::vector<Zivid::Motion::Obstacle> obstacles;
65 for(const float dx : { -2.f * radius, 0.f, 2.f * radius })
66 {
67 for(const float dy : { -2.f * radius, 0.f, 2.f * radius })
68 {
69 const Zivid::Motion::PointXYZ c{ center.x + dx, center.y + dy, center.z };
70 const auto name = "obstacle_" + std::to_string(obstacles.size());
71 obstacles.push_back(
72 Zivid::Motion::Obstacle::fromPointCloud(
73 name, Zivid::Motion::Obstacle::PointCloud{ generateSpherePoints(c, radius, 400) }));
74 }
75 }
76 return obstacles;
77 }
78} // namespace
79
80int main()
81{
82 try
83 {
84 const Zivid::Motion::Application app;
85
86 std::cout << "Starting planner\n";
87 auto planner = startPlanner(app);
88 auto visualizer =
89 useVisualizer ? std::optional{ Zivid::Motion::Visualizer::viewPlanner(planner) } : std::nullopt;
90
91 constexpr Zivid::Motion::PointXYZ obstacleCenter{ 1.1f, 1.0f, 0.2f };
92 constexpr float obstacleRadius = 0.15f;
93 planner.setObstacles(getDummyObstacles(obstacleCenter, obstacleRadius));
94
95 // For example purposes, simulate a suction cup gripper that is mounted asymmetrically at a 45 degree angle
96 constexpr float angle = M_PI_4;
97 const float cosA = std::cos(angle);
98 const float sinA = std::sin(angle);
99 const Zivid::Motion::Pose toolTransform{ {
100 { cosA, -sinA, 0.f, 0.f },
101 { sinA, cosA, 0.f, 0.f },
102 { 0.f, 0.f, 1.f, 0.05f },
103 { 0.f, 0.f, 0.f, 1.f },
104 } };
105 constexpr Zivid::Motion::VectorXYZ toolDimensions{ 0.4f, 0.4f, 0.06f };
106 // Specify the last 2cm of the gripper as the compliant suction cup area
107 constexpr Zivid::Motion::Pose compliantTransform{ {
108 { 1.f, 0.f, 0.f, 0.f },
109 { 0.f, 1.f, 0.f, 0.f },
110 { 0.f, 0.f, 1.f, toolDimensions.z - 0.02f },
111 { 0.f, 0.f, 0.f, 1.f },
112 } };
113 constexpr Zivid::Motion::VectorXYZ compliantDimensions{ 0.4f, 0.4f, 0.02f };
114 planner.setReplaceableTool(
115 Zivid::Motion::ReplaceableTool::fromBoxElements(
116 { Zivid::Motion::ToolBoxElement{
117 .transform = toolTransform,
118 .rigidBoxDimensions = toolDimensions,
119 .compliantBox = Zivid::Motion::TransformedBox{ compliantTransform, compliantDimensions } } }));
120
121 // Set the new tcp to be at the center tip of the gripper, compressed 10 mm
122 Zivid::Motion::Pose tcpTransform = toolTransform;
123 tcpTransform[2][3] += toolDimensions.z - 0.01f;
124 planner.setTcp(Zivid::Motion::Tcp{ tcpTransform, { 0.f, 0.f, 1.f } });
125
126 const Zivid::Motion::Configuration startConfiguration = { 0.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
127
128 // Define a pick pose: above the obstacle center, end-effector rotated 180 degrees around Y to point downwards
129 constexpr Zivid::Motion::Pose pickPose{ {
130 { -1.f, 0.f, 0.f, obstacleCenter.x },
131 { 0.f, 1.f, 0.f, obstacleCenter.y },
132 { 0.f, 0.f, -1.f, obstacleCenter.z + obstacleRadius },
133 { 0.f, 0.f, 0.f, 1.f },
134 } };
135 const auto pickConfiguration = planner.computeInverseKinematics(pickPose, startConfiguration);
136 if(!pickConfiguration.has_value())
137 {
138 throw std::runtime_error("No valid inverse kinematics solution found for pick pose");
139 }
140
141 // Compute a pick approach path with the new tool
142 const auto approachResult = planner.path(
143 Zivid::Motion::InitialState{ startConfiguration },
144 Zivid::Motion::PathRequest{ .type = Zivid::Motion::PathRequest::Type::touch,
145 .goalConfigurations = { pickConfiguration.value() },
146 .description = "Pick approach" });
147 if(!approachResult)
148 {
149 throw std::runtime_error("Planning pick approach failed with result: " + approachResult.toString());
150 }
151
152 // Set carried object (bounding box around the picked obstacle)
153 constexpr float objectSize = obstacleRadius * 2.f;
154 constexpr Zivid::Motion::Pose identityPose{ {
155 { 1.f, 0.f, 0.f, 0.f },
156 { 0.f, 1.f, 0.f, 0.f },
157 { 0.f, 0.f, 1.f, 0.f },
158 { 0.f, 0.f, 0.f, 1.f },
159 } };
160 planner.setCarriedObject(
161 Zivid::Motion::CarriedObject::fromBox({ identityPose, { objectSize, objectSize, objectSize } }));
162
163 // Compute a pick retract path with custom retract direction
164 // This retract direction is the same as the negative tcp tool direction expressed in the base frame when the robot
165 // is in the start configuration for this path call. Meaning it in this case is a redundant specification, just to
166 // show the signature for example purposes.
167 const auto retractResult = planner.path(
168 Zivid::Motion::InitialState{ approachResult },
169 Zivid::Motion::PathRequest{ .type = Zivid::Motion::PathRequest::Type::touch,
170 .retractDirection = Zivid::Motion::VectorXYZ{ 0.f, 0.f, 1.f },
171 .goalConfigurations = { startConfiguration },
172 .description = "Pick retract" });
173 if(!retractResult)
174 {
175 throw std::runtime_error("Planning pick retract failed with result: " + retractResult.toString());
176 }
177
178 std::cout << retractResult << "\n";
179
180 if(useVisualizer)
181 {
182 std::cout << "Close the window to exit.\n";
183 visualizer->wait();
184 }
185 }
186 catch(const std::exception &exception)
187 {
188 printException(exception);
189 std::cout << "Press enter to exit." << std::endl;
190 std::cin.get();
191 return EXIT_FAILURE;
192 }
193 return EXIT_SUCCESS;
194}
Install additional dependencies with:
pip install scipy
1from typing import Optional
2
3import numpy as np
4from scipy.spatial.transform import Rotation
5from zividmotion import (
6 Application,
7 CarriedObject,
8 InitialState,
9 Obstacle,
10 PathRequest,
11 Planner,
12 PlannerSettings,
13 Profile,
14 ReplaceableTool,
15 Tcp,
16 ToolBoxElement,
17 TransformedBox,
18 Visualizer,
19)
20
21USE_VISUALIZER = True
22
23
24def _start_planner(app: Application) -> Planner:
25 planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
26 return app.create_planner(planner_settings)
27
28
29# Utility method for creating a dummy obstacle
30def _generate_sphere_points(center_point: list[float], radius: float, num_points: int) -> list[list[float]]:
31 np.random.seed(42) # Set a fixed random seed for determinism
32 points = []
33 for _ in range(num_points):
34 theta = np.random.uniform(0, 2 * np.pi)
35 phi = np.random.uniform(0, np.pi)
36 x = center_point[0] + radius * np.sin(phi) * np.cos(theta)
37 y = center_point[1] + radius * np.sin(phi) * np.sin(theta)
38 z = center_point[2] + radius * np.cos(phi)
39 points.append([x, y, z])
40 return points
41
42
43def _get_dummy_obstacles(center_point: list[float], radius: float) -> list[Obstacle]:
44 obstacles: list[Obstacle] = []
45 for x in [center_point[0] - 2 * radius, center_point[0], center_point[0] + 2 * radius]:
46 for y in [center_point[1] - 2 * radius, center_point[1], center_point[1] + 2 * radius]:
47 center = [x, y, center_point[2]]
48 points = _generate_sphere_points(center_point=center, radius=radius, num_points=400)
49 obstacles.append(Obstacle.from_point_cloud(name=f"obstacle_{len(obstacles)}", points=points))
50 return obstacles
51
52
53def _main() -> None:
54 app = Application()
55
56 print("Starting planner")
57 planner = _start_planner(app)
58 visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
59
60 obstacle_center = [1.1, 1.0, 0.2]
61 obstacle_radius = 0.15
62 planner.set_obstacles(obstacles=_get_dummy_obstacles(center_point=obstacle_center, radius=obstacle_radius))
63
64 # For example purposes, simulate a suction cup gripper that is mounted asymmetrically at a 45 degree angle
65 tool_transform = np.eye(4)
66 tool_transform[1, 3] = 0.05
67 tool_transform[:3, :3] = Rotation.from_rotvec([0, 0, np.pi / 4]).as_matrix()
68 tool_dimensions = [0.4, 0.4, 0.06]
69 # Specify the last 2cm of the gripper as the compliant suction cup area
70 compliant_transform = tool_transform.copy()
71 compliant_transform[1, 3] = tool_dimensions[2] - 0.02
72 compliant_dimensions = [0.4, 0.4, 0.02]
73 planner.set_replaceable_tool(
74 replaceable_tool=ReplaceableTool.from_box_elements(
75 tool_elements=[
76 ToolBoxElement(
77 tool_transform,
78 tool_dimensions,
79 compliant_box=TransformedBox(compliant_transform, compliant_dimensions),
80 )
81 ]
82 )
83 )
84
85 # Set the new tcp to be at the center tip of the gripper, when the suction cup is compressed 1 cm.
86 tcp_transform = tool_transform.copy()
87 tcp_transform[2, 3] += tool_dimensions[2] - 0.01
88 planner.set_tcp(tcp=Tcp(transform=tcp_transform, tool_direction=[0.0, 0.0, 1.0]))
89
90 start_configuration = [0.0, 0.0, 0.0, 0.0, 1.57, 0.0]
91
92 # Define a pick pose and compute the pick joint configuration with the new tcp
93 pick_position = [obstacle_center[0], obstacle_center[1], obstacle_center[2] + obstacle_radius]
94 pick_pose = np.eye(4)
95 pick_pose[:3, 3] = pick_position
96 # Rotate 180 degrees around Y-axis to pick perpendicular to the obstacles
97 pick_pose[:3, :3] = Rotation.from_rotvec([0, np.pi, 0]).as_matrix()
98 pick_configuration = planner.compute_inverse_kinematics(pose=pick_pose, reference_configuration=start_configuration)
99 if pick_configuration is None:
100 raise RuntimeError("No valid inverse kinematics solution found for pick pose")
101
102 # Compute a pick approach path with the new tool
103 approach_result = planner.path(
104 initial_state=InitialState(start_configuration=start_configuration),
105 request=PathRequest(
106 type=PathRequest.Type.touch,
107 goal_configurations=[pick_configuration],
108 description="Pick approach",
109 ),
110 )
111 if not approach_result:
112 raise RuntimeError(f"Planning pick approach failed with error: {approach_result.error}")
113
114 # Set carried object
115 obstacle_size = obstacle_radius * 2
116 planner.set_carried_object(
117 carried_object=CarriedObject.from_box(
118 transformed_box=TransformedBox(
119 bottom_centered_transform=np.eye(4), box_dimensions=[obstacle_size, obstacle_size, obstacle_size]
120 )
121 )
122 )
123
124 # Compute a pick retract path with custom retract direction
125 # This retract direction is the same as the negative tcp tool direction expressed in the base frame when the robot
126 # is in the start configuration for this path call. Meaning it in this case is a redundant specification, just to
127 # show the signature for example purposes.
128 retract_result = planner.path(
129 initial_state=InitialState(previous_result=approach_result),
130 request=PathRequest(
131 type=PathRequest.Type.touch,
132 retract_direction=[0.0, 0.0, 1.0],
133 goal_configurations=[start_configuration],
134 description="Pick retract",
135 ),
136 )
137 if not retract_result:
138 raise RuntimeError(f"Planning pick retract failed with error: {retract_result.error}")
139
140 print(retract_result)
141
142 if visualizer is not None:
143 print("Close the window to exit.")
144 visualizer.wait()
145
146
147if __name__ == "__main__":
148 _main()
Robot Attachments
Path Planning with Attachment
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Planner.h>
3#include <Zivid/Motion/Visualizer.h>
4
5#include <cassert>
6#include <cmath>
7#include <iostream>
8#include <random>
9
10namespace
11{
12 constexpr auto useVisualizer = true;
13
14 void printException(const std::exception &e, const int level = 0)
15 {
16 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
17 try
18 {
19 std::rethrow_if_nested(e);
20 }
21 catch(const std::exception &nestedException)
22 {
23 printException(nestedException, level + 1);
24 }
25 catch(...)
26 {}
27 }
28
29 Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
30 {
31 return app.createPlanner(
32 Zivid::Motion::PlannerSettings{
33 "demo_cell",
34 Zivid::Motion::Profile::testing,
35 });
36 }
37
38 // Utility function for creating a dummy point-cloud obstacle
39 std::vector<Zivid::Motion::PointXYZ>
40 generateSpherePoints(const Zivid::Motion::PointXYZ ¢er, const float radius, const int numPoints)
41 {
42 // Fixed seed for determinism
43 std::mt19937 rng(42);
44 std::uniform_real_distribution<float> thetaDist(0, 2 * M_PI);
45 std::uniform_real_distribution<float> phiDist(0, M_PI);
46
47 std::vector<Zivid::Motion::PointXYZ> points;
48 points.reserve(numPoints);
49 for(int i = 0; i < numPoints; ++i)
50 {
51 const float theta = thetaDist(rng);
52 const float phi = phiDist(rng);
53 points.push_back(
54 {
55 center.x + radius * std::sin(phi) * std::cos(theta),
56 center.y + radius * std::sin(phi) * std::sin(theta),
57 center.z + radius * std::cos(phi),
58 });
59 }
60 return points;
61 }
62} // namespace
63
64int main()
65{
66 try
67 {
68 const Zivid::Motion::Application app;
69
70 std::cout << "Starting planner\n";
71 auto planner = startPlanner(app);
72 auto visualizer =
73 useVisualizer ? std::optional{ Zivid::Motion::Visualizer::viewPlanner(planner) } : std::nullopt;
74
75 const Zivid::Motion::Configuration startConfiguration = { 0.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
76 const Zivid::Motion::Configuration goalConfiguration = { 1.57f, 0.f, 0.f, 0.f, 1.57f, 0.f };
77
78 // Must match a name in the attachments definition in the planner config file
79 const std::string attachmentName = "demo_attachment";
80
81 // Set an obstacle that obstructs the direct path for the attachment
82 planner.setObstacles(
83 { Zivid::Motion::Obstacle::fromPointCloud(
84 "sphere_obstacle", generateSpherePoints({ 1.1f, 1.0f, 1.2f }, 0.2f, 1000)) });
85
86 const Zivid::Motion::InitialState initialState{ startConfiguration };
87
88 const auto resultWithoutAttachment = planner.path(
89 initialState,
90 Zivid::Motion::PathRequest{ .goalConfigurations = { goalConfiguration },
91 .description = "Path without attachment" });
92 if(!resultWithoutAttachment)
93 {
94 throw std::runtime_error(
95 "Planning without active attachment failed with result: " + resultWithoutAttachment.toString());
96 }
97 assert(resultWithoutAttachment.path().size() == 1);
98 std::cout << "Path without attachment: " << resultWithoutAttachment.path().size() << " waypoints\n";
99
100 std::cout << "Setting attachment " << attachmentName << "\n";
101 planner.setAttachments({ attachmentName });
102
103 const auto resultWithAttachment = planner.path(
104 initialState,
105 Zivid::Motion::PathRequest{ .goalConfigurations = { goalConfiguration },
106 .description = "Path with attachment" });
107 if(!resultWithAttachment)
108 {
109 std::cout << "Planning with active attachment failed with result: " << resultWithAttachment.toString()
110 << "\n";
111 }
112 assert(resultWithAttachment.path().size() > 1);
113 std::cout << "With attachment: " << resultWithAttachment.path().size() << " waypoints\n";
114
115 if(useVisualizer)
116 {
117 std::cout << "Close the window to exit.\n";
118 visualizer->wait();
119 }
120 }
121 catch(const std::exception &exception)
122 {
123 printException(exception);
124 std::cout << "Press enter to exit." << std::endl;
125 std::cin.get();
126 return EXIT_FAILURE;
127 }
128 return EXIT_SUCCESS;
129}
1from typing import Optional
2
3import numpy as np
4from zividmotion import Application, InitialState, Obstacle, PathRequest, Planner, PlannerSettings, Profile, Visualizer
5
6USE_VISUALIZER = True
7
8
9def _start_planner(app: Application) -> Planner:
10 planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
11 return app.create_planner(planner_settings)
12
13
14# Utility method for creating a dummy obstacle
15def _generate_sphere_points(center_point: list[float], radius: float, num_points: int) -> list[list[float]]:
16 np.random.seed(42) # Set a fixed random seed for determinism
17 points = []
18 for _ in range(num_points):
19 theta = np.random.uniform(0, 2 * np.pi)
20 phi = np.random.uniform(0, np.pi)
21 x = center_point[0] + radius * np.sin(phi) * np.cos(theta)
22 y = center_point[1] + radius * np.sin(phi) * np.sin(theta)
23 z = center_point[2] + radius * np.cos(phi)
24 points.append([x, y, z])
25 return points
26
27
28def _main() -> None:
29 app = Application()
30
31 print("Starting planner")
32 planner = _start_planner(app)
33 visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
34
35 start_configuration = [0.0, 0.0, 0.0, 0.0, 1.57, 0.0]
36 goal_configuration = [1.57, 0.0, 0.0, 0.0, 1.57, 0.0]
37 attachment_name = "demo_attachment" # Must match a name in the attachments definition in the planner config file
38
39 # Set an obstacle that obstructs the direct path for the attachment
40 planner.set_obstacles(
41 obstacles=[
42 Obstacle.from_point_cloud(
43 name="sphere_obstacle",
44 points=_generate_sphere_points(center_point=[1.1, 1.0, 1.2], radius=0.2, num_points=1000),
45 )
46 ]
47 )
48
49 initial_state = InitialState(start_configuration=start_configuration)
50 result_without_attachment = planner.path(
51 initial_state=initial_state,
52 request=PathRequest(
53 goal_configurations=[goal_configuration],
54 description="Path without attachment",
55 ),
56 )
57 if not result_without_attachment:
58 raise RuntimeError(f"Planning without active attachment failed with error: {result_without_attachment.error}")
59 assert len(result_without_attachment.path) == 1
60 print(f"Path without attachment: {len(result_without_attachment.path)} waypoints")
61
62 print("Setting attachment", attachment_name)
63 planner.set_attachments(attachments=[attachment_name])
64
65 result_with_attachment = planner.path(
66 initial_state=initial_state,
67 request=PathRequest(
68 goal_configurations=[goal_configuration],
69 description="Path with attachment",
70 ),
71 )
72 if not result_with_attachment:
73 print(f"Planning with active attachment failed with error: {result_with_attachment.error}")
74 assert len(result_with_attachment.path) > 1
75 print(f"Path with attachment: {len(result_with_attachment.path)} waypoints")
76
77 if visualizer is not None:
78 print("Close the window to exit.")
79 visualizer.wait()
80
81
82if __name__ == "__main__":
83 _main()
Debugging
Export and Package API Log
This example shows how to create a zip archive containing everything needed to recreate your planner setup and re-run a failing API call from the same planner state. Note that this example is expected to throw an error.
1#include <Zivid/Motion/Application.h>
2#include <Zivid/Motion/Packaging.h>
3#include <Zivid/Motion/Planner.h>
4
5#include <filesystem>
6#include <iostream>
7
8namespace
9{
10 void printException(const std::exception &e, const int level = 0)
11 {
12 std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
13 try
14 {
15 std::rethrow_if_nested(e);
16 }
17 catch(const std::exception &nestedException)
18 {
19 printException(nestedException, level + 1);
20 }
21 catch(...)
22 {}
23 }
24
25 Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
26 {
27 return app.createPlanner(
28 Zivid::Motion::PlannerSettings{
29 "demo_cell",
30 Zivid::Motion::Profile::testing,
31 });
32 }
33} // namespace
34
35int main()
36{
37 const Zivid::Motion::Application app;
38
39 std::cout << "Starting planner\n";
40 auto planner = startPlanner(app);
41
42 try
43 {
44 const Zivid::Motion::Configuration startConfiguration = { 0.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
45 const Zivid::Motion::Configuration invalidGoalConfiguration = { 100.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
46
47 const auto unsuccessfulResult = planner.path(
48 Zivid::Motion::InitialState{ startConfiguration },
49 Zivid::Motion::PathRequest{ .goalConfigurations = { invalidGoalConfiguration },
50 .description = "Path with unreachable goal" });
51 std::cout << "Path result has status: " << unsuccessfulResult.toString() << "\n";
52
53 // Some operation that fails, like creating an InitialState from an unsuccessful path result
54 const Zivid::Motion::InitialState initialState{ unsuccessfulResult };
55 const auto result = planner.path(
56 Zivid::Motion::InitialState{ startConfiguration },
57 Zivid::Motion::PathRequest{ .goalConfigurations = { startConfiguration },
58 .description = "Path with invalid initial state" });
59 std::cout << result << "\n";
60 }
61 catch(const std::exception &exception)
62 {
63 const std::filesystem::path outputFolder = "my/desired/output/folder";
64
65 const auto logPath = planner.exportApiLog(outputFolder);
66 const auto packagePath = Zivid::Motion::packageApiLog(app, logPath);
67 std::cerr << "Planner failed, debug package available at: " + std::filesystem::canonical(packagePath).string()
68 << std::endl;
69
70 printException(exception);
71 std::cout << "Press enter to exit." << std::endl;
72 std::cin.get();
73 return EXIT_FAILURE;
74 }
75 return EXIT_SUCCESS;
76}
1from pathlib import Path
2
3from zividmotion import Application, InitialState, PathRequest, Planner, PlannerSettings, Profile, package_api_log
4
5
6def _start_planner(app: Application) -> Planner:
7 planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
8 return app.create_planner(planner_settings)
9
10
11def _main() -> None:
12 app = Application()
13
14 print("Starting planner")
15 planner = _start_planner(app)
16
17 start_configuration = [0, 0, 0, 0, 1.57, 0]
18 invalid_goal_configuration = [100, 0, 0, 0, 1.57, 0]
19
20 unsuccessful_result = planner.path(
21 initial_state=InitialState(start_configuration=start_configuration),
22 request=PathRequest(
23 goal_configurations=[invalid_goal_configuration],
24 description="Path with unreachable goal",
25 ),
26 )
27 assert unsuccessful_result.error is not None
28 print("Path result has error: ", unsuccessful_result.error)
29
30 try:
31 # Some operation that fails, like creating an InitialState from an unsuccessful path result
32 initial_state = InitialState(previous_result=unsuccessful_result)
33 result = planner.path(
34 initial_state=initial_state,
35 request=PathRequest(
36 goal_configurations=[start_configuration],
37 description="Path with invalid initial state",
38 ),
39 )
40 print(result)
41
42 except Exception as e:
43 output_folder = Path("my/desired/output/folder")
44
45 log_path = planner.export_api_log(output_directory=output_folder)
46 package_path = package_api_log(application=app, api_log_path=log_path)
47
48 raise RuntimeError(f"Planner failed, debug package available at: {package_path.resolve()}") from e
49
50
51if __name__ == "__main__":
52 _main()