From f0efca281d9ab2415b416db1b8b5f60e38024d3a Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Tue, 19 Apr 2022 12:46:35 -0400 Subject: [PATCH 01/11] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 70fb47a3..94c50943 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +The latest version of this code is now hosted here: https://github.com/SherbyRobotics/pyro + # Pyro An object-based toolbox for robot dynamic simulation, analysis, control and planning. From 93a6bc6b887b9424be46cfbb06656bff66b6c19f Mon Sep 17 00:00:00 2001 From: Alexandre Girard Date: Thu, 21 Apr 2022 23:54:27 -0400 Subject: [PATCH 02/11] Created using Colaboratory --- .../2DoF_robot_arm_impedance_control.ipynb | 618 ++++++++++++++++++ 1 file changed, 618 insertions(+) create mode 100644 examples/notebooks/2DoF_robot_arm_impedance_control.ipynb diff --git a/examples/notebooks/2DoF_robot_arm_impedance_control.ipynb b/examples/notebooks/2DoF_robot_arm_impedance_control.ipynb new file mode 100644 index 00000000..f96d7308 --- /dev/null +++ b/examples/notebooks/2DoF_robot_arm_impedance_control.ipynb @@ -0,0 +1,618 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "2DoF_robot_arm_impedance_control.ipynb", + "provenance": [], + "private_outputs": true, + "collapsed_sections": [], + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + }, + "language_info": { + "name": "python" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "source": [ + "# Robot arm impedance control\n", + "\n", + "This page introduce the concept of controlling a robot arm position by adding virtual spring (i.e. what we can call impedance control) in both the joint-space (angles) and the task-space (x,y,z).\n", + "\n", + "We will:\n", + "\n", + "1. Load an object describing a planar arm behavior;\n", + "2. Add virtual angular springs on the joint (joint-space impedance control);\n", + "3. Add virtual linear springs on the end-effector (task-space impedance control);\n", + "4. Try to improve the controller with your custom solution;" + ], + "metadata": { + "id": "OfiozzVs1Upg" + } + }, + { + "cell_type": "markdown", + "metadata": { + "id": "eOFBWst7WmCb" + }, + "source": [ + "## Importing Librairies" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "tH-1a9VJGXi8" + }, + "source": [ + "# Loading a robotic toolbox\n", + "!git clone https://github.com/SherbyRobotics/pyro\n", + "import sys\n", + "sys.path.append('/content/pyro')\n", + "import pyro\n", + "\n", + "# Generic python tools\n", + "import numpy as np\n", + "from IPython import display" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cau_ePoSW6Z_" + }, + "source": [ + "# Defining the robot arm\n", + "\n", + "Here we load a already defined class from the library including the dynamic equations defining the robot arm behavior, which is a function of the form:\n", + "\n", + "$H(q)\\ddot{q} + C(\\dot{q},q)\\dot{q} + d(\\dot{q},q) + g(q) = B \\tau $\n", + "\n", + "where $q$ is a 2x1 column vector of the joint angular position and $\\tau$ is a 2x1 column vector of motor torque.\n", + "\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "5pf-lSEZG5gy" + }, + "source": [ + "from pyro.dynamic import manipulator\n", + "\n", + "robot = manipulator.TwoLinkManipulator()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "The properties like the lenght of the two links can be modified here:" + ], + "metadata": { + "id": "bMLY64dV7RUg" + } + }, + { + "cell_type": "code", + "source": [ + "robot.l1 = 0.5 # length of first rigid link\n", + "robot.l2 = 0.3 # length of second rigid link" + ], + "metadata": { + "id": "o_8bX0k_7ST1" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "P2wLQ8lDYY9S" + }, + "source": [ + "## Showing the robot natural behavior with no controllers\n", + "\n", + "Here we run a simulation of the robot arm with no controllers:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "lfjr9zXlYYMb" + }, + "source": [ + "# robot initial states [ joint 1 angle (rad), joint 2 angle (rad), joint 1 velocity (rad/sec), joint 1 velocity (rad/sec)]\n", + "robot.x0 = np.array([ 0.1, 0.1, 0.0, 0.0]) \n", + "\n", + "# run the simulation\n", + "robot.compute_trajectory( tf = 6 )\n", + "\n", + "# Animate and display the simulation\n", + "ani = robot.generate_simulation_html_video()\n", + "html = display.HTML( ani )\n", + "display.display(html)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "nxKK7RWBa8kd" + }, + "source": [ + "# Joint-space impedance control\n", + "\n", + "First lets try to put simple independent joint-controller where the torque is proportionnal to the joint position error and the joint velocity. This is basically equivalent to independent proportionnal-derivative controller on each joint:\n", + "\n", + "$\\tau = K_p \\left(q_{d} - q\\right) + \n", + "K_d \\left(- \\dot{q}\\right)$\n", + "\n", + "Physically the effect of this controller is equivalent to add angular spring and damper on each joint of the robot that have equilibrium points on the desired joint position. Let try to control the two-link robot with this approach. Here we first defined a controller object that implement this control law and set the desire parameter:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "M4cQxkQTD0Qn" + }, + "source": [ + "from pyro.control import robotcontrollers\n", + "\n", + "# Target\n", + "q_desired = np.array([0.5,0.5]) # this set the desired joint angles vector\n", + "\n", + "# Joint PID\n", + "joint_pd = robotcontrollers.JointPD( dof = 2 )\n", + "joint_pd.rbar = q_desired\n", + "joint_pd.kp = np.array([25, 5 ]) # This set the diagonal values of the Kp matrix\n", + "joint_pd.kd = np.array([ 0, 0 ]) # This set the diagonal values of the Kd matrix" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# This shows the commanded torque as a function of the configuration (at zero velocity)\n", + "#joint_pd.plot_control_law( sys = robot , n = 100 )" + ], + "metadata": { + "id": "oBiu3AODx1av" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rOXYBwANcsc9" + }, + "source": [ + "## Simulating the robot in closed-loop\n", + "\n", + "Here we run a simulation of the robot with the joint-impedance controller." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "5YuyYOPSc5Gy" + }, + "source": [ + "# Create the closed-loop system\n", + "robot_with_joint_pd = joint_pd + robot\n", + "\n", + "# robot initial states [ joint 1 angle (rad), joint 2 angle (rad), joint 1 velocity (rad/sec), joint 1 velocity (rad/sec)]\n", + "#closed_loop_robot.x0 = np.array([0.1,0.1,0,0]) \n", + "\n", + "# Run the simulation\n", + "robot_with_joint_pd.compute_trajectory( tf = 5 )" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Animate the simulation\n", + "ani = robot_with_joint_pd.generate_simulation_html_video()\n", + "html = display.HTML(ani)\n", + "display.display(html)" + ], + "metadata": { + "id": "6b5LMggz7kUI" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Plot systems states\n", + "robot_with_joint_pd.plot_trajectory('x')" + ], + "metadata": { + "id": "zqNOHkbr73JT" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Plot control inputs\n", + "robot_with_joint_pd.plot_trajectory('u')" + ], + "metadata": { + "id": "aAWOXQjo75pR" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Analyzing the results\n", + "\n", + "Does this controller give a satisfactory behavior? Try go back and update the controller parameters ($Kp$ and $Kd$) to improve the results. Pay attention the the following performance indicator:\n", + "\n", + "1. How fast does the robot reach the target position?\n", + "2. How much torque are required?\n", + "3. Is there a large final error after the system stabilize?\n", + "4. Is there a big overshoot and large oscillations?\n", + "\n", + "\n" + ], + "metadata": { + "id": "bwFYbJNTGRm2" + } + }, + { + "cell_type": "markdown", + "source": [ + "### Frequency domain point of view\n", + "\n", + "The following plot shows the frequency response of the closed-loop for the first joint, where we can also look at the bandwidth and the presence (or not) of a resonance." + ], + "metadata": { + "id": "__P502AnH0I-" + } + }, + { + "cell_type": "code", + "source": [ + "robot_with_joint_pd.plot_linearized_bode()" + ], + "metadata": { + "id": "6t32b3k7EJz2" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Task-space impedance controller\n", + "\n", + "Here we will repeat the process but with a task-space impedance controller. The idea is similar but here we want a controller that will have the effect of attaching a linear spring between the end-effector and the target. \n", + "\n", + "This controller will required the knowledge of robot kinematic equations defining the relationship between the end-effector position (column vector $r$) and the joint position, i.e. a forward kinematic function of the form $r = f(q)$ and a differential kinematic function of the form $\\dot{r} = J(q) \\dot{q}$.\n", + "\n", + "The controller equation we implement to do this is:\n", + "\n", + "$\\tau = J(q)^T \\left[ K_p \\left(r_{d} - f(q)\\right) + \n", + "K_d \\left(- J(q) \\dot{q}\\right) \\right]$\n", + "\n", + "This equation first compute the cartesien error at the effector, mulitply it by a gain matrix represeting linear stiffness and then convert this desired linear force in equavalent motor torques by multiplying the linear force vector by the transpose of the Jacobian matrix. The theoretical background regarding this approach is summarised in the following videos:" + ], + "metadata": { + "id": "w-UvWXb2vXB-" + } + }, + { + "cell_type": "code", + "source": [ + "display.IFrame(\"https://www.youtube.com/embed/videoseries?list=PL6adNeJ0A8UtKTsdDwfei6sKDKJt25ZBO\",600,450)" + ], + "metadata": { + "id": "bYkjocc6QmGg" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "Here we load a controller class implementing this end-effector impedance control law. We will set the desired position of the end-effector $r_d$ to be equivalent to the previously used target joint position." + ], + "metadata": { + "id": "HPfnnFveRYKu" + } + }, + { + "cell_type": "code", + "source": [ + "# End-effector desired position \n", + "r_desired = robot.forward_kinematic_effector( q_desired )\n", + "print('Target joint angles [q_1,q_2] =', q_desired )\n", + "print('Target effector [x,y] =', r_desired )\n", + "\n", + "effector_pd = robotcontrollers.EndEffectorPD( robot )\n", + "effector_pd.rbar = r_desired\n", + "effector_pd.kp = np.array([100, 100 ]) # This set the diagonal values of the Kp matrix\n", + "effector_pd.kd = np.array([ 0, 0 ]) # This set the diagonal values of the Kd matrix" + ], + "metadata": { + "id": "g1ZseJYnvmrN" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# This shows graphically the computed torque for joint 1 as a function of the configuration\n", + "effector_pd.plot_control_law( sys = robot , n = 100 )" + ], + "metadata": { + "id": "uD29PtDbxk6W" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Create the closed-loop system\n", + "robot_with_effector_pd = effector_pd + robot\n", + "\n", + "# robot initial states [ joint 1 angle (rad), joint 2 angle (rad), joint 1 velocity (rad/sec), joint 1 velocity (rad/sec) ]\n", + "#closed_loop_robot.x0 = np.array([0.1,0.1,0,0,0,0]) \n", + "\n", + "# Run the simulation\n", + "robot_with_effector_pd.compute_trajectory( tf = 5 )" + ], + "metadata": { + "id": "mhRm12j2wknn" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Animate the simulation\n", + "ani = robot_with_effector_pd.generate_simulation_html_video()\n", + "html = display.HTML(ani)\n", + "display.display(html)" + ], + "metadata": { + "id": "EyoyjPsbwwuf" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "robot_with_effector_pd.plot_trajectory('x')" + ], + "metadata": { + "id": "3A9escy-SyIc" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "robot_with_effector_pd.plot_trajectory('u')" + ], + "metadata": { + "id": "lOJ4Gen27hIm" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "## Analyzing the results\n", + "\n", + "Does this controller give a satisfactory behavior? Try go back and update the controller parameters ($Kp$ and $Kd$) to improve the results. Pay attention the the following performance indicator:\n", + "\n", + "1. How fast does the robot reach the target position?\n", + "2. How much torque are required?\n", + "3. Is there a large final error after the system stabilize?\n", + "4. Is there a big overshoot and large oscillations?\n", + "\n", + "Do you think the task-space impedance controller is an improvement over the joint-space impedance controller?\n", + "\n", + "Try to also changing the initial condition (see robot.x0 at the begining) and the target position." + ], + "metadata": { + "id": "qsq74zarThRY" + } + }, + { + "cell_type": "markdown", + "source": [ + "# Custom controller\n", + "\n", + "Now instead of simply trying to tune the parameters of a given control law, lets go free style. Here bellow you can write your own custom robot control law. Try to design a controller that is an improvement over the simple joint or task impedance controller we tried before." + ], + "metadata": { + "id": "WlRtUchNxdgR" + } + }, + { + "cell_type": "code", + "source": [ + "def feedback_law( y , r , t = 0):\n", + " \"\"\"\n", + " y = [ q0, q1, dq0 , dq1 ] : Feedback signal y = Robot joint angles and joint velocities\n", + " r = [ qd0, qd1] : Reference signal r = Robot desired joint angles\n", + " \"\"\"\n", + "\n", + " q = y[0:2] # Joint position vector\n", + " dq = y[2:4] # Join velocity vector\n", + " q_d = r # Desired joint position vector\n", + "\n", + " u = np.array([ 0.0, 0.0]) # Place-holder to overwrite with your control law\n", + "\n", + " # The default placeholer code set the torque of the first joint increasing \n", + " # linearly with the time and the torque of the second joint to zero.\n", + " u[0] = t\n", + " u[1] = 0\n", + "\n", + " ##############################\n", + " # YOUR CODE HERE !!\n", + " ##############################\n", + " \n", + " return u" + ], + "metadata": { + "id": "sfev_wwZxV2l" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "custom_controller = robotcontrollers.RobotController( dof = 2 )\n", + "custom_controller.c = feedback_law" + ], + "metadata": { + "id": "vmEMeJVqyYYp" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "custom_controller.rbar = np.array([0.5,0.5]) # Desired robot joint configuration [ joint 1 angle (rad) , joint 2 angle (rad)]" + ], + "metadata": { + "id": "CwP3a9HLyjcQ" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Create the closed-loop system\n", + "robot_with_custom_controller = custom_controller + robot\n", + "\n", + "# Run the simulation\n", + "traj = robot_with_custom_controller.compute_trajectory()" + ], + "metadata": { + "id": "kWpt3ViRzBqi" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "# Animate the simulation\n", + "ani = robot_with_custom_controller.generate_simulation_html_video()\n", + "html = display.HTML(ani)\n", + "display.display(html)" + ], + "metadata": { + "id": "pB1K0-hszaJY" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "robot_with_custom_controller.plot_trajectory('x')" + ], + "metadata": { + "id": "pav4FGce6VMw" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "source": [ + "robot_with_custom_controller.plot_trajectory('u')" + ], + "metadata": { + "id": "aAgjM02V6c7I" + }, + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "source": [ + "# Evaluation of the custom controller \n", + "\n", + "Does this controller give a satisfactory behavior? Try go back and update your control law to improve the results. Pay attention the the following performance indicator:\n", + "\n", + "1. How fast does the robot reach the target position?\n", + "2. How much torque are required?\n", + "3. Is there a large final error after the system stabilize?\n", + "4. Is there a big overshoot and large oscillations?\n", + "\n", + "The following evaluation script can help you:" + ], + "metadata": { + "id": "JONHiavc2uXQ" + } + }, + { + "cell_type": "code", + "source": [ + "ef = traj.x[-1,0:2] - custom_controller.rbar\n", + "print('Final error=',ef)\n", + "\n", + "if np.linalg.norm(ef) > 0.2 :\n", + " print('The final error is large, try an approach to reduce the steady state error.')\n", + "\n", + "max_torque_joint_1 = traj.u[:,0].max()\n", + "max_torque_joint_2 = traj.u[:,1].max()\n", + "\n", + "print('Maximum torque of joint 1 = ', max_torque_joint_1 )\n", + "print('Maximum torque of joint 2 = ', max_torque_joint_2 )\n", + "\n", + "if (max_torque_joint_1 > 9) or (max_torque_joint_2 > 9) :\n", + " print('The commanded torque are very large, try to reduce the feedback gains')" + ], + "metadata": { + "id": "2cdEL8oj2tuQ" + }, + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file From 9b59e5f420325fd5123d5bd16232ed9540cc9a7d Mon Sep 17 00:00:00 2001 From: IanLalonde Date: Tue, 28 May 2024 08:00:28 -0400 Subject: [PATCH 03/11] Changed setup.py to pyproject.toml --- .vs/ProjectSettings.json | 3 +++ .vs/VSWorkspaceState.json | 6 ++++++ .vs/pyro/v16/.suo | Bin 0 -> 14336 bytes .vs/slnx.sqlite | Bin 0 -> 139264 bytes pyproject.toml | 32 ++++++++++++++++++++++++++++++++ setup.py | 30 ------------------------------ 6 files changed, 41 insertions(+), 30 deletions(-) create mode 100644 .vs/ProjectSettings.json create mode 100644 .vs/VSWorkspaceState.json create mode 100644 .vs/pyro/v16/.suo create mode 100644 .vs/slnx.sqlite create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json new file mode 100644 index 00000000..f8b48885 --- /dev/null +++ b/.vs/ProjectSettings.json @@ -0,0 +1,3 @@ +{ + "CurrentProjectSetting": null +} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json new file mode 100644 index 00000000..6b611411 --- /dev/null +++ b/.vs/VSWorkspaceState.json @@ -0,0 +1,6 @@ +{ + "ExpandedNodes": [ + "" + ], + "PreviewInSolutionExplorer": false +} \ No newline at end of file diff --git a/.vs/pyro/v16/.suo b/.vs/pyro/v16/.suo new file mode 100644 index 0000000000000000000000000000000000000000..b1626a58e87ccdcf6b95368663f59298dfd046bb GIT binary patch literal 14336 zcmeI2%Xd>n9LJ|1D!vg#<)INPf)ebDLJ)biyp(`a+Pdh%=23`k+MJ{y>{z;R?aqZ8 z*Ro{iamn!r3y;U+O3!icAHb3N`QDpJCb!AWZHhp0GC9ASxpQacH^2Ek=W*xuhCjc% zvH33(NQcZ?b8l(AdE6=Qaq{K`V_xQ6W%rhrmh?$^vfb21GjQH4@LM!7bKK1HUo_Xm z%E)PxSy$otx+bkY@14An{^5_Fr>XbjM!)hWU|cX?nA2dNA$QzdZPH8a=S^l!1;3`f z_G|sOvlJt~Ub6Uww_xTNLD6J^G3H_#$d>Ch`s+1odR%rls^^v;=Ote*IA<<{WzOWF zUvSaEJW5H?ZCEES{Etz-28c@k8R}ZS-bDUimjLpwmHSfhD^C6y^3MX#0nY=P!wtZT zfY`YiP#f6;$%6i$;T@Of)1ia` z-f8^s3C1zUd&$_yAj?P=%?P7Xe3bQ`)lnS)zv5u)GrdlmJfusu$|(Jc(tNq+NiPb> zQ2He!SYRAEu;x7MRjjtPF@4~dPgYD7mHyMzwf1j}p69%o_Hp=h&VcbErA03W^1oDZ zy1Cupm;V=TCHGsS{3$;4l|P;{DWbn5*ow>Mc*5*&)J3IyJNRXno&uucKS^E1OG+b9 zF9|G}324qT>y0?=*AG6CzY6V9@h|6pIM>fJt~`8FM4IxXvpS0X;8!drn;jMZD0TOi zyBMYK6r8@uPg*ePaXgI{N(Y*!SPS%z(5-xhQV{V6F<1@X%sq!~dWRO|eAh_NN|x-^yedMaHvFVdEKw{*Cmf`HV{c zp%DDCfAip#U6B3@cUXe{9<2wo4v30hzGAzK7AZ)775_D4KIK^_zh?bsEAwBgf3@N{njY27*p}4(irot$U_!KScF;jPI4#R{}d^d zU6O4sRiXsHt)AGw703TB|J|8QG7?1rz9rm)|CKJxgE>Q7AYUjiT`LLDUzz);^ow@+ zETu5~6CUM@$cpT~jgZtL!^~XrMslPD{EC;X{SCuE>3M;9a}Eg*H#85yG|&Qm`Cc1; zgyA27_r4_N%}M587O7y)*6`sF{MWvN-KPkSuim@j)hP4t;=cfPMM{dr3XTjA|E-w+ z8k5CeZ+t5)lib;~(zeotMj#uZZ;GuHUuw)@`#+K?#lo@$;)m>#;$rb<3$PW~2D}cu z0mOhef$hLsKriq%umgAp*a_?c-UZ$Rb^~!>5AZ&q7_uK200w~}Kyl45un$o5m;ep{ z9{>k|4*{3nqr4vrl${8?yY+3}&Ud^2i>K0uR?}+R%MPptzhqABt9=xX@ESR-cKneM zHKp4|w7J5Zh?-wIfSNqgAs!VwhCe0b zRr1-MIwOshbd~r|w=GYCR9wX-4Rs7PT2tjcYVNe51qkvcjZK%G_T|o)pF4o293Du1 zP*&jA4xqhy_}I zUI+Yj+c z{jBx%3`@1#469lH+uiR6ejobh;NM%e?K*w)=C@zful4Ab+WpLW|K0uH#(U$MuTDro z?eQ%@^LwvWqPzEh+WY=b*!f%c{I7ccXm^$G_xWFv)9!IRR`4E{yJv27t=@kt_aBmU zq*z5Mc>kb!|044J5AQzsL%jdto&W26QTEs)s>wsP+g1tE-@X5#Ray7`*9z``t@!@Q oVT4byl~Pdt?0r<-!Fmw)zq;|i&xcl@cKs)d(n|dQu-6a&0Sa_sd;kCd literal 0 HcmV?d00001 diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..a4f2e01b938ea94aa6d97e55bd0b4cd9190504a2 GIT binary patch literal 139264 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

