visose / Robots

Create and simulate ABB, KUKA, UR, and Staubli robot programs.
MIT License
306 stars 127 forks source link

Robots plugin on C Sharp editor in Rhino #36

Closed petrasvestartas closed 4 years ago

petrasvestartas commented 4 years ago

Hi,

I have an issue, when trying to create a valid tool-path, I need to rotate manually planes 90 degrees until I reach a good rotation that is reachable. I would like to automate this process, because manually guessing is very slow.

Is there any ready-made examples for C Sharp I could use to mimic similar workflow to this?

RobotsPLugin

If yes where can I get ABB library? ABB

visose commented 4 years ago

I assume this issue is because you are using some tool that has 5 degrees of freedom (the rotation around the normal of the tool doesn't matter), and you're trying to search for solutions that are reachable given the same normal direction and position.

You can do this in a number of ways, depending on how the tool is mounted. Assuming the normal is perpendicular to the flange, rotate the tool around the normal depending on it position so that the 5th is extended (use the arm orientation as a vector guide to align the plane).

You could also use iterative approaches, incrementally rotating until you don't get an error. To do this faster, if its only on certain parts, use the kinematics component rather than the full create program component. If you use this be careful not to select solutions as soon as they don't give an error, have some margin, as the real robot might not not be exactly the same. You might want to make some small calibration adjustments without having to create a new program, also things like absolute accuracy will have slightly different joint positions as in the simulation.

Regarding the ABB library, you can find it here: https://developercenter.robotstudio.com/pc-sdk

petrasvestartas commented 4 years ago

Yes It is exactly this, because I am just milling and using saw-blade for cutting process.

Do you have examples what are explaining considering rotations?

I use Robots plugin for simulation, for real movement I need to export .txt file to our ABB robot, because the company installed specific software package considering robot controller. But the simulation is almost the same, and without robots it would be really painful to do anything.

I am somehow stuck in this grasshopper interface and seeking for your help to setup iterative approach for plane rotation. It would be a huge help, because each tool-path takes a day to setup.

visose commented 4 years ago

Not that I can quickly post here, but it's not complicated and you are pretty good and Grasshopper and C#, I'm sure you will figure it out.

A really simple variation of option 1 is, assuming the robot base is in the world origin, connect an align plane component after the target planes and use the position of the target as the alignment vector (the X axis), you might have to rotate the tool 90/-90/180 degrees after this.

petrasvestartas commented 4 years ago

It is not a hurry, but do you think that you could help to set it up correctly?

Also I do not know how to reference ABB .dll correctly, it seems the project is a bit different that I use for grasshopper components: Relink

visose commented 4 years ago

Right click where it says 'dependencies' then 'add project reference' then 'browse'.

petrasvestartas commented 4 years ago

What I am missing: Ref

visose commented 4 years ago

Did you try removing it and adding it again?

petrasvestartas commented 4 years ago

I am trying to write this iterative solver after painful 2 weeks milling and saw-blading with the robot, I realized that I cannot rotate planes manually...

I would like to ask how can I create targets in C# ?

I see that input of the program component takes Targets as input list, but then it is somehow casted to toolpath. How can I manually write the target and append to GH_Toolpath list?

I am not very familiar with abstract classes.

        var toolpathsA = new List<GH_Toolpath>(toolpathsA_.DataCount);
        for (int i = 0; i < toolpathsA_.Branches.Count; i++)
        {
               // I would like to create Target here but do not know how
            GH_Toolpath defaultToolpath = new GH_Toolpath(new IToolpath());

        }

Toolpath

Toolpath1

petrasvestartas commented 4 years ago

I tried this but also does not work

        var toolpathsA = new List<GH_Toolpath>(toolpathsA_.DataCount);
        for (int i = 0; i < toolpathsA_.Branches.Count; i++)
        {
            GH_Target sourceTarget = null;
            sourceTarget.Value = new JointTarget(new double[] { 0,Math.PI*0.5,0,0,0,0 }, tool.Value);

            //GH_Toolpath defaultToolpath = new GH_Toolpath(new IToolpath());
        }
petrasvestartas commented 4 years ago

I found one way, but I do not know if it is right why targets have to be casted to a toolpath?

        var toolpathsA = new List<GH_Toolpath>(toolpathsA_.DataCount);
        for (int i = 0; i < toolpathsA_.Branches.Count; i++)
        {

            JointTarget jointTarget = new JointTarget(new double[] { 0, Math.PI * 0.5, 0, 0, 0, 0 }, tool.Value);

            SimpleToolpath itoolpath = new SimpleToolpath();
            itoolpath.Add(jointTarget);
            GH_Toolpath toolpath = new GH_Toolpath(itoolpath);// (jointTargetGH.Value);

            toolpathsA.Add(toolpath);

        }
visose commented 4 years ago

See here for a minimal robot program written in C#: https://github.com/visose/Robots/blob/master/RobotsStandalone/TestProgram.cs

The Grasshopper component "Create Program" accepts a list of objects that implement IToolpath. This interface requires the implementing type to return a list of targets (an IEnumerable). The Create Program component then joins all these toolpaths together into a single one.

You can connect a list of targets directly to Create Program because the Target class implements IToolpath. Each target returns an IEnumerable with a single target (itself).

petrasvestartas commented 4 years ago

Hi,

I think I got it, thank you, what might take some much time to compute these simple planes? I am running this code 2 times and it takes one second. There are 30 planes at one iteration. Planes

Currently the code is just this:

  protected override void SolveInstance(IGH_DataAccess DA)
        {
        //https://github.com/visose/Robots/blob/master/RobotsStandalone/TestProgram.cs
        string name = null;
        GH_RobotSystem robotSystem = null;
        var initCommandsGH = new List<GH_Command>();
        var toolpathsA_ = new GH_Structure<GH_Plane>();
        var toolpathsB = new List<GH_Toolpath>();
        var multiFileIndices = new List<int>();
        double stepSize = 1;
        GH_Tool tool = null;
        var toolpathsA_Type = new GH_Structure<GH_String>();

        if (!DA.GetData(0, ref name)) { return; }
        if (!DA.GetData(1, ref robotSystem)) { robotSystem = new GH_RobotSystem(RobotSystem.Load("IBOIS-IRB6400B", new Rhino.Geometry.Plane(new Point3d(0, 0, 517), Vector3d.XAxis, Vector3d.YAxis))); }
        if (!DA.GetDataTree(2, out toolpathsA_)) { return; }
        DA.GetDataList(3, toolpathsB);
        DA.GetDataList(4, initCommandsGH);
        DA.GetDataList(5, multiFileIndices);
        if (!DA.GetData(6, ref stepSize)) { return; }
        if (!DA.GetData(8, ref tool)) { return; }
        if (!DA.GetDataTree(7, out toolpathsA_Type)) { return; }
        if (toolpathsA_.DataCount == 0 || toolpathsA_.DataCount != toolpathsA_Type.DataCount) return;
        RobotConfigurations? configuration = null;

        ///////////////////////////////////////////
        //Cast planes to targets
        //Simple Tool Path contains targets, and toolpaths contains many toolpaths
        ///////////////////////////////////////////

        var oneToolPath = new SimpleToolpath();

        for (int i = 0; i < toolpathsA_.Branches.Count; i++)   {

            var oneToolPathCurrent = new SimpleToolpath();

            ////////////////////////////////////////////////////////////////////////
            //Default plane that goes to 0
            JointTarget jointTarget = new JointTarget(new double[] { 0, Math.PI * 0.5, 0, 0, 0, 0 }, tool.Value);
            oneToolPathCurrent.Add(jointTarget);
            ////////////////////////////////////

            ////////////////////////////////////////////////////////////////////////
            //Plane Target
            for (int j = 0; j < toolpathsA_.get_Branch(i).Count; j++)   {
                //Input planes
                var JointType = toolpathsA_Type[i][j].Value == "Joint" ? Motions.Joint : Motions.Linear;
                CartesianTarget cartesianTarget = new CartesianTarget(toolpathsA_[i][j].Value, configuration, JointType, tool.Value, Speed.Default, Zone.Default);
                oneToolPathCurrent.Add(cartesianTarget);
            }
            ////////////////////////////////////////////////////////////////////////

            ////////////////////////////////////////////////////////////////////////
            //Run Current Branch program
            var allToolPathCurrent = new List<IToolpath>() { oneToolPathCurrent };
            var programCurrent = new Program(name, robotSystem.Value, allToolPathCurrent, null, multiFileIndices, stepSize);
            //////////////////////////////////////

            ////////////////////////////////////////////////////////////////////////
            //If there are no errors add them to final target list
            if (programCurrent.Errors.Count == 0)
            foreach (var t in oneToolPathCurrent)
                   oneToolPath.Add(t);
            else
            {

            }
            ////////////////////////////////////////////////////////////////////////

        }//for i

        var allToolpaths = new List<IToolpath>() { oneToolPath };
        var program = new Program(name, robotSystem.Value, allToolpaths, null, multiFileIndices, stepSize);
        DA.SetData(0, new GH_Program(program));

        ///////////////////////////////////////////////////////////////////////////////
        //Create Errors
        ///////////////////////////////////////////////////////////////////////////////
        if (program.Errors.Count > 0)
        {
            DA.SetDataList(4, program.Errors);
            this.AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Errors in program");
        }

    }
}
visose commented 4 years ago

