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        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()

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 = "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()

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 = "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()

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        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()

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 = false;
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{ std::vector{ goalConfiguration } };
52        pathRequest.description = "Path with joint goal";
53        const auto result = planner.path(Zivid::Motion::InitialState{ startConfiguration }, pathRequest);
54        if(!result)
55        {
56            throw std::runtime_error("Planning failed with result: " + result.toString());
57        }
58
59        std::cout << "Path result:\n" << result << "\n";
60
61        if(useVisualizer)
62        {
63            std::cout << "Close the window to exit.\n";
64            visualizer->wait();
65        }
66    }
67    catch(const std::exception &exception)
68    {
69        printException(exception);
70        return EXIT_FAILURE;
71    }
72    return EXIT_SUCCESS;
73}
 1from typing import Optional
 2
 3from zividmotion import (
 4    Application,
 5    Configuration,
 6    InitialState,
 7    PathRequest,
 8    Planner,
 9    PlannerSettings,
10    Profile,
11    Visualizer,
12)
13
14USE_VISUALIZER = False
15
16
17def _start_planner(app: Application) -> Planner:
18    planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
19    return app.create_planner(planner_settings)
20
21
22def _main() -> None:
23    app = Application()
24
25    print("Starting planner")
26    planner = _start_planner(app)
27    visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
28
29    start_configuration = Configuration([0.0, 0.0, 0.0, 0.0, 1.57, 0.0])
30    goal_configuration = Configuration([1.57, 0.0, 0.0, 0.0, 1.57, 0.0])
31
32    result = planner.path(
33        InitialState(start_configuration),
34        PathRequest(
35            goals=[goal_configuration],
36            description="Path with joint goal",
37        ),
38    )
39    if not result:
40        raise RuntimeError(f"Planning failed with error: {result.error}")
41
42    print(f"Path result: \n{result}\n")
43
44    if visualizer is not None:
45        print("Close the window to exit.")
46        visualizer.wait()
47
48
49if __name__ == "__main__":
50    _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 = false;
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        const Zivid::Motion::Pose poseGoal{ Zivid::Motion::Matrix4x4{
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 jointGoal = planner.computeInverseKinematics(std::vector{ poseGoal }, startConfiguration);
56        if(jointGoal.noneValid())
57        {
58            throw std::runtime_error("No valid inverse kinematics solution found");
59        }
60
61        Zivid::Motion::PathRequest pathRequest{ jointGoal };
62        pathRequest.description = "Path with goal from pose";
63        const auto result = planner.path(Zivid::Motion::InitialState{ startConfiguration }, pathRequest);
64        if(!result)
65        {
66            throw std::runtime_error("Planning failed with result: " + result.toString());
67        }
68
69        std::cout << "Path result:\n" << result << "\n";
70
71        if(useVisualizer)
72        {
73            std::cout << "Close the window to exit.\n";
74            visualizer->wait();
75        }
76    }
77    catch(const std::exception &exception)
78    {
79        printException(exception);
80        return EXIT_FAILURE;
81    }
82    return EXIT_SUCCESS;
83}
 1from typing import Optional
 2
 3from zividmotion import (
 4    Application,
 5    Configuration,
 6    InitialState,
 7    PathRequest,
 8    Planner,
 9    PlannerSettings,
10    Pose,
11    Profile,
12    Visualizer,
13)
14
15USE_VISUALIZER = False
16
17
18def _start_planner(app: Application) -> Planner:
19    planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
20    return app.create_planner(planner_settings)
21
22
23def _main() -> None:
24    app = Application()
25
26    print("Starting planner")
27    planner = _start_planner(app)
28    visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
29
30    start_configuration = Configuration([0.0, 0.0, 0.0, 0.0, 1.57, 0.0])
31    pose_goal = Pose(
32        [
33            [0.0, -1.0, 0.0, 0.0],
34            [-1.0, 0.0, 0.0, 1.45],
35            [0.0, 0.0, -1.0, 1.63],
36            [0.0, 0.0, 0.0, 1.0],
37        ]
38    )
39
40    joint_goal = planner.compute_inverse_kinematics(
41        poses=[pose_goal],
42        reference_configuration=start_configuration,
43    )
44    if joint_goal.none_valid():
45        raise RuntimeError("No valid inverse kinematics solution found")
46
47    result = planner.path(
48        InitialState(start_configuration),
49        PathRequest(
50            goals=joint_goal,
51            description="Path with goal from pose",
52        ),
53    )
54    if not result:
55        raise RuntimeError(f"Planning failed with error: {result.error}")
56
57    print(f"Path result: \n{result}\n")
58
59    if visualizer is not None:
60        print("Close the window to exit.")
61        visualizer.wait()
62
63
64if __name__ == "__main__":
65    _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 = false;
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 Zivid::Motion::ConfigurationGoals goals{ { goalFarAway, goalCloser, goalClosest } };
55
56        Zivid::Motion::PathRequest shortestPathRequest{ goals };
57        shortestPathRequest.description = "Multiple goals - Default (ShortestPath) prioritization";
58        const auto resultShortestPath = planner.path(initialState, shortestPathRequest);
59        if(!resultShortestPath)
60        {
61            throw std::runtime_error("Planning with ShortestPath failed with result: " + resultShortestPath.toString());
62        }
63        assert(resultShortestPath.selectedGoalIdx == 2u);
64        std::cout << "ShortestPath selected goal index: " << resultShortestPath.selectedGoalIdx.value() << "\n";
65
66        Zivid::Motion::PathRequest listOrderRequest{ goals };
67        listOrderRequest.goalPrioritizationMethod = Zivid::Motion::PathRequest::GoalPrioritizationMethod::listOrder;
68        listOrderRequest.description = "Multiple goals - ListOrder prioritization";
69        const auto resultListOrder = planner.path(initialState, listOrderRequest);
70        if(!resultListOrder)
71        {
72            throw std::runtime_error("Planning with ListOrder failed with result: " + resultListOrder.toString());
73        }
74        assert(resultListOrder.selectedGoalIdx == 0u);
75        std::cout << "ListOrder selected goal index: " << resultListOrder.selectedGoalIdx.value() << "\n";
76
77        if(useVisualizer)
78        {
79            std::cout << "Close the window to exit.\n";
80            visualizer->wait();
81        }
82    }
83    catch(const std::exception &exception)
84    {
85        printException(exception);
86        return EXIT_FAILURE;
87    }
88    return EXIT_SUCCESS;
89}
 1from typing import Optional
 2
 3from zividmotion import (
 4    Application,
 5    Configuration,
 6    ConfigurationGoals,
 7    InitialState,
 8    PathRequest,
 9    Planner,
10    PlannerSettings,
11    Profile,
12    Visualizer,
13)
14
15USE_VISUALIZER = False
16
17
18def _start_planner(app: Application) -> Planner:
19    planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
20    return app.create_planner(planner_settings)
21
22
23def _main() -> None:
24    app = Application()
25
26    print("Starting planner")
27    planner = _start_planner(app)
28    visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
29
30    start_configuration = Configuration([0.0, 0.0, 0.0, 0.0, 1.57, 0.0])
31    initial_state = InitialState(start_configuration)
32
33    goal_far_away = Configuration([3.14, 0.0, 0.0, 0.0, 1.57, 0.0])
34    goal_closer = Configuration([1.57, 0.1, -0.1, 0.0, 1.57, 0.0])
35    goal_closest = Configuration([1.57, 0.0, 0.0, 0.0, 1.57, 0.0])
36    goals = ConfigurationGoals([goal_far_away, goal_closer, goal_closest])
37
38    result_shortest_path = planner.path(
39        initial_state,
40        PathRequest(
41            goals=goals,
42            description="Multiple goals - Default (ShortestPath) prioritization",
43        ),
44    )
45    if not result_shortest_path:
46        raise RuntimeError(f"Planning with ShortestPath failed with error: {result_shortest_path.error}")
47    assert result_shortest_path.selected_goal_idx == 2
48    print(f"ShortestPath selected goal index: {result_shortest_path.selected_goal_idx}")
49
50    result_list_order = planner.path(
51        initial_state,
52        PathRequest(
53            goal_prioritization_method=PathRequest.GoalPrioritizationMethod.listOrder,
54            goals=goals,
55            description="Multiple goals - ListOrder prioritization",
56        ),
57    )
58    if not result_list_order:
59        raise RuntimeError(f"Planning with ListOrder failed with error: {result_list_order.error}")
60    assert result_list_order.selected_goal_idx == 0
61    print(f"ListOrder selected goal index: {result_list_order.selected_goal_idx}")
62
63    if visualizer is not None:
64        print("Close the window to exit.")
65        visualizer.wait()
66
67
68if __name__ == "__main__":
69    _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 = false;
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        const Zivid::Motion::InitialState initialState{ startConfiguration };
51        const Zivid::Motion::ConfigurationGoals goal{ { goalConfiguration } };
52
53        Zivid::Motion::PathRequest withoutObstacleRequest{ goal };
54        withoutObstacleRequest.description = "Without obstacle";
55        const auto resultWithoutObstacle = planner.path(initialState, withoutObstacleRequest);
56        if(!resultWithoutObstacle)
57        {
58            throw std::runtime_error(
59                "Planning before obstacle is set failed with result: " + resultWithoutObstacle.toString());
60        }
61        assert(resultWithoutObstacle.path().size() == 1);
62        std::cout << "Path before obstacle is set: " << resultWithoutObstacle.path().size() << " waypoint\n";
63
64        // Set an obstacle that obstructs the direct path
65        const auto boxMesh = Zivid::Motion::Mesh::createBox(Zivid::Motion::Vector3f{ 0.2f, 0.2f, 0.2f })
66                                 .transform(
67                                     Zivid::Motion::Pose{ Zivid::Motion::Matrix4x4{
68                                         { 1.f, 0.f, 0.f, 1.1f },
69                                         { 0.f, 1.f, 0.f, 1.0f },
70                                         { 0.f, 0.f, 1.f, 1.6f },
71                                         { 0.f, 0.f, 0.f, 1.f },
72                                     } });
73        planner.setObstacles({ Zivid::Motion::Obstacle::fromMesh("box_obstacle", boxMesh) });
74
75        Zivid::Motion::PathRequest withObstacleRequest{ goal };
76        withObstacleRequest.description = "With obstacle";
77        const auto resultWithObstacle = planner.path(initialState, withObstacleRequest);
78        if(!resultWithObstacle)
79        {
80            throw std::runtime_error(
81                "Planning after obstacle is set failed with result: " + resultWithObstacle.toString());
82        }
83        assert(resultWithObstacle.path().size() > 1);
84        std::cout << "Path after obstacle is set: " << resultWithObstacle.path().size() << " waypoints\n";
85
86        if(useVisualizer)
87        {
88            std::cout << "Close the window to exit.\n";
89            visualizer->wait();
90        }
91    }
92    catch(const std::exception &exception)
93    {
94        printException(exception);
95        return EXIT_FAILURE;
96    }
97    return EXIT_SUCCESS;
98}
 1from typing import Optional
 2
 3from zividmotion import (
 4    Application,
 5    Configuration,
 6    ConfigurationGoals,
 7    InitialState,
 8    Mesh,
 9    Obstacle,
10    PathRequest,
11    Planner,
12    PlannerSettings,
13    Pose,
14    Profile,
15    Vector3f,
16    Visualizer,
17)
18
19USE_VISUALIZER = False
20
21
22def _start_planner(app: Application) -> Planner:
23    planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
24    return app.create_planner(planner_settings)
25
26
27def _main() -> None:
28    app = Application()
29
30    print("Starting planner")
31    planner = _start_planner(app)
32    visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
33
34    start_configuration = Configuration([0.0, 0.0, 0.0, 0.0, 1.57, 0.0])
35    goal_configuration = Configuration([1.57, 0.0, 0.0, 0.0, 1.57, 0.0])
36    initial_state = InitialState(start_configuration=start_configuration)
37    goal = ConfigurationGoals([goal_configuration])
38
39    result_without_obstacle = planner.path(
40        initial_state=initial_state,
41        request=PathRequest(
42            goals=goal,
43            description="Without obstacle",
44        ),
45    )
46    if not result_without_obstacle:
47        raise RuntimeError(f"Planning before obstacle is set failed with error: {result_without_obstacle.error}")
48    assert len(result_without_obstacle.path) == 1
49    print(f"Path before obstacle is set: {len(result_without_obstacle.path)} waypoint")
50
51    # Set an obstacle that obstructs the direct path
52    box_mesh = Mesh.create_box(Vector3f(0.2, 0.2, 0.2)).transform(
53        Pose(
54            [
55                [1.0, 0.0, 0.0, 1.1],
56                [0.0, 1.0, 0.0, 1.0],
57                [0.0, 0.0, 1.0, 1.6],
58                [0.0, 0.0, 0.0, 1.0],
59            ]
60        )
61    )
62    planner.set_obstacles(
63        obstacles=[
64            Obstacle.from_mesh(name="box_obstacle", mesh=box_mesh),
65        ]
66    )
67
68    result_with_obstacle = planner.path(
69        initial_state=initial_state,
70        request=PathRequest(
71            goals=goal,
72            description="With obstacle",
73        ),
74    )
75    if not result_with_obstacle:
76        raise RuntimeError(f"Planning after obstacle is set failed with error: {result_with_obstacle.error}")
77    assert len(result_with_obstacle.path) > 1
78    print(f"Path after obstacle is set: {len(result_with_obstacle.path)} waypoints")
79
80    if visualizer is not None:
81        print("Close the window to exit.")
82        visualizer.wait()
83
84
85if __name__ == "__main__":
86    _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 <iostream>
 6#include <string>
 7
 8namespace
 9{
10    constexpr auto useVisualizer = false;
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        // Make sure the CAD uses meters as unit, not millimeters
49        const std::string cadFilePath = "obstacle.stl";
50
51        std::cout << "Loading CAD file from: " << cadFilePath << "\n";
52        const auto mesh = Zivid::Motion::Experimental::loadMesh(cadFilePath);
53
54        planner.setObstacles({ Zivid::Motion::Obstacle::fromMesh("cad_obstacle", mesh) });
55
56        if(useVisualizer)
57        {
58            std::cout << "Close the window to exit.\n";
59            visualizer->wait();
60        }
61    }
62    catch(const std::exception &exception)
63    {
64        printException(exception);
65        return EXIT_FAILURE;
66    }
67    return EXIT_SUCCESS;
68}
 1from typing import Optional
 2
 3from zividmotion import Application, Obstacle, Planner, PlannerSettings, Profile, Visualizer, experimental
 4
 5USE_VISUALIZER = False
 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    # Make sure the CAD uses meters as unit, not millimeters