##!=>sCeSPcTHPlXJrVFm=s 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 literal 0 HcmV?d00001 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..7790032b --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,32 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[tool.hatch.build.targets.wheel] +packages = ["pyro"] + +[project] +name = "pyro_udes" +version = "2.3" +authors = [ + { name="Alexandre Girard", email="alx87grd@gmail.com" }, + { name="Ian Lalonde", email="lali3401@usherbrooke.ca" }, +] +description = "object-based toolbox for robot dynamic simulation, analysis, control and planning" +readme = "README.md" +requires-python = ">=3.5" +dependencies =[ + 'numpy>=1.10', + 'matplotlib>=3.0', + 'scipy>=1.5.2' +] +classifiers=[ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering" +] + +[project.urls] +Homepage = "https://github.com/SherbyRobotics/pyro/tree/master" \ No newline at end of file diff --git a/setup.py b/setup.py deleted file mode 100644 index 078d20ec..00000000 --- a/setup.py +++ /dev/null @@ -1,30 +0,0 @@ -import setuptools - -with open("README.md", "r") as fh: - long_description = fh.read() - -setuptools.setup( - name="pyro", - version="2.2", - author="Alexandre Girard", - author_email="alx87grd@gmail.com", - description=("object-based toolbox for robot dynamic simulation, analysis, " - "control and planning"), - long_description=long_description, - long_description_content_type="text/markdown", - url="https://github.com/SherbyRobotics/pyro", - packages=setuptools.find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]), - python_requires='~=3.4', - install_requires=[ - 'numpy>=1.10', - 'matplotlib>3.0', - 'scipy>=1.5.2' - ], - classifiers=[ - "Programming Language :: Python :: 3", - "License :: OSI Approved :: MIT License", - "Operating System :: OS Independent", - "Intended Audience :: Science/Research", - "Topic :: Scientific/Engineering" - ], -) From 00cb153cd4c5e6f88892bfee683c0465c8e36a7d Mon Sep 17 00:00:00 2001 From: IanLalonde <46567178+IanLalonde@users.noreply.github.com> Date: Tue, 28 May 2024 08:13:10 -0400 Subject: [PATCH 04/11] Create python-app.yml --- .github/workflows/python-app.yml | 39 ++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .github/workflows/python-app.yml diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml new file mode 100644 index 00000000..cf8b26d1 --- /dev/null +++ b/.github/workflows/python-app.yml @@ -0,0 +1,39 @@ +# This workflow will install Python dependencies, run tests and lint with a single version of Python +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python + +name: Python application + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +permissions: + contents: read + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python 3.10 + uses: actions/setup-python@v3 + with: + python-version: "3.x" + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install flake8 pytest + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 + run: | + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Test with pytest + run: | + pytest From c679ce232f0a90f4b09c6128916ca3e43a380347 Mon Sep 17 00:00:00 2001 From: IanLalonde <46567178+IanLalonde@users.noreply.github.com> Date: Tue, 28 May 2024 08:24:43 -0400 Subject: [PATCH 05/11] Create requirements.txt --- requirements.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..13513fe3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +numpy>=1.10 +matplotlib>3.0 +scipy>=1.5.2 From 75d1d059613e98723a4c3c6138213d5f9b2abb84 Mon Sep 17 00:00:00 2001 From: IanLalonde Date: Tue, 28 May 2024 16:20:09 -0400 Subject: [PATCH 06/11] added tests --- .gitignore | 1 + pyproject.toml | 12 +++++- test/__init__.py | 0 test/test_system.py | 84 ++++++++++++++++++++++++++++++++++++++++ test/test_with_pytest.py | 7 ++++ 5 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 test/__init__.py create mode 100644 test/test_system.py create mode 100644 test/test_with_pytest.py diff --git a/.gitignore b/.gitignore index a80e6628..a36e2f45 100644 --- a/.gitignore +++ b/.gitignore @@ -40,6 +40,7 @@ output/ # Editor Config # .vscode/ +.vs/ .spyproject/ .spyderproject/ diff --git a/pyproject.toml b/pyproject.toml index 7790032b..a589b507 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,4 +29,14 @@ classifiers=[ ] [project.urls] -Homepage = "https://github.com/SherbyRobotics/pyro/tree/master" \ No newline at end of file +Homepage = "https://github.com/SherbyRobotics/pyro/tree/master" + +[tool.pytest.ini_options] +testpaths = "test" +addopts = "-v -ra -q" +log_cli = true +log_cli_level = "INFO" +log_format = "%(asctime)s %(levelname)s %(message)s" +log_date_format = "%Y-%m-%d %H:%M:%S" +minversion = "6.0" +filterwarnings = "ignore" \ No newline at end of file diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/test/test_system.py b/test/test_system.py new file mode 100644 index 00000000..a2c3fb15 --- /dev/null +++ b/test/test_system.py @@ -0,0 +1,84 @@ +import pytest +import numpy as np +import matplotlib.pyplot as plt +import matplotlib +from pyro.dynamic import ContinuousDynamicSystem # Replace 'your_module' with the actual module name + +matplotlib.use('Template') +# Mock subclass to implement the f method +class MockContinuousDynamicSystem(ContinuousDynamicSystem): + def f(self, x, u, t): + # Simple dynamic equation for testing + return -x + u + +# Test cases +@pytest.fixture +def system(): + return MockContinuousDynamicSystem(n=2, m=2, p=2) + +@pytest.fixture +def system_3d(): + return MockContinuousDynamicSystem(n=3, m=2, p=2) + +def test_initialization(system): + assert system.n == 2 + assert system.m == 2 + assert system.p == 2 + assert system.state_label == ["State 0", "State 1"] + assert system.input_label == ["Input 0", "Input 1"] + assert system.output_label == ["Output 0", "Output 1"] + +def test_f_method(system): + x = np.array([1.0, 2.0]) + u = np.array([0.5, 1.5]) + t = 0 + dx = system.f(x, u, t) + assert np.array_equal(dx, -x + u) + +def test_h_method(system): + x = np.array([1.0, 2.0]) + u = np.array([0.5, 1.5]) + t = 0 + y = system.h(x, u, t) + assert np.array_equal(y, x) + +def test_t2u_method(system): + t = 0 + u = system.t2u(t) + assert np.array_equal(u, system.ubar) + +def test_isavalidstate(system): + x_valid = np.array([0.0, 0.0]) + x_invalid = np.array([11.0, 0.0]) + assert system.isavalidstate(x_valid) is True + assert system.isavalidstate(x_invalid) is False + +def test_isavalidinput(system): + x = np.array([0.0, 0.0]) + u_valid = np.array([0.0, 0.0]) + u_invalid = np.array([2.0, 0.0]) + assert system.isavalidinput(x, u_valid) is True + assert system.isavalidinput(x, u_invalid) is False + +def test_fsim(system): + x = np.array([1.0, 2.0]) + t = 0 + dx = system.fsim(x, t) + expected_dx = -x + system.ubar + assert np.array_equal(dx, expected_dx) + +def test_x_next(system): + x = np.array([1.0, 2.0]) + u = np.array([0.5, 1.5]) + t = 0 + dt = 0.1 + steps = 1 + x_next = system.x_next(x, u, t, dt, steps) + expected_x_next = x + dt * (-x + u) + assert np.allclose(x_next, expected_x_next) + +def test_animate_simulation(system): + system.compute_trajectory(tf=1.0, n=11) + animation = system.animate_simulation() + assert animation is not None + plt.close('all') \ No newline at end of file diff --git a/test/test_with_pytest.py b/test/test_with_pytest.py new file mode 100644 index 00000000..ead5f5f8 --- /dev/null +++ b/test/test_with_pytest.py @@ -0,0 +1,7 @@ +# test_with_pytest.py + +def test_always_passes(): + assert True + +def test_always_fails(): + assert False \ No newline at end of file From 1eacaf6841f0db6128b18bde5f6dad579314217b Mon Sep 17 00:00:00 2001 From: IanLalonde <46567178+IanLalonde@users.noreply.github.com> Date: Tue, 28 May 2024 16:29:05 -0400 Subject: [PATCH 07/11] Update python-app.yml --- .github/workflows/python-app.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index cf8b26d1..45bd155d 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -5,9 +5,9 @@ name: Python application on: push: - branches: [ "master" ] + branches: [ "dev-ian" ] pull_request: - branches: [ "master" ] + branches: [ "dev-ian" ] permissions: contents: read From 292ef89f99ada05323d4c614807bfadd1a05f1dd Mon Sep 17 00:00:00 2001 From: IanLalonde Date: Tue, 28 May 2024 16:30:05 -0400 Subject: [PATCH 08/11] rm vs --- .vs/ProjectSettings.json | 3 --- .vs/VSWorkspaceState.json | 6 ------ .vs/pyro/v16/.suo | Bin 14336 -> 0 bytes .vs/slnx.sqlite | Bin 139264 -> 0 bytes 4 files changed, 9 deletions(-) delete mode 100644 .vs/ProjectSettings.json delete mode 100644 .vs/VSWorkspaceState.json delete mode 100644 .vs/pyro/v16/.suo delete mode 100644 .vs/slnx.sqlite diff --git a/.vs/ProjectSettings.json b/.vs/ProjectSettings.json deleted file mode 100644 index f8b48885..00000000 --- a/.vs/ProjectSettings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "CurrentProjectSetting": null -} \ No newline at end of file diff --git a/.vs/VSWorkspaceState.json b/.vs/VSWorkspaceState.json deleted file mode 100644 index 6b611411..00000000 --- a/.vs/VSWorkspaceState.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "ExpandedNodes": [ - "" - ], - "PreviewInSolutionExplorer": false -} \ No newline at end of file diff --git a/.vs/pyro/v16/.suo b/.vs/pyro/v16/.suo deleted file mode 100644 index b1626a58e87ccdcf6b95368663f59298dfd046bb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14336 zcmeI2%Xd>n9LJ|1D!vg#<)INPf)ebDLJ)biyp(`a+Pdh%=23`k+MJ{y>{z;R?aqZ8 z*Ro{iamn!r3y;U+O3!icAHb3N`QDpJCb!AWZHhp0GC9ASxpQacH^2Ek=W*xuhCjc% zvH33(NQcZ?b8l(AdE6=Qaq{K`V_xQ6W%rhrmh?$^vfb21GjQH4@LM!7bKK1HUo_Xm z%E)PxSy$otx+bkY@14An{^5_Fr>XbjM!)hWU|cX?nA2dNA$QzdZPH8a=S^l!1;3`f z_G|sOvlJt~Ub6Uww_xTNLD6J^G3H_#$d>Ch`s+1odR%rls^^v;=Ote*IA<<{WzOWF zUvSaEJW5H?ZCEES{Etz-28c@k8R}ZS-bDUimjLpwmHSfhD^C6y^3MX#0nY=P!wtZT zfY`YiP#f6;$%6i$;T@Of)1ia` z-f8^s3C1zUd&$_yAj?P=%?P7Xe3bQ`)lnS)zv5u)GrdlmJfusu$|(Jc(tNq+NiPb> zQ2He!SYRAEu;x7MRjjtPF@4~dPgYD7mHyMzwf1j}p69%o_Hp=h&VcbErA03W^1oDZ zy1Cupm;V=TCHGsS{3$;4l|P;{DWbn5*ow>Mc*5*&)J3IyJNRXno&uucKS^E1OG+b9 zF9|G}324qT>y0?=*AG6CzY6V9@h|6pIM>fJt~`8FM4IxXvpS0X;8!drn;jMZD0TOi zyBMYK6r8@uPg*ePaXgI{N(Y*!SPS%z(5-xhQV{V6F<1@X%sq!~dWRO|eAh_NN|x-^yedMaHvFVdEKw{*Cmf`HV{c zp%DDCfAip#U6B3@cUXe{9<2wo4v30hzGAzK7AZ)775_D4KIK^_zh?bsEAwBgf3@N{njY27*p}4(irot$U_!KScF;jPI4#R{}d^d zU6O4sRiXsHt)AGw703TB|J|8QG7?1rz9rm)|CKJxgE>Q7AYUjiT`LLDUzz);^ow@+ zETu5~6CUM@$cpT~jgZtL!^~XrMslPD{EC;X{SCuE>3M;9a}Eg*H#85yG|&Qm`Cc1; zgyA27_r4_N%}M587O7y)*6`sF{MWvN-KPkSuim@j)hP4t;=cfPMM{dr3XTjA|E-w+ z8k5CeZ+t5)lib;~(zeotMj#uZZ;GuHUuw)@`#+K?#lo@$;)m>#;$rb<3$PW~2D}cu z0mOhef$hLsKriq%umgAp*a_?c-UZ$Rb^~!>5AZ&q7_uK200w~}Kyl45un$o5m;ep{ z9{>k|4*{3nqr4vrl${8?yY+3}&Ud^2i>K0uR?}+R%MPptzhqABt9=xX@ESR-cKneM zHKp4|w7J5Zh?-wIfSNqgAs!VwhCe0b zRr1-MIwOshbd~r|w=GYCR9wX-4Rs7PT2tjcYVNe51qkvcjZK%G_T|o)pF4o293Du1 zP*&jA4xqhy_}I zUI+Yj+c z{jBx%3`@1#469lH+uiR6ejobh;NM%e?K*w)=C@zful4Ab+WpLW|K0uH#(U$MuTDro z?eQ%@^LwvWqPzEh+WY=b*!f%c{I7ccXm^$G_xWFv)9!IRR`4E{yJv27t=@kt_aBmU zq*z5Mc>kb!|044J5AQzsL%jdto&W26QTEs)s>wsP+g1tE-@X5#Ray7`*9z``t@!@Q oVT4byl~Pdt?0r<-!Fmw)zq;|i&xcl@cKs)d(n|dQu-6a&0Sa_sd;kCd diff --git a/.vs/slnx.sqlite b/.vs/slnx.sqlite deleted file mode 100644 index a4f2e01b938ea94aa6d97e55bd0b4cd9190504a2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 139264 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

