{"id":625,"date":"2021-06-18T02:55:32","date_gmt":"2021-06-18T06:55:32","guid":{"rendered":"https:\/\/blogs.sw.siemens.com\/plm-components\/?p=625"},"modified":"2026-03-26T08:03:47","modified_gmt":"2026-03-26T12:03:47","slug":"kineoworks-step-by-step-working-with-trajectories","status":"publish","type":"post","link":"https:\/\/blogs.sw.siemens.com\/plm-components\/kineoworks-step-by-step-working-with-trajectories\/","title":{"rendered":"KineoWorks step-by-step #3: working with trajectories"},"content":{"rendered":"\n<p><a href=\"https:\/\/www.plm.automation.siemens.com\/global\/en\/products\/plm-components\/kineo.html\" target=\"_blank\" rel=\"noreferrer noopener\">KineoWorks<\/a><sup>TM<\/sup> is a software component that automatically computes collision-free motion, solving complex path-planning problems in applications such as robotic collision-free trajectory optimization. In this series of articles, Etienne Ferr\u00e9, Development Director of Kineo components, describes some of the key steps that you can easily implement to develop a sophisticated robotics simulation.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Part 3: Working with Trajectories<\/h2>\n\n\n\n<p>So far in this series, we have described processes for <a href=\"https:\/\/blogs.sw.siemens.com\/plm-components\/kineoworks%e2%80%afstep-by-step-path-planning\/\">path planning<\/a> and <a href=\"https:\/\/blogs.sw.siemens.com\/plm-components\/kineoworks-step-by-step-building-your-robot-with-kwik\/\">building a robot<\/a>. This article takes a closer look at <em>trajectories<\/em> that describe the movement of a kinematic system over time. But first, a quick reminder about paths\u2026<\/p>\n\n\n\n<p>In KineoWorks, a path is described by a sequence of configurations. Elementary paths made of pairs of configurations are called direct paths.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/im1.png\" alt=\"A kineo path between configurations\" class=\"wp-image-626\" width=\"651\" height=\"345\" srcset=\"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/im1.png 868w, https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/im1-600x318.png 600w, https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/im1-768x407.png 768w\" sizes=\"auto, (max-width: 651px) 100vw, 651px\" \/><\/figure><\/div>\n\n\n<p>The purpose of a path is to connect a start configuration to a goal configuration in a configuration space. You can associate a configuration with any 1-dimensional position between 0.0 and the path length.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Path length meaning<\/h3>\n\n\n\n<p>Path length is usually the total distance along the path in a configuration space, but <em>not always<\/em>: it mainly depends on the path steering method. Most of the time, it is safe to assume that the lower the length is, the shorter the path is. This allows you to optimize paths, for instance, using length as a criterion. But length is not suitable for representing durations as it doesn\u2019t describe where the system is at a given <em>time<\/em>. This is one limitation of using paths.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Path guarantee on continuity<\/h3>\n\n\n\n<p>A path is guaranteed to be continuous in position because losing position continuity would break the connection between the start and the goal configurations. However, there is no such guarantee of continuity of the derivative.<\/p>\n\n\n\n<p>If you interpret the derivative of a path as velocity, you are not guaranteed continuous velocity. Typically, a sequence of direct paths, with a linear steering method, going in different directions will jump from a constant velocity in one direction to a constant velocity in another direction. You would not be able to run such a path on a real system which requires at least a continuous velocity. This is a second limitation of using paths.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"835\" height=\"524\" src=\"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/gif1.gif\" alt=\"Kineo path wth linear steering method\" class=\"wp-image-627\"\/><figcaption class=\"wp-element-caption\">An example of a path with two direct paths. <br>With a linear steering method, the velocity on the y axis jumps from a positive value to a negative value<\/figcaption><\/figure><\/div>\n\n\n<h3 class=\"wp-block-heading\">Executing a path on a real system<\/h3>\n\n\n\n<p>Despite these limitations, as a solution to a path planning problem, a path does the job: it connects start and goal configurations. A path is all we need to make this connection in the valid configuration space. However, as seen above, a path in general does not describe how the system should move over time, because it does not take into account maximum velocity, maximum acceleration, continuity constraints and other parameters of the real system.<\/p>\n\n\n\n<p>A solution to this problem is to use specific velocity profiles such as the trapezoidal velocity profile. Such profiles allow a path to be followed exactly while guaranteeing a continuous velocity at one cost: the velocity must be null at waypoints when the direction changes. Such profiles can be managed by a 3rd party robot controller (requiring only path configurations as input) or they can be simulated with the corresponding steering method. The trapezoidal steering method is an example where the length of a path is no longer a distance because it simulates motion over time.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"835\" height=\"524\" src=\"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/gif2.gif\" alt=\"kineo path with trapezoidal steering method\" class=\"wp-image-629\"\/><figcaption class=\"wp-element-caption\">With a trapezoidal steering method, the velocity becomes continuous<\/figcaption><\/figure><\/div>\n\n\n<p>The solution above is not always satisfactory because:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Stopping at each waypoint can slow the overall motion significantly<\/li>\n\n\n\n<li>The trapezoidal steering method is unsuitable for systems that require continuous acceleration<\/li>\n\n\n\n<li>Even with continuous acceleration, the relation between path length and motion time is obscured<\/li>\n\n\n\n<li>You will not benefit from collision-free motion planning in KineoWorks if you select a 3rd party robot controller to compute the real motion<\/li>\n<\/ul>\n\n\n\n<p>So when it comes to simulating system motion, paths are not the best choice. This is why we introduced the concept of a <em>trajectory<\/em>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Introduction to Trajectories<\/h2>\n\n\n\n<p>A trajectory fully describes the movement of a real system, in other words, the position of the system over time. While paths are parameterized with respect to distance, trajectories are parameterized with respect to time. The conventional unit of trajectory duration is the second. A trajectory still follows a path in a spatial sense, but adds information to it: two different trajectories can follow the same path with different timings. For each time interval, you can get the position of the system\u2019s active axes, and also their velocity, acceleration and jerk (derivative of acceleration). In addition you can get the energy consumed by the system when it runs the trajectory.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"835\" height=\"524\" src=\"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/gif3.gif\" alt=\"working with trajectories - cubic spline \" class=\"wp-image-630\"\/><figcaption class=\"wp-element-caption\">A simple cubic spline trajectory: it has a continuous acceleration (white trace, lower panel) and it doesn\u2019t stop at waypoints<\/figcaption><\/figure><\/div>\n\n\n<p>The <strong>CkwsTrajectory<\/strong> class is the most abstract interface for a trajectory. It provides enough information to run and evaluate trajectories obtained through other API functions (see later)..<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Playing a trajectory<\/h3>\n\n\n\n<p>Playing a trajectory is similar to playing a path. But while paths explore configurations as a function of distance along the path, trajectories are more natural \u2013 they explore how configurations evolve as a function of time. So in the example code below, both trajectory duration and <strong>CkitTime<\/strong> are measured in seconds..<\/p>\n\n\n\n<p>Here is an example of how you can play a trajectory:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>if(m_trajectory &amp;&amp; m_trajectory-&gt;isValid())\n{\n   CkttDisplay::message(\"Playing trajectory...\");\n   CkwsConfig config(m_trajectory-&gt;device());\n   CkitTime    timer;\n   timer.start();\n   double t = 0.;\n   while(t &lt;= m_trajectory-&gt;duration())\n   {\n      if(KD_OK == m_trajectory-&gt;getConfigAtTime(t, config))\n      {\n         setCurrentConfig(config);\n      }\n      t = timer.elapsedTime();\n   }\n   if(KD_OK == m_trajectory-&gt;getConfigAtTime(m_trajectory-&gt;duration(), config))\n   {\n      setCurrentConfig(config);\n   }\n   CkttDisplay::message(\"Ready\");\n}\n<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Testing a trajectory for collision<\/h3>\n\n\n\n<p>You can test a trajectory for collisions using a <strong>trajectory tester<\/strong>. This code illustrates the simplest way to do this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CkwsTrajectoryTesterShPtr trajectoryTester = CkwsTrajectoryTester::create();\nbool doesCollide = false;\nktStatus status = trajectoryTester-&gt;collisionTest(trajectory, doesCollide);\nif(status.succeeded())\n{\n   std::cout &lt;&lt; \"the trajectory is\" &lt;&lt; (doesCollide ? \"\" : \" not\") &lt;&lt; \" colliding\" &lt;&lt; std::endl;\n}\nelse\n{\n   std::cout &lt;&lt; \"collision test failed : \" &lt;&lt; status.description() &lt;&lt; std::endl;\n}\n<\/code><\/pre>\n\n\n\n<p>For maximum control over the collision test, you can customize validation parameters and other properties. In addition, other overloads of <strong>CkwsTrajectoryTester::collisionTest<\/strong> allow you to test for collisions in a parent configuration space.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Transforming a Path into a Trajectory<\/h3>\n\n\n\n<p>It\u2019s not always possible to transform the output of a path-planner into a trajectory that follows the precise path while avoiding collisions. For example, a spline trajectory cannot follow a straight line path.<\/p>\n\n\n\n<p>This is why we introduced the concept of a motion planner. This is an object that enables you to transform a (collision-free) path into a (collision-free) trajectory. Even if it is possible to follow the original path with a robot controller trajectory, motion planners provide an abstract API to transform a path into a trajectory.<\/p>\n\n\n\n<p>There is a motion planner for each of the two implementations of trajectory in KineoWorks:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>the robot controller motion planner<\/li>\n\n\n\n<li>the cubic spline motion planner<\/li>\n<\/ul>\n\n\n\n<p>Here we consider the cubic spline motion planner. You can assign a cubic spline motion planner to a robot using the following code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>robot-&gt;motionPlanner(CkwsCubicSplineMotionPlanner::create());<\/code><\/pre>\n\n\n\n<p>You can then use the robot motion planner to make a trajectory (with m_trajectory as an output parameter):<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  CkitParameterMapShPtr parameters = CkitParameterMap::create();\n  parameters-&gt;setBool(\"com.siemens.kineo.motion-planner.trajectories-must-be-valid\", true);\n  parameters-&gt;setBool(\"com.siemens.kineo.cubic-spline-motion-planner.is-constrained-trajectory\", false);\n  robot()-&gt;motionPlanner()-&gt;makeTrajectory(m_path, parameters, m_trajectory);\n<\/code><\/pre>\n\n\n\n<p>The call to makeTrajectory also takes a parameter map as input. You can find details of possible parameters in the parent class documentation and the specific motion planner class documentation.<\/p>\n\n\n\n<p>You may choose to replace the cubic spline motion planner in the tutorial with a robot controller motion planner. In this case, you will need to load the robot controller simulator (RCS module) and create an RRS robot to pass to the <strong>create<\/strong> method.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Optimizing Trajectories<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Using KineoWorks Trajectory Optimizer<\/h3>\n\n\n\n<p>You can optimize mutable trajectories using a <em>trajectory optimizer<\/em>.<\/p>\n\n\n\n<p>In this tutorial, the paths that you have transformed into trajectories using a motion planner are not yet optimal &#8211; they can be slow to play. In order to improve the trajectories, you should use a trajectory optimizer.<\/p>\n\n\n\n<p>Create and configure the trajectory optimizer with this code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Create and configure the trajectory optimizer\nCkwsTrajectoryOptimizerShPtr optimizer = CkwsTrajectoryOptimizer::create();\noptimizer-&gt;inputTrajectories(std::vector&lt;CkwsMutableTrajectoryConstShPtr&gt;(1, m_trajectory));\noptimizer-&gt;optimizationType(CkwsTrajectoryOptimizer::CYCLE_TIME);\noptimizer-&gt;iterationCount(5);\nCkitParameterMapShPtr inputParams = CkitParameterMap::create();\ninputParams-&gt;setDouble(\"com.kineocam.genetic-optimizer.standard-deviation\", 0.01);\n<\/code><\/pre>\n\n\n\n<p>You can set some parameters directly through the <strong>CkwsTrajectoryOptimizer <\/strong>API, whereas others must be set in the parameter map. You can find details of possible parameters in the class documentation.<\/p>\n\n\n\n<p>The operation is executed with a blocking call, then the output trajectory is retrieved:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CkitParameterMapShPtr outputParams = CkitParameterMap::create();\noptimizer-&gt;operate(inputParams, outputParams);\nCkttDisplay::removeTrajectory(m_trajectory);\nm_trajectory = optimizer-&gt;outputTrajectory();\n<\/code><\/pre>\n\n\n\n<p>The optimization can take up to several minutes. Optimization duration and output quality depend on the parameters. For instance, you can change the iteration count to see how it affects the optimization duration and the output quality.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/im2-1024x435.png\" alt=\"working with trajectories - kineo trajectory optimization\" class=\"wp-image-633\" width=\"768\" height=\"326\" srcset=\"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/im2-1024x435.png 1024w, https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/im2-600x255.png 600w, https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/im2-768x326.png 768w, https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/im2-900x383.png 900w, https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/im2.png 1028w\" sizes=\"auto, (max-width: 768px) 100vw, 768px\" \/><figcaption class=\"wp-element-caption\">A trajectory before and after optimization<\/figcaption><\/figure><\/div>\n\n\n<p>In this example, the optimized trajectory appears to match the original quite well, but if you look closely, the number of waypoints has been reduced and the trajectory is smoother. As a result, the optimized trajectory will play much faster compared to the original, which is what we would expect as duration was the criterion for optimization. A fast-to-play long path will be preferred over a slow-to-play short path, and we may be in a local optimum that prevents the trajectory getting shorter.<\/p>\n\n\n\n<figure class=\"wp-block-video\"><video controls src=\"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/03\/robotcontroler.mp4\"><\/video><figcaption class=\"wp-element-caption\">Robot executing two trajectories. The first one stops at each waypoint. The second one flies over the waypoints to optimize the cycle time.<\/figcaption><\/figure>\n\n\n\n<p>In the the <a href=\"https:\/\/blogs.sw.siemens.com\/plm-components\/kineoworks-step-by-step-4-pick-place\/\" target=\"_blank\" rel=\"noreferrer noopener\">final part of this series<\/a>, we\u2019ll take a closer look at how you can easily build a pick &amp; place operation.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In article 3 of his series, Etienne takes a closer look at trajectories that describe the movement of a kinematic system over time&#8230;<\/p>\n","protected":false},"author":36655,"featured_media":774,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spanish_translation":"","french_translation":"","german_translation":"","italian_translation":"","polish_translation":"","japanese_translation":"","chinese_translation":"","footnotes":""},"categories":[94],"tags":[2],"industry":[83],"product":[],"coauthors":[410],"class_list":["post-625","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tips-tricks","tag-kineo","industry-software-development"],"featured_image_url":"https:\/\/blogs.sw.siemens.com\/wp-content\/uploads\/sites\/10\/2021\/06\/kineoworks-step-by-step-trajectories.png","_links":{"self":[{"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/posts\/625","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/users\/36655"}],"replies":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/comments?post=625"}],"version-history":[{"count":4,"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/posts\/625\/revisions"}],"predecessor-version":[{"id":1115,"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/posts\/625\/revisions\/1115"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/media\/774"}],"wp:attachment":[{"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/media?parent=625"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/categories?post=625"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/tags?post=625"},{"taxonomy":"industry","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/industry?post=625"},{"taxonomy":"product","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/product?post=625"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blogs.sw.siemens.com\/plm-components\/wp-json\/wp\/v2\/coauthors?post=625"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}