1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
|
// Copyright 2019 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using UnityEngine;
namespace Mujoco {
// Actuators provide means to set joints in motion.
public class MjActuator : MjComponent {
public enum ActuatorType {
General,
Motor,
Position,
Velocity,
Cylinder,
Muscle
}
// This structure holds the parameters shared by all types of actuators.
//
// All constants found in this class are copied from the official documentation, and can be found
// here: http://mujoco.org/book/XMLreference.html#actuator
[Serializable]
public class CommonParameters {
// If true, the control input to this actuator is automatically clamped to ctrlrange at runtime.
// If false, control input clamping is disabled.
public bool CtrlLimited;
// If true, the force output of this actuator is automatically clamped to forcerange at runtime.
// If false, force output clamping is disabled.
public bool ForceLimited;
// Range for clamping the control input.
public Vector2 CtrlRange;
// Range for clamping the force output.
public Vector2 ForceRange;
// Range of feasible lengths of the actuator's transmission.
public Vector2 LengthRange;
// This attribute scales the length (and consequently moment arms, velocity and force) of the
// actuator, for all transmission types. It is different from the gain in the force generation
// mechanism, because the gain only scales the force output and does not affect the length,
// moment arms and velocity.
public List<float> Gear = new List<float>() { 1.0f };
public void ToMjcf(XmlElement mjcf) {
mjcf.SetAttribute("ctrllimited", $"{CtrlLimited}".ToLowerInvariant());
mjcf.SetAttribute("forcelimited", $"{ForceLimited}".ToLowerInvariant());
mjcf.SetAttribute(
"ctrlrange",
MjEngineTool.MakeLocaleInvariant($"{MjEngineTool.GetSorted(CtrlRange).x} {MjEngineTool.GetSorted(CtrlRange).y}"));
mjcf.SetAttribute(
"forcerange",
MjEngineTool.MakeLocaleInvariant($"{MjEngineTool.GetSorted(ForceRange).x} {MjEngineTool.GetSorted(ForceRange).y}"));
mjcf.SetAttribute(
"lengthrange",
MjEngineTool.MakeLocaleInvariant($"{MjEngineTool.GetSorted(LengthRange).x} {MjEngineTool.GetSorted(LengthRange).y}"));
mjcf.SetAttribute("gear", MjEngineTool.ListToMjcf(Gear));
}
public void FromMjcf(XmlElement mjcf) {
CtrlLimited = mjcf.GetBoolAttribute("ctrllimited", defaultValue: false);
ForceLimited = mjcf.GetBoolAttribute("forcelimited", defaultValue: false);
CtrlRange = mjcf.GetVector2Attribute("ctrlrange", defaultValue: Vector2.zero);
ForceRange = mjcf.GetVector2Attribute("forcerange", defaultValue: Vector2.zero);
LengthRange = mjcf.GetVector2Attribute("lengthrange", defaultValue: Vector2.zero);
Gear = mjcf.GetFloatArrayAttribute("gear", defaultValue: new float[] { 1.0f }).ToList();
}
}
// This structure holds all parameters unique to each type of the actuator.
//
// Because UnityEditor doesn't handle polymorphism well, I decided to put all parameters here
// and then create a custom editor (MjActuatorEditor), that will display only the values
// relevant to the selected articulation type. The choice will be made based on the value of
// the 'Type' field.
//
// All constants found in this class are copied from the official documentation, and can be found
// here: http://mujoco.org/book/XMLreference.html#actuator
[Serializable]
public class CustomParameters {
//// General actuator parameters.
// Activation dynamics type for the actuator.
public MujocoLib.mjtDyn DynType;
// The gain and bias together determine the output of the force generation mechanism, which is
// currently assumed to be affine. As already explained in Actuation model, the general formula
// is:
// scalar_force = gain_term * (act or ctrl) + bias_term.
// The formula uses the activation state when present, and the control otherwise.
public MujocoLib.mjtGain GainType;
// Bias type.
public MujocoLib.mjtBias BiasType;
// Activation dynamics parameters. The built-in activation types (except for muscle) use only
// the first parameter, but we provide additional parameters in case user callbacks implement a
// more elaborate model. The length of this array is not enforced by the parser, so the user can
// enter as many parameters as needed.
public List<float> DynPrm = new List<float>() { 1.0f, 0.0f, 0.0f };
// Gain parameters. The built-in gain types (except for muscle) use only the first parameter,
// but we provide additional parameters in case user callbacks implement a more elaborate model.
// The length of this array is not enforced by the parser, so the user can enter as many
// parameters as needed.
public List<float> GainPrm = new List<float>() { 1.0f, 0.0f, 0.0f };
// Bias parameters. The affine bias type uses three parameters. The length of this array is not
// enforced by the parser, so the user can enter as many parameters as needed.
public List<float> BiasPrm = new List<float>() { 0.0f, 0.0f, 0.0f };
public void GeneralToMjcf(XmlElement mjcf) {
mjcf.SetAttribute("dyntype", $"{DynType}".Substring(6).ToLowerInvariant());
mjcf.SetAttribute("gaintype", $"{GainType}".Substring(7).ToLowerInvariant());
mjcf.SetAttribute("biastype", $"{BiasType}".Substring(7).ToLowerInvariant());
mjcf.SetAttribute("dynprm", MjEngineTool.ListToMjcf(DynPrm));
mjcf.SetAttribute("gainprm", MjEngineTool.ListToMjcf(GainPrm));
mjcf.SetAttribute("biasprm", MjEngineTool.ListToMjcf(BiasPrm));
}
public void GeneralFromMjcf(XmlElement mjcf) {
var dynTypeStr = mjcf.GetStringAttribute("dyntype", defaultValue: "none");
var gainTypeStr = mjcf.GetStringAttribute("gaintype", defaultValue: "fixed");
var biasTypeStr = mjcf.GetStringAttribute("biastype", defaultValue: "none");
var ignoreCase = true;
Enum.TryParse<MujocoLib.mjtDyn>($"mjdyn_{dynTypeStr}", ignoreCase, out DynType);
Enum.TryParse<MujocoLib.mjtGain>($"mjgain_{gainTypeStr}", ignoreCase, out GainType);
Enum.TryParse<MujocoLib.mjtBias>($"mjbias_{biasTypeStr}", ignoreCase, out BiasType);
DynPrm = mjcf.GetFloatArrayAttribute(
"dynprm", defaultValue: new float[] { 1.0f, 0.0f, 0.0f }).ToList();
GainPrm = mjcf.GetFloatArrayAttribute(
"gainprm", defaultValue: new float[] { 1.0f, 0.0f, 0.0f }).ToList();
BiasPrm = mjcf.GetFloatArrayAttribute(
"biasprm", defaultValue: new float[] { 0.0f, 0.0f, 0.0f }).ToList();
}
//// Position actuator parameters.
// Position feedback gain.
[AbsoluteValue]
public float Kp = 1.0f;
public void PositionToMjcf(XmlElement mjcf) {
mjcf.SetAttribute("kp", MjEngineTool.MakeLocaleInvariant($"{Math.Abs(Kp)}"));
}
public void PositionFromMjcf(XmlElement mjcf) {
Kp = mjcf.GetFloatAttribute("kp", defaultValue: 1.0f);
}
//// Velocity actuator parameters.
// Velocity feedback gain.
[AbsoluteValue]
public float Kv = 1.0f;
public void VelocityToMjcf(XmlElement mjcf) {
mjcf.SetAttribute("kv", MjEngineTool.MakeLocaleInvariant($"{Math.Abs(Kv)}"));
}
public void VelocityFromMjcf(XmlElement mjcf) {
Kv = mjcf.GetFloatAttribute("kv", defaultValue: 1.0f);
}
//// Cylinder actuator parameters.
// Time constant of the activation dynamics.
public float CylinderTimeConst = 1.0f;
// Area of the cylinder. This is used internally as actuator gain.
[AbsoluteValue]
public float Area = 1.0f;
// Instead of area the user can specify diameter. If both are specified, diameter has
// precedence.
[AbsoluteValue]
public float Diameter = 0.0f;
// Bias parameters, copied internally into biasprm.
public float[] Bias = new float[] { 0.0f, 0.0f, 0.0f };
public void CylinderToMjcf(XmlElement mjcf) {
mjcf.SetAttribute("timeconst", MjEngineTool.MakeLocaleInvariant($"{CylinderTimeConst}"));
mjcf.SetAttribute("area", MjEngineTool.MakeLocaleInvariant($"{Math.Abs(Area)}"));
mjcf.SetAttribute("diameter", MjEngineTool.MakeLocaleInvariant($"{Math.Abs(Diameter)}"));
mjcf.SetAttribute("bias", MjEngineTool.ArrayToMjcf(Bias));
}
public void CylinderFromMjcf(XmlElement mjcf) {
CylinderTimeConst = mjcf.GetFloatAttribute("timeconst", defaultValue: 1.0f);
Area = mjcf.GetFloatAttribute("area", defaultValue: 1.0f);
Diameter = mjcf.GetFloatAttribute("diameter", defaultValue: 0.0f);
Bias = mjcf.GetFloatArrayAttribute("bias", defaultValue: new float[] { 0.0f, 0.0f, 0.0f });
}
//// Muscle actuator parameters.
// Time constants for activation and de-activation dynamics.
public Vector2 MuscleTimeConst = new Vector2(0.01f, 0.04f);
// Operating length range of the muscle, in units of L0.
public Vector2 Range = new Vector2(0.75f, 1.05f);
// Peak active force at rest. If this value is negative, the peak force is determined
// automatically using the scale attribute below.
public float Force = -1.0f;
// If the force attribute is negative, the peak active force for the muscle is set to this value
// divided by mjModel.actuator_acc0. The latter is the norm of the joint-space acceleration
// vector caused by unit force on the actuator's transmission in qpos0. In other words, scaling
// produces higher peak forces for muscles that pull more weight.
public float Scale = 200.0f;
// Lower position range of the normalized FLV curve, in units of L0.
public float LMin = 0.5f;
// Upper position range of the normalized FLV curve, in units of L0.
public float LMax = 1.6f;
// Shortening velocity at which muscle force drops to zero, in units of L0 per second.
public float VMax = 1.5f;
// Passive force generated at lmax, relative to the peak rest force.
public float FpMax = 1.3f;
// Active force generated at saturating lengthening velocity, relative to the peak rest force.
public float FvMax = 1.2f;
public void MuscleToMjcf(XmlElement mjcf) {
mjcf.SetAttribute("timeconst", MjEngineTool.MakeLocaleInvariant($"{MuscleTimeConst[0]} {MuscleTimeConst[1]}"));
mjcf.SetAttribute(
"range", MjEngineTool.MakeLocaleInvariant($"{MjEngineTool.GetSorted(Range).x} {MjEngineTool.GetSorted(Range).y}"));
mjcf.SetAttribute("force", MjEngineTool.MakeLocaleInvariant($"{Force}"));
mjcf.SetAttribute("scale", MjEngineTool.MakeLocaleInvariant($"{Scale}"));
mjcf.SetAttribute("lmin", MjEngineTool.MakeLocaleInvariant($"{LMin}"));
mjcf.SetAttribute("lmax", MjEngineTool.MakeLocaleInvariant($"{LMax}"));
mjcf.SetAttribute("vmax", MjEngineTool.MakeLocaleInvariant($"{VMax}"));
mjcf.SetAttribute("fpmax", MjEngineTool.MakeLocaleInvariant($"{FpMax}"));
mjcf.SetAttribute("fvmax", MjEngineTool.MakeLocaleInvariant($"{FvMax}"));
}
public void MuscleFromMjcf(XmlElement mjcf) {
MuscleTimeConst = mjcf.GetVector2Attribute(
"timeconst", defaultValue: new Vector2(0.01f, 0.04f));
Range = mjcf.GetVector2Attribute("range", defaultValue: new Vector2(0.75f, 1.05f));
Force = mjcf.GetFloatAttribute("force", defaultValue: -1.0f);
Scale = mjcf.GetFloatAttribute("scale", defaultValue: 200.0f);
LMin = mjcf.GetFloatAttribute("lmin", defaultValue: 0.5f);
LMax = mjcf.GetFloatAttribute("lmax", defaultValue: 1.6f);
VMax = mjcf.GetFloatAttribute("vmax", defaultValue: 1.5f);
FpMax = mjcf.GetFloatAttribute("fpmax", defaultValue: 1.3f);
FvMax = mjcf.GetFloatAttribute("fvmax", defaultValue: 1.2f);
}
}
public ActuatorType Type;
[Tooltip("Joint actuation target. Mutually exclusive with tendon target.")]
public MjBaseJoint Joint;
[Tooltip("Tendon actuation target. Mutually exclusive with joint target.")]
public MjBaseTendon Tendon;
[Tooltip("Parameters specific to each actuator type.")]
[HideInInspector]
public CustomParameters CustomParams = new CustomParameters();
[Tooltip("Parameters shared by all types of actuators.")]
public CommonParameters CommonParams = new CommonParameters();
[Tooltip("Actuator control.")]
public float Control;
// Actuator length.
public float Length { get; private set; }
// Actuator velocity.
public float Velocity { get; private set; }
// Actuator force.
public float Force { get; private set; }
public override MujocoLib.mjtObj ObjectType => MujocoLib.mjtObj.mjOBJ_ACTUATOR;
// Parse the component settings from an external Mjcf.
protected override void OnParseMjcf(XmlElement mjcf) {
if (!Enum.TryParse(mjcf.Name, ignoreCase: true, result: out Type)) {
throw new ArgumentException($"Unknown actuator type {mjcf.Name}.");
}
CommonParams.FromMjcf(mjcf);
switch (Type) {
case MjActuator.ActuatorType.General: {
CustomParams.GeneralFromMjcf(mjcf);
break;
}
case MjActuator.ActuatorType.Position: {
CustomParams.PositionFromMjcf(mjcf);
break;
}
case MjActuator.ActuatorType.Velocity: {
CustomParams.VelocityFromMjcf(mjcf);
break;
}
case MjActuator.ActuatorType.Cylinder: {
CustomParams.CylinderFromMjcf(mjcf);
break;
}
case MjActuator.ActuatorType.Muscle: {
CustomParams.MuscleFromMjcf(mjcf);
break;
}
}
Joint = mjcf.GetObjectReferenceAttribute<MjBaseJoint>("joint");
Tendon = mjcf.GetObjectReferenceAttribute<MjBaseTendon>("tendon");
}
// Generate implementation specific XML element.
protected override XmlElement OnGenerateMjcf(XmlDocument doc) {
if (Joint == null && Tendon == null) {
throw new InvalidOperationException($"Actuator {name} is not assigned a joint nor tendon.");
}
if (Joint != null && Tendon != null) {
throw new InvalidOperationException(
$"Actuator {name} can't have both a tendon and a joint target.");
}
var mjcf = doc.CreateElement(Type.ToString().ToLowerInvariant());
if (Joint != null) {
mjcf.SetAttribute("joint", Joint.MujocoName);
} else {
mjcf.SetAttribute("tendon", Tendon.MujocoName);
}
CommonParams.ToMjcf(mjcf);
switch (Type) {
case MjActuator.ActuatorType.General: {
CustomParams.GeneralToMjcf(mjcf);
break;
}
case MjActuator.ActuatorType.Position: {
CustomParams.PositionToMjcf(mjcf);
break;
}
case MjActuator.ActuatorType.Velocity: {
CustomParams.VelocityToMjcf(mjcf);
break;
}
case MjActuator.ActuatorType.Cylinder: {
CustomParams.CylinderToMjcf(mjcf);
break;
}
case MjActuator.ActuatorType.Muscle: {
CustomParams.MuscleToMjcf(mjcf);
break;
}
}
return mjcf;
}
// Synchronize the state of the component.
public override unsafe void OnSyncState(MujocoLib.mjData_* data) {
data->ctrl[MujocoId] = Control;
Length = (float)data->actuator_length[MujocoId];
Velocity = (float)data->actuator_velocity[MujocoId];
Force = (float)data->actuator_force[MujocoId];
}
public void OnValidate() {
if (Joint != null && Tendon != null) {
Debug.LogError(
$"Actuator {name} can't have both a tendon and a joint target.", this);
}
}
}
}
|