21    cad_file_path = "obstacle.stl"
22
23    print("Loading CAD file from:", cad_file_path)
24    mesh = experimental.load_mesh(cad_file_path)
25
26    planner.set_obstacles(
27        obstacles=[
28            Obstacle.from_mesh(name="cad_obstacle", mesh=mesh),
29        ]
30    )
31
32    if visualizer is not None:
33        print("Close the window to exit.")
34        visualizer.wait()
35
36
37if __name__ == "__main__":
38    _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 = false;
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("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::Vector3f> points;
76        points.reserve(xyzData.size());
77        for(const auto &p : xyzData)
78        {
79            points.emplace_back(Zivid::Motion::Vector3f{ p.x, p.y, p.z });
80        }
81        planner.setObstacles({ Zivid::Motion::Obstacle::fromPointCloud("point_cloud_obstacle", points) });
82
83        if(useVisualizer)
84        {
85            std::cout << "Close the window to exit.\n";
86            visualizer->wait();
87        }
88    }
89    catch(const std::exception &e)
90    {
91        std::cerr << "Error: " << Zivid::toString(e) << std::endl;
92        return EXIT_FAILURE;
93    }
94    return EXIT_SUCCESS;
95}
 1from typing import Optional
 2
 3import numpy as np
 4import zivid
 5from zividmotion import Application, Obstacle, Planner, PlannerSettings, Profile, Visualizer
 6
 7USE_VISUALIZER = False
 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 = camera_app.create_file_camera("FileCamera.zfc") if USE_FILE_CAMERA else camera_app.connect_camera()
