Space Engineers

Space Engineers

226 ratings
MArmOS User guide, examples and tutorials
By Timotei~
A guide to explain how to use MArmOS with documentation, tutorials and examples.
2
2
2
   
Award
Favorite
Favorited
Unfavorite
Presentation
MArmOS means Mechanical Arm Operating System and it is a script made to simplify the coding needed to properly control your mechanical arm. It allow to take any complex arm configuration and bind it on a ( X, Y, Z ) reference frame. Using MArmOS should make it way easier to control ground based machines like mobile mining rig, welding arms or cranes.​

Basic concept
MArmOS work by using a user-written expression I call "arm definition".
These definitions are used to represent the physical shape of your arm. (Like some kind of virtual model)
Those definitions are made out of elements called Hardwares.
An hardware can be a Solid shape, a Rotors Joint or a Pistons Joint.
When assembled properly, all these hardwares will allow MArmOS to take control of them and solve all the complex kinematic calculations.

The Script can be found here
Video tutorial
This video describe step by step how to build a simple 4 DOF piston based connector crane using MArmOS.
Working examples
Here are some examples of contraptions using MArmOS.
The Welding Crane
The Boom Truck
The Large Excavator
The Turrets
The ArmConfiguration() function
The ArmConfiguration() function is where you need to configure your contraption.
This function is called once when the script is compiled or when the game is loaded.
Arm Configuration
MArmOS works by using a user written configuration that represent the mechanical composition of a contraption.

These expression are made out of Harwares.
The idea is to look at your contraption and figure out how to translate it into Hardwares.
Tutorial: Basic definitions
The basic setup only require you to define your hardware in order.
It is perfect for fast and easy general use configurations.
Note that the basic mode is sensible to the order in witch you declare your hardware, it will not consider parallelism and it can only manage a single arm.

Step by step example
Step 1
Build your arm and set a unique names for all of it's components.

Step 2
declare all components in order. MArmOS will automaticaly assemble a default arm with these.
e.g.
new Rotor( Name: "Motor1", Axis: "Z", OriMode: 0 ); new Piston( Name: "Piston1", Axis: "X" ); new SolidLG( 3, 0, 0 );
In this example, a DefaultArm is automatically created.

Step 3
Defining the controller. It is an optional step because if none is defined, a default one will automatically be created with default values.
e.g.
new UserControl( Arm: DefaultArm );
Check the Controller class section for more details.

Step 4
Rename a seat so it contains the ArmControllerKeyword it it's name. The default ArmControllerKeyword is "Arm Controller".
Axis tips
When defining a Part, you will need to find the right axis. Here is how to do so.
The axis system in MArmOS is following the right hand rule. The "X" is forward, "Y" is left and "Z" is up.


When defining axis for rotation, the right hand rule still apply. "X" is roll clockwise, "Y" is pitch downward and "Z" is yaw right to left.









If you want to reverse an axis, you just need to add a -. "X" become "-X", "Y" become "-Y" and "Z" become "-Z".


Rotors and pistons
When working with rotors, the axis of rotation is in the direction the rotor head is facing.


Pistons uses the same logic.










Finally, to avoid making axis mistakes, Make sure that your arm is at it's Home position. Use the "GoHome" command for that.
Tutorial: Advanced definitions
Assembling your arm manually in MArmOS is just like a mathematical equation using " + ", " * " and " () ".
The " + " operator is used to represent two hardware attached in series. "end to end"
The " * " operator is used to represent two hardware attached in parallel. "side by side, working together"
Don't forget that, like in math, order of operations is a thing. "2+2*3 = 8, not 12 ... "
Because of that, you may want to use parenthesis in some cases. "(2+2)*3 = 12 yay!"

Here is a step-by-step example:
Step 1
Build your arm.

Step 2
Declare local variables for each components of your arm (including solid pieces).

Step 3
Assemble all your pieces together like a mathematical equation using " + ", " * " and " () ".

Step 4
Define your controller and tell it to control your arm instead of the default one. Check the Controller class section for more details.

Step 5
Rename a seat so it contains the ArmControllerKeyword it it's name. The default ArmControllerKeyword is "Arm Controller".
Class Map
Here is a map showing how MArmOS is structured.
Class in [] are planed but not done yet. edit: all classes in the map are done now.

Class to use
The normal user should only need to use the following class:
  • Rotor
  • Piston
  • SolidLG
  • SolidSG
  • Hydraulic
  • UserControl
