Skip to content

Commit 382474b

Browse files
yuvaltassacopybara-github
authored andcommitted
Minor improvements to dcmotor
PiperOrigin-RevId: 895907301 Change-Id: Ia50a6d06c1ede9894cc71f375db837d104f0211e
1 parent b61d304 commit 382474b

8 files changed

Lines changed: 115 additions & 58 deletions

File tree

doc/APIreference/functions.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4667,6 +4667,8 @@ Set actuator to active adhesion; return error if any.
46674667

46684668
Set actuator to DC motor; return error if any.
46694669

4670+
*Nullable:* ``motorconst``, ``nominal``, ``saturation``, ``inductance``, ``cogging``, ``controller``, ``thermal``, ``lugre``
4671+
46704672
.. _AddAssets:
46714673

46724674
Assets

doc/XMLreference.rst

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6327,17 +6327,16 @@ This element has a subset of the common attributes and two custom attributes.
63276327

63286328
:el-prefix:`actuator/` |-| **dcmotor** |*|
63296329
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6330-
This element creates a DC motor actuator. Note that :el:`dcmotor` is quite different from the :ref:`general actuation
6331-
model<geActuation>`. Unlike the general model where the components of force generation are independent affine functions
6332-
mapping from control to force, :el:`dcmotor` relies on highly coupled physical dynamics. See the `DC motor technical
6333-
note <_static/dcmotor.pdf>`__ for complete mathematical formulations and parameter semantics, but we include a few
6334-
important notes here:
6335-
6336-
- Note that while :ref:`resistance<actuator-dcmotor-resistance>`, :ref:`motorconst<actuator-dcmotor-motorconst>` and
6337-
:ref:`nominal<actuator-dcmotor-nominal>` are each optional, some combination of them is required.
6330+
This element creates a DC motor actuator. See the `DC motor technical note <_static/dcmotor.pdf>`__ for complete
6331+
mathematical formulations and parameter semantics, but we include a few important notes below. Note that :el:`dcmotor`
6332+
does not conform to the affine gain / bias structure of the :ref:`general actuation model<geActuation>`, except for
6333+
the stateless case.
6334+
6335+
- :ref:`resistance<actuator-dcmotor-resistance>`, :ref:`motorconst<actuator-dcmotor-motorconst>` and
6336+
:ref:`nominal<actuator-dcmotor-nominal>` are each optional, but some combination of them is required.
63386337
See Section 2.1 of the `technical note <_static/dcmotor.pdf>`__.
6339-
- The control :ref:`input<actuator-dcmotor-input>` semantic is either the voltage applied to the motor terminals, or a
6340-
position or velocity target for a PID :ref:`controller<actuator-dcmotor-controller>`.
6338+
- The control :ref:`input<actuator-dcmotor-input>` semantic is either the voltage applied to the motor terminals (the
6339+
default), or a position or velocity target for a :ref:`PID controller<actuator-dcmotor-controller>`.
63416340
- Optional features include electrical dynamics (:ref:`inductance<actuator-dcmotor-inductance>`),
63426341
:ref:`cogging torque<actuator-dcmotor-cogging>`, :ref:`thermal resistance variation<actuator-dcmotor-thermal>`, and
63436342
:ref:`LuGre<actuator-dcmotor-lugre>` friction.
@@ -6408,24 +6407,23 @@ This element has the following custom attributes in addition to the common attri
64086407
.. _actuator-dcmotor-resistance:
64096408

64106409
:at:`resistance`: :at-val:`real, optional`
6411-
Terminal resistance :math:`R` in Ohm. (see `tech note <_static/dcmotor.pdf>`__ for details)
6410+
Terminal resistance :math:`R` in Ohm. (see `tech note <_static/dcmotor.pdf>`__, Sections 1.1 and 2.1)
64126411

64136412
.. _actuator-dcmotor-motorconst:
64146413