22
23    print("Starting planner")
24    planner = _start_planner(motion_app)
25    visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
26
27    # Retrieved from hand-eye calibration of your setup.
28    # In an eye-in-hand setup, this would be the handeye_transform * robot_capture_pose.
29    # In an eye-to-hand setup, this would simply be the handeye_transform.
30    camera_to_base_transform = np.eye(4)
31
32    millimeters_to_meters_transform = np.diag([0.001, 0.001, 0.001, 1])
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    # Convert point cloud to unorganized point cloud
47    unorganized_point_cloud = frame.point_cloud().to_unorganized_point_cloud()
48
49    # Transform the point cloud from the camera frame to the base frame of the planner
50    unorganized_point_cloud.transform(camera_to_base_transform)
51
52    # Transform the point cloud from millimeters to meters, which is the expected unit in the planner
53    unorganized_point_cloud.transform(millimeters_to_meters_transform)
54
55    points = Obstacle.PointCloud(unorganized_point_cloud.copy_data("xyz"))
56    obstacle = Obstacle.from_point_cloud(name="point_cloud_obstacle", points=points)
57    planner.set_obstacles(obstacles=[obstacle])
58
59    if USE_VISUALIZER:
60        input("Press enter to continue:")
61
62    # Set the obstacle again, but this time with color.
63    # Note that this has a slight performance impact, not recommended in production code.
64    planner.clear_obstacles()
65
66    colors = Obstacle.Colors(unorganized_point_cloud.copy_data("rgba"))
67    colored_obstacle = Obstacle.from_colored_point_cloud(name="colored_obstacle", points=points, colors=colors)
68    planner.set_obstacles(obstacles=[colored_obstacle])
69
70    if visualizer is not None:
71        print("Close the window to exit.")
72        visualizer.wait()
73
74
75if __name__ == "__main__":
76    _main()

Interaction Planning

Simple Touch Approach

