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