示例
设置
生成规划数据
本示例展示了如何为机器人单元生成规划数据,这是在启动运动规划器之前必须执行的操作。
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 return EXIT_FAILURE;
50 }
51 return EXIT_SUCCESS;
52}
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()
规划器软件包设置
本示例展示了如何将运行运动规划器所需的所有文件打包到一个 zip 压缩包中,该压缩包可以轻松分发并在其他机器上解压缩。
在已完成规划器设置的工作站上运行以下代码片段:
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 = "demo_cell.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 return EXIT_FAILURE;
43 }
44 return EXIT_SUCCESS;
45}
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("demo_cell.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()
收到压缩包的用户随后可以运行以下代码片段来解压安装程序:
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 = "demo_cell.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 return EXIT_FAILURE;
41 }
42 return EXIT_SUCCESS;
43}
1from pathlib import Path
2
3from zividmotion import Application, install_package
4
5
6def _main() -> None:
7 app = Application()
8
9 package_path = Path("demo_cell.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()
可视化工作站
本示例展示了如何为已配置的机器人工作单元打开 3D 可视化窗口。在生成或运行运动规划器之前,这有助于检查您的工作单元设置。
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 return EXIT_FAILURE;
43 }
44 return EXIT_SUCCESS;
45}
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()
规划器
基本路径调用
关节目标
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 Zivid::Motion::PathRequest pathRequest{};
52 pathRequest.goalConfigurations = { goalConfiguration };
53 pathRequest.description = "Path with joint goal";
54 const auto result = planner.path(Zivid::Motion::InitialState{ startConfiguration }, pathRequest);
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()
位姿目标
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 Zivid::Motion::PathRequest pathRequest{};
62 pathRequest.goalConfigurations = { poseGoalConfiguration.value() };
63 pathRequest.description = "Path with goal from pose";
64 const auto result = planner.path(Zivid::Motion::InitialState{ startConfiguration }, pathRequest);
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()
多重目标和优先级排序
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 Zivid::Motion::PathRequest shortestPathRequest{};
57 shortestPathRequest.goalConfigurations = goals;
58 shortestPathRequest.description = "Multiple goals - Default (ShortestPath) prioritization";
59 const auto resultShortestPath = planner.path(initialState, shortestPathRequest);
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 Zivid::Motion::PathRequest listOrderRequest{};
68 listOrderRequest.goalPrioritizationMethod = Zivid::Motion::PathRequest::GoalPrioritizationMethod::listOrder;
69 listOrderRequest.goalConfigurations = goals;
70 listOrderRequest.description = "Multiple goals - ListOrder prioritization";
71 const auto resultListOrder = planner.path(initialState, listOrderRequest);
72 if(!resultListOrder)
73 {
74 throw std::runtime_error("Planning with ListOrder failed with result: " + resultListOrder.toString());
75 }
76 assert(resultListOrder.selectedGoalIdx == 0u);
77 std::cout << "ListOrder selected goal index: " << resultListOrder.selectedGoalIdx.value() << "\n";
78
79 if(useVisualizer)
80 {
81 std::cout << "Close the window to exit.\n";
82 visualizer->wait();
83 }
84 }
85 catch(const std::exception &exception)
86 {
87 printException(exception);
88 std::cout << "Press enter to exit." << std::endl;
89 std::cin.get();
90 return EXIT_FAILURE;
91 }
92 return EXIT_SUCCESS;
93}
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()
设置运行时障碍物
简单障碍物几何体
本示例展示了如何向运动规划器的动态环境模型中添加简单的障碍物(例如长方体)。
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 Zivid::Motion::PathRequest withoutObstacleRequest{};
94 withoutObstacleRequest.goalConfigurations = { goalConfiguration };
95 withoutObstacleRequest.description = "Without obstacle";
96 const auto resultWithoutObstacle = planner.path(initialState, withoutObstacleRequest);
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 Zivid::Motion::PathRequest withObstacleRequest{};
110 withObstacleRequest.goalConfigurations = { goalConfiguration };
111 withObstacleRequest.description = "With obstacle";
112 const auto resultWithObstacle = planner.path(initialState, withObstacleRequest);
113 if(!resultWithObstacle)
114 {
115 throw std::runtime_error(
116 "Planning after obstacle is set failed with result: " + resultWithObstacle.toString());
117 }
118 assert(resultWithObstacle.path().size() > 1);
119 std::cout << "Path after obstacle is set: " << resultWithObstacle.path().size() << " waypoints\n";
120
121 if(useVisualizer)
122 {
123 std::cout << "Close the window to exit.\n";
124 visualizer->wait();
125 }
126 }
127 catch(const std::exception &exception)
128 {
129 printException(exception);
130 std::cout << "Press enter to exit." << std::endl;
131 std::cin.get();
132 return EXIT_FAILURE;
133 }
134 return EXIT_SUCCESS;
135}
使用以下命令安装其他依赖项:
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()
基于 CAD 文件的障碍物
本示例展示了如何向运动规划器的动态环境模型中添加自定义 CAD 文件。需要注意的是,对于代表静态障碍物的 CAD 文件,如果在 URDF 文件中将其作为静态障碍物添加,其处理效率会比在运行时作为动态障碍物添加更高。
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}
使用以下命令安装其他依赖项:
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()
基于 Zivid 点云的障碍物
本示例展示了如何将 Zivid 相机采集的点云添加到运动规划器的动态环境模型中。使用此功能需要安装 Zivid SDK,并确保已连接到 Zivid 相机。
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 const Zivid::Matrix4x4 millimetersToMetersTransform{ {
49 { 0.001f, 0.f, 0.f, 0.f },
50 { 0.f, 0.001f, 0.f, 0.f },
51 { 0.f, 0.f, 0.001f, 0.f },
52 { 0.f, 0.f, 0.f, 1.f },
53 } };
54
55 std::cout << "Capturing with default capture settings\n";
56 const auto settings =
57 Zivid::Settings{ Zivid::Settings::Acquisitions{ Zivid::Settings::Acquisition{} },
58 Zivid::Settings::Color{ Zivid::Settings2D{
59 Zivid::Settings2D::Acquisitions{ Zivid::Settings2D::Acquisition{} } } } };
60 const auto frame = camera.capture(settings);
61
62 // Downsampling improves speed, if you don't need the extra resolution
63 frame.pointCloud().downsample(Zivid::PointCloud::Downsampling::by4x4);
64
65 // Convert point cloud to unorganized point cloud
66 auto unorganizedPointCloud = frame.pointCloud().toUnorganizedPointCloud();
67
68 // Transform the point cloud from the camera frame to the base frame of the planner
69 unorganizedPointCloud.transform(cameraToBaseTransform);
70
71 // Transform the point cloud from millimeters to meters, which is the expected unit in the planner
72 unorganizedPointCloud.transform(millimetersToMetersTransform);
73
74 const auto xyzData = unorganizedPointCloud.copyPointsXYZ();
75 std::vector<Zivid::Motion::PointXYZ> points;
76 points.reserve(xyzData.size());
77 for(const auto &p : xyzData)
78 {
79 constexpr auto mmToM = 1.f / 1000.f; // Convert from millimeters to meters
80 points.emplace_back(Zivid::Motion::PointXYZ{ p.x * mmToM, p.y * mmToM, p.z * mmToM });
81 }
82 planner.setObstacles({ Zivid::Motion::Obstacle::fromPointCloud("point_cloud_obstacle", points) });
83
84 if(useVisualizer)
85 {
86 std::cout << "Close the window to exit.\n";
87 visualizer->wait();
88 }
89 }
90 catch(const std::exception &e)
91 {
92 std::cerr << "Error: " << e.what() << std::endl;
93 std::cout << "Press enter to exit." << std::endl;
94 std::cin.get();
95 return EXIT_FAILURE;
96 }
97 return EXIT_SUCCESS;
98}
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 millimeters_to_meters_transform = np.diag([0.001, 0.001, 0.001, 1])
35
36 print("Capturing with default capture settings")
37 # Replace with your own capture settings for better results
38 capture_settings = zivid.Settings(
39 acquisitions=[zivid.Settings.Acquisition()],
40 color=zivid.Settings2D(acquisitions=[zivid.Settings2D.Acquisition()]),
41 )
42
43 frame = camera.capture(capture_settings)
44
45 # Downsampling improves speed, if you don't need the extra resolution
46 frame.point_cloud().downsample(zivid.PointCloud.Downsampling.by4x4)
47
48 # Convert point cloud to unorganized point cloud
49 unorganized_point_cloud = frame.point_cloud().to_unorganized_point_cloud()
50
51 # Transform the point cloud from the camera frame to the base frame of the planner
52 unorganized_point_cloud.transform(camera_to_base_transform)
53
54 # Transform the point cloud from millimeters to meters, which is the expected unit in the planner
55 unorganized_point_cloud.transform(millimeters_to_meters_transform)
56
57 points = unorganized_point_cloud.copy_data("xyz")
58 obstacle = Obstacle.from_point_cloud(name="point_cloud_obstacle", points=points)
59 planner.set_obstacles(obstacles=[obstacle])
60
61 if USE_VISUALIZER:
62 input("Press enter to continue:")
63
64 # Set the obstacle again, but this time with color.
65 # Note that this has a slight performance impact, not recommended in production code.
66 planner.clear_obstacles()
67
68 rgba = unorganized_point_cloud.copy_data("rgba")
69 colored_obstacle = Obstacle.from_colored_point_cloud(name="colored_obstacle", points=points, colors=rgba)
70 planner.set_obstacles(obstacles=[colored_obstacle])
71
72 if visualizer is not None:
73 print("Close the window to exit.")
74 visualizer.wait()
75
76
77if __name__ == "__main__":
78 _main()
交互规划
简单触碰接近
本示例展示了在简化的设置中,如何利用触碰(Touch)功能来规划接近待抓取物体的动作。
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 Zivid::Motion::PathRequest freeRequest{};
114 freeRequest.goalConfigurations = { pickConfiguration.value() };
115 freeRequest.description = "Free path to pick goal";
116 const auto resultFree = planner.path(initialState, freeRequest);
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 Zivid::Motion::PathRequest touchRequest{};
121 touchRequest.type = Zivid::Motion::PathRequest::Type::touch;
122 touchRequest.goalConfigurations = { pickConfiguration.value() };
123 touchRequest.description = "Touch path to pick goal";
124 const auto resultTouch = planner.path(initialState, touchRequest);
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}
使用以下命令安装其他依赖项:
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()
可替换工具的抓取接近与回撤
本示例展示了在更换工具并携带物体的情况下,如何执行简化的物体抓取操作(包括接近和回撤路径)。
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 Zivid::Motion::ToolBoxElement toolElement{};
115 toolElement.transform = toolTransform;
116 toolElement.rigidBoxDimensions = toolDimensions;
117 toolElement.compliantBox = Zivid::Motion::TransformedBox{ compliantTransform, compliantDimensions };
118 planner.setReplaceableTool(Zivid::Motion::ReplaceableTool::fromBoxElements({ toolElement }));
119
120 // Set the new tcp to be at the center tip of the gripper, compressed 10 mm
121 Zivid::Motion::Pose tcpTransform = toolTransform;
122 tcpTransform[2][3] += toolDimensions.z - 0.01f;
123 planner.setTcp(Zivid::Motion::Tcp{ tcpTransform, { 0.f, 0.f, 1.f } });
124
125 const Zivid::Motion::Configuration startConfiguration = { 0.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
126
127 // Define a pick pose: above the obstacle center, end-effector rotated 180 degrees around Y to point downwards
128 constexpr Zivid::Motion::Pose pickPose{ {
129 { -1.f, 0.f, 0.f, obstacleCenter.x },
130 { 0.f, 1.f, 0.f, obstacleCenter.y },
131 { 0.f, 0.f, -1.f, obstacleCenter.z + obstacleRadius },
132 { 0.f, 0.f, 0.f, 1.f },
133 } };
134 const auto pickConfiguration = planner.computeInverseKinematics(pickPose, startConfiguration);
135 if(!pickConfiguration.has_value())
136 {
137 throw std::runtime_error("No valid inverse kinematics solution found for pick pose");
138 }
139
140 // Compute a pick approach path with the new tool
141 Zivid::Motion::PathRequest approachRequest{};
142 approachRequest.type = Zivid::Motion::PathRequest::Type::touch;
143 approachRequest.goalConfigurations = { pickConfiguration.value() };
144 approachRequest.description = "Pick approach";
145 const auto approachResult = planner.path(Zivid::Motion::InitialState{ startConfiguration }, approachRequest);
146 if(!approachResult)
147 {
148 throw std::runtime_error("Planning pick approach failed with result: " + approachResult.toString());
149 }
150
151 // Set carried object (bounding box around the picked obstacle)
152 constexpr float objectSize = obstacleRadius * 2.f;
153 constexpr Zivid::Motion::Pose identityPose{ {
154 { 1.f, 0.f, 0.f, 0.f },
155 { 0.f, 1.f, 0.f, 0.f },
156 { 0.f, 0.f, 1.f, 0.f },
157 { 0.f, 0.f, 0.f, 1.f },
158 } };
159 planner.setCarriedObject(
160 Zivid::Motion::CarriedObject::fromBox({ identityPose, { objectSize, objectSize, objectSize } }));
161
162 // Compute a pick retract path with custom retract direction
163 // This retract direction is the same as the negative tcp tool direction expressed in the base frame when the robot
164 // is in the start configuration for this path call. Meaning it in this case is a redundant specification, just to
165 // show the signature for example purposes.
166 Zivid::Motion::PathRequest retractRequest{};
167 retractRequest.type = Zivid::Motion::PathRequest::Type::touch;
168 retractRequest.retractDirection = Zivid::Motion::VectorXYZ{ 0.f, 0.f, 1.f };
169 retractRequest.goalConfigurations = { startConfiguration };
170 retractRequest.description = "Pick retract";
171 const auto retractResult = planner.path(Zivid::Motion::InitialState{ approachResult }, retractRequest);
172 if(!retractResult)
173 {
174 throw std::runtime_error("Planning pick retract failed with result: " + retractResult.toString());
175 }
176
177 std::cout << retractResult << "\n";
178
179 if(useVisualizer)
180 {
181 std::cout << "Close the window to exit.\n";
182 visualizer->wait();
183 }
184 }
185 catch(const std::exception &exception)
186 {
187 printException(exception);
188 std::cout << "Press enter to exit." << std::endl;
189 std::cin.get();
190 return EXIT_FAILURE;
191 }
192 return EXIT_SUCCESS;
193}
使用以下命令安装其他依赖项:
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()
机器人附件
带附件的路径规划
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 Zivid::Motion::PathRequest withoutAttachmentRequest{};
89 withoutAttachmentRequest.goalConfigurations = { goalConfiguration };
90 withoutAttachmentRequest.description = "Path without attachment";
91 const auto resultWithoutAttachment = planner.path(initialState, withoutAttachmentRequest);
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 Zivid::Motion::PathRequest withAttachmentRequest{};
104 withAttachmentRequest.goalConfigurations = { goalConfiguration };
105 withAttachmentRequest.description = "Path with attachment";
106 const auto resultWithAttachment = planner.path(initialState, withAttachmentRequest);
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()
调试
导出和打包 API 日志
本示例展示了如何创建一个 zip 压缩包,其中包含重现您的规划器设置以及从同一规划器状态重新运行失败的 API 调用所需的所有内容。请注意,此示例预期会抛出错误。
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 Zivid::Motion::PathRequest unreachableRequest{};
48 unreachableRequest.goalConfigurations = { invalidGoalConfiguration };
49 unreachableRequest.description = "Path with unreachable goal";
50 const auto unsuccessfulResult =
51 planner.path(Zivid::Motion::InitialState{ startConfiguration }, unreachableRequest);
52 std::cout << "Path result has status: " << unsuccessfulResult.toString() << "\n";
53
54 // Some operation that fails, like creating an InitialState from an unsuccessful path result
55 const Zivid::Motion::InitialState initialState{ unsuccessfulResult };
56 Zivid::Motion::PathRequest invalidStateRequest{};
57 invalidStateRequest.goalConfigurations = { startConfiguration };
58 invalidStateRequest.description = "Path with invalid initial state";
59 const auto result = planner.path(Zivid::Motion::InitialState{ startConfiguration }, invalidStateRequest);
60 std::cout << result << "\n";
61 }
62 catch(const std::exception &exception)
63 {
64 const std::filesystem::path outputFolder = "/tmp";
65
66 const auto logPath = planner.exportApiLog(outputFolder);
67 const auto packagePath = Zivid::Motion::packageApiLog(app, logPath);
68 std::cerr << "Planner failed, debug package available at: " + std::filesystem::canonical(packagePath).string()
69 << std::endl;
70
71 printException(exception);
72 return EXIT_SUCCESS; // for the purpose of this sample, saving a debug package is success
73 }
74 return EXIT_FAILURE;
75}
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:
43 output_folder = Path("/tmp")
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 print(f"Planner failed, debug package available at: {package_path.resolve()}")
49
50
51if __name__ == "__main__":
52 _main()