64156414
:at:`motorconst`: :at-val:`real(2), optional`
64166415
Motor constants, defined as :at:`motorconst` = ":at-val:`Kt` :at-val:`Ke`" (N·m/A, equivalently V·s/rad).
64176416
:at-val:`Kt` is the torque constant and :at-val:`Ke` the back-EMF constant; they can differ when magnetic saturation
64186417
is present. If both are positive, the effective constant is :math:`K = \sqrt{K_t K_e}` (geometric mean). If only one
6419-
is positive, :math:`K` equals that value; a single value is interpreted as :math:`K_t = K_e`. If your datasheet gives
6420-
the speed constant :math:`K_v` in rad/(V·s), use :math:`K_e = 1/K_v`. (see `tech note <_static/dcmotor.pdf>`__ for
6421-
details)
6418+
is positive, :math:`K` equals that value. If a datasheet specifies the speed constant :math:`K_v` in rad/(V·s), use
6419+
:math:`K_e = 1/K_v`. (see `tech note <_static/dcmotor.pdf>`__, Sections 1.1 and 2.1)
64226420

64236421
.. _actuator-dcmotor-nominal:
64246422

64256423
:at:`nominal`: :at-val:`real(3), optional`
64266424
Nominal operating point, defined as :at:`nominal` = ":at-val:`voltage` :at-val:`stall_torque`
64276425
:at-val:`no_load_speed`". The compiler derives :math:`K =` :at-val:`voltage` / :at-val:`no_load_speed` and :math:`R =
6428-
K` · :at-val:`voltage` / :at-val:`stall_torque`. (see `tech note <_static/dcmotor.pdf>`__ for details)
6426+
K` · :at-val:`voltage` / :at-val:`stall_torque`. (see `tech note <_static/dcmotor.pdf>`__, Sections 1.1 and 2.1)
64296427

64306428
.. _actuator-dcmotor-inductance:
64316429

@@ -6434,7 +6432,7 @@ This element has the following custom attributes in addition to the common attri
64346432
alternative specifications: :at-val:`L` is the winding inductance and :at-val:`timeconst` :math:`= L/R` is the
64356433
electrical time constant. Specify one; if both are given, :at-val:`L` takes precedence. If both are 0 (the default),
64366434
no electrical dynamics are modeled and the current is computed algebraically. Adds one activation variable for
6437-
armature current. (see `tech note <_static/dcmotor.pdf>`__ for details)
6435+
armature current. (see `tech note <_static/dcmotor.pdf>`__, Sections 1.1.1 and 2.2)
64386436

64396437
.. _actuator-dcmotor-thermal:
64406438

@@ -6444,7 +6442,7 @@ This element has the following custom attributes in addition to the common attri
64446442
specify the thermal time constant: :at-val:`timeconst` = :at-val:`resistance` :math:`\times` :at-val:`capacitance`.
64456443
Specify either :at-val:`timeconst` directly, or :at-val:`resistance` and :at-val:`capacitance`; if all three are
64466444
given, :at-val:`timeconst` takes precedence. If all are 0 (the default), thermal modeling is disabled. Adds one
6447-
activation variable for winding temperature. (see `tech note <_static/dcmotor.pdf>`__ for details)
6445+
activation variable for winding temperature. (see `tech note <_static/dcmotor.pdf>`__, Sections 1.3 and 2.3)
64486446

64496447
.. _actuator-dcmotor-saturation:
64506448

@@ -6455,15 +6453,15 @@ This element has the following custom attributes in addition to the common attri
64556453
given, :at-val:`torque` takes precedence. Sets :at:`forcerange` to [:math:`-\tau_{\max},\, \tau_{\max}`].
64566454
:at-val:`voltage` sets the maximum voltage :math:`V_{\max}`. :at-val:`current_rate` sets the maximum rate of change
64576455
of current :math:`(di/dt)_{\max}` (requires :ref:`inductance<actuator-dcmotor-inductance>`). A value of 0 (the
6458-
default) for any sub-value disables the respective limit. (see `tech note <_static/dcmotor.pdf>`__ for details)
6456+
default) for any sub-value disables the respective limit. (see `tech note <_static/dcmotor.pdf>`__, Section 2)
64596457