All other classes are for MArmOS's inner structure.
The UserControl
The UserControl class is the main controller in MArmOS. It is also the default controller. Its role is to convert all keyboard, mouse and arguments into Movement input for the arm.

Constructor:
new UserControl( Hardware Arm , bool StartOn , String Name , double Speed , double YawSpeed , double PitchSpeed , double RollSpeed , double Softness , bool ReadKeyboard , bool ReadMouse , String ShipControllerKeyword , bool UseArmAsReference , Hardware ReferenceFrame , Limits Workzone );

Public variables:
(r/w) bool OnOff
(r/w) double YawSpeed
(r/w) double PitchSpeed
(r/w) double RollSpeed
(r/w) bool ReadKeyboard
(r/w) bool ReadMouse
(r/w) String ShipControllerKeyword
(r/w) Hardware ReferenceFrame
(r/w) cPose ConstantMovement
(r/w) cPose Target

Public methods:
void ExecuteCommand( String Argument )
void Update( String Argument )
void GoHome()
void SetHome()

Default settings:
When no controllers are defined on the arm configuration, a UserControl controller is automatically created with default values.
Those default values can be set in the Global settings section on page 9 of the script.

Details:
OnOff
When false, the Controller will actively keep the arm from moving.
YawSpeed
How fast you want to orient the arm on Yaw.
PitchSpeed
How fast you want to orient the arm on Pitch.
RollSpeed
How fast you want to orient the arm on Roll.
ReadKeyboard
If true, the controler will read the W, A, S, D, C and "spacebar" keys. This can depend on your game settings.
ReadMouse
If true, the controler will read the mouse input plus the Q and E keys. This can depend on your game settings.
ShipControllerKeyword
A keyword that your cockpit/remote control must contain for MArmOS to read its inputs.
ReferenceFrame
The reference frame is what allow to have relative controls. You can set any Hardware object.
Using the UseArmAsReference parameter override this one.
UseArmAsReference
A shortcut to automatically set the ReferenceFrame to Arm.
Using this parameter override this ReferenceFrame parameter.
ConstantMovement
A constant movement command that overlap the other inputs.
It can be set directly or by using the "Move" argument.
Target
A target position. When set, the Arm will start moving toward it. If a keyboard or mouse input is detected, it get disabled.
It can be set directly or by using the "MoveTo" argument.
StartOn
Whether or not the controller should start activated after compilation. If false, you'll need to activate it manually.
ExecuteCommand
This method take a String containing one command.
If the command is valid, it will execute it. The name of the controller is not necessary here.
The Rotor
The Rotor class is a child of the Rotary Joint class. It inherit all of it's public variables.
It is a low level interface allowing Rotary to control a rotor ingame.