This example shows how to use the Touch functionality with a compliant replaceable tool to plan an approach to an object to be interacted with in a simplified setup.

  1#include <Zivid/Motion/Application.h>
  2#include <Zivid/Motion/Mesh.h>
  3#include <Zivid/Motion/Planner.h>
  4#include <Zivid/Motion/ReplaceableTool.h>
  5#include <Zivid/Motion/Visualizer.h>
  6
  7#include <cassert>
  8#include <cmath>
  9#include <iostream>
 10#include <random>
 11
 12namespace
 13{
 14    constexpr auto useVisualizer = false;
 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    // Utility function for creating a dummy point-cloud obstacle
 41    std::vector<Zivid::Motion::Vector3f>
 42    generateSpherePoints(const Zivid::Motion::Vector3f &center, const float radius, const int numPoints)
 43    {
 44        // Fixed seed for determinism
 45        std::mt19937 rng(42);
 46        std::uniform_real_distribution<float> thetaDist(0, 2 * M_PI);
 47        std::uniform_real_distribution<float> phiDist(0, M_PI);
 48
 49        std::vector<Zivid::Motion::Vector3f> points;
 50        points.reserve(numPoints);
 51        for(int i = 0; i < numPoints; ++i)
 52        {
 53            const float theta = thetaDist(rng);
 54            const float phi = phiDist(rng);
 55            points.push_back(
 56                {
 57                    center.x + radius * std::sin(phi) * std::cos(theta),
 58                    center.y + radius * std::sin(phi) * std::sin(theta),
 59                    center.z + radius * std::cos(phi),
 60                });
 61        }
 62        return points;
 63    }
 64
 65    // Create a pick pose with the end-effector pointing downwards at the given point
 66    // (180-degree rotation around Y-axis)
 67    Zivid::Motion::Pose getPickPose(const Zivid::Motion::Vector3f &pickPoint)
 68    {
 69        return Zivid::Motion::Pose{ Zivid::Motion::Matrix4x4{
 70            { -1.f, 0.f, 0.f, pickPoint.x },
 71            { 0.f, 1.f, 0.f, pickPoint.y },
 72            { 0.f, 0.f, -1.f, pickPoint.z },
 73            { 0.f, 0.f, 0.f, 1.f },
 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
 91        // Set a dummy obstacle to be interacted with
 92        constexpr Zivid::Motion::Vector3f obstacleCenter{ 1.1f, 1.0f, 0.9f };
 93        constexpr float obstacleRadius = 0.2f;
 94        planner.setObstacles(
 95            { Zivid::Motion::Obstacle::fromPointCloud(
 96                "interaction_object",
 97                Zivid::Motion::Obstacle::PointCloud{ generateSpherePoints(obstacleCenter, obstacleRadius, 1000) }) });
 98
 99        // Set a tool that simulates a vacuum gripper with a suction cup with 2 cm compliance
100        constexpr Zivid::Motion::Vector3f toolBaseDimensions{ 0.15, 0.15, 0.15 };
101        auto suctionCupMatrix = Zivid::Motion::Matrix4x4::identity();
102        suctionCupMatrix(2, 3) = toolBaseDimensions.z;
103        constexpr Zivid::Motion::Vector3f suctionCupDimensions{ 0.05, 0.05, 0.02 };
104
105        auto rigidTool = Zivid::Motion::Mesh::createBox(toolBaseDimensions).bottomCenterTransform();
106        auto compliantTool = Zivid::Motion::Mesh::createBox(suctionCupDimensions)
107                                 .bottomCenterTransform()
108                                 .transform(Zivid::Motion::Pose{ suctionCupMatrix });
109        const Zivid::Motion::ToolGeometry toolGeometry{ rigidTool, compliantTool };
110        planner.setReplaceableTool(Zivid::Motion::ReplaceableTool{ "vacuum_gripper", toolGeometry });
111
112        // Set the new tcp to be at the center tip of the gripper, when the suction cup is compressed 1 cm for good contact
113        auto tcpMatrix = Zivid::Motion::Matrix4x4::identity();
114        tcpMatrix(2, 3) += toolBaseDimensions.z + suctionCupDimensions.z - 0.01f;
115        planner.setTcp(Zivid::Motion::Tcp{ Zivid::Motion::Pose{ tcpMatrix }, { 0.f, 0.f, 1.f } });
116
117        // Find the pick joint configuration, gripping the top of the object with the new TCP
118        constexpr Zivid::Motion::Vector3f pickPoint{ obstacleCenter.x,
119                                                     obstacleCenter.y,
120                                                     obstacleCenter.z + obstacleRadius };
121        const auto pickGoal =
122            planner.computeInverseKinematics(std::vector{ getPickPose(pickPoint) }, startConfiguration);
123        if(pickGoal.noneValid())
124        {
125            throw std::runtime_error("No valid inverse kinematics solution found for pick pose");
126        }
127
128        const Zivid::Motion::InitialState initialState{ startConfiguration };
129
130        // Path type defaults to Free
131        Zivid::Motion::PathRequest freeRequest{ pickGoal };
132        freeRequest.description = "Free path to pick goal";
133        const auto resultFree = planner.path(initialState, freeRequest);
134        // Expect blocked end with path type Free, since the compliant part of the tool has to enter the obstacle for the
135        // TCP to reach the goal
136        assert(resultFree.error == Zivid::Motion::PathResult::Error::blockedEnd);
137
138        Zivid::Motion::PathRequest touchRequest{ pickGoal };
139        touchRequest.type = Zivid::Motion::PathRequest::Type::touch;
140        touchRequest.description = "Touch path to pick goal";
141        const auto resultTouch = planner.path(initialState, touchRequest);
142        if(!resultTouch)
143        {
144            throw std::runtime_error("Planning with path type Touch failed with result: " + resultTouch.toString());
145        }
146
147        std::cout << "\nSuccessful touch path: " << resultTouch << "\n";
148
149        if(useVisualizer)
150        {
151            std::cout << "Close the window to exit.\n";
152            visualizer->wait();
153        }
154    }
155    catch(const std::exception &exception)
156    {
157        printException(exception);
158        return EXIT_FAILURE;
159    }
160    return EXIT_SUCCESS;
161}

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    Configuration,
  8    InitialState,
  9    Matrix4x4,
 10    Mesh,
 11    Obstacle,
 12    PathRequest,
 13    PathResult,
 14    Planner,
 15    PlannerSettings,
 16    Pose,
 17    Profile,
 18    ReplaceableTool,
 19    Tcp,
 20    ToolGeometry,
 21    Vector3f,
 22    Visualizer,
 23)
 24
 25USE_VISUALIZER = False
 26
 27
 28def _start_planner(app: Application) -> Planner:
 29    planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
 30    return app.create_planner(planner_settings)
 31
 32
 33# Utility method for creating a dummy obstacle
 34def _generate_sphere_points(center_point: list[float], radius: float, num_points: int) -> Obstacle.PointCloud:
 35    np.random.seed(42)  # Set a fixed random seed for determinism
 36    angles = np.random.uniform(0.0, 1.0, size=(num_points, 2))
 37    theta = angles[:, 0] * 2 * np.pi
 38    phi = angles[:, 1] * np.pi
 39    points = np.empty((num_points, 3), dtype=np.float32)
 40    points[:, 0] = center_point[0] + radius * np.sin(phi) * np.cos(theta)
 41    points[:, 1] = center_point[1] + radius * np.sin(phi) * np.sin(theta)
 42    points[:, 2] = center_point[2] + radius * np.cos(phi)
 43    return Obstacle.PointCloud(points)
 44
 45
 46def _get_pick_pose(pick_point: list[float]) -> Pose:
 47    # Create a simple pick pose with the end-effector pointing downwards at the pick point
 48    pick_pose = np.eye(4, dtype=np.float32)
 49    pick_pose[:3, 3] = pick_point
 50    # Rotate 180 degrees around Y-axis to point downwards
 51    pick_pose[:3, :3] = Rotation.from_rotvec([0, np.pi, 0]).as_matrix()
 52    return Pose(pick_pose)
 53
 54
 55def _main() -> None:
 56    app = Application()
 57
 58    print("Starting planner")
 59    planner = _start_planner(app)
 60    visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
 61
 62    start_configuration = Configuration([0.0, 0.0, 0.0, 0.0, 1.57, 0.0])
 63
 64    # Set a dummy obstacle to be interacted with
 65    center_point = [1.1, 1.0, 0.9]
 66    radius = 0.2
 67    planner.set_obstacles(
 68        obstacles=[
 69            Obstacle.from_point_cloud(
 70                name="interaction_object",
 71                points=_generate_sphere_points(center_point=center_point, radius=radius, num_points=1000),
 72            )
 73        ]
 74    )
 75
 76    # Set a tool that simulates a vacuum gripper with a suction cup with 2 cm compliance
 77    tool_base_dimensions = Vector3f(0.15, 0.15, 0.15)
 78    suction_cup_matrix = Matrix4x4.identity()
 79    suction_cup_matrix[2, 3] = tool_base_dimensions.z
 80    suction_cup_dimensions = Vector3f(0.05, 0.05, 0.02)
 81    rigid_tool = Mesh.create_box(tool_base_dimensions).bottom_center_transform()
 82    compliant_tool = (
 83        Mesh.create_box(suction_cup_dimensions).bottom_center_transform().transform(Pose(suction_cup_matrix))
 84    )
 85    planner.set_replaceable_tool(
 86        replaceable_tool=ReplaceableTool(
 87            name="vacuum_gripper",
 88            geometry=ToolGeometry(rigid_section=rigid_tool, compliant_section=compliant_tool),
 89        )
 90    )
 91
 92    # Set the new tcp to be at the center tip of the gripper, when the suction cup is compressed 1 cm for good contact
 93    tcp_matrix = Matrix4x4.identity()
 94    tcp_matrix[2, 3] += tool_base_dimensions.z + suction_cup_dimensions.z - 0.01
 95    planner.set_tcp(tcp=Tcp(transform=Pose(tcp_matrix), tool_direction=Vector3f(0.0, 0.0, 1.0)))
 96
 97    # Find the pick joint configuration, gripping the top of the object with the new TCP
 98    pick_point = [center_point[0], center_point[1], center_point[2] + radius]
 99    pick_pose = _get_pick_pose(pick_point)
100
101    pick_goal = planner.compute_inverse_kinematics(poses=[pick_pose], reference_configuration=start_configuration)
102    if pick_goal.none_valid():
103        raise RuntimeError("No valid inverse kinematics solution found for pick pose")
104
105    initial_state = InitialState(start_configuration=start_configuration)
106    # Path type defaults to Free
107    result_free = planner.path(
108        initial_state=initial_state,
109        request=PathRequest(
110            goals=pick_goal,
111            description="Free path to pick goal",
112        ),
113    )
114    # Expect blocked end with path type Free, since the compliant part of the tool has to enter the obstacle for the
115    # TCP to reach the goal
116    assert result_free.error == PathResult.Error.blockedEnd
117
118    # Specify type touch
119    result_touch = planner.path(
120        initial_state=initial_state,
121        request=PathRequest(
122            type=PathRequest.Type.touch,
123            goals=pick_goal,
124            description="Touch path to pick goal",
125        ),
126    )
127    if not result_touch:
128        raise RuntimeError(f"Planning with path type Touch failed with error: {result_touch.error}")
129
130    print("\nSuccessful touch path:", result_touch.path)
131
132    if visualizer is not None:
133        print("Close the window to exit.")
134        visualizer.wait()
135
136
137if __name__ == "__main__":
138    _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/Mesh.h>
  3#include <Zivid/Motion/Planner.h>
  4#include <Zivid/Motion/ReplaceableTool.h>
  5#include <Zivid/Motion/Visualizer.h>
  6
  7#include <cmath>
  8#include <iostream>
  9#include <random>
 10
 11namespace
 12{
 13    constexpr auto useVisualizer = false;
 14
 15    void printException(const std::exception &e, const int level = 0)
 16    {
 17        std::cerr << std::string(level * 4, ' ') << (level ? "+ " : "") << "Exception: " << e.what() << '\n';
 18        try
 19        {
 20            std::rethrow_if_nested(e);
 21        }
 22        catch(const std::exception &nestedException)
 23        {
 24            printException(nestedException, level + 1);
 25        }
 26        catch(...)
 27        {}
 28    }
 29
 30    Zivid::Motion::Planner startPlanner(const Zivid::Motion::Application &app)
 31    {
 32        return app.createPlanner(
 33            Zivid::Motion::PlannerSettings{
 34                "demo_cell",
 35                Zivid::Motion::Profile::testing,
 36            });
 37    }
 38
 39    // Utility function for creating a dummy point-cloud obstacle
 40    std::vector<Zivid::Motion::Vector3f>
 41    generateSpherePoints(const Zivid::Motion::Vector3f &center, const float radius, const int numPoints)
 42    {
 43        // Fixed seed for determinism
 44        std::mt19937 rng(42);
 45        std::uniform_real_distribution<float> thetaDist(0, 2 * M_PI);
 46        std::uniform_real_distribution<float> phiDist(0, M_PI);
 47
 48        std::vector<Zivid::Motion::Vector3f> points;
 49        points.reserve(numPoints);
 50        for(int i = 0; i < numPoints; ++i)
 51        {
 52            const float theta = thetaDist(rng);
 53            const float phi = phiDist(rng);
 54            points.push_back(
 55                {
 56                    center.x + radius * std::sin(phi) * std::cos(theta),
 57                    center.y + radius * std::sin(phi) * std::sin(theta),
 58                    center.z + radius * std::cos(phi),
 59                });
 60        }
 61        return points;
 62    }
 63
 64    std::vector<Zivid::Motion::Obstacle> getDummyObstacles(const Zivid::Motion::Vector3f &center, const float radius)
 65    {
 66        std::vector<Zivid::Motion::Obstacle> obstacles;
 67        for(const float dx : { -2.f * radius, 0.f, 2.f * radius })
 68        {
 69            for(const float dy : { -2.f * radius, 0.f, 2.f * radius })
 70            {
 71                const Zivid::Motion::Vector3f c{ center.x + dx, center.y + dy, center.z };
 72                const auto name = "obstacle_" + std::to_string(obstacles.size());
 73                obstacles.push_back(
 74                    Zivid::Motion::Obstacle::fromPointCloud(
 75                        name, Zivid::Motion::Obstacle::PointCloud{ generateSpherePoints(c, radius, 400) }));
 76            }
 77        }
 78        return obstacles;
 79    }
 80} // namespace
 81
 82int main()
 83{
 84    try
 85    {
 86        const Zivid::Motion::Application app;
 87
 88        std::cout << "Starting planner\n";
 89        auto planner = startPlanner(app);
 90        auto visualizer =
 91            useVisualizer ? std::optional{ Zivid::Motion::Visualizer::viewPlanner(planner) } : std::nullopt;
 92
 93        constexpr Zivid::Motion::Vector3f obstacleCenter{ 1.1f, 1.0f, 0.2f };
 94        constexpr float obstacleRadius = 0.15f;
 95        planner.setObstacles(getDummyObstacles(obstacleCenter, obstacleRadius));
 96
 97        // For example purposes, simulate a tool mounted asymmetrically at 45 degrees with a compliant tip
 98        // (e.g. a vacuum gripper with compliant suction cups)
 99        constexpr float angle = M_PI_4;
100        const float cosA = std::cos(angle);
101        const float sinA = std::sin(angle);
102        const Zivid::Motion::Pose toolTransform{ Zivid::Motion::Matrix4x4{
103            { cosA, -sinA, 0.f, 0.f },
104            { sinA, cosA, 0.f, 0.f },
105            { 0.f, 0.f, 1.f, 0.05f },
106            { 0.f, 0.f, 0.f, 1.f },
107        } };
108        constexpr Zivid::Motion::Vector3f rigidToolDimensions{ 0.35f, 0.35f, 0.04f };
109        // Specify the last 2cm of the tool as the compliant area
110        const Zivid::Motion::Pose compliantToolTransform{ Zivid::Motion::Matrix4x4{
111            { 1.f, 0.f, 0.f, 0.f },
112            { 0.f, 1.f, 0.f, 0.f },
113            { 0.f, 0.f, 1.f, rigidToolDimensions.z },
114            { 0.f, 0.f, 0.f, 1.f },
115        } };
116        constexpr Zivid::Motion::Vector3f compliantToolDimensions{ 0.4f, 0.4f, 0.02f };
117        auto rigidTool =
118            Zivid::Motion::Mesh::createBox(rigidToolDimensions).bottomCenterTransform().transform(toolTransform);
119        auto compliantTool = Zivid::Motion::Mesh::createBox(compliantToolDimensions)
120                                 .bottomCenterTransform()
121                                 .transform(compliantToolTransform)
122                                 .transform(toolTransform);
123        const Zivid::Motion::ToolGeometry toolGeometry{ rigidTool, compliantTool };
124        planner.setReplaceableTool(Zivid::Motion::ReplaceableTool{ "vacuum_gripper", toolGeometry });
125
126        // Set the new tcp to be at the center tip of the tool, with the compliant area compressed by 1cm for good contact
127        auto tcpTransform = toolTransform.toMatrix();
128        tcpTransform(2, 3) += rigidToolDimensions.z + compliantToolDimensions.z - 0.01f;
129        planner.setTcp(Zivid::Motion::Tcp{ Zivid::Motion::Pose{ tcpTransform }, { 0.f, 0.f, 1.f } });
130
131        const Zivid::Motion::Configuration startConfiguration{ 0.f, 0.f, 0.f, 0.f, 1.57f, 0.f };
132
133        // Define a pick pose on the top center of the object with the end-effector rotated 180 degrees around Y to point downwards
134        const Zivid::Motion::Pose pickPose{ Zivid::Motion::Matrix4x4{
135            { -1.f, 0.f, 0.f, obstacleCenter.x },
136            { 0.f, 1.f, 0.f, obstacleCenter.y },
137            { 0.f, 0.f, -1.f, obstacleCenter.z + obstacleRadius },
138            { 0.f, 0.f, 0.f, 1.f },
139        } };
140        // Compute the pick joint configuration with the new tcp
141        const auto pickGoal = planner.computeInverseKinematics(std::vector{ pickPose }, startConfiguration);
142        if(pickGoal.noneValid())
143        {
144            throw std::runtime_error("No valid inverse kinematics solution found for pick pose");
145        }
146
147        // Compute a pick approach path with the new tool
148        Zivid::Motion::PathRequest approachRequest{ pickGoal };
149        approachRequest.type = Zivid::Motion::PathRequest::Type::touch;
150        approachRequest.description = "Pick approach";
151        const auto approachResult = planner.path(Zivid::Motion::InitialState{ startConfiguration }, approachRequest);
152        if(!approachResult)
153        {
154            throw std::runtime_error("Planning pick approach failed with result: " + approachResult.toString());
155        }
156
157        // Set carried object (bounding box around the picked obstacle)
158        constexpr float objectSize = obstacleRadius * 2.f;
159        planner.setCarriedObject(
160            Zivid::Motion::Mesh::createBox(Zivid::Motion::Vector3f{ objectSize, objectSize, objectSize })
161                .bottomCenterTransform());
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        Zivid::Motion::PathRequest retractRequest{ std::vector{ startConfiguration } };
168        retractRequest.retractDirection = { 0.f, 0.f, 1.f };
169        retractRequest.description = "Pick retract";
170        const auto retractResult = planner.path(Zivid::Motion::InitialState{ approachResult }, retractRequest);
171        if(!retractResult)
172        {
173            throw std::runtime_error("Planning pick retract failed with result: " + retractResult.toString());
174        }
175
176        std::cout << retractResult << "\n";
177
178        if(useVisualizer)
179        {
180            std::cout << "Close the window to exit.\n";
181            visualizer->wait();
182        }
183    }
184    catch(const std::exception &exception)
185    {
186        printException(exception);
187        return EXIT_FAILURE;
188    }
189    return EXIT_SUCCESS;
190}

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    Configuration,
  8    InitialState,
  9    Matrix4x4,
 10    Mesh,
 11    Obstacle,
 12    PathRequest,
 13    Planner,
 14    PlannerSettings,
 15    Pose,
 16    Profile,
 17    ReplaceableTool,
 18    Tcp,
 19    ToolGeometry,
 20    Vector3f,
 21    Visualizer,
 22)
 23
 24USE_VISUALIZER = False
 25
 26
 27def _start_planner(app: Application) -> Planner:
 28    planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
 29    return app.create_planner(planner_settings)
 30
 31
 32# Utility method for creating a dummy obstacle
 33def _generate_sphere_points(center_point: list[float], radius: float, num_points: int) -> Obstacle.PointCloud:
 34    np.random.seed(42)  # Set a fixed random seed for determinism
 35    angles = np.random.uniform(0.0, 1.0, size=(num_points, 2))
 36    theta = angles[:, 0] * 2 * np.pi
 37    phi = angles[:, 1] * np.pi
 38    points = np.empty((num_points, 3), dtype=np.float32)
 39    points[:, 0] = center_point[0] + radius * np.sin(phi) * np.cos(theta)
 40    points[:, 1] = center_point[1] + radius * np.sin(phi) * np.sin(theta)
 41    points[:, 2] = center_point[2] + radius * np.cos(phi)
 42    return Obstacle.PointCloud(points)
 43
 44
 45def _get_dummy_obstacles(center_point: list[float], radius: float) -> list[Obstacle]:
 46    obstacles: list[Obstacle] = []
 47    for x in [center_point[0] - 2 * radius, center_point[0], center_point[0] + 2 * radius]:
 48        for y in [center_point[1] - 2 * radius, center_point[1], center_point[1] + 2 * radius]:
 49            center = [x, y, center_point[2]]
 50            points = _generate_sphere_points(center_point=center, radius=radius, num_points=400)
 51            obstacles.append(Obstacle.from_point_cloud(name=f"obstacle_{len(obstacles)}", points=points))
 52    return obstacles
 53
 54
 55def _main() -> None:
 56    app = Application()
 57
 58    print("Starting planner")
 59    planner = _start_planner(app)
 60    visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
 61
 62    obstacle_center = [1.1, 1.0, 0.2]
 63    obstacle_radius = 0.15
 64    planner.set_obstacles(obstacles=_get_dummy_obstacles(center_point=obstacle_center, radius=obstacle_radius))
 65
 66    # For example purposes, simulate a tool mounted asymmetrically at 45 degrees with a compliant tip
 67    # (e.g. a vacuum gripper with compliant suction cups)
 68    tool_matrix = np.eye(4)
 69    tool_matrix[1, 3] = 0.05
 70    tool_matrix[:3, :3] = Rotation.from_rotvec([0, 0, np.pi / 4]).as_matrix()
 71    rigid_tool_dimensions = Vector3f(0.35, 0.35, 0.04)
 72    # Specify the last 2cm of the tool as the compliant area
 73    compliant_matrix = Matrix4x4.identity()
 74    compliant_matrix[2, 3] = rigid_tool_dimensions.z
 75    compliant_tool_dimensions = Vector3f(0.35, 0.35, 0.02)
 76    rigid_tool = Mesh.create_box(rigid_tool_dimensions).bottom_center_transform().transform(Pose(tool_matrix))
 77    compliant_tool = (
 78        Mesh.create_box(compliant_tool_dimensions)
 79        .bottom_center_transform()
 80        .transform(Pose(tool_matrix @ compliant_matrix))
 81    )
 82    planner.set_replaceable_tool(
 83        replaceable_tool=ReplaceableTool(
 84            name="vacuum_gripper",
 85            geometry=ToolGeometry(rigid_section=rigid_tool, compliant_section=compliant_tool),
 86        )
 87    )
 88
 89    # Set the new tcp to be at the center tip of the tool, with the compliant area compressed by 1cm for good contact
 90    tcp_matrix = tool_matrix.copy()
 91    tcp_matrix[2, 3] += rigid_tool_dimensions.z + compliant_tool_dimensions.z - 0.01
 92    planner.set_tcp(tcp=Tcp(transform=Pose(tcp_matrix), tool_direction=Vector3f(0.0, 0.0, 1.0)))
 93
 94    start_configuration = Configuration([0.0, 0.0, 0.0, 0.0, 1.57, 0.0])
 95
 96    # Define a pick pose on the top center of the object with the end-effector rotated 180 degrees around Y to point downwards
 97    pick_position = [obstacle_center[0], obstacle_center[1], obstacle_center[2] + obstacle_radius]
 98    pick_matrix = np.eye(4)
 99    pick_matrix[:3, 3] = pick_position
