From c07279d0e75a3305d5b8eb0936f2546cb0f03b41 Mon Sep 17 00:00:00 2001 From: Namlak Date: Sun, 29 Jul 2018 20:40:24 -0700 Subject: [PATCH 01/19] Update getting-started.md Fixed link to Interfaces, added .md --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index ee221541..b0bbb489 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -230,7 +230,7 @@ The ODrive also supports velocity control and current (torque) control. You can now: * See what other [commands and parameters](commands.md) are available, including setting tuning parameters for better performance. - * Control the ODrive from your own program or hook it up to an existing system through one of it's [interfaces](interfaces). + * Control the ODrive from your own program or hook it up to an existing system through one of it's [interfaces](interfaces.md). * See how you can improve the behavior during the startup procedure, like [bypassing encoder calibration](encoders.md#encoder-with-index-signal). If you have any issues or any questions please get in touch. The [ODrive Community](https://discourse.odriverobotics.com/) warmly welcomes you. From cd06834ade73fb552add8daeadb35a592f71337c Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 6 Aug 2018 17:55:10 -0700 Subject: [PATCH 02/19] Update odrivetool.md --- docs/odrivetool.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/odrivetool.md b/docs/odrivetool.md index d3c4b67a..375df993 100644 --- a/docs/odrivetool.md +++ b/docs/odrivetool.md @@ -77,7 +77,7 @@ To compile firmware from source, refer to the [developer guide](developer-guide) * If the DFU script can't find the device, try forcing it into DFU mode.
How to force DFU mode (ODrive v3.5)
- Flick the DIP switch that "DFU, RUN" to "DFU" and power cycle the board. If that alone doesn't work, also connect the After you're done, put the switch back into the "RUN" position and power cycle the board again. + Flick the DIP switch that "DFU, RUN" to "DFU" and power cycle the board. After you're done, put the switch back into the "RUN" position and power cycle the board again.
How to force DFU mode (ODrive v3.1, v3.2)
From 3089069f1def939d4c6b252124195641708852d6 Mon Sep 17 00:00:00 2001 From: Capo01 <503426+Capo01@users.noreply.github.com> Date: Sat, 11 Aug 2018 14:50:10 +1000 Subject: [PATCH 03/19] Updated: how to increase amplifier gains --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 563b172f..ae2bef2b 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -163,7 +163,7 @@ In the previous step we started `odrivetool`. In there, you can assign variables For instance, to set the current limit of M0 to 10A you would type: `odrv0.axis0.motor.config.current_lim = 10` Enter
-* The current limit: `odrv0.axis0.motor.config.current_lim` [A]. The default current limit, for safety reasons, is set to 10A. This is quite weak, and good for making sure the drive is stable. Once you have tuned the drive, you can increase this to 75A to get some performance. Note that above 75A, you must change the current amplifier gains. +* The current limit: `odrv0.axis0.motor.config.current_lim` [A]. The default current limit, for safety reasons, is set to 10A. This is quite weak, and good for making sure the drive is stable. Once you have tuned the drive, you can increase this to 75A to get some performance. Note that above 75A, you must change the current amplifier gains by requesting a different current range. i.e. for 90A on M0 'odrv0.axis0.motor.config.requested_current_range = 90' [A], then save the configeration and reboot as the gains are written out to the DRV (MOSFET driver) only during startup. * Note: The motor current and the current drawn from the power supply is not the same in general. You should not look at the power supply current to see what is going on with the motor current.
Ok so tell me how it actually works then...
The current in the motor is only connected to the current in the power supply _sometimes_ and other times it just cycles out of one phase and back in the other. This is what the modulation magnitude is (sometimes people call this duty cycle, but that's a bit confusing because we use SVM not straight PWM). When the modulation magnitude is 0, the average voltage seen across the motor phases is 0, and the motor current is never connected to the power supply. When the magnitude is 100%, it is always connected, and at 50% it's connected half the time, and cycled in just the motor half the time. From ada46be933f276e8ea6fe4a512cebb720512397b Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Fri, 10 Aug 2018 23:16:40 -0700 Subject: [PATCH 04/19] Update getting-started.md --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index ae2bef2b..844e99e8 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -163,7 +163,7 @@ In the previous step we started `odrivetool`. In there, you can assign variables For instance, to set the current limit of M0 to 10A you would type: `odrv0.axis0.motor.config.current_lim = 10` Enter
-* The current limit: `odrv0.axis0.motor.config.current_lim` [A]. The default current limit, for safety reasons, is set to 10A. This is quite weak, and good for making sure the drive is stable. Once you have tuned the drive, you can increase this to 75A to get some performance. Note that above 75A, you must change the current amplifier gains by requesting a different current range. i.e. for 90A on M0 'odrv0.axis0.motor.config.requested_current_range = 90' [A], then save the configeration and reboot as the gains are written out to the DRV (MOSFET driver) only during startup. +* The current limit: `odrv0.axis0.motor.config.current_lim` [A]. The default current limit, for safety reasons, is set to 10A. This is quite weak, and good for making sure the drive is stable. Once you have tuned the drive, you can increase this to 75A to get some performance. Note that above 75A, you must change the current amplifier gains. You do this by requesting a different current range. i.e. for 90A on M0: 'odrv0.axis0.motor.config.requested_current_range = 90' [A], then save the configeration and reboot as the gains are written out to the DRV (MOSFET driver) only during startup. * Note: The motor current and the current drawn from the power supply is not the same in general. You should not look at the power supply current to see what is going on with the motor current.
Ok so tell me how it actually works then...
The current in the motor is only connected to the current in the power supply _sometimes_ and other times it just cycles out of one phase and back in the other. This is what the modulation magnitude is (sometimes people call this duty cycle, but that's a bit confusing because we use SVM not straight PWM). When the modulation magnitude is 0, the average voltage seen across the motor phases is 0, and the motor current is never connected to the power supply. When the magnitude is 100%, it is always connected, and at 50% it's connected half the time, and cycled in just the motor half the time. From b9424461dd234b569a1fa18109aeb3246a3e451b Mon Sep 17 00:00:00 2001 From: Capo01 <503426+Capo01@users.noreply.github.com> Date: Tue, 14 Aug 2018 19:45:53 +1000 Subject: [PATCH 05/19] Added position, velocity and current definitions --- docs/commands.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/commands.md b/docs/commands.md index 0ec87a36..177e57cf 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -71,6 +71,18 @@ An upcoming feature will enable automatic tuning. Until then, here is a rough tu * Back down `pos_gain` until you do not have overshoot anymore. * The integrator is not easily tuned, nor is it strictly required. Tune at your own discretion. +## System monitoring commands + +### Encoder position and velocity +* View encoder position with `.encoder.pos_estimate` [counts] +* View rotational velocity with `.encoder.ppl_vel` [counts/s] + +### Motor current and torque estimation +* View the commanded motor current with `.motor.current_control.Iq_setpoint` [A] +* View the measured motor current with `.motor.current_control.Iq_measured` [A]. If you find that this returns noisy data then use the command motor current instead. The two values should be close so long as you are not approching the maximim achieveable rotational velocity of your motor for a given supply votlage, in which case the commanded current may become larger than the measured current. + +Using the motor current and the known KV of your motor you can estimate the motors torque using the following relationship: Torque [N.m] = 8.27 * Current [A] / KV. + ## General system commands ### Saving the configuration From 81691eecbd7e8003de6ada652427a6367445cc6a Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Tue, 14 Aug 2018 16:44:37 -0700 Subject: [PATCH 06/19] Update commands.md --- docs/commands.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/commands.md b/docs/commands.md index 177e57cf..16710ab0 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -75,7 +75,7 @@ An upcoming feature will enable automatic tuning. Until then, here is a rough tu ### Encoder position and velocity * View encoder position with `.encoder.pos_estimate` [counts] -* View rotational velocity with `.encoder.ppl_vel` [counts/s] +* View rotational velocity with `.encoder.pll_vel` [counts/s] ### Motor current and torque estimation * View the commanded motor current with `.motor.current_control.Iq_setpoint` [A] From bdc175317e8482a04c2fc2bcde47f4c068aea989 Mon Sep 17 00:00:00 2001 From: Capo01 <503426+Capo01@users.noreply.github.com> Date: Wed, 15 Aug 2018 21:30:13 +1000 Subject: [PATCH 07/19] Added a brief overview of liveplotter --- docs/odrivetool.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/docs/odrivetool.md b/docs/odrivetool.md index 375df993..f1737d72 100644 --- a/docs/odrivetool.md +++ b/docs/odrivetool.md @@ -131,3 +131,39 @@ adapter speed: 2000 kHz ``` If something doesn't work, make sure `openocd` is in your `PATH` variable, check that the wires are connected properly and try with elevated privileges. + +## Liveplotter + +Liveplotter is used for the graphical plotting of odrive parameters (i.e. position) in real time. To start liveplotter, close any other instances of liveplotter and run `odrivetool liveplotter` from a new anaconda prompt window. By defult two parameters are plotted on startup; the encoder positon of axis 1 and axis 2. In the below example the motors are running in open `closed_loop_control` while they are being forced off position by hand. + +Image here + +To change what parameters are plotted open odrivetool (located in Anaconda3\Scripts or ODrive-master\tools) with a text editor and modify the liveplotter function: +``` + # If you want to plot different values, change them here. + # You can plot any number of values concurrently. + cancellation_token = start_liveplotter(lambda: [ + my_odrive.axis0.encoder.pos_estimate, + my_odrive.axis1.encoder.pos_estimate, + ]) +``` +For example, to plot the approximate motor torque [N.cm] and the velocity [RPM] of axis1 with a 150KV motor and an 8192 count per rotation econder you would modify the function to read: +``` + # If you want to plot different values, change them here. + # You can plot any number of values concurrently. + cancellation_token = start_liveplotter(lambda: [ + (((my_odrive.axis0.encoder.pll_vel)/8192)*60), # 8192 CPR encoder + ((8.27*my_odrive.axis0.motor.current_control.Iq_setpoint/150) * 100), # Torque [N.cm] = (8.27 * Current [A] / KV) * 100 + ]) +``` +In the example below the motor is forced off axis by hand and held there. In response the motor controller increases the torque (orange line) to counteract this disturbance up to a peak of 500 N.cm at which point the motor current limit is reached. When the motor is released it returns back to its commanded position very quickly as can be seen by the spike in the motor velocity (blue line). + +Image here + +To change the scale and sample rate of the plot modify the following parameters located at the beginning of utils.py (located in Anaconda3\Lib\site-packages\odrive): + +`data_rate = 100 +plot_rate = 10 +num_samples = 1000` + +For more examples on how to interact with the plotting functinality refer to the [Matplotlib examples.](https://matplotlib.org/examples) From 8ffc4003540ea5ce1112a68af81d2dee332abdac Mon Sep 17 00:00:00 2001 From: Capo01 <503426+Capo01@users.noreply.github.com> Date: Wed, 15 Aug 2018 21:31:48 +1000 Subject: [PATCH 08/19] images for liveplotter overview --- docs/figure_1-1.png | Bin 0 -> 21221 bytes docs/figure_1.png | Bin 0 -> 28413 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 docs/figure_1-1.png create mode 100644 docs/figure_1.png diff --git a/docs/figure_1-1.png b/docs/figure_1-1.png new file mode 100644 index 0000000000000000000000000000000000000000..036ca2625a1c0eaa67659c4eb98116eb429470c9 GIT binary patch literal 21221 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∾Q^D2gSX35d=oJ-$tf~D~k z?GYM85r@Z^3G*AD$K~Bm+-{LSkBOHLeA>4E-;mu!%;sjg%`Q}<>6=|k zqjfT-w_l3E@>sTx)d$+Z(ZQH(3~Hd(ISW!FB5K^f{wizHUC!Y44PDEnYiXyS*Q7?k z%p_RbP>8VI*>mm^UlN*~N>`fsErF?cKciEE%b13lU*7}@fG4gg#2l7N6q{B(>*!D) zDK`0xJDSG*L;bwOY@#_#F~!1<)zHweva)i0K_@I-^(V2$f2~D#c$e62h5F|1{!?9; zOAV;}({kS6T!~ThCfSw21F5%flzq!g4-1Ajj9xydJbjzpVdz1nUt6bvX-RX=L~{Q> z)FX>JJ&@y<99~~Y`p|s1@B*vA>GZG2GUAEfg2Ty}{RL6{Ga?E~Q zHHPIyX~GTb%61M8MTLdxadMlgGFH^uFBDs;XR~i~KV#)rWHYe4P;@@gO90*2g@{op zQQ=B3Fc3HJFvMQ;ohxaGzyyd-5o z^)4g0__2f#L3Y9E%loaZ&gf-oYYoMDn9OAT5qVSyN@pf6tI4E%x5Db1GXsa-?2_>c z2U)8)PDM+IWEO-^bI1g>_gksm#>-X04kmF1y1K6`dKJv*<+om}HXXRjuVcxmA1j+y zyQZ0%nQb2udIe8YFF-$BAFQj|f;cWrz~Yl7dScY?>~pOVqjPrT*qL{Fh{~w2=t8Dl zx-VF-GvQk`NXKqmjKGUc0c)F>k+xoNY?UZ*use_N6gQ?t?EdU~eM5kMLF%Jdce;4` zHM?g50s@)s_TB04AWgb6BxT)%>wj}UR(}0R0?t{+b5?0zCXVUF1RFL)YlE6AV{%70 zNg7SrR9>obibyArj~v4RM=fuswRQzGZTuOP*eb!<-Gw3bNHjv^fiIY93i+5Fw8(!1 zlb8HCiZB=*s<4Di%+k`5zWZQea`G7!?)XRawvjlPx4!F#qBmj2Qp2;LVaHr%fw;mU zJJz@%;lPQVqeX|moCgyGVp}p^fGxL>Cg9r(Dm1LniVIe-!f=|^xNH5%h9E*NJImws zhnt;M)-4g0Tiy!;?*y(~@lhx&EF9RJfAcXmQ7I@a|6o^PNy|r$X$qCnp@1gGZk0u$ z2Sq9S=+OBOlwrNmjr(-7?EX`)_XzX56ZVIHV%_*)w{Ia%(argsu%>L(YTYA0c$iFU z+$}!Y(9+WOc<&5{UNOnYo`u7q`oya}!d3c_kB6LmNLwV4vqkox)B!!#ld zTwy75@10#-s%*b8ic3foHZ+(gRC@hVXFh$}aUfSsJ3}SXgyn0UvC;GzmvMNGMNwS)E)(*#2k8ta@7)jy2VEw=h~hJ z&1d=h;6iqtzJGeG9q@6nu;?`PV;@+u=K^aUgSLl_x4$sy@EGihR*v(9v+#z!J-F7? z;-+S)${YAPcjZ-+F`|YoId-{1fe+A?GFg%{LPx?IeL`x#p958@)eC@OWn{)0l$h7) zP1bmf!fA-xccng>Uw(~M;(GO~Gi50+!(GqKtz>g^^SZ~3oIG>@Yfpaqq+D|MgEGxo z;h~`deQ~d!szR0xsY2G~UviWwJk8#2<8N(?P=u_I1$gHBb_*wz(+m#sVW`<}Q;42P z`tdijZH|J@$P#kP>-sr=~ORwyWLNr8uo|kMY$J&A>Oiy$%8cO;@A6^7I1sFH$yNFDM*K*_Mv-0KWMK6F8`ljUN-Py$UUdmZDa-~{5 zD{nwAULzf?q|IsgqSU=ErDD%Bq2%^^kQdiGN(@Bzf1$P(h(<3h3t7Jh$iG8ui&J{e zl|D?!T}&{4-sP~AtuRjLNpMrp1e%!R>=TR_(DpMizIk+bBjNOOR)=TgFynvb@7#tC z*s%3Mf?qWio-SIcD{nCL>CcKrdZP!0rg}@Pm%;CfSnnkly4jt!&(2P9{IZ>x#e}K3 z6<%=mBKVZFFLN^2hC+1s`OB>{Qgo8)E8PjbBFtW!TBck6R8sa?NXBT7<-Soi>~4(- zN+w&f)Bq7iT@-^jCHr@weBone=9<4Y@a}u=Z&eBD80*5Cj-kd?UIybHUG?7P!=y*{p)wf^oFf2RxLeYbpB~M^EWThdwyAvxs(jD4Srw*` zN~8=pk6C_IZ{JvC~;9g1YR^or4Mt6QBxciUjEv1b9AaGe{QyE#@^z#Hmulf=sJlm zFamSYN{2=iWi`_19c~6B*Q^_9TP|B44ZA(RbOLK}P_l?qYn5huFl?nwQ8w5hhC+P( zsy~S16oGYLd3O;p)#{6P$rD`ZUR`b5V=WNqk^Z27^b|*OnSUz<)z$vAXd_>nQy%U| zql1=cKX64>=;&yp{MZOgmV_Kl9NkmUG>#}+d0+9(8#7T{Xn7kfOCu&|lmuwXnd!>aWM=LDo^56&BfuGz zd4I{-<6sjPSK?xs_R=gNO(D>*cJLCuz?EV!R1;oCO02Cu@c)2hQDTDxdwCsjl+8l@UX`vFHB+g39kbOr{H#sgBv7wXC1~xRr4NaDYI}C9 zE6EPqe1NnM@_blgunl{nRP{@J3xFZ<5oLJ;Ah{|7m9nD3%NIku2B4C`{F4W6N@f2U93ITv;k&9^+1#-0;J z^G+-@p{^+)OCO;bLT7y?wE^ka8z=OYPVWMx5_lvleFJ-vS;>X!Pw`c9?3~|fa6v$( zFJtR4@pgw`w@+L{ks_imo9y=$>mq#6*@* z+_5+tUa4Z!!)79Y20W0&9rFZ!S(X<(Rh7O5;I=;Ei)Vk%+u7LFlp%iLR{&O}$BG@h zE<@?C`OkO6$|c|rXI@oaQQX`F@6*aVrd>@nvDtS@Giu-mJmw{ya)3i>qLA$N zB#+u#>i6J&s;V@+YNgV=)NN7zAB{l?_{xz6T;bI<(hjDZHQu;b>U9Ib{L{0RHu9-~ z4pMre0O?4nH0TX-kyKd7i(STVU}c-Ck=DE!N;Xnz+dK>?DybT83egYWMx#ok(d0@% z0urv14g$hfAZOnzycd*8qz@O? z*sxQK6baB>H9?17Mo@9Z0;*B!cBopTGIbGO$`Te8&{;+ZJ{t6MIFbd49y<(WB?;ik z_gsPk+Tx5ak5p}EeeC;XDsd8_hdh{QwXx(kkT%{d`_Ybz}4chBD9vKvtn|C z#awf-%iaPzqHORU_W1^Np!?-lhoVJ45qC+{i&!pxgh?ZNa<8=%hI5UPoYNxgPL&Fr zf$4`$tISG~VTi3qaC!nAE9XD~xX-ddnX4pM+1k6mg6|81oUiV5kFjVUyoWZ4hVoY~ z0_1Pf=ZkEhqT*1X>)YZx@mb~9e?&dYsjc-Jec5&S)v~I?7Zk|N6(XQc>B9xjk_=^2 zdV-upc-#8iG6iIRW0j-zu`Il5@}(<`8hIk`_HlnOVK5xWPGr^E``H&5)iy*-%!$u*b$%n7?*~eu3a>@zJTg$q$)sC9o zCAw;Qbi<0kqE-S_S62RpsFRQl4MJO&F?+JkJ0IVb_I7H@JSin*sYiCr0EI#+XY?u@ zXr8qUcwJsm0i7K0ZgT|kWKC0|o3^WIyGr9qmi$B0{s?WTTe)1%vsp0$T|Hhaire7C zQx~Ek3HjuDkk4*+*xUa1q`pT+j6(i=kAhGwDO*3HMVuiLg?7e&JOeNT?$**MW)vc4 z6jE{oDD}$kn#))r!Itp&tvKvjaWOqv6DtMgd>xb?e-!4sv~KEZ-K(I;knRAe2~f4E2RoqI0nJW=T+ zUtRFO81I>vb-4kcLy_JH_-}?hV7dOy2*G*OR*z5iBPPFMrYfQ4idF(!uBzl%Xg1$g z$ia-pm6+8)keF;eef`AM#@(5i|H)3jHb4(J;){`EwZpIzpM&ILZEqlZCY%A1_#EQ_ zn1Q8G>55^Y7`L`KPRwwTu?uu$P)B_}*$eS`w7lnghBZVQbD?Jpjw1YhFEa9knx38A zJMSt^jgdS)i1v$dZ$9#yUsB~!rXm*&C{MV8+&k16@m+pA-Sp1pA60lRN7$6kgoEGC zaL(oB*Px-lJ-+&lFJ}l7cH}a@B#+v0gkCq2RVyB{@1sVtJCm=NylHE@1&^vmf>Eh? zK|};nEB*R6rwYe@?y9OPenG)bC+`WT{DRtAsf8R)x~Pwjbs(*JbUknL*vQ}y*k>Fp zbkh1)%owY~@6mCgdCa#l%D%o;mvpk1gGnh(3Au?n3MMvfG4VXznbP`+m$U}NN6p_p zKQFnM7&>sK6eb(#ZHP|$DY(Kx)cn;nQx3nCuvQWm)Y z&mTw=JuV&iSu=A z{y^nQw8j-o9X<7xcXE#FUZI{H)e?HvR@}Ya6Y8ecbe$2ph)c9ZwS^Kz}H=H2_wb| zS;MyVP&Q)kB%5(F$5klcHXW8^Hiv$8-*`$mf-C-gl0*g>*>(0<3^~VD0jI$!&mSjq z;3DNOkeV3>dHopOvs25!>mtZsAdy_)l&=MuwPkOHk_J1JZm|C#NN)7&(yf<$bSt-f z!7Y6(r9J_k!o6FaCHQ92Okq3-w8{>kmi-160Kjnrc6jd+8|R7a2SJ-hNJPMS_;=B+ z!+R|?+hN2#t7-4?FPi=oSOyi+hxX1|AM!>{JJg>Ck)m)=0OTTJ*I@Q4qzV9WkS+iO zoPu-%SR1(h{|o@YIt!!@K)1sU`9C&^NLxi`u-fpb#jEmsmWSUKl;dhvb{~jvgKTtO z-X%3XDBL1ZaFG3?f&#V2j~}P4-n#WCCS`8zLK|w*bkGn;=>K){nGx+czlVfUri>2$pHAWY*+FNlPLe030itq11HfY9SuG+Mn zTFlYCa|f#^>Vyr^rvD$!9{T1qn(kEuitUir$?~6dB1viMWNEA9L1(DQeV|uRki|AP zRP@1rA8A%0pK>>Fy^M;SL>4GU{&xhyUiiI`pEEM{!6qpyt7Ks?-vxqJr>!QqT(PJ( zAfc&WzyF1bB=&DB?#C}KE;3HA*i^!&L!O@#SC^KSPQPjs2_eXn`|z*_(^}8h>KYnJ zIXRe|g8=gSBQW=*HC7){fOy*s~=`>5~qvUO5MAoWws-O~j)$m#yFZn|r}QglW>cb2R%q&f7U|K+!zqv+}Pm%POvUVsR1mRDcj`6@>|B*5%|ZhI)B^WMMz zdj0x!+j^H$QJ0Y`gikagsZe62(Vmw`T?<)5<<-|uK!EZwC&Kz?`V8p1B;YeV(jkLS z=RBOHw4bVdkXKhX?&J;SaXQ@Z3g=+~0u)NA9x#%ChoOSrFB<1IA)c2*TP(Xvj~qeM z8zSG!#ChjXwn0EIEiJ9*X^LS@CLjGu_BK03x@FfIY^ptQtCim&1$Z@*QwIk0DuVaC zDs0;62oo-tw8O(-0(9C+xz+X_?LwUNH0WPWgK*;0s@FUkHg0_)kooGs5Qkfj%+eJ? z|835+U0fJ0)2o9Y-yw+6>~!%FM9wmJL-6l3l3=pB{Z>~jXv?v}XJO#fPDkUp^XH%Q z5FjWXt4++)*86$oUJVP(c7td&1Ixm zZHW>lq|%&A1JArDTKHI0T(JY}n5 zk-U-Us2A}UZwoL``It=)_4U2#=;%1L-I3+qv}Z45)nvZbbWjTScTUb7*FLlQ0IG;E z=H|OT+mh?n;C{5f?d!}W%&Q6N02PCiynm+>t$Dlz>f2j5Ssgx7m`WW>JAVf9{2KP=ZPmYfN=QDlr6 zseLfVn9V8ao(Y4SOsfxg82jN7$U@0Mb<7UpURD+q6%E4a>=7CKp<%N~=v5qr7FA}P z(_o&{>QvoC*}aEQaqbP_;5us#m7`3 zhkMCU!~VXsR06J4A}4tBDU4`|Lj^i5K&`Ytkh$#Y)O&^bU`uDRc_a* z>F!<{2(MI@#MBa#Bn$v~52*1;PhuO6_m}K{evQXNM&LE!nR5UY&Tqlc{KCWM7Q_>} z=He9H6^^#_2~f#pwK=hx-IFapnF&vuJrt*ShzTglbV<|te5wKihcwA;gwv{R_~>VWuP)@4Q9Hx`3;6TaBd{pBfBw|B36%DUX{{ug++NPIkf zanU(3D~n|EctC(tTL^rbDKRyFjM|;{M;%HA&NKwJrG|gZ#|0Cf*TLP;3Jq=IzkFF# z@nF1H6F#O=ZRsK5MQs6784nh-g?CTK)@V(^S`Q9w;a`(~-K Hb;N%GUqUJF literal 0 HcmV?d00001 From 707a135bbfb84075405dfaf3a41e46ec7c1ceb07 Mon Sep 17 00:00:00 2001 From: Capo01 <503426+Capo01@users.noreply.github.com> Date: Wed, 15 Aug 2018 21:37:14 +1000 Subject: [PATCH 09/19] added figures --- docs/odrivetool.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/odrivetool.md b/docs/odrivetool.md index f1737d72..fd2b0878 100644 --- a/docs/odrivetool.md +++ b/docs/odrivetool.md @@ -136,7 +136,7 @@ If something doesn't work, make sure `openocd` is in your `PATH` variable, check Liveplotter is used for the graphical plotting of odrive parameters (i.e. position) in real time. To start liveplotter, close any other instances of liveplotter and run `odrivetool liveplotter` from a new anaconda prompt window. By defult two parameters are plotted on startup; the encoder positon of axis 1 and axis 2. In the below example the motors are running in open `closed_loop_control` while they are being forced off position by hand. -Image here +![Liveplotter position plot](figure_1.png) To change what parameters are plotted open odrivetool (located in Anaconda3\Scripts or ODrive-master\tools) with a text editor and modify the liveplotter function: ``` @@ -158,12 +158,14 @@ For example, to plot the approximate motor torque [N.cm] and the velocity [RPM] ``` In the example below the motor is forced off axis by hand and held there. In response the motor controller increases the torque (orange line) to counteract this disturbance up to a peak of 500 N.cm at which point the motor current limit is reached. When the motor is released it returns back to its commanded position very quickly as can be seen by the spike in the motor velocity (blue line). -Image here +![Liveplotter torque vel plot](figure_1-1.png) To change the scale and sample rate of the plot modify the following parameters located at the beginning of utils.py (located in Anaconda3\Lib\site-packages\odrive): -`data_rate = 100 +``` +data_rate = 100 plot_rate = 10 -num_samples = 1000` +num_samples = 1000 +``` For more examples on how to interact with the plotting functinality refer to the [Matplotlib examples.](https://matplotlib.org/examples) From 23ab2af36f9af30bf48cad64c60787821b73be68 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 16 Aug 2018 15:16:39 -0700 Subject: [PATCH 10/19] Update odrivetool.md --- docs/odrivetool.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/odrivetool.md b/docs/odrivetool.md index fd2b0878..90e71ee3 100644 --- a/docs/odrivetool.md +++ b/docs/odrivetool.md @@ -134,7 +134,7 @@ If something doesn't work, make sure `openocd` is in your `PATH` variable, check ## Liveplotter -Liveplotter is used for the graphical plotting of odrive parameters (i.e. position) in real time. To start liveplotter, close any other instances of liveplotter and run `odrivetool liveplotter` from a new anaconda prompt window. By defult two parameters are plotted on startup; the encoder positon of axis 1 and axis 2. In the below example the motors are running in open `closed_loop_control` while they are being forced off position by hand. +Liveplotter is used for the graphical plotting of odrive parameters (i.e. position) in real time. To start liveplotter, close any other instances of liveplotter and run `odrivetool liveplotter` from a new anaconda prompt window. By defult two parameters are plotted on startup; the encoder positon of axis 1 and axis 2. In the below example the motors are running in `closed_loop_control` while they are being forced off position by hand. ![Liveplotter position plot](figure_1.png) @@ -169,3 +169,10 @@ num_samples = 1000 ``` For more examples on how to interact with the plotting functinality refer to the [Matplotlib examples.](https://matplotlib.org/examples) + +### Liveplotter from interactive odrivetool instance +You can also run `start_liveplotter(...)` directly from the interactive odrivetool prompt. This is useful if you want to issue commands or otherwise keep interacting with the odrive while plotting. + +For example you can type the following directly into the interactive prompt: `start_liveplotter(lambda: [odrv0.axis0.encoder.pos_estimate])`. Just like the examples above, you can list several parameters to plot separated by comma in the square brackets. +In general, you can plot any variable that you are able to read like normal in odrivetool. + From 90801c887f3b90e3efaa483e60882523c7a33a2b Mon Sep 17 00:00:00 2001 From: Capo01 <503426+Capo01@users.noreply.github.com> Date: Mon, 20 Aug 2018 22:00:20 +1000 Subject: [PATCH 11/19] Mention that reboot should be called after saving a config https://github.com/madcowswe/ODrive/issues/183 --- docs/getting-started.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 844e99e8..4bcdb482 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -197,8 +197,11 @@ For instance, to set the current limit of M0 to 10A you would type: `odrv0.axis0 ### 3. Save configuration. -You can save all `.config` parameters to persistent memory such that the ODrive remembers them between power cycles. -* `odrv0.save_configuration()` Enter +You can save all `.config` parameters to persistent memory such that the ODrive remembers them between power cycles. +* `odrv0.save_configuration()` Enter. + +Due to a [known issue](https://github.com/madcowswe/ODrive/issues/183) it is strongly reccomneded that you reboot following every save of your configerations using `odrv0.reboot()`. + ## Position control of M0 From 0f743939ecd243657ddc008c93bce5bb5249e0c5 Mon Sep 17 00:00:00 2001 From: Capo01 <503426+Capo01@users.noreply.github.com> Date: Thu, 23 Aug 2018 22:14:23 +1000 Subject: [PATCH 12/19] added calibration_current for small motors reference Akex_ was unable to calibrate a small 15A peak current motor. Reducing `calibration_current` from 10A to 5A resolved the issue. I have included a reference to the use of small motors and the calibration current. --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 844e99e8..3e0ca944 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -171,7 +171,7 @@ For instance, to set the current limit of M0 to 10A you would type: `odrv0.axis0 The largest effect on modulation magnitude is speed. There are other smaller factors, but in general: if the motor is still it's not unreasonable to have 50A in the motor from 5A on the power supply. When the motor is spinning close to top speed, the power supply current and the motor current will be somewhat close to each other.
* The velocity limit: `odrv0.axis0.controller.config.vel_limit` [counts/s]. The motor will be limited to this speed; again the default value is quite slow. -* You can change `odrv0.axis0.motor.config.calibration_current` [A] to the largest value you feel comfortable leaving running through the motor continously when the motor is stationary. +* You can change `odrv0.axis0.motor.config.calibration_current` [A] to the largest value you feel comfortable leaving running through the motor continously when the motor is stationary. If you are using a small motor (i.e. 15A current rated) you may need to reduce `calibration_current` to a value smaller than the defualt 10A. ### 2. Set other hardware parameters: From d37cc446c851d406048e2877ea69ba80500ef243 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 23 Aug 2018 14:49:35 -0700 Subject: [PATCH 13/19] Update getting-started.md --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 4bcdb482..bf6f1f60 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -200,7 +200,7 @@ For instance, to set the current limit of M0 to 10A you would type: `odrv0.axis0 You can save all `.config` parameters to persistent memory such that the ODrive remembers them between power cycles. * `odrv0.save_configuration()` Enter. -Due to a [known issue](https://github.com/madcowswe/ODrive/issues/183) it is strongly reccomneded that you reboot following every save of your configerations using `odrv0.reboot()`. +Due to a [known issue](https://github.com/madcowswe/ODrive/issues/183) it is strongly recommended that you reboot following every save of your configuration using `odrv0.reboot()`. ## Position control of M0 From bd89f90615ace6deb5e9149c26f87aea2218d80f Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Thu, 23 Aug 2018 14:51:04 -0700 Subject: [PATCH 14/19] Update getting-started.md --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 3e0ca944..8bc42c33 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -171,7 +171,7 @@ For instance, to set the current limit of M0 to 10A you would type: `odrv0.axis0 The largest effect on modulation magnitude is speed. There are other smaller factors, but in general: if the motor is still it's not unreasonable to have 50A in the motor from 5A on the power supply. When the motor is spinning close to top speed, the power supply current and the motor current will be somewhat close to each other. * The velocity limit: `odrv0.axis0.controller.config.vel_limit` [counts/s]. The motor will be limited to this speed; again the default value is quite slow. -* You can change `odrv0.axis0.motor.config.calibration_current` [A] to the largest value you feel comfortable leaving running through the motor continously when the motor is stationary. If you are using a small motor (i.e. 15A current rated) you may need to reduce `calibration_current` to a value smaller than the defualt 10A. +* You can change `odrv0.axis0.motor.config.calibration_current` [A] to the largest value you feel comfortable leaving running through the motor continously when the motor is stationary. If you are using a small motor (i.e. 15A current rated) you may need to reduce `calibration_current` to a value smaller than the default. ### 2. Set other hardware parameters: From ed2c72d8396fbc72acb0353858a5aab31575145a Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sat, 18 Aug 2018 21:14:21 -0700 Subject: [PATCH 15/19] fix char to int conversion --- .../examples/ODriveArduinoTest/ODriveArduinoTest.ino | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Arduino/ODriveArduino/examples/ODriveArduinoTest/ODriveArduinoTest.ino b/Arduino/ODriveArduino/examples/ODriveArduinoTest/ODriveArduinoTest.ino index a06c9818..f948f7f6 100644 --- a/Arduino/ODriveArduino/examples/ODriveArduinoTest/ODriveArduinoTest.ino +++ b/Arduino/ODriveArduino/examples/ODriveArduinoTest/ODriveArduinoTest.ino @@ -46,19 +46,20 @@ void loop() { // Run calibration sequence if (c == '0' || c == '1') { + int motornum = c-'0'; int requested_state; requested_state = ODriveArduino::AXIS_STATE_MOTOR_CALIBRATION; Serial << "Axis" << c << ": Requesting state " << requested_state << '\n'; - odrive.run_state(atoi(c), requested_state, true); + odrive.run_state(motornum, requested_state, true); requested_state = ODriveArduino::AXIS_STATE_ENCODER_OFFSET_CALIBRATION; Serial << "Axis" << c << ": Requesting state " << requested_state << '\n'; - odrive.run_state(atoi(c), requested_state, true); + odrive.run_state(motornum, requested_state, true); requested_state = ODriveArduino::AXIS_STATE_CLOSED_LOOP_CONTROL; Serial << "Axis" << c << ": Requesting state " << requested_state << '\n'; - odrive.run_state(atoi(c), requested_state, false); // don't wait + odrive.run_state(motornum, requested_state, false); // don't wait } // Sinusoidal test move From 1d31d23d0e5297c0207b3e217d1300f4d96a0bca Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Sun, 26 Aug 2018 13:42:32 -0700 Subject: [PATCH 16/19] Update hoverboard.md --- docs/hoverboard.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/hoverboard.md b/docs/hoverboard.md index 54cd70f5..5cf64332 100644 --- a/docs/hoverboard.md +++ b/docs/hoverboard.md @@ -33,6 +33,7 @@ Lets also start in velocity control mode since that is probably what you want fo odrv0.axis0.encoder.config.bandwidth = 100 odrv0.axis0.controller.config.pos_gain = 1 odrv0.axis0.controller.config.vel_gain = 0.02 +odrv0.axis0.controller.config.vel_integrator_gain = 0.1 odrv0.axis0.controller.config.vel_limit = 1000 odrv0.axis0.controller.config.control_mode = CTRL_MODE_VELOCITY_CONTROL ``` From a2e0ea6374c4ad13637245f600df39de80b93123 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 27 Aug 2018 22:49:44 -0700 Subject: [PATCH 17/19] add DfuSeDemo tool instructions --- docs/odrivetool.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/odrivetool.md b/docs/odrivetool.md index 90e71ee3..b2f8cf9e 100644 --- a/docs/odrivetool.md +++ b/docs/odrivetool.md @@ -84,6 +84,37 @@ To compile firmware from source, refer to the [developer guide](developer-guide) Connect the pin "BOOT0" to "3.3V" and power cycle the board. If that alone doesn't work, also connect the pin "GPIO1" to "GND". After you're done, remove the wires and power cycle the board again. +### Upgrading firmware with a different DFU tool +Some people have had issues using the python dfu tool, so below is a guide on how to manually use a different tool. + +Before starting the below steps, you need to get firmware binary. You can download one of the officially released firmware files from [here](https://github.com/madcowswe/ODrive/releases). Make sure you select the file that matches your board version, and that you get the __.hex__ file (not the __.elf__ file). + +To compile firmware from source, refer to the [developer guide](developer-guide). + +#### Windows +You can use the DfuSe app from ST. + +1. Download the tool [here](https://www.st.com/en/development-tools/stsw-stm32080.html). Unfortunately they make you create a login to download. Sorry about that. +1. After installing the tool, launch `DfuFileMgr.exe` which probably got added to the start menu as "Dfu file manager". +1. Select "I want to GENERATE a DFU file from S19, HEX or BIN files", press OK. +1. Click the button that says "S19 or Hex...", find the `ODriveFirmware.hex` file you built or downloaded. +1. Leave all the other settings as default and click the "Generate..." button. +1. Save the output file as `ODriveFirmware.dfu`. Note that the success message has a warning sign for some reason... +1. Launch `DfuSeDemo.exe` which probably got added to the start menu as "DfuSeDemo". +1. Force the ODrive into DFU mode, as per the instructions above "How to force DFU mode". +1. In the top left it should now be connected to "STM Device in DFU Mode". + 1. If it doesn't appear, it may be because the driver is set to libusb by Zadig. We need to set it back to the original driver. Follow [these instructions](https://github.com/pbatard/libwdi/wiki/FAQ#Help_Zadig_replaced_the_driver_for_the_wrong_device_How_do_I_restore_it). +1. In the bottom right section called "Upgrade or Verify Action" click the button "Choose...". +1. Locate the `ODriveFirmware.dfu` we made before. +1. Click button "Upgrade". +1. If you get a warning that it's not possible to check that it's the correct device type: click yes to continue. +1. Congratulations your ODrive should now be flashed; you can now quit DfuSeDemo. + +#### MacOS or Linux +**This section needs more detail. Please consider adding detail if you got it to work.** +You may be able to use [dfu-util](http://dfu-util.sourceforge.net/) to upgrade the firmware. You will need to convert the .hex file to a .dfu file. You may be able to do it with the python script [dfu-convert](https://github.com/plietar/dfuse-tool/blob/master/dfu-convert) or the c program [hex2dfu](https://github.com/encedo/hex2dfu). + +You probably need to force DFU mode, as per the instructions above. ## Flashing with an STLink From fb16534067f11ca7d5ab7b67af151c346fc49a96 Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Mon, 27 Aug 2018 23:07:19 -0700 Subject: [PATCH 18/19] power cycle after dfu docs --- docs/odrivetool.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/odrivetool.md b/docs/odrivetool.md index b2f8cf9e..80d926b7 100644 --- a/docs/odrivetool.md +++ b/docs/odrivetool.md @@ -109,6 +109,7 @@ You can use the DfuSe app from ST. 1. Click button "Upgrade". 1. If you get a warning that it's not possible to check that it's the correct device type: click yes to continue. 1. Congratulations your ODrive should now be flashed; you can now quit DfuSeDemo. +1. Turn off the power to the ODrive and set the DIP switch back to RUN mode. #### MacOS or Linux **This section needs more detail. Please consider adding detail if you got it to work.** From b3bcdd5d5ea5fd9eb39d40885d5244812792136f Mon Sep 17 00:00:00 2001 From: Oskar Weigl Date: Wed, 29 Aug 2018 23:56:43 -0700 Subject: [PATCH 19/19] fill out sensorless guide a bit more --- docs/commands.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/commands.md b/docs/commands.md index 16710ab0..212a4899 100644 --- a/docs/commands.md +++ b/docs/commands.md @@ -100,10 +100,15 @@ All variables that are part of a `[...].config` object can be saved to non-volat ## Setting up sensorless The ODrive can run without encoder/hall feedback, but there is a minimum speed, usually around a few hunderd RPM. -However the +However the units of this mode is different from when using an encoder. Velocities are not measured in counts/s, instead it is electrical rad/s. This also applies to the gains. For example, `vel_gain` is in units of `A / (rad/s)` instead of `A / (count/s)`. + +To give an example, suppose you have a motor with 7 pole pairs, and you want to spin it at 3000 RPM. Then you would set the `vel_setpoint` to `3000 * 2*pi/60 * 7 = 2199 rad/s electrical`. + +Below are some suggested starting parameters that you can use. Note that you _must_ set the `pm_flux_linkage` correctly for sensorless mode to work. + ``` -odrv0.axis0.controller.config.vel_gain = 0.1 -odrv0.axis0.controller.config.vel_integrator_gain = 0 +odrv0.axis0.controller.config.vel_gain = 0.01 +odrv0.axis0.controller.config.vel_integrator_gain = 0.05 odrv0.axis0.controller.config.control_mode = 2 odrv0.axis0.controller.vel_setpoint = 400 odrv0.axis0.sensorless_estimator.config.pm_flux_linkage = 5.51328895422 / ( * )