Constructor:
new Rotor( String Name , String Axis , double OriMode , bool Override , double MaxSpeed , double Offset // (deg) , double SoftMaxLimit , double SoftMinLimit , double Home // (deg) );

Usage example:
new Rotor( Name: "Motor", Axis: "Y" );

Public variables:
(R/W) String Name
(R/W) bool Override
(R/W) double SoftMinLimit
(R/W) double SoftMaxLimit
(R/W) double Offset // (rad)


Details:
Name
The name of the rotor you want to control. MArmOS will search the grid for that rotor every seconds to be sure it still exist.
Override
Whether or not the rotor should follow MArmOS's order. Set to true for manual control. The default value is false.
Offset
Allow to correct misalignment. The default value is 0.
SoftMaxLimit
Slow down the rotor before hitting its maximum angular limit. The default value is 1. higher is smoother.
SoftMinLimit
Same as SoftMaxLimit but for Min.
Missing info?
Refer to the Rotary Joint class section for higher level details.
The Piston
The Piston class is a child of the Linear class. It inherit all of it's public variables.
It is only an interface allowing Linear to control a piston ingame.
Note that a Piston object will only represent the extension of the piston. The unextended physical shape of the piston is not taken into account here.

Constructor:
new Piston( , String Axis , bool Override , double MaxSpeed // (m/s) , double SoftMaxLimit , double SoftMinLimit , double Home // (m) );

Usage example:
new Piston( Name: "Piston" , Axis: "X" );

Public variables:
(r/w) String Name
(r/w) double SoftMinLimit
(r/w) double SoftMaxLimit

Details:
Name
The Name parameter refer to the name of the piston you want to control.
SoftMaxLimit
SoftMaxLimit slow down the piston before hitting its maximum limit. The default value is 1. Higher is smoother.
SoftMinLimit
The same as SoftMaxLimit but for Min.
Missing info?
Refer to the Linear class section for higher level details.
The RotorWheel
The RotorWheel class is a child of the Linear class. It inherit all of it's public variables.
It is an interface allowing linear movements using a rotor and a wheel.
Note that a RotorWheel can only show a limited fidelity when it comes to show it's position. Slipping and others external movements can't be seen by the RotorWheel.

Constructor:
new RotorWheel( , String Axis , Rotary Wheel , double Radius // (m) , int Direction );
Usage example:
var MyRotor = new Rotor( Name: "Motor", Axis: "Y" ); var MyRotorWheel = new RotorWheel( Wheel: MyRotor, Axis: "X", Radius: 1.25*SG );
Public variables:
(r/w) Rotary Wheel
(r/w) double Radius // (m)
(r/w) int Direction

Details:
Wheel
The wheel you want to control. It is usually a Rotor but can be anything from the Rotary family.
Radius
Radius of the wheel in meter.
Direction
Corrective factor to help with direction of rotation
Missing info?
The SolidLG
The SolidLG class is a child of the Solid class.
Its only purpose is to declare a Solid using large grid dimensions. You can as well use the Solid constructor and multiply the number of blocks by 2.5

Constructor:
new SolidLG( double X // (large grid block) double Y // (large grid block) double Z // (large grid block) );

Public variables:
SolidLG does not expose any public variables.

Details:
Refer to the Solid class section for other details.
The SolidSG
The SolidSG class is a child of the Solid class.
Its only purpose is to declare a Solid using small grid dimensions. You can as well use the Solid constructor and multiply the number of blocks by 0.5

Constructor
new SolidSG( double X // (small grid block) double Y // (small grid block) double Z // (small grid block) );

Public variables
SolidSG does not expose any public variables.

Missing info?
Refer to the Solid class section for higher level details.
The Hydraulic
The Hydraulic class is a child of the Rotary class. It inherit all of it's public variables.
It is a semi-low level interface allowing Rotary to control a Hydraulic settup ingame.
Note that when using Hydraulic, it is imperative to use the advanced definition. This is because the actuating bit is Seen by MArmOS as a fully indpendent arm to which the Hydraulic object is sending commands.
It can be hard to define an Hydraulic settup properly. I recomend getting used to simpler aspect of MArmOS before trying it.

Constructor:
new Hydraulic( String Axis , double OriMode , Hardware Actuator , double MaxSpeed // (rpm) , double Tangent1 // (m) , double Normal1 // (m) , double Tangent2 // (m) , double Normal2 // (m) , bool Invert , double Home // (°) );

Public variables:
(r/w) Hardware Actuator;

Details:
Actuator
The actuating part (usually pistons but could be anything)
Basically MArmOS see it as an arm to which the Hydraulic object send velocity commands along the "X" axis.
The only movement considered in the calculations is the one along the "X" Axis. Hydraulic doesn't care about anything else.
Tangent1
Tangent distance between the pivot and the "Tail" of the actuator. See picture.
Normal1
Normal distance between the pivot and the "Tail" of the actuator. See picture.
Tangent2
Tangent distance between the pivot and the "Head" of the actuator. See picture.
Normal2
Normal distance between the pivot and the "Head" of the actuator. See picture.
Invert
Value to adjust the direction of rotation if needed.
Missing info?
Refer to the Rotary class section for higher level details.
The Rotary Joint class
The Rotary class is the virtual parent of all kind of rotating joints such as Rotors and Hydraulics.
It is the high level class that does all calculations involved by rotational kinematics.
It is also a child of the Hardware class and, as such, inherit all of it's public variables.

Constructor:
The normal user should never need to directly call the Rotary constructor but someone wanting to make a new child class could need it.
new Rotary( String Axis , double OriMode , double MaxSpeed // (rpm) , double Home // (°) );

Public variables:[/b]
(R/W) double MaxSpeed
(R/W) double OriMode
(R ) double Angle // (rad)
(R/W) double dangle // (rad/ds)
(R/W) double Home // (rad)

Details:
Axis
The axis, around which, the joint will rotate. See the "Axis tips" section for details.
OriMode <-- To control with mouse
The OriMode parameter is a value between 0 and 1 that specify if the joint should favour the orientation of the end effector or it's position.
A Joint with OriMode set to 1 will follow the orientation inputs (usually given by the mouse) while a OriMode of 0 will prioritise position. Something in between will give a mix of both.
MaxSpeed
The Maximum speed the Joint should rotate.
Angle
The current angle. As seen by MArmOS. In (rad)
dAngle
The angle derivation. Basically, Speed measured in (rad/ds). Divide by "dt" to get angular speed in (rad/s)
Home
The Home position. The joint will move to that position if the GoHome command is used.
Missing info?
Refer to the Hardware class section for higher level details.
The Linear Joint class
The Linear class is the virtual parent of all kind of linear joints such as pistons.
It is the high level class that calculate the linear kinematic.
It is also a child of the Hardware class and, as such, inherits all of its public variables.

Constructor:
The normal user should never need to directly call the Linear constructor but someone wanting to make a new child class could need it.
new Linear( String Axis , double MaxSpeed , double Home );

Public variables:
(R/W) double MaxSpeed
(R ) double Length // (m)
(R/W) double dLength // (m/ds)
(R/W) double Home // (rad)

Details:
Axis
The axis along which the linear joint will slide.
MaxSpeed
The maximum speed the joint will move.
Length
The current Length of the Joint.
dLength
The current Length derivation of the Joint.
Home
The Home position. The joint will move to that position if the GoHome command is used.
Missing info?
Refer to the Hardware class section for higher level details.
The Solid class
The Solid class is the parent of all kind of Solid shapes.
It basically is an unmoving piece.
It is also a child of the Hardware class and, as such, inherits all of its public variables.
Look at the tips bellow for usage details.

Constructor
There are child constructors for Small and Large grids but you can as well use this one. It use meters instead of blocks.
new Solid( double X // (m) , double Y // (m) , double Z // (m) );

Public variables
Solid does not expose any public variables.

Details
X
The length in meter along the "X" axis. See the Axis tips section for details.

Y
The length in meter along the "Y" axis.

Z
The length in meter along the "Z" axis.

Tips
You can see Solids as if your arm was some kind of skeleton made of joints and bones. The Solids are the bones linking each joints.

A solid part is basically the "3D distance" between the center of each Joints.
As an example, to define two rotors reparated by 3 large blocks(7.5m), you would actually need a 10m long Solid (4 blocks). See some examples.

Here is another example with a Piston. Pistons can be completely ignored since they have no rotary contribution.








Here is a special case of Rotor along the same axis as the arm. This example shows how axis of rotations of rotors are affecting Solids. It shows that while the ends of a Solid can move along the axis of a Rotor it must never cross it.








Here is how it should be done. Both pictures shows the same settup with two valid approaches. Both are correct and give the same result.











Missing info?
Refer to the Hardware class section for higher level details.
The Hardware class
this part of the guide is not done yet.
The Controller class
The Controller class is parent of all controllers. It is the object that receive the movement inputs and make sure that the arm is properly folowing them.

Constructor:
The normal user should never need to directly call the Controller constructor but someone wanting to make a new child class could need it. Look for UserControl instead.
new Controller( Hardware Arm , String Name , double Speed , double Softness , bool StartOn );

Public variables:
(r/w) String Name
(r/w) double Softness
(r/w) double Speed
(r/w) bool GoingHome
(r/w) Hardware Arm

Public methods:
void Update( String Argument )
void GoHome()

Details:
Hardware
The arm that will be controlled.
Name
The name of the controller. It is used to diferenciate controllers when sending argument commands.
Speed
The maximum speed your arm should go in (m/s)
Softness
How soft you want the controls
GoingHome
If true, every joints on the arm will move to their Home position.
It is set to false if a nonzero input is detected.
Update
This is the method that run the controller. MArmOS automatically run it every cycles.
GoHome
This method set the GoingHome variable to true.
The UserControl list of commands
ReadKeyboard
Synopsis: ReadKeyboard On/Off/Toggle
Shortcut: -RK 1/0/-1
Description: Tell the controller to read keyboard inputs or not.
ReadMouse
Synopsis: ReadMouse On/Off/Toggle
Shortcut: -RM 1/0/-1
Description: Tell the controller to read mouse inputs or not.
Move
Synopsis: Move X Y Z [Yaw] [Pitch] [Roll]
Shortcut: -M X Y Z [Yaw] [Pitch] [Roll]
Description: Follow a linear motion forever.
MoveTo
Synopsis: MoveTo X Y Z [Yaw] [Pitch] [Roll]
Shortcut: -MT X Y Z [Yaw] [Pitch] [Roll]
Description: Follow a linear motion toward a specific point then stop.
Tip: You can use the PrintPosition command to get the actual position of your arm in a nice MoveTo format. This can be usefull to do sequences.
RelMove
Synopsis: RelMove X Y Z [Yaw] [Pitch] [Roll]
Shortcut: -RelM X Y Z [Yaw] [Pitch] [Roll]
Description: Follow a linear motion relative to the Reference Frame.
PrintPosition
Synopsis: PrintPosition [TextPanelName]
Shortcut: -PP TextPanelName
Description: Print the current position of the arm into a text panel. The printed format is the same as a MoveTo command to make automation easier.
ClearPanel
Synopsis: ClearPanel TextPanelName
Shortcut: -CP TextPanelName
Description: Clear the content of a text panel.
Speed
Synopsis: Speed Value
Shortcut: -S Value
Description: Set the Speed parameter to a specified Value.
Softness
Synopsis: Softness Value
Shortcut: -SO Value
Description: Set the Softness parameter to a specified Value.
GoHome
Synopsis: GoHome Speed
Shortcut: -GH Speed
Description: Tell all joints to go to their respective Home position. The Speed argument allow an approximated speed control of the movement.
SetHome
Synopsis: SetHome
SetPartHome
Synopsis: SetPartHome Value PartName
Description: Temporaly set the current Home of a rotor or a piston to a specific value. This can be useful when you want to do joint based movements.
Override
Synopsis: Override On/Off/Toggle PartName
Shortcut: -O 1/0/-1 PartName
Description: Set the current Override status of a rotor or a piston. This can be useful when you want to manually control a joint.
Ingame Debug tools
There is three tools allowing to display diferent levels of information on text panels.
  • Echo Panel
  • Log Panel
  • Debug Panel

Echo Panel:
The Echo Panel allow to display the exact same information as in the PB terminal.
This can be usefull when you want to get general informations without having to open the terminal.
The default Echo Panel name is: "MArmOS Echo"
To write messages in the Echo Panel, use the MyEcho( Text ); function.

Log Panel:
The Log Panel is a log of the last 40 events that has happened.
You can get Loading status, warning messages, argument interpretation and error messages displayed on that screen.
The default Log Panel name is: "MArmOS Log"
To write messages in the Log Panel, use the MyLog( Text ); function.

Debug Panel:
The Debug Panel is my personnal tool to chase bugs in MArmOS. What it display is always subject to changes.
To write messages in the Debug Panel, use the MyDebug( Text ); function.
282 Comments
catmousedog May 4 @ 11:17am 
When the home position of a hinge is at 45 degrees and there is a piston on top. Is that piston going in the Z or X direction? What are you supposed to do in this situation?
BustyCatbot Apr 8 @ 10:00pm 
I have no idea what's going on with mine, but it's not even keeping track of it's own position correctly, I basically picks a completely random origin point to where 0,0,0 can mean something completely different from moment to moment.
Radio Active Jan 19 @ 11:29am 
how to set the Reference to the Controlling cockpit?
Oleg Jan 12 @ 12:51am 
Do not use two "Solid" in a row. This is not at all obvious. This information should be added to the manual in bold type. Or fix it.
jackik Dec 31, 2023 @ 11:26am 
you're talking about the arm reference, aren't you?
Look at the UserControl description, especially UseArmAsReference and ReferenceFrame
Doctor WHO Dec 31, 2023 @ 3:03am 
Is there a way to change or set the 0/Origin point or point of reference to the tool tip, like a real life robot arm set it up in tool mode??
Narsil Dec 19, 2023 @ 11:39am 
I got it working with 2 programable blocks by using ShipControllerKeyword in my usercontrols, instead of defaultshipcontrollerkeyword in the arm config
Narsil Dec 19, 2023 @ 11:28am 
Can one programable block running MARMOS script control two arms separately, each with their own cockpit? I have 2 programable block, two cockpits, two arms on the same grid. And even if I change the arm controller keyword, both cockpits control both arms. Work around for now is to turn off the programable block for the arm I don't want to move. Any help appriciated :D
Shonnas Nov 23, 2023 @ 2:26am 
the pre-made ones downloaded have stopped working for me ? Anyone else got this issue ?
Rykarn Nov 15, 2023 @ 9:45pm 
okay hang on, we are supposed to add a solid for each member even if that member is entirely composed of actuating parts?
is that correct?
so every time we add a piston we add
new Piston(Name: "Crane piston upper",Axis:"Y", Home: 0);
new SolidSG(0, 2,0);
that doesnt seem right but it seems correct based on the example in the tips section?