The plugin is not really optimized for performance, if I ever was to rewrite it I would make it considerably faster.

But I don't think that's the issue. If you have many long linear motions, you could increase the 'stepsize' variable to 100 mm or so. As you have it right now it will compute the robot position every 1 mm to check for errors, but this might not be necessary.

petrasvestartas commented 4 years ago

Thank you, as long as it produces good results it is worth waiting.

I would like to ask about axes limitations. Why the program stops at -92 rotations in axes 5, if the limit is -180? In the XML I constrain rotations like this.

Rotations

<RobotSystems>

  <RobotCell name="IBOIS-IRB6400B" manufacturer="ABB">
    <Mechanisms>
      <RobotArm model="IRB6400" manufacturer="ABB" payload="100">
        <Base x="0.000" y="0.000" z="0.000" q1="1.000" q2="0.000" q3="0.000" q4="0.000"/>
        <Joints>
          <Revolute number="1" a ="240" d ="800" minrange = "-110" maxrange ="110" maxspeed ="110"/>
          <Revolute number="2" a ="1050" d ="0" minrange = "-360" maxrange ="360" maxspeed ="110"/>
          <Revolute number="3" a ="225" d ="0" minrange = "-360" maxrange ="360" maxspeed ="110"/>
          <Revolute number="4" a ="0" d ="1520" minrange = "-360" maxrange ="360" maxspeed ="190"/>
          <Revolute number="5" a ="0" d ="0" minrange = "-180" maxrange ="90" maxspeed ="150"/>
          <Revolute number="6" a ="0" d ="200" minrange = "-180" maxrange ="180" maxspeed ="210"/>
        </Joints>
      </RobotArm>
    </Mechanisms>
    <IO>
      <DO names="DO10_1,DO10_2,DO10_3,DO10_4,DO10_5,DO10_6,DO10_7,DO10_8,DO10_9,DO10_10,DO10_11,DO10_12,DO10_13,DO10_14,DO10_15,DO10_16"/>
      <DI names="DI10_1,DI10_2,DI10_3,DI10_4,DI10_5,DI10_6,DI10_7,DI10_8,DI10_9,DI10_10,DI10_11,DI10_12,DI10_13,DI10_14,DI10_15,DO10_16"/>
    </IO>
  </RobotCell>