64606458
.. _actuator-dcmotor-cogging:
64616459

64626460
:at:`cogging`: :at-val:`real(3), "0 0 0"`
64636461
Cogging torque, defined as :at:`cogging` = ":at-val:`amplitude` :at-val:`poles` :at-val:`phase`" (N·m, integer, rad).
64646462
Adds a position-dependent torque :math:`= \textsf{amplitude} \cdot \sin(\textsf{poles} \cdot \theta +
64656463
\textsf{phase})`. Disabled when :at-val:`amplitude` = 0 (the default).
6466-
(see `tech note <_static/dcmotor.pdf>`__ for details)
6464+
(see `tech note <_static/dcmotor.pdf>`__, Sections 1.2 and 2.1)
64676465

64686466
.. _actuator-dcmotor-lugre:
64696467

@@ -6473,28 +6471,28 @@ This element has the following custom attributes in addition to the common attri
64736471
:at-val:`stiffness` = 0 (the default). Adds one activation variable for bristle deflection. Note that the
64746472
:at-val:`viscous` coefficient is mapped directly to the actuator :ref:`damping<actuator-general-damping>` array
64756473
(specifically the linear term, :at-val:`damping[0]`). If both are specified, their values are summed.
6476-
(see `tech note <_static/dcmotor.pdf>`__ for details)
6474+
(see `tech note <_static/dcmotor.pdf>`__, Sections 1.4 and 2.4)
64776475

64786476
.. _actuator-dcmotor-input:
64796477

64806478
:at:`input`: :at-val:`[voltage, position, velocity], "voltage"`
64816479
Specifies the input signal semantics. In "voltage" mode, the control directly sets applied motor voltage. In
6482-
"position" or "velocity" modes, the PID :ref:`controller<actuator-dcmotor-controller>` uses the control as a
6483-
reference setpoint relative to the joint trajectory. (see `tech note <_static/dcmotor.pdf>`__ for details)
6480+
"position" or "velocity" modes, the :ref:`PID controller<actuator-dcmotor-controller>` uses the control as a
6481+
reference setpoint relative to the joint trajectory. (see `tech note <_static/dcmotor.pdf>`__, Section 2.5)
64846482

64856483
.. _actuator-dcmotor-controller:
64866484

64876485
:at:`controller`: :at-val:`real(5), "0 0 0 0 0"`
64886486
PID controller parameters, defined as :at:`controller` = ":at-val:`kp` :at-val:`ki` :at-val:`kd`
64896487
:at-val:`slewmax` :at-val:`Imax`". Depending on the :at:`input` mode, the controller stabilizes either position or
6490-
velocity. If the :at:`input` mode is voltage, the controller is ignored. A value of 0 (the default) disables the
6488+
velocity. If the :at:`input` mode is voltage, this attribute is ignored. A value of 0 (the default) disables the
64916489
respective feature: :at-val:`slewmax` = 0 means no slew-rate limiting, :at-val:`Imax` = 0 means no anti-windup
6492-
clamping. (see `tech note <_static/dcmotor.pdf>`__ for details)
6490+
clamping. (see `tech note <_static/dcmotor.pdf>`__, Section 2.5)
64936491

64946492
.. _actuator-plugin:
64956493

64966494
:el-prefix:`actuator/` |-| **plugin** |?|
6497-
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6495+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
64986496

64996497
Associate this actuator with an :ref:`engine plugin<exPlugin>`. Either :at:`plugin` or :at:`instance` are required.
65006498

