First of all thanks to the entire Unity Robotics team for this amazing package.
The purpose of this post is to highlight how to implement Unity’s new input system
# Current SetUp
- Windows 10
- Unity 2020.3.19f1
- URDF-Importer 0.5.2 GitHub - Unity-Technologies/URDF-Importer: URDF importer
# Let’s START
# Let’s solve a minor BUG in URDF-Importer 0.5.2
Issue:
Controller doesn’t highlight the link that it selects to be moved.
Context:
Checking my old project using URDF Importer V 0.5.0. I noticed that when importing the Robot from the URDF file the structure of the Game Objects in the herarchy had changed. This structure is critical because this is the way Unity understands your robot.
So, in some part the Controller
script uses this structure to highlight the link that is going to be moved.
How to solve it:
-
In the search bar of the project window search for Controller. Note: Change the search from “Assets” to “All”. Once found open it.
-
In the controller there is a Highlight function (line 88). In this function on line 102. All materials that are part of the selected link are stored to change its color.
Renderer[] rendererList = articulationChain[selectedIndex].transform.GetChild(0).GetComponentsInChildren<Renderer>();
As you can see it tries to take the component “Renderer” from the child 0, but in the new structure the child 0 is “Collision”, the correct way would be to take the materials from the child 1 “Visuals”.
So we will change to child 1 in:
- Line 102
- Line 165
- Line 179
Do not forget to save the changes, Now everything is working
# Now we are ready to go
Input Action
- Download the new input system.
Window → Package Manager → Packages: Unity Resgistry → Search: input system them Click en Install !
YES, the project will be restarted
- Create a new folder and name it “InputAction” then right click and at the bottom click InputAtions and name it “RobotController”.
- Double click on "RobotController. Create the control map for the robot and the actions in this case are two: SelectJoint and MoveJoint.
Do the same for MoveJoint, but in 2D Vector select right and left.
## Create the new Controller
In the folder “Scripts” create a new script and name it “RobotController”
using UnityEngine;
using Unity.Robotics;
using Unity.Robotics.UrdfImporter.Control;
using UrdfControlRobot = Unity.Robotics.UrdfImporter.Control;
using UnityEngine.InputSystem;
public class RobotController : MonoBehaviour
{
public enum RotationDirection { None = 0, Positive = 1, Negative = -1 };
public enum ControlType { PositionControl };
//Fields for inputs
[Header("Robot Control References")]
[SerializeField] private InputActionProperty moveJointInput;
[SerializeField] private InputActionProperty selectJointInput;
//Robot properties
private ArticulationBody[] articulationChain;
private Color[] prevColor;
private int previousIndex;
[InspectorReadOnly(hideInEditMode: true)]
public string selectedJoint;
[HideInInspector]
public int selectedIndex;
[Header("Robot properties")]
public ControlType control = ControlType.PositionControl;
public float stiffness;
public float damping;
public float forceLimit;
public float speed = 5f; // Units: degree/s
public float torque = 100f; // Units: Nm or N
public float acceleration = 5f;// Units: m/s^2 / degree/s^2
[Tooltip("Color to highlight the currently selected Join")]
public Color highLightColor = new Color(1, 0, 0, 1);
//The old controller
private Controller controller;
private void OnEnable()
{
this.gameObject.AddComponent(typeof(Controller));
controller = GetComponent<Controller>();
SetControllerValues();
}
void Start()
{
previousIndex = selectedIndex = 1;
this.gameObject.AddComponent<FKRobot>();
articulationChain = this.GetComponentsInChildren<ArticulationBody>();
int defDyanmicVal = 10;
foreach (ArticulationBody joint in articulationChain)
{
joint.gameObject.AddComponent<JointControl>();
joint.jointFriction = defDyanmicVal;
joint.angularDamping = defDyanmicVal;
ArticulationDrive currentDrive = joint.xDrive;
currentDrive.forceLimit = forceLimit;
joint.xDrive = currentDrive;
}
DisplaySelectedJoint(selectedIndex);
StoreJointColors(selectedIndex);
//Enable to read new inputs
selectJointInput.action.Enable();
moveJointInput.action.Enable();
}
private void Update()
{
SetSelectedJointIndex(selectedIndex);
UpdateDirection(selectedIndex);
//If one key has been triggered to selectJointInput
if (selectJointInput.action.triggered)
{
//Read the value: Up = (0.0, 1.0) // Down = (0.0, -1.0)
Vector2 inputValue = selectJointInput.action.ReadValue<Vector2>();
if (inputValue.y > 0)
{
SetSelectedJointIndex(selectedIndex + 1);
Highlight(selectedIndex);
}
else
{
SetSelectedJointIndex(selectedIndex - 1);
Highlight(selectedIndex);
}
}
UpdateDirection(selectedIndex);
}
private void SetSelectedJointIndex(int index)
{
if (articulationChain.Length > 0)
{
selectedIndex = (index + articulationChain.Length) % articulationChain.Length;
}
}
private void Highlight(int selectedIndex)
{
if (selectedIndex == previousIndex || selectedIndex < 0 || selectedIndex >= articulationChain.Length)
{
return;
}
ResetJointColors(previousIndex);
StoreJointColors(selectedIndex);
DisplaySelectedJoint(selectedIndex);
Renderer[] rendererList = articulationChain[selectedIndex].transform.GetChild(1).GetComponentsInChildren<Renderer>();
foreach (var mesh in rendererList)
{
MaterialExtensions.SetMaterialColor(mesh.material, highLightColor);
}
}
private void UpdateDirection(int jointIndex)
{
if (jointIndex < 0 || jointIndex >= articulationChain.Length)
return;
//Read the current value from moveJointInput
Vector2 inputValue = moveJointInput.action.ReadValue<Vector2>();
JointControl current = articulationChain[jointIndex].GetComponent<JointControl>();
if (previousIndex != jointIndex)
{
JointControl previous = articulationChain[previousIndex].GetComponent<JointControl>();
previous.direction = UrdfControlRobot.RotationDirection.None;
previousIndex = jointIndex;
}
if (current.controltype != UrdfControlRobot.ControlType.PositionControl)
{
UpdateControlType(current);
}
// Move Positive = (1.0, 0.0)
if (inputValue.x > 0)
{
current.direction = UrdfControlRobot.RotationDirection.Positive;
Debug.Log(inputValue);
}
// Move Negative = (1.0, 0.0)
else if (inputValue.x < 0)
{
Debug.Log(inputValue);
current.direction = UrdfControlRobot.RotationDirection.Negative;
}
else
{
current.direction = UrdfControlRobot.RotationDirection.None;
}
}
private void StoreJointColors(int index)
{
Renderer[] materialLists = articulationChain[index].transform.GetChild(1).GetComponentsInChildren<Renderer>();
prevColor = new Color[materialLists.Length];
for (int counter = 0; counter < materialLists.Length; counter++)
{
prevColor[counter] = MaterialExtensions.GetMaterialColor(materialLists[counter]);
}
}
private void ResetJointColors(int index)
{
Renderer[] previousRendererList = articulationChain[index].transform.GetChild(1).GetComponentsInChildren<Renderer>();
for (int counter = 0; counter < previousRendererList.Length; counter++)
{
MaterialExtensions.SetMaterialColor(previousRendererList[counter].material, prevColor[counter]);
}
}
void DisplaySelectedJoint(int selectedIndex)
{
if (selectedIndex < 0 || selectedIndex >= articulationChain.Length)
{
return;
}
selectedJoint = articulationChain[selectedIndex].name + " (" + selectedIndex + ")";
}
public void UpdateControlType(JointControl joint)
{
joint.controltype = UrdfControlRobot.ControlType.PositionControl;
if (control == ControlType.PositionControl)
{
ArticulationDrive drive = joint.joint.xDrive;
drive.stiffness = stiffness;
drive.damping = damping;
joint.joint.xDrive = drive;
}
}
private void SetControllerValues()
{
controller.stiffness = stiffness;
controller.damping = damping;
controller.forceLimit = forceLimit;
controller.speed = speed;
controller.torque = torque;
controller.acceleration = acceleration;
}
private void FixedUpdate()
{
SetControllerValues();
}
}
NOTE
If you notice in OnEnable, I am getting the old control, and then I am assigning the properties in SetControllerValues() and referencing the values in FixedUpdate(). I do this because within the package there are other scripts that use the Controller so to avoid breaking it was the quickest solution.
Now to avoid further conflicts, we are going to modify the old controller.
using System;
using Unity.Robotics;
using UnityEngine;
namespace Unity.Robotics.UrdfImporter.Control
{
public enum RotationDirection { None = 0, Positive = 1, Negative = -1 };
public enum ControlType { PositionControl };
public class Controller : MonoBehaviour
{
[HideInInspector]
public ControlType control = ControlType.PositionControl;
[HideInInspector]
public float stiffness;
[HideInInspector]
public float damping;
[HideInInspector]
public float forceLimit;
[HideInInspector]
public float speed = 5f; // Units: degree/s
[HideInInspector]
public float torque = 100f; // Units: Nm or N
[HideInInspector]
public float acceleration = 5f;// Units: m/s^2 / degree/s^2
[HideInInspector]
public void UpdateControlType(JointControl joint)
{
joint.controltype = control;
if (control == ControlType.PositionControl)
{
ArticulationDrive drive = joint.joint.xDrive;
drive.stiffness = stiffness;
drive.damping = damping;
joint.joint.xDrive = drive;
}
}
}
}
I have removed almost everything, I have only kept the properties, I do this because as I said before scripts like JointController use Controller to get the properties.
(I know a bit dirty).
Delete the old controller, add the new one and reference the fields.
It’s done, now you can use your robot.
If you have any questions I am happy to answer
Greetings,