1# =============================================================================
2# PROJECT CHRONO - http:#projectchrono.org
3#
4# Copyright (c) 2014 projectchrono.org
5# All rights reserved.
6#
7# Use of this source code is governed by a BSD-style license that can be found
8# in the LICENSE file at the top level of the distribution and at
9# http:#projectchrono.org/license-chrono.txt.
10#
11# =============================================================================
12# Authors: Simone Benatti
13# =============================================================================
14#
15#   Demo code about using motors to impose rotation or traslation between parts
16#
17# =============================================================================
18
19import pychrono.core as chrono
20import pychrono.irrlicht as chronoirr
21import math as m
22from copy import deepcopy
23
24# Shortcut function that creates two bodies (a slider and a guide) in a given position,
25# just to simplify the creation of multiple linear motors in this demo.
26# (skip this and go to main() for the tutorial)
27""" Passing the ChBody as argument and assigning a derivate class won't work in Python, and the reference to  the old body will go lost.
28Therefore we don't pass the bodies, but instantiate them in the function and pass them"""
29def CreateSliderGuide(material,
30                      msystem,
31                      mpos) :
32    mguide = chrono.ChBodyEasyBox(4, 0.3, 0.6, 1000, True, True, material)
33    mguide.SetPos(mpos)
34    mguide.SetBodyFixed(True)
35    msystem.Add(mguide)
36
37    mslider = chrono.ChBodyEasyBox(0.4, 0.2, 0.5, 1000, True, True, material)
38    mslider.SetPos(mpos + chrono.ChVectorD(0, 0.3, 0))
39    msystem.Add(mslider)
40
41    mcolor = chrono.ChColorAsset(0.6, 0.6, 0.0)
42    mslider.AddAsset(mcolor)
43
44    obstacle = chrono.ChBodyEasyBox(0.4, 0.4, 0.4, 8000, True, True, material)
45    obstacle.SetPos(mpos + chrono.ChVectorD(1.5, 0.4, 0))
46    msystem.Add(obstacle)
47    mcolorobstacle = chrono.ChColorAsset(0.2, 0.2, 0.2)
48    mslider.AddAsset(mcolorobstacle)
49    return mguide, mslider
50
51
52
53# Shortcut function that creates two bodies (a stator and a rotor) in a given position,
54# just to simplify the creation of multiple linear motors in this demo
55# (skip this and go to main() for the tutorial)
56
57def CreateStatorRotor(material,
58                      msystem,
59                      mpos) :
60    mstator = chrono.ChBodyEasyCylinder(0.5, 0.1, 1000, True, True, material)
61    mstator.SetPos(mpos)
62    mstator.SetRot(chrono.Q_from_AngAxis(chrono.CH_C_PI_2, chrono.VECT_X))
63    mstator.SetBodyFixed(True)
64    msystem.Add(mstator)
65
66
67    mrotor = chrono.ChBodyEasyBox(1, 0.1, 0.1, 1000, True, True, material)
68    mrotor.SetPos(mpos + chrono.ChVectorD(0.5, 0, -0.15))
69    msystem.Add(mrotor)
70
71    mcolor = chrono.ChColorAsset(0.6, 0.6, 0.0)
72    mrotor.AddAsset(mcolor)
73    return mstator, mrotor
74
75print("Copyright (c) 2017 projectchrono.org")
76
77# Create a ChronoENGINE physical system
78mphysicalSystem = chrono.ChSystemNSC()
79
80# Contact material shared among all objects
81material = chrono.ChMaterialSurfaceNSC()
82
83# Create a floor that is fixed (that is used also to represent the absolute reference)
84floorBody = chrono.ChBodyEasyBox(20, 2, 20, 3000, True, True, material)
85floorBody.SetPos(chrono.ChVectorD(0, -2, 0))
86floorBody.SetBodyFixed(True)
87mphysicalSystem.Add(floorBody)
88
89mtexture = chrono.ChTexture()
90mtexture.SetTextureFilename(chrono.GetChronoDataFile("textures/blue.png"))
91floorBody.AddAsset(mtexture)
92
93# In the following we will create different types of motors
94# - rotational motors: examples A.1, A.2, etc.
95# - linear motors, examples B.1, B.2 etc.
96
97# EXAMPLE A.1
98#
99# - class:   ChLinkMotorRotationSpeed
100# - type:    rotational motor
101# - control: impose a time-dependent speed=v(t)
102#
103# This is a simple type of rotational actuator. It assumes that
104# you know the exact angular speed of the rotor respect to the stator,
105# as a function of time:   angular speed = w(t).
106# Use this to simulate fans, rotating cranks, etc.
107# Note: this is a rheonomic motor that enforces the motion
108# geometrically no compliance is allowed, this means that if the
109# rotating body hits some hard contact, the solver might give unpredictable
110# oscillatory or diverging results because of the contradiction.
111
112positionA1 = chrono.ChVectorD(-3, 2, -3)
113stator1, rotor1 = CreateStatorRotor(material, mphysicalSystem, positionA1)
114
115# Create the motor
116rotmotor1 = chrono.ChLinkMotorRotationSpeed()
117
118# Connect the rotor and the stator and add the motor to the system:
119rotmotor1.Initialize(rotor1,                # body A (slave)
120                      stator1,               # body B (master)
121                      chrono.ChFrameD(positionA1)  # motor frame, in abs. coords
122)
123mphysicalSystem.Add(rotmotor1)
124
125# Create a ChFunction to be used for the ChLinkMotorRotationSpeed
126mwspeed = chrono.ChFunction_Const(chrono.CH_C_PI_2)  # constant angular speed, in [rad/s], 1PI/s =180°/s
127# Let the motor use this motion function:
128rotmotor1.SetSpeedFunction(mwspeed)
129
130# The ChLinkMotorRotationSpeed contains a hidden state that performs the time integration
131# of the angular speed setpoint: such angle is then imposed to the
132# constraat the positional level too, thus avoiding angle error
133# accumulation (angle drift). Optionally, such positional constraint
134# level can be disabled as follows:
135#
136# rotmotor1.SetAvoidAngleDrift(False)
137
138# EXAMPLE A.2
139#
140# - class:   ChLinkMotorRotationAngle
141# - type:    rotational motor
142# - control: impose a time-dependent angle=a(t)
143#
144# This is a simple type of rotational actuator. It assumes that
145# you know the exact angular angle of the rotor respect to the stator,
146# as a function of time:   angle = a(t).
147# Use this to simulate servo drives in robotic systems and automation,
148# where you can assume that the motor rotates with an infinitely stiff
149# and reactive control, that exactly follows your prescribed motion profiles.
150# Note: this is a rheonomic motor that enforces the motion
151# geometrically no compliance is allowed, this means that if the
152# rotating body hits some hard contact, the solver might give unpredictable
153# oscillatory or diverging results because of the contradiction.
154
155positionA2 = chrono.ChVectorD(-3, 2, -2)
156stator2, rotor2 = CreateStatorRotor(material, mphysicalSystem, positionA2)
157
158# Create the motor
159rotmotor2 = chrono.ChLinkMotorRotationAngle()
160
161# Connect the rotor and the stator and add the motor to the system:
162rotmotor2.Initialize(rotor2,                # body A (slave)
163                      stator2,               # body B (master)
164                      chrono.ChFrameD(positionA2)  # motor frame, in abs. coords
165)
166mphysicalSystem.Add(rotmotor2)
167
168# Create a ChFunction to be used for the ChLinkMotorRotationAngle
169msineangle = chrono.ChFunction_Sine(0,       # phase [rad]
170                                    0.05,    # frequency [Hz]
171                                    chrono.CH_C_PI)  # amplitude [rad]
172
173# Let the motor use this motion function as a motion profile:
174rotmotor2.SetAngleFunction(msineangle)
175
176# EXAMPLE A.3
177#
178# - class:   ChLinkMotorRotationTorque
179# - type:    rotational motor
180# - control: impose a (time-dependent) torque=T(t)
181#
182# For this motor, you must specify a time-dependent torque as torque = T(t).
183# (If you want to use this motor to follow some desired motion profiles, you
184# must implement a PID controller that continuously adjusts the value of the
185# torque during the simulation).
186
187positionA3 = chrono.ChVectorD(-3, 2, -1)
188stator3, rotor3 = CreateStatorRotor(material, mphysicalSystem, positionA3)
189
190# Create the motor
191rotmotor3 = chrono.ChLinkMotorRotationTorque()
192
193# Connect the rotor and the stator and add the motor to the system:
194rotmotor3.Initialize(rotor3,                # body A (slave)
195                      stator3,               # body B (master)
196                      chrono.ChFrameD(positionA3))  # motor frame, in abs. coords
197
198mphysicalSystem.Add(rotmotor3)
199
200# The torque(time) function:
201mtorquetime = chrono.ChFunction_Sine(0,   # phase [rad]
202                                     2,   # frequency [Hz]
203                                     160)  # amplitude [Nm]
204
205# Let the motor use this motion function as a motion profile:
206rotmotor3.SetTorqueFunction(mtorquetime)
207
208# EXAMPLE A.4
209#
210# As before, use a ChLinkMotorRotationTorque, but this time compute
211# torque by a custom function. In this example we implement a
212# basic torque(speed) model of a three-phase induction electric motor..
213
214positionA4 = chrono.ChVectorD(-3, 2, 0)
215stator4, rotor4 =  CreateStatorRotor( material, mphysicalSystem, positionA4)
216
217# Create the motor
218rotmotor4 = chrono.ChLinkMotorRotationTorque()
219
220# Connect the rotor and the stator and add the motor to the system:
221rotmotor4.Initialize(rotor4,                # body A (slave)
222                      stator4,               # body B (master)
223                      chrono.ChFrameD(positionA4))  # motor frame, in abs. coords
224
225mphysicalSystem.Add(rotmotor4)
226
227# Implement our custom  torque function.
228# We could use pre-defined ChFunction classes like sine, constant, ramp, etc.,
229# but in this example we show how to implement a custom function: a
230# torque(speed) function that represents a three-phase electric induction motor.
231# Just inherit from ChFunction and implement Get_y() so that it returns different
232# values (regrdless of time x) depending only on the slip speed of the motor:
233class MyTorqueCurve(chrono.ChFunction) :
234  def __init__(self, e2, r2, x2, n_s, mot) :
235        super().__init__()
236        self.E2 = e2
237        self.R2 = r2
238        self.X2 = x2
239        self.ns = n_s
240        self.mymotor = mot
241
242  def Clone(self) :
243        return deepcopy(self)
244
245  def Get_y(self, x) :
246        # The three-phase torque(speed) model
247        w = self.mymotor.GetMotorRot_dt()
248        s = (self.ns - w) / self.ns  # slip
249        T = (3.0 / 2 * chrono.CH_C_PI * self.ns) * (s * self.E2 * self.E2 * self.R2) / (self.R2 * self.R2 + pow(s * self.X2, 2))  # electric torque curve
250        T -= w * 5  # simulate also a viscous brake
251        return T
252
253
254# Create the function object from our custom class
255mtorquespeed = MyTorqueCurve(120, 80, 1, 6, rotmotor4)
256
257
258# Let the motor use this motion function as a motion profile:
259rotmotor4.SetTorqueFunction(mtorquespeed)
260
261# EXAMPLE A.5
262#
263#
264# - class:   ChLinkMotorRotationDriveline
265# - type:    rotational motor
266# - control: delegated to an embedded user-defined driveline/powertrain
267#
268# This is the most powerful motor type. It allows the creation of
269# generic 1D powertrain inside this 3D motor.
270# Powertrains/drivelines are defined by connecting a variable number of
271# 1D objects such as ChShaft, ChClutch, ChShaftsMotor, etc. In this way, for
272# example, you can represent a drive+flywheel+reducer, hence taking into account
273# of the inertia of the flywheel without the complication of adding a full 3D shape that
274# represents the flywheel, and withoput needing 3D constrafor gears, bearings, etc.
275# The 1D driveline is "interfaced" to the two connected threedimensional
276# parts using two "inner" 1D shafts, each rotating as the connected 3D part
277# it is up to the user to build the driveline that connects those two shafts.
278# Most often the driveline is like a graph starting at inner shaft 2 (consider
279# it to be the truss for holding the motor drive, also the support for reducers
280# if any) and ending at inner shaft 1 (consider it to be the output, i.e. the
281# slow-rotation spindle).
282
283positionA5 = chrono.ChVectorD(-3, 2, 1)
284stator5, rotor5 = CreateStatorRotor(material, mphysicalSystem, positionA5)
285
286# Create the motor
287rotmotor5 = chrono.ChLinkMotorRotationDriveline()
288
289# Connect the rotor and the stator and add the motor to the system:
290rotmotor5.Initialize(rotor5,                # body A (slave)
291                      stator5,               # body B (master)
292                      chrono.ChFrameD(positionA5) ) # motor frame, in abs. coords
293
294mphysicalSystem.Add(rotmotor5)
295
296# You may want to change the inertia of 'inner' 1d shafts, (each has default 1kg/m^2)
297# Note: they adds up to 3D inertia when 3D parts rotate about the link shaft.
298# Note: do not use too small values compared to 3D inertias: it might negatively affect
299# the precision of some solvers if so, rather diminish the 3D inertia of stator/rotor parts and add to these.
300rotmotor5.GetInnerShaft1().SetInertia(0.2)  # [kg/m^2]
301rotmotor5.GetInnerShaft2().SetInertia(0.2)  # [kg/m^2]
302
303# Now create the driveline. We want to model a drive+reducer sytem.
304# This driveline must connect "inner shafts" of s1 and s2, where:
305#  s1, is the 3D "rotor5"  part A (ex. a robot arm) and
306#  s2, is the 3D "stator5" part B (ex. a robot base).
307# In the following scheme, the motor is [ DRIVE ], the reducer is [ REDUCER ],
308# the shafts ( shown with symbol || to mean inertia) are:
309#  S1: the 1D inner shaft for s1 robot arm (already present in ChLinkMotorRotationDriveline)
310#  S2: the 1D inner shaft for s2 robot base (already present in ChLinkMotorRotationDriveline)
311#  A : the shaft of the electric drive
312#
313#      S2                A                    S1
314# 3d<--||---[ DRIVE ]---||-----[ REDUCER ]----||-.3d
315# s2   ||                      [         ]         s1
316#      ||----------------------[         ]
317#
318
319# Create 'A', a 1D shaft. This is the shaft of the electric drive, representing its inertia.
320
321my_shaftA = chrono.ChShaft()
322my_shaftA.SetInertia(0.03)
323mphysicalSystem.Add(my_shaftA)
324
325# Create 'DRIVE', the hi-speed motor model - as a simple example use a 'imposed speed' motor: this
326# is the equivalent of the ChLinkMotorRotationSpeed, but for 1D elements:
327
328my_drive = chrono.ChShaftsMotorSpeed()
329my_drive.Initialize(my_shaftA,                   # A , the rotor of the drive
330                     rotmotor5.GetInnerShaft2() ) # S2, the stator of the drive
331
332mphysicalSystem.Add(my_drive)
333# Create a speed(time) function, and use it in my_drive:
334my_driveangle = chrono.ChFunction_Const(25 * chrono.CH_C_2PI)  # 25 [rps] = 1500 [rpm]
335my_drive.SetSpeedFunction(my_driveangle)
336
337# Create the REDUCER. We should not use the simple ChShaftsGear because
338# it does not transmit torque to the support. So use ChShaftsPlanetary
339# and use it in ordinary mode, keeping the carrier as truss: so it
340# will connect three parts: the carrier(here the truss), the in shaft, the out shaft.
341
342my_reducer = chrono.ChShaftsPlanetary()
343my_reducer.Initialize(rotmotor5.GetInnerShaft2(),  # S2, the carrier (truss)
344                       my_shaftA,                    # A , the input shaft
345                       rotmotor5.GetInnerShaft1() )  # S1, the output shaft
346
347my_reducer.SetTransmissionRatioOrdinary(1.0 / 100.0)  # ratio between wR/wA
348mphysicalSystem.Add(my_reducer)
349
350# Btw:  later, if you want, you can access / plot speeds and
351# torques for whatever part of the driveline by putting lines like the following
352# in the  while() {...} simulation loop:
353#
354# GetLog() << " 1D shaft 'A' angular speed: "      << my_shaftA.GetPos_dt() << " [rad/s] \n"
355# GetLog() << " 1D Drive angular speed: rot-stat " << my_drive.GetMotorRot_dt() << " [rad/s] \n"
356# GetLog() << " 1D Drive torque: "                 << my_drive.GetMotorTorque() << " [Ns] \n"
357# GetLog() << " 3D motor angular speed: rot-stat " << rotmotor5.GetMotorRot_dt() << " [rad/s] \n"
358# GetLog() << " 3D motor torque: "                 << rotmotor5.GetMotorTorque() << " [Ns] \n"
359# etc.
360
361# EXAMPLE B.1
362#
363# - class:   ChLinkMotorLinearPosition
364# - type:    linear motor
365# - control: impose a time-dependent position=f(t)
366#
367# This is the simpliest type of linear actuator. It assumes that
368# you know the exact position of the slider respect to the guide,
369# as a function of time:   position = f(t)
370# Therefore, this is a rheonomic motor that enforces the motion
371# geometrically no compliance is allowed, this means that if the
372# sliding body hits some hard contact, the solver might give unpredictable
373# oscillatory or diverging results because of the contradiction.
374
375positionB1 = chrono.ChVectorD(0, 0, -3)
376guide1, slider1 = CreateSliderGuide(material, mphysicalSystem, positionB1)
377
378# Create the linear motor
379motor1 = chrono.ChLinkMotorLinearPosition()
380
381# Connect the guide and the slider and add the motor to the system:
382motor1.Initialize(slider1,               # body A (slave)
383                   guide1,                # body B (master)
384                   chrono.ChFrameD(positionB1)  # motor frame, in abs. coords
385)
386mphysicalSystem.Add(motor1)
387
388# Create a ChFunction to be used for the ChLinkMotorLinearPosition
389msine = chrono.ChFunction_Sine(0,    # phase
390                               0.5,  # frequency
391                               1.6)   # amplitude
392
393# Let the motor use this motion function:
394motor1.SetMotionFunction(msine)
395
396# EXAMPLE B.2
397#
398# - class:   ChLinkMotorLinearSpeed
399# - type:    linear motor
400# - control: impose a time-dependent speed=v(t)
401#
402# This is a simple type of linear actuator. It assumes that
403# you know the exact speed of the slider respect to the guide,
404# as a function of time:   speed = v(t)
405# Therefore, this is a rheonomic motor that enforces the motion
406# geometrically no compliance is allowed, this means that if the
407# sliding body hits some hard contact, the solver might give unpredictable
408# oscillatory or diverging results because of the contradiction.
409# It contains a hidden state that performs the time integration
410# of the required speed, such position is then imposed too to the
411# constraat the positional level, thus avoiding position error
412# accumulation (position drift). Optionally, such constraon
413# position level can be disabled if you are not interested in pos.drift.
414
415positionB2 = chrono.ChVectorD(0, 0, -2)
416guide2, slider2 = CreateSliderGuide(material, mphysicalSystem, positionB2)
417
418# Create the linear motor
419motor2 = chrono.ChLinkMotorLinearSpeed()
420
421# Connect the guide and the slider and add the motor to the system:
422motor2.Initialize(slider2,               # body A (slave)
423                   guide2,                # body B (master)
424                   chrono.ChFrameD(positionB2) ) # motor frame, in abs. coords
425
426mphysicalSystem.Add(motor2)
427
428# Create a ChFunction to be used for the ChLinkMotorLinearSpeed
429msp = chrono.ChFunction_Sine(chrono.CH_C_PI_2,            # phase
430                             0.5,                  # frequency
431                             1.6 * 0.5 * chrono.CH_C_2PI)  # amplitude
432
433# Let the motor use this motion function:
434motor2.SetSpeedFunction(msp)
435
436# The ChLinkMotorLinearSpeed contains a hidden state that performs the time integration
437# of the speed setpoint: such position is then imposed to the
438# constraat the positional level too, thus avoiding position error
439# accumulation (position drift). Optionally, such position constraint
440# level can be disabled as follows:
441#
442# motor2.SetAvoidPositionDrift(False)
443
444# EXAMPLE B.3
445#
446# - class:   ChLinkMotorLinearForce
447# - type:    linear motor
448# - control: impose a time-dependent force=F(t)
449#
450# This actuator is moved via force as a function of time, F=F(t).
451# The basic "open loop" option is to provide a F(t) at the beginning (ex using
452# a feedforward model), but then there is no guarantee about the
453# precise position of the slider, when the simulation runs.
454# This means that, unless you update the force F at each time
455# step using some type of feedback controller, this actuator
456# cannot be used to follow some position setpoint. Implementing
457# your controller might complicate things, but it could be closer to
458# the behavior of a real actuator, that have some delay, bandwidth
459# latency and compliance - for example, differently from
460# other types such as ChLinkMotorLinearPosition  and
461# ChLinkMotorLinearSpeed, this force motor does not enforce any
462# constraon the direction of motion, so if it the slider hits
463# some hard contact, it just stops and keeps pushing, and no troubles
464# with the solver happen.
465
466positionB3 = chrono.ChVectorD(0, 0, -1)
467guide3, slider3 = CreateSliderGuide(material, mphysicalSystem, positionB3)
468
469# just for fun: modify the initial speed of slider to match other examples
470slider3.SetPos_dt(chrono.ChVectorD(1.6 * 0.5 * chrono.CH_C_2PI))
471
472# Create the linear motor
473motor3 = chrono.ChLinkMotorLinearForce()
474
475# Connect the guide and the slider and add the motor to the system:
476motor3.Initialize(slider3,               # body A (slave)
477                   guide3,                # body B (master)
478                   chrono.ChFrameD(positionB3) ) # motor frame, in abs. coords
479
480mphysicalSystem.Add(motor3)
481
482# Create a ChFunction to be used for F(t) in ChLinkMotorLinearForce.
483mF = chrono.ChFunction_Const(200)
484# Let the motor use this motion function:
485motor3.SetForceFunction(mF)
486
487# Alternative: just for fun, use a sine harmonic whose max force is F=M*A, where
488# M is the mass of the slider, A is the max acceleration of the previous examples,
489# so finally the motion should be quite the same - but without feedback, if hits a disturb, it goes crazy:
490mF2 =chrono.ChFunction_Sine(0,                                                        # phase
491                            0.5,                                                      # frequency
492                            slider3.GetMass() * 1.6 * pow(0.5 * chrono.CH_C_2PI, 2) ) # amplitude
493
494# motor3.SetForceFunction(mF2) # uncomment to test this
495
496# EXAMPLE B.4
497#
498# As before, use a ChLinkMotorLinearForce, but this time compute
499# F by a user-defined procedure (as a callback). For example, here we write a very
500# basic PID control algorithm that adjusts F trying to chase a sinusoidal position.
501
502positionB4 = chrono.ChVectorD(0, 0, 0)
503guide4, slider4 = CreateSliderGuide(material, mphysicalSystem, positionB4)
504
505# Create the linear motor
506motor4 = chrono.ChLinkMotorLinearForce()
507
508# Connect the guide and the slider and add the motor to the system:
509motor4.Initialize(slider4,                       # body A (slave)
510                  guide4,                       # body B (master)
511                  chrono.ChFrameD(positionB4))  # motor frame, in abs. coords
512
513mphysicalSystem.Add(motor4)
514
515# Create a ChFunction that computes F by a user-defined algorithm, as a callback.
516# One quick option would be to inherit from the ChFunction base class, and implement the Get_y()
517# function by putting the code you wish, as explained in demo_CH_functions.cpp. However this has some
518# limitations. A more powerful approach is to inherit from ChFunction_SetpointCallback, that automatically
519# computes the derivatives, if needed, by BDF etc. Therefore:
520# 1. You must inherit from the ChFunction_SetpointCallback base class, and implement the SetpointCallback()
521#    function by putting the code you wish. For example something like the follow:
522
523class MyForceClass (chrono.ChFunction_SetpointCallback) :
524      def __init__(self, amp, freq, prop, der, ltime, lerr, f, motor) :
525        """ THIS IS CRUCIAL FOR CORRECT INITIALIZATION: Initialize all the three in down-up order """
526        chrono.ChFunction_Setpoint.__init__(self)
527        chrono.ChFunction_SetpointCallback.__init__(self)
528
529        # Here some specific data to be used in Get_y(),
530        # add whatever you need, ex:
531        self.setpoint_position_sine_amplitude = amp
532        self.setpoint_position_sine_freq = freq
533        self.controller_P = prop  # for our basic PID
534        self.controller_D = der  # for our basic PID
535        self.last_time = ltime
536        self.last_error = lerr
537        self.F = f
538        self.linearmotor= motor  # may be useful later
539
540        # Here we will compute F(t) by emulating a very basic PID scheme.
541        # In practice, it works like a callback that is executed at each time step.
542        # Implementation of this function is mandatory!!!
543      def SetpointCallback(self, x) :
544            # Trick: in this PID example, we need the following if(..)  to update PID
545            # only when time changes (as the callback could be invoked more than once per timestep):
546            time = x
547            if (time > self.last_time) :
548                dt = time - self.last_time
549                # for example, the position to chase is this sine formula:
550                setpo= self.setpoint_position_sine_amplitude * m.sin(self.setpoint_position_sine_freq * chrono.CH_C_2PI * x)
551                error = setpo- self.linearmotor.GetMotorPos()
552                error_dt = (error - self.last_error) / dt
553                # for example, finally compute the force using the PID idea:
554                self.F = self.controller_P * error + self.controller_D * error_dt
555                self.last_time = time
556                self.last_error = error
557
558            return self.F
559     # def SetSetpoint(self,y,x):
560     #       chrono.ChFunction_Setpoint.SetSetpoint(self,y,x)
561
562#      def Update(self, x):
563#            y = self.SetpointCallback(x)
564#            self.SetSetpoint(y, x)
565
566
567
568# 2. Create the function from the custom class
569mFcallback = MyForceClass(1.6,0.5,42000,1000,0,0,0,motor4)
570
571# 3. Let the motor use our custom force:
572motor4.SetForceFunction(mFcallback)
573
574# EXAMPLE B.5
575#
576#
577# - class:   ChLinkMotorLinearDriveline
578# - type:    linear motor
579# - control: delegated to an embedded user-defined driveline/powertrain
580#
581# This is the most powerful linear actuator type. It allows the creation of
582# generic 1D powertrain inside this 3D motor.
583# Powertrains/drivelines are defined by connecting a variable number of
584# 1D objects such as ChShaft, ChClutch, ChShaftsMotor, etc. In this way, for
585# example, you can represent a drive+flywheel+reducer+pulley system, hence taking into account
586# of the inertia of the flywheel and the elasticity of the synchro belt without
587# the complication of adding full 3D shapes that represents all parts.
588# The 1D driveline is "interfaced" to the two connected threedimensional
589# parts using two "inner" 1D shafts, each translating as the connected 3D part
590# it is up to the user to build the driveline that connects those two shafts.
591# Most often the driveline is like a graph starting at inner shaft 2 and ending
592# at inner shaft 1.
593# Note: in the part 2 there is an additional inner shaft that operates on rotation
594# this is needed because, for example, maybe you want to model a driveline like a
595# drive+screw you will anchor the drive to part 2 using this rotational shaft so
596# reaction torques arising because of inner flywheel accelerations can be transmitted to this shaft.
597
598positionB5 = chrono.ChVectorD(0, 0, 1)
599guide5, slider5 = CreateSliderGuide(material, mphysicalSystem, positionB5)
600
601# Create the motor
602motor5 = chrono.ChLinkMotorLinearDriveline()
603
604# Connect the rotor and the stator and add the motor to the system:
605motor5.Initialize(slider5,               # body A (slave)
606                   guide5,                # body B (master)
607                   chrono.ChFrameD(positionB5) ) # motor frame, in abs. coords
608
609mphysicalSystem.Add(motor5)
610
611# You may want to change the inertia of 'inner' 1D shafts, ("translating" shafts: each has default 1kg)
612# Note: they adds up to 3D inertia when 3D parts translate about the link shaft.
613# Note: do not use too small values compared to 3D inertias: it might negatively affect
614# the precision of some solvers if so, rather diminish the 3D inertia of guide/slider parts and add to these.
615motor5.GetInnerShaft1lin().SetInertia(3.0)  # [kg]
616motor5.GetInnerShaft2lin().SetInertia(3.0)  # [kg]
617motor5.GetInnerShaft2rot().SetInertia(0.8)  # [kg/m^2]
618
619# Now create the driveline. We want to model a drive+reducer sytem.
620# This driveline must connect "inner shafts" of s1 and s2, where:
621#  s1, is the 3D "slider5" part B (ex. a gantry robot gripper) and
622#  s2, is the 3D "guide5"  part A (ex. a gantry robot base).
623# In the following scheme, the motor is [ DRIVE ], the reducer is [ RACKPINION ],
624# the shafts ( shown with symbol || to mean inertia) are:
625#  S1: the 1D inner shaft for s1 robot arm (already present in ChLinkMotorLinearDriveline)
626#  S2: the 1D inner shaft for s2 robot base (already present in ChLinkMotorLinearDriveline)
627#  B : the shaft of the electric drive
628# Note that s1 and s2 are translational degrees of freedom, differently
629# from ChLinkMotorLinearDriveline.
630#
631#     S2rot              B                      S1lin
632#    <--||---[ DRIVE ]---||-----[ RACKPINION ]----||-. 3d
633# 3d <                          [            ]          s1
634# s2 < S2lin                    [            ]
635#    <--||----------------------[            ]
636#
637
638# Tell the motor that the inner shaft  'S2rot' is Z, orthogonal to
639# the X direction of the guide (default was X, as for screw actuators)
640
641motor5.SetInnerShaft2RotDirection(chrono.VECT_Z)  # in link coordinates
642
643# Create 'B', a 1D shaft. This is the shaft of the electric drive, representing its inertia.
644
645my_shaftB = chrono.ChShaft()
646my_shaftB.SetInertia(0.33)  # [kg/m^2]
647mphysicalSystem.Add(my_shaftB)
648
649# Create 'DRIVE', the hispeed motor - as a simple example use a 'imposed speed' motor: this
650# is the equivalent of the ChLinkMotorRotationAngle, but for 1D elements:
651
652my_driveli = chrono.ChShaftsMotorAngle()
653my_driveli.Initialize(my_shaftB,                   # B    , the rotor of the drive
654                       motor5.GetInnerShaft2rot() ) # S2rot, the stator of the drive
655
656mphysicalSystem.Add(my_driveli)
657
658# Create a angle(time) function. It could be something as simple as
659#   my_functangle = chrono.ChFunction_Ramp>(0,  180)
660# but here we'll rather do a back-forth motion, made with a repetition of a sequence of 4 basic functions:
661
662my_functsequence = chrono.ChFunction_Sequence()
663my_funcsigma1 = chrono.ChFunction_Sigma(180, 0, 0.5)  # diplacement, t_start, t_end
664my_funcpause1 = chrono.ChFunction_Const(0)
665my_funcsigma2 = chrono.ChFunction_Sigma(-180, 0, 0.3)  # diplacement, t_start, t_end
666my_funcpause2 = chrono.ChFunction_Const(0)
667my_functsequence.InsertFunct(my_funcsigma1, 0.5, 1.0, True)  # fx, duration, weight, enforce C0 continuity
668my_functsequence.InsertFunct(my_funcpause1, 0.2, 1.0, True)  # fx, duration, weight, enforce C0 continuity
669my_functsequence.InsertFunct(my_funcsigma2, 0.3, 1.0, True)  # fx, duration, weight, enforce C0 continuity
670my_functsequence.InsertFunct(my_funcpause2, 0.2, 1.0, True)  # fx, duration, weight, enforce C0 continuity
671my_functangle = chrono.ChFunction_Repeat()
672my_functangle.Set_fa(my_functsequence)
673my_functangle.Set_window_length(0.5 + 0.2 + 0.3 + 0.2)
674my_driveli.SetAngleFunction(my_functangle)
675
676# Create the RACKPINION.
677# It will connect three parts:
678# - S2lin, the carrier (here the truss) to transmit reaction force,
679# - B,     the in (rotational) shaft,
680# - S1lin, the out (translational) shaft.
681
682my_rackpinion = chrono.ChShaftsPlanetary()
683my_rackpinion.Initialize(motor5.GetInnerShaft2lin(),  # S2lin, the carrier (truss)
684                          my_shaftB,                    # B,     the input shaft
685                          motor5.GetInnerShaft1lin() )  # S1lin, the output shaft
686
687my_rackpinion.SetTransmissionRatios(-1, -1.0 / 100.0, 1)
688mphysicalSystem.Add(my_rackpinion)
689
690# Btw:  later, if you want, you can access / plot speeds and
691# torques for whatever part of the driveline by putting lines like the   following
692# in the  while() {...} simulation loop:
693#
694# GetLog() << " 1D shaft 'B' angular speed: "      << my_shaftB.GetPos_dt() << " [rad/s] \n"
695# GetLog() << " 1D Drive angular speed: rot-stat " << my_driveli.GetMotorRot_dt() << " [rad/s] \n"
696# GetLog() << " 1D Drive torque: "                 << my_driveli.GetMotorTorque() << " [Ns] \n"
697# GetLog() << " 3D actuator speed: rot-stat " << motor5.GetMotorPos() << " [rad/s] \n"
698# GetLog() << " 3D actuator force: "                 << motor5.GetMotorForce() << " [Ns] \n"
699# etc.
700
701# EXAMPLE B.6
702#
703# - class:   ChLinkMotorLinearPosition
704# - type:    linear motor
705# - control: impose a position by continuously changing it during the while{} simulation
706#
707# We use again the  ChLinkMotorLinearPosition as in EXAMPLE B.1, but
708# this time we change its position using a "brute force" approach, that is:
709# we put a line in the while{...} simulation loop that continuously changes the
710# position by setting a value from some computation.
711#  Well: here one might be tempted to do  motor6.SetMotionFunction(myconst) where
712# myconst is a ChFunction_Const object where you would continuously change the value
713# of the constant by doing  myconst.Set_yconst() in the while{...} loop this would
714# work somehow but it would miss the derivative of the function, something that is used
715# in the guts of ChLinkMotorLinearPosition. To overcome this, we'll use a  ChFunction_Setpoint
716# function, that is able to guess the derivative of the changing setpoby doing numerical
717# differentiation each time you call  myfunction.SetSetpoint().
718#  Note: A more elegant solution would be to inherit our custom motion function
719# from  ChFunction_SetpointCallback  as explained in EXAMPLE B.4, and then setting
720# motor6.SetMotionFunction(mycallback) this would avoid polluting the while{...} loop
721# but sometimes is faster to do the quick & dirty approach of this example.
722
723positionB6 = chrono.ChVectorD(0, 0, 2)
724guide6, slider6 = CreateSliderGuide(material, mphysicalSystem, positionB6)
725
726# Create the linear motor
727motor6 = chrono.ChLinkMotorLinearPosition()
728
729# Connect the guide and the slider and add the motor to the system:
730motor6.Initialize(slider6,               # body A (slave)
731                   guide6,                # body B (master)
732                   chrono.ChFrameD(positionB6) ) # motor frame, in abs. coords
733
734mphysicalSystem.Add(motor6)
735
736# Create a ChFunction to be used for the ChLinkMotorLinearPosition
737# Note! look later in the while{...} simulation loop, we'll continuously
738# update its value using  motor6setpoint.SetSetpoint()
739motor6setpoint= chrono.ChFunction_Setpoint()
740# Let the motor use this motion function:
741motor6.SetMotionFunction(motor6setpoint)
742
743#
744# THE VISUALIZATION SYSTEM
745#
746
747# Create the Irrlicht visualization (open the Irrlicht device,
748# bind a simple user interface, etc. etc.)
749application = chronoirr.ChIrrApp(mphysicalSystem, "Motors", chronoirr.dimension2du(800, 600))
750
751# Easy shortcuts to add camera, lights, logo and sky in Irrlicht scene:
752application.AddTypicalLogo()
753application.AddTypicalSky()
754application.AddTypicalLights()
755application.AddTypicalCamera(chronoirr.vector3df(1, 3, -7))
756application.AddLightWithShadow(chronoirr.vector3df(20.0, 35.0, -25.0), chronoirr.vector3df(0, 0, 0), 55, 20, 55, 35, 512,
757                               chronoirr.SColorf(0.6, 0.8, 1.0))
758
759# Use this function for adding a ChIrrNodeAsset to all items
760# Otherwise use application.AssetBind(myitem) on a per-item basis.
761application.AssetBindAll()
762
763# Use this function for 'converting' assets into Irrlicht meshes
764application.AssetUpdateAll()
765
766# This is to enable shadow maps (shadow casting with soft shadows) in Irrlicht
767# for all objects (or use application.AddShadow(..) for enable shadow on a per-item basis)
768application.AddShadowAll()
769
770#
771# THE SOFT-REAL-TIME CYCLE
772#
773
774# Modify some setting of the physical system for the simulation, if you want
775solver = chrono.ChSolverPSOR()
776solver.SetMaxIterations(50)
777mphysicalSystem.SetSolver(solver)
778
779application.SetTimestep(0.005)
780application.SetTryRealtime(True)
781
782while application.GetDevice().run() :
783    application.BeginScene(True, True, chronoirr.SColor(255, 140, 161, 192))
784
785    application.DrawAll()
786
787    # Example B.6 requires the setpoto be changed in the simulation loop:
788    # for example use a clamped sinusoid, just for fun:
789    t = mphysicalSystem.GetChTime()
790    Sp = chrono.ChMin(chrono.ChMax(2.6 * m.sin(t * 1.8), -1.4), 1.4)
791    motor6setpoint.SetSetpoint(Sp, t)
792
793    application.DoStep()
794
795    application.EndScene()
796
797
798