include/mujoco/mujoco.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1727,6 +1727,7 @@ MJAPI const char* mjs_setToMuscle(mjsActuator* actuator, double timeconst[2], do
17271727
MJAPI const char* mjs_setToAdhesion(mjsActuator* actuator, double gain);
17281728

17291729
// Set actuator to DC motor; return error if any.
1730+
// Nullable: motorconst, nominal, saturation, inductance, cogging, controller, thermal, lugre
17301731
MJAPI const char* mjs_setToDCMotor(mjsActuator* actuator, double motorconst[2], double resistance,
17311732
double nominal[3], double saturation[4], double inductance[2],
17321733
double cogging[3], double controller[5], double thermal[6],

python/mujoco/introspect/functions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10805,6 +10805,7 @@
1080510805
inner_type=ValueType(name='double'),
1080610806
extents=(2,),
1080710807
),
10808+
nullable=True,
1080810809
),
1080910810
FunctionParameterDecl(
1081010811
name='resistance',
@@ -10816,48 +10817,55 @@
1081610817
inner_type=ValueType(name='double'),
1081710818
extents=(3,),
1081810819
),
10820+
nullable=True,
1081910821
),
1082010822
FunctionParameterDecl(
1082110823
name='saturation',
1082210824
type=ArrayType(
1082310825
inner_type=ValueType(name='double'),
1082410826
extents=(4,),
1082510827
),
10828+
nullable=True,
1082610829
),
1082710830
FunctionParameterDecl(
1082810831
name='inductance',
1082910832
type=ArrayType(
1083010833
inner_type=ValueType(name='double'),
1083110834
extents=(2,),
1083210835
),
10836+
nullable=True,
1083310837
),
1083410838
FunctionParameterDecl(
1083510839
name='cogging',
1083610840
type=ArrayType(
1083710841
inner_type=ValueType(name='double'),
1083810842
extents=(3,),
1083910843
),
10844+
nullable=True,
1084010845
),
1084110846
FunctionParameterDecl(
1084210847
name='controller',
1084310848
type=ArrayType(
1084410849
inner_type=ValueType(name='double'),
1084510850
extents=(5,),
1084610851
),
10852+
nullable=True,
1084710853
),
1084810854
FunctionParameterDecl(
1084910855
name='thermal',
1085010856
type=ArrayType(
1085110857
inner_type=ValueType(name='double'),
1085210858
extents=(6,),
1085310859
),
10860+
nullable=True,
1085410861
),
1085510862
FunctionParameterDecl(
1085610863
name='lugre',
1085710864
type=ArrayType(
1085810865
inner_type=ValueType(name='double'),
1085910866
extents=(6,),
1086010867
),
10868+
nullable=True,
1086110869
),
1086210870
FunctionParameterDecl(
1086310871
name='input_mode',

src/user/user_api.cc

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,18 +1125,18 @@ const char* mjs_setToDCMotor(mjsActuator* actuator, double motorconst[2], double
11251125
double nominal[3], double saturation[4], double inductance[2],
11261126
double cogging[3], double controller[5], double thermal[6],
11271127
double lugre[6], int input_mode) {
1128-
double Kt = motorconst[0]; // torque constant
1129-
double Ke = motorconst[1]; // back-EMF constant
1130-
double R = resistance; // electrical resistance
1131-
double vn = nominal[0]; // nominal voltage
1132-
double tau0 = nominal[1]; // stall torque
1133-
double omega0 = nominal[2]; // no-load speed
1128+
double R = resistance; // electrical resistance
1129+
double Kt = motorconst ? motorconst[0] : 0; // torque constant
1130+
double Ke = motorconst ? motorconst[1] : 0; // back-EMF constant
1131+
double vn = nominal ? nominal[0] : 0; // nominal voltage
1132+
double tau0 = nominal ? nominal[1] : 0; // stall torque
1133+
double omega0 = nominal ? nominal[2] : 0; // no-load speed
11341134

11351135
// derive Ke from nominal: omega0 = vn*Ke / (Ke^2 + R*B)
11361136
if (vn > 0 && Ke <= 0 && omega0 > 0) {
11371137
// viscous damping (linear), add lugre sigma2 contribution if any
11381138
double B = actuator->damping[0];
1139-
if (lugre[0] > 0) B += lugre[2];
1139+
if (lugre && lugre[0] > 0) B += lugre[2];
11401140

11411141
if (B > 0 && R > 0) {
11421142
// R known: solve quadratic Ke^2*omega0 - Ke*vn + R*B*omega0 = 0
@@ -1176,24 +1176,24 @@ const char* mjs_setToDCMotor(mjsActuator* actuator, double motorconst[2], double
11761176
actuator->gainprm[1] = K;
11771177

11781178
// controller parameters: gainprm[4:6] for kp, ki, kd
1179-
actuator->gainprm[4] = controller[0]; // kp
1180-
actuator->gainprm[5] = controller[1]; // ki
1181-
actuator->gainprm[6] = controller[2]; // kd
1179+
actuator->gainprm[4] = controller ? controller[0] : 0; // kp
1180+
actuator->gainprm[5] = controller ? controller[1] : 0; // ki
1181+
actuator->gainprm[6] = controller ? controller[2] : 0; // kd
11821182

11831183
// controller parameters: dynprm[7,8] for slewmax, Imax
1184-
actuator->dynprm[7] = controller[3]; // slewmax
1185-
actuator->dynprm[8] = controller[4]; // Imax
1184+
actuator->dynprm[7] = controller ? controller[3] : 0; // slewmax
1185+
actuator->dynprm[8] = controller ? controller[4] : 0; // Imax
11861186

11871187
// saturation: [tau_max, i_max, (di/dt)_max, v_max]
1188-
if (saturation[2] > 0) {
1188+
if (saturation && saturation[2] > 0) {
11891189
actuator->dynprm[1] = saturation[2]; // (di/dt)_max
11901190
}
1191-
if (saturation[3] > 0) {
1191+
if (saturation && saturation[3] > 0) {
11921192
actuator->gainprm[7] = saturation[3]; // v_max
11931193
}
11941194

11951195
// saturation -> forcerange
1196-
if (saturation[0] > 0 || saturation[1] > 0) {
1196+
if (saturation && (saturation[0] > 0 || saturation[1] > 0)) {
11971197
double tau_max = saturation[0];
11981198
if (tau_max == 0 && saturation[1] > 0) {
11991199
tau_max = K * saturation[1]; // tau_max = K * i_max
@@ -1204,34 +1204,34 @@ const char* mjs_setToDCMotor(mjsActuator* actuator, double motorconst[2], double
12041204
}
12051205

12061206
// cogging: [amplitude, periodicity, phase] -> biasprm[0:3]
1207-
actuator->biasprm[0] = cogging[0]; // amplitude
1208-
actuator->biasprm[1] = cogging[1]; // periodicity
1209-
actuator->biasprm[2] = cogging[2]; // phase
1207+
actuator->biasprm[0] = cogging ? cogging[0] : 0; // amplitude
1208+
actuator->biasprm[1] = cogging ? cogging[1] : 0; // periodicity
1209+
actuator->biasprm[2] = cogging ? cogging[2] : 0; // phase
12101210

12111211
// count activation variables: slot order is slew, integral, temperature, bristle, current
12121212
int actdim = 0;
12131213

12141214
// inductance: [L, te]
1215-
if (inductance[0] < 0) return "DC motor: inductance must be non-negative";
1216-
if (inductance[1] < 0) return "DC motor: electrical time constant must be non-negative";
1217-
double te = inductance[0] > 0 ? inductance[0] / R : inductance[1];
1215+
if (inductance && inductance[0] < 0) return "DC motor: inductance must be non-negative";
1216+
if (inductance && inductance[1] < 0) return "DC motor: electrical time constant must be non-negative";
1217+
double te = (inductance && inductance[0] > 0) ? inductance[0] / R : (inductance ? inductance[1] : 0);
12181218
actuator->dynprm[0] = te;
12191219
if (te > 0) {
12201220
actdim++;
12211221
}
12221222

12231223
// controller states: slew rate limiting
1224-
if (controller[3] > 0) { // slewmax
1224+
if (controller && controller[3] > 0) { // slewmax
12251225
actdim++;
12261226
}
12271227

12281228
// controller states: integral
1229-
if (controller[1] > 0) { // ki
1229+
if (controller && controller[1] > 0) { // ki
12301230
actdim++;
12311231
}
12321232

12331233
// thermal -> temperature activation
1234-
if (thermal[0] > 0 || thermal[1] > 0 || thermal[2] > 0) {
1234+
if (thermal && (thermal[0] > 0 || thermal[1] > 0 || thermal[2] > 0)) {
12351235
double RT = thermal[0]; // thermal resistance
12361236
double C = thermal[1]; // thermal capacitance
12371237
double tth = thermal[2]; // thermal time constant
@@ -1259,7 +1259,7 @@ const char* mjs_setToDCMotor(mjsActuator* actuator, double motorconst[2], double
12591259
}
12601260

12611261
// lugre: {stiffness, damping, viscous, coulomb, static, stribeck}
1262-
if (lugre[0] > 0) {
1262+
if (lugre && lugre[0] > 0) {
12631263
actuator->dynprm[5] = lugre[0]; // stiffness -> sigma0
12641264
actuator->dynprm[6] = lugre[1]; // damping -> sigma1
12651265
actuator->damping[0] += lugre[2]; // viscous -> sigma2

src/user/user_api.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,13 @@ MJAPI const char* mjs_setToMuscle(mjsActuator* actuator, double timeconst[2], do
192192
// Set actuator to adhesion, return error on failure.
193193
MJAPI const char* mjs_setToAdhesion(mjsActuator* actuator, double gain);
194194

195+
// Set actuator to DC motor, return error on failure.
196+
MJAPI const char* mjs_setToDCMotor(mjsActuator* actuator, double motorconst[2], double resistance,
197+
double nominal[3], double saturation[4], double inductance[2],
198+
double cogging[3], double controller[5], double thermal[6],
199+
double lugre[6], int input_mode);
200+
201+
195202
//---------------------------------- Add assets ----------------------------------------------------
196203

197204
// Add mesh.

test/user/user_api_test.cc

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,47 @@ TEST_F(MujocoTest, DeletePlugin) {
239239
mj_deleteModel(newmodel);
240240
}
241241

242+
TEST_F(MujocoTest, SetToDCMotorNullable) {
243+
mjSpec* spec = mj_makeSpec();
244+
mjsActuator* actuator = mjs_addActuator(spec, 0);
245+
246+
double motorconst[2] = {0.05, 0.05};
247+
double resistance = 2.0;
248+
249+
const char* err = mjs_setToDCMotor(actuator, motorconst, resistance,
250+
nullptr, nullptr, nullptr,
251+
nullptr, nullptr, nullptr,
252+
nullptr, 0);
253+
EXPECT_STREQ(err, "");
254+
EXPECT_EQ(actuator->gainprm[0], 2.0);
255+
EXPECT_EQ(actuator->gainprm[1], 0.05);
256+
EXPECT_EQ(actuator->gainprm[4], 0);
257+
EXPECT_EQ(actuator->gainprm[5], 0);
258+
EXPECT_EQ(actuator->gainprm[6], 0);
259+
EXPECT_EQ(actuator->dynprm[7], 0);
260+
EXPECT_EQ(actuator->dynprm[8], 0);
261+
262+
mj_deleteSpec(spec);
263+
}
264+
265+
TEST_F(MujocoTest, SetToDCMotorDeriveKe) {
266+
mjSpec* spec = mj_makeSpec();
267+
mjsActuator* actuator = mjs_addActuator(spec, 0);
268+
269+
double resistance = 2.0;
270+
double nominal[3] = {12.0, 0, 100.0}; // vn=12, omega0=100
271+
272+
const char* err = mjs_setToDCMotor(actuator, nullptr, resistance,
273+
nominal, nullptr, nullptr,
274+
nullptr, nullptr, nullptr,
275+
nullptr, 0);
276+
EXPECT_STREQ(err, "");
277+
EXPECT_EQ(actuator->gainprm[0], 2.0);
278+
EXPECT_NEAR(actuator->gainprm[1], 0.12, 1e-5);
279+
280+
mj_deleteSpec(spec);
281+
}
282+
242283
static constexpr char xml_plugin_1[] = R"(
243284
<mujoco model="MuJoCo Model">
244285
<worldbody>

0 commit comments

Comments
 (0)