</RobotSystems>
visose commented 4 years ago

The plugin rotation values in radians don't translate directly to degrees. More info here: https://github.com/visose/Robots/issues/8 Probably also discussed in another issue or the wiki.

visose commented 4 years ago

Also see here: https://github.com/visose/Robots/issues/10

petrasvestartas commented 4 years ago

Sorry to ask too many questions but is this a right conversion? Ques

visose commented 4 years ago

No, that component is to convert from degrees to radians and not the other way around. I haven't exposed radians to degrees as a Grasshopper component since I haven't found the need to do this.

If you want to know if the axis in in the limit, plug the axis limit in degrees into that component to get the limit in radians and compare to the values in radians that you're getting.

petrasvestartas commented 4 years ago

I think I am lost.

Would it be possible to give an example to convert axes degrees correctly?

visose commented 4 years ago

You almost have it. You mentioned the limit of axis 5 is -180 degrees. You just have to connect a panel with the value -180 to the D input of degrees component from the plugin. This will return a value in radians. This is the value you should compare to the J output of the simulation component. Don't transform the values from the J output to anything.

petrasvestartas commented 4 years ago

Sorry I still do not understand it:

Qu

visose commented 4 years ago

Which part you don't understand? That grasshopper file seems good. Axis 5 is at an angle of 0 radians, which not close to its limit of -3.14159 radians.