##!=>sCeSPcTHPlXJrVFm=s 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 From 8bd3e218c402b07cf3e3a5543b22ffe0e066612f Mon Sep 17 00:00:00 2001 From: IanLalonde Date: Tue, 28 May 2024 16:32:38 -0400 Subject: [PATCH 09/11] rm test test --- test/test_with_pytest.py | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 test/test_with_pytest.py diff --git a/test/test_with_pytest.py b/test/test_with_pytest.py deleted file mode 100644 index ead5f5f8..00000000 --- a/test/test_with_pytest.py +++ /dev/null @@ -1,7 +0,0 @@ -# test_with_pytest.py - -def test_always_passes(): - assert True - -def test_always_fails(): - assert False \ No newline at end of file From a28b036c3f3f2bb5b194f7dab00f1c235bb12058 Mon Sep 17 00:00:00 2001 From: IanLalonde <46567178+IanLalonde@users.noreply.github.com> Date: Tue, 28 May 2024 16:40:56 -0400 Subject: [PATCH 10/11] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index c4755a70..fe8ee950 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ The latest version of this code is now hosted here: https://github.com/SherbyRobotics/pyro +![example workflow](https://github.com/SherbyRobotics/pyro/actions/workflows/python-app.yml/badge.svg) + # Pyro An object-based toolbox for robot dynamic simulation, analysis, control and planning. From a0ec1aab98b1c1621d420ea33037c8eca739c4e6 Mon Sep 17 00:00:00 2001 From: IanLalonde Date: Tue, 28 May 2024 16:41:16 -0400 Subject: [PATCH 11/11] test again --- test/test_with_pytest.py | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 test/test_with_pytest.py diff --git a/test/test_with_pytest.py b/test/test_with_pytest.py new file mode 100644 index 00000000..ead5f5f8 --- /dev/null +++ b/test/test_with_pytest.py @@ -0,0 +1,7 @@ +# test_with_pytest.py + +def test_always_passes(): + assert True + +def test_always_fails(): + assert False \ No newline at end of file