100    pick_matrix[:3, :3] = Rotation.from_rotvec([0, np.pi, 0]).as_matrix()
101    pick_pose = Pose(pick_matrix)
102
103    # Compute the pick joint configuration with the new tcp
104    pick_goal = planner.compute_inverse_kinematics(poses=[pick_pose], reference_configuration=start_configuration)
105    if pick_goal.none_valid():
106        raise RuntimeError("No valid inverse kinematics solution found for pick pose")
107
108    # Compute a pick approach path with the new tool
109    approach_result = planner.path(
110        initial_state=InitialState(start_configuration=start_configuration),
111        request=PathRequest(
112            type=PathRequest.Type.touch,
113            goals=pick_goal,
114            description="Pick approach",
115        ),
116    )
117    if not approach_result:
118        raise RuntimeError(f"Planning pick approach failed with error: {approach_result.error}")
119
120    # Set carried object (bounding box around the picked obstacle)
121    obstacle_size = obstacle_radius * 2
122    planner.set_carried_object(
123        carried_object=Mesh.create_box(Vector3f(obstacle_size, obstacle_size, obstacle_size)).bottom_center_transform()
124    )
125
126    # Compute a pick retract path with custom retract direction
127    # This retract direction is the same as the negative tcp tool direction expressed in the base frame when the robot
128    # is in the start configuration for this path call. Meaning it in this case is a redundant specification, just to
129    # show the signature for example purposes.
130    retract_result = planner.path(
131        initial_state=InitialState(previous_result=approach_result),
132        request=PathRequest(
133            retract_direction=Vector3f(0.0, 0.0, 1.0),
134            goals=[start_configuration],
135            description="Pick retract",
136        ),
137    )
138    if not retract_result:
139        raise RuntimeError(f"Planning pick retract failed with error: {retract_result.error}")
140
141    print(retract_result)
142
143    if visualizer is not None:
144        print("Close the window to exit.")
145        visualizer.wait()
146
147
148if __name__ == "__main__":
149    _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 = false;
 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::Vector3f>
 40    generateSpherePoints(const Zivid::Motion::Vector3f &center, 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::Vector3f> 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        const Zivid::Motion::ConfigurationGoals goal{ { goalConfiguration } };
 88
 89        Zivid::Motion::PathRequest withoutAttachmentRequest{ goal };
 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{ goal };
104        withAttachmentRequest.description = "Path with attachment";
105        const auto resultWithAttachment = planner.path(initialState, withAttachmentRequest);
106        if(!resultWithAttachment)
107        {
108            std::cout << "Planning with active attachment failed with result: " << resultWithAttachment.toString()
109                      << "\n";
110        }
111        assert(resultWithAttachment.path().size() > 1);
112        std::cout << "With attachment: " << resultWithAttachment.path().size() << " waypoints\n";
113
114        if(useVisualizer)
115        {
116            std::cout << "Close the window to exit.\n";
117            visualizer->wait();
118        }
119    }
120    catch(const std::exception &exception)
121    {
122        printException(exception);
123        return EXIT_FAILURE;
124    }
125    return EXIT_SUCCESS;
126}
 1from typing import Optional
 2
 3import numpy as np
 4from zividmotion import (
 5    Application,
 6    Configuration,
 7    ConfigurationGoals,
 8    InitialState,
 9    Obstacle,
10    PathRequest,
11    Planner,
12    PlannerSettings,
13    Profile,
14    Visualizer,
15)
16
17USE_VISUALIZER = False
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) -> Obstacle.PointCloud:
27    np.random.seed(42)  # Set a fixed random seed for determinism
28    angles = np.random.uniform(0.0, 1.0, size=(num_points, 2))
29    theta = angles[:, 0] * 2 * np.pi
30    phi = angles[:, 1] * np.pi
31    points = np.empty((num_points, 3), dtype=np.float32)
32    points[:, 0] = center_point[0] + radius * np.sin(phi) * np.cos(theta)
33    points[:, 1] = center_point[1] + radius * np.sin(phi) * np.sin(theta)
34    points[:, 2] = center_point[2] + radius * np.cos(phi)
35    return Obstacle.PointCloud(points)
36
37
38def _main() -> None:
39    app = Application()
40
41    print("Starting planner")
42    planner = _start_planner(app)
43    visualizer: Optional[Visualizer] = Visualizer.view_planner(planner) if USE_VISUALIZER else None
44
45    start_configuration = Configuration([0.0, 0.0, 0.0, 0.0, 1.57, 0.0])
46    goal_configuration = Configuration([1.57, 0.0, 0.0, 0.0, 1.57, 0.0])
47    attachment_name = "demo_attachment"  # Must match a name in the attachments definition in the planner config file
48
49    # Set an obstacle that obstructs the direct path for the attachment
50    planner.set_obstacles(
51        obstacles=[
52            Obstacle.from_point_cloud(
53                name="sphere_obstacle",
54                points=_generate_sphere_points(center_point=[1.1, 1.0, 1.2], radius=0.2, num_points=1000),
55            )
56        ]
57    )
58
59    initial_state = InitialState(start_configuration=start_configuration)
60    goal = ConfigurationGoals([goal_configuration])
61
62    result_without_attachment = planner.path(
63        initial_state=initial_state,
64        request=PathRequest(
65            goals=goal,
66            description="Path without attachment",
67        ),
68    )
69    if not result_without_attachment:
70        raise RuntimeError(f"Planning without active attachment failed with error: {result_without_attachment.error}")
71    assert len(result_without_attachment.path) == 1
72    print(f"Path without attachment: {len(result_without_attachment.path)} waypoints")
73
74    print("Setting attachment", attachment_name)
75    planner.set_attachments(attachments=[attachment_name])
76
77    result_with_attachment = planner.path(
78        initial_state=initial_state,
79        request=PathRequest(
80            goals=goal,
81            description="Path with attachment",
82        ),
83    )
84    if not result_with_attachment:
85        print(f"Planning with active attachment failed with error: {result_with_attachment.error}")
86    assert len(result_with_attachment.path) > 1
87    print(f"Path with attachment: {len(result_with_attachment.path)} waypoints")
88
89    if visualizer is not None:
90        print("Close the window to exit.")
91        visualizer.wait()
92
93
94if __name__ == "__main__":
95    _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        Zivid::Motion::PathRequest unreachableRequest{ std::vector{ invalidGoalConfiguration } };
48        unreachableRequest.description = "Path with unreachable goal";
49        const auto unsuccessfulResult =
50            planner.path(Zivid::Motion::InitialState{ startConfiguration }, unreachableRequest);
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    }
56    catch(const std::exception &exception)
57    {
58        const std::filesystem::path outputFolder = "/tmp";
59
60        const auto logPath = planner.exportApiLog(outputFolder);
61        const auto packagePath = Zivid::Motion::packageApiLog(app, logPath);
62        std::cerr << "Planner failed, debug package available at: " + std::filesystem::canonical(packagePath).string()
63                  << std::endl;
64
65        printException(exception);
66        return EXIT_SUCCESS; // for the purpose of this sample, saving a debug package is success
67    }
68    return EXIT_FAILURE;
69}
 1from pathlib import Path
 2
 3from zividmotion import (
 4    Application,
 5    Configuration,
 6    InitialState,
 7    PathRequest,
 8    Planner,
 9    PlannerSettings,
10    Profile,
11    package_api_log,
12)
13
14
15def _start_planner(app: Application) -> Planner:
16    planner_settings = PlannerSettings(cell_name="demo_cell", profile=Profile.testing)
17    return app.create_planner(planner_settings)
18
19
20def _main() -> None:
21    app = Application()
22
23    print("Starting planner")
24    planner = _start_planner(app)
25
26    start_configuration = Configuration([0, 0, 0, 0, 1.57, 0])
27    invalid_goal_configuration = Configuration([100, 0, 0, 0, 1.57, 0])
28
29    unsuccessful_result = planner.path(
30        initial_state=InitialState(start_configuration=start_configuration),
31        request=PathRequest(
32            goals=[invalid_goal_configuration],
33            description="Path with unreachable goal",
34        ),
35    )
36    print("Path result has status: ", unsuccessful_result)
37
38    try:
39        # Some operation that fails, like creating an InitialState from an unsuccessful path result
40        InitialState(previous_result=unsuccessful_result)
41    except Exception:
42        output_folder = Path("/tmp")
43
44        log_path = planner.export_api_log(output_directory=output_folder)
45        package_path = package_api_log(application=app, api_log_path=log_path)
46
47        print(f"Planner failed, debug package available at: {package_path.resolve()}")
48
49
50if __name__ == "__main__":
51    _main()