Are we doing needle calibration right?

mrandt
Posts: 407
Joined: Mon Apr 27, 2015 10:56 am
Location: Stuttgart, Germany

Re: Are we doing needle calibration right?

Post by mrandt »

JuKu wrote:You are right if we assume there is no wobble on the axis itself. Maybe a safe assumption, but I haven't actually measured it (I should). If there is, I think we'll get ellipses, therefore the multiple point calibration.
There are several variables: A-axis shaft, bearings, needle connector (or nozzle holder) etc. If only one of those is not perfectly round or not all connections are perfectly straight, the nozzle tip runout will not describe a perfect circle but rather an ellipsis.

This is what I see on my machine and I would assume that it is the same on most other LitePlacers. RMod fork visualizes the measured points during wobble calibration and thus the ellipsis is easy to spot.

I think it would be best to measure several points at different angles ant then calculate the rotation matrix for compensation. I also like the "Test calibration" feature in RMod, which allows to validate that matrix - works great.
sgraves wrote: I agree that the compensation should be relative to the A axis.
That's another +1 for Manfred's and my suggestion :-D
sgraves wrote:Unless one moves the camera the A axis to cam offset will not change, so changing needles will not require re-calibration.
...and this is one of the arguments to do it. Another, as said before, is auto tool change. If the A-axis and nozzle holder have a bit of runout for whatever reason, we want to make sure the holder is centered for changing the nozzle.
User avatar
dampfboot
Posts: 48
Joined: Tue Jul 28, 2015 5:51 pm
Location: Hannover, Germany
Contact:

Re: Are we doing needle calibration right?

Post by dampfboot »

Please have in mind that quite some z-axis are not perfectly perpendicular oriented to the table surface. You can see this in your up looking camera. In my machine the center of the needle is in different positions if the needle is up or down. This little angle can cause some deviations if you pick up a big 470µF C or a low profile R.

Different nozzle length during a nozzle change will cause similar effects.

All this are facts with a DIY machine. Working with 0805 sizes only might not cause any problem with such a calculated theoretical A-axis center - but one has to keep it in mind...

Rainer
sgraves
Posts: 30
Joined: Mon Jul 04, 2016 2:48 am
Location: Tampa, FL USA

Re: Are we doing needle calibration right?

Post by sgraves »

Ok, the wobble algorithm is not that simple :( . But assuming the ellipse shape is repeatable (i.e. the A axis is not precisely vertical so we are looking at a circle at an angle), our wobble compensation should be to the center of the ellipse and the cam to needle offset to that center. It takes multiple readings to characterize an ellipse, but I believe the center of the lines connecting two points 180 apart should pass through the center of the ellipse. The correction factor for every point should be relative to that point. If the ellipse is true (all lines from two 180 degree points find the same center), we should only need half of the measured points stored since the opposite point (180 degrees away) is mirrored across the center point.
JuKu
Site Admin
Posts: 1110
Joined: Thu Feb 14, 2013 3:06 pm
Location: Tampere, Finland
Contact:

Re: Are we doing needle calibration right?

Post by JuKu »

dampfboot wrote:Please have in mind that quite some z-axis are not perfectly perpendicular oriented to the table surface. You can see this in your up looking camera. In my machine the center of the needle is in different positions if the needle is up or down.
And we don't know if the up camera is looking directly up either. Both are calibrated out, but only at one Z level.
This little angle can cause some deviations if you pick up a big 470µF C or a low profile R.
True. If I would design the head again, I would try to build in set screws to adjust both z axis and up camera perpendicularity.
Different nozzle length during a nozzle change will cause similar effects.
Of course, unless calibrated out. That is doable, although not in the software yet.
sgraves
Posts: 30
Joined: Mon Jul 04, 2016 2:48 am
Location: Tampa, FL USA

Re: Are we doing needle calibration right?

Post by sgraves »

I have implemented my version for the algorithm implementing this discussion. I have been using it and it seems to work quite well. I don't have a completely unbiased comparison because we are still tuning the machine. We have corrected issues since the change that would make the machine work better regardless of algorithm. i.e. Binding drive pulley on the X axis stepper

In any case, here it is for your critique and suggestions for improvement.

I added a new public variable to the Needle class.

Code: Select all

	public List<NeedlePoint> CalibrationPoints = new List<NeedlePoint>();
becomes

Code: Select all

	public List<NeedlePoint> CalibrationPoints = new List<NeedlePoint>();
	public NeedlePoint CalibrationPointsCenter = new NeedlePoint();
I have inserted new code at the bottom of the Calibrate method in the Needle class.

Code: Select all

            Calibrated = true;
            return true;
becomes

Code: Select all

            /*********************************************************************************
             * Here we are going to calculate the center of the calibration points.  It will
             * be the center of the line connecting each two points that are 180 degrees apart.
             * We are assuming that the points are on even spacing, cover 360 degrees and that
             * there are an even number of points.  For example, 16 points with 22.5 degree
             * spacing will work.  In the ideal world all of the centers will be at the same
             * point.  But to account for some variation we will take the average of all the
             * centers.  To do that we will sum all of the points in the following loop and
             * divide the sum by the loop count
             * 
             * A little more discussion here to validate my thinking for my present self and to
             * let my future self and other programmers know what is happening.  The coordinate
             * system is setup by the camera and is relative to the camera.  But the working
             * part of the PNP is the needle.  We need a precise reference from the camera
             * position to the needle position.  The needle position wobbles as it is rotated.
             * We have calculated the center of that wobble and will be setting our needle
             * correction relative to that center.  The idea being that even a new needle will
             * wobble about that same point. Now the most important consideration is the sign
             * of the correction.  The present logic is that the correction factors are added
             * to the move.  Subtracting the center from the points gives us the right polarity
             * for a correction factor that will be added to compensate.  Then when we set the 
             * needle to camera offset we need to subtract the correction factor from the 
             * needle position to give us the point to be used in the offset calculations
             ********************************************************************************/
            // There are half as many lines as there are points
            int HalfCount = CalibrationPoints.Count / 2;
            CalibrationPointsCenter.X = 0;
            CalibrationPointsCenter.Y = 0;
            for (int i = 0; i < HalfCount; i++)
            {
                //The following block could be used if one is anal about validating the situation
                //if(CalibrationPoints[i + HalfCount].Angle - CalibrationPoints[i ].Angle != 180)
                //{
                //    Calibrated = false;
                //    return false;
                //}
                CalibrationPointsCenter.X += (CalibrationPoints[i].X + CalibrationPoints[i + HalfCount].X) / 2;
                CalibrationPointsCenter.Y += (CalibrationPoints[i].Y + CalibrationPoints[i + HalfCount].Y) / 2;
            }
            CalibrationPointsCenter.X /= HalfCount;
            CalibrationPointsCenter.Y /= HalfCount;
            /**********************************************************************************
             * The previous technique was calculating the correction based on the needle being
             * near the center of the screen at rotation 0.  Effectively we are normalizing to
             * a point in the center of the distribution of points.  Instead of changing the
             * CorrectedPosition_m routine, we can normalize the values here and leave that
             * routine alone.
             **********************************************************************************/
            for (int i = 0; i < CalibrationPoints.Count; i++)
            {
                NeedlePoint Point = CalibrationPoints[i];
                Point.X -= CalibrationPointsCenter.X;
                Point.Y -= CalibrationPointsCenter.Y;
            }
            Calibrated = true;
            return true;
I rewrote the Offset2Method_button_Click function. It now does the wobble correction first and then calculates the Needle to Camera Offset. The wobble correction is used so that the offset is actually the center of the correction points (the center of A axis rotation?) to camera.

Code: Select all

        /**********************************************************************
         * This function sets the needle to camera offset and wobble correction.
         * Previously the offset was calculated and then the wobble correction.
         * That order does not give the best results.  Doing the wobble correction
         * first will give the desired results
         *********************************************************************/
        private void Offset2Method_button_Click(object sender, EventArgs e)
        {
            ZGuardOff();
            SelectCamera(DownCamera);
            SetCurrentCameraParameters();
            switch (SetNeedleOffset_stage)
            {
                case 0:
                    ShowMessageBox(
                        "Jog the needle above the up camera,\n\rtake needle down, jog it to the image center\n\rand set Up Camera location",
                        "Setup for Wobble Calibration",
                        MessageBoxButtons.OK);
                    SelectCamera(UpCamera);
                    //SetNeedleMeasurement();
                    NeedleOffset_label.Visible = true;
                    NeedleOffset_label.Text = "Click \"Next\" when needle is in position for wobble calibration.";
                    SetNeedleOffset_stage = 1;
                    Offset2Method_button.Text = "Next";
                    break;

                case 1:
                    Properties.Settings.Default.UpCam_PositionX = Cnc.CurrentX;
                    Properties.Settings.Default.UpCam_PositionY = Cnc.CurrentY;
                    if (!CalibrateNeedle_m())
                    {
                        SetNeedleOffset_stage = 0;
                        NeedleOffset_label.Visible = false;
                        NeedleOffset_label.Text = "   ";
                        Offset2Method_button.Text = "Start";
                        ZGuardOn();
                        break;
                    }
                    SetNeedleOffset_stage = 2;
                    NeedleOffset_label.Visible = true;
                    NeedleOffset_label.Text = "Click \"Next\" when wobble calibration is complete.";
                    break;

                case 2:
                    SetNeedleOffset_stage = 3;
                    CNC_A_m(0.0);
                    NeedleOffset_label.Visible = true;
                    NeedleOffset_label.Text = "Jog needle to a point on a PCB, then click \"Next\"";
                    break;

                case 3:
                    double dX;
                    double dY;
                    SetNeedleOffset_stage = 4;
                    Needle.CorrectedPosition_m(Cnc.CurrentA, out dX, out dY);
                    NeedleOffsetMarkX = Cnc.CurrentX - dX;
                    NeedleOffsetMarkY = Cnc.CurrentY - dY;
                    CNC_Z_m(0);
                    CNC_XY_m(Cnc.CurrentX - 75.0, Cnc.CurrentY - 29.0);
                    DownCamera.DrawCross = true;
                    NeedleOffset_label.Text = "Jog camera above the same point, \n\rthen click \"Next\"";
                    break;

                case 4:
                    SetNeedleOffset_stage = 0;
                    Properties.Settings.Default.DownCam_NeedleOffsetX = NeedleOffsetMarkX - Cnc.CurrentX;
                    Properties.Settings.Default.DownCam_NeedleOffsetY = NeedleOffsetMarkY - Cnc.CurrentY;
                    NeedleOffsetX_textBox.Text = Properties.Settings.Default.DownCam_NeedleOffsetX.ToString("0.00", CultureInfo.InvariantCulture);
                    NeedleOffsetY_textBox.Text = Properties.Settings.Default.DownCam_NeedleOffsetY.ToString("0.00", CultureInfo.InvariantCulture);
                    NeedleOffset_label.Visible = false;
                    NeedleOffset_label.Text = "   ";
                    Offset2Method_button.Text = "Start";
                    CNC_Z_m(0.0);
                    ZGuardOn();
                    break;
            }
        }
And I changed the central part of the CalibrateNeedle_m function to have the correction points more about the center of the video

Code: Select all

            // take needle to camera
            result &= CNC_XY_m(Properties.Settings.Default.UpCam_PositionX, Properties.Settings.Default.UpCam_PositionY);
            result &= CNC_Z_m(Properties.Settings.Default.General_ZtoPCB - 1.0); // Average small component height 1mm (?)
            int iTries = 0;

            //Used in place of Do While because this form allows the exit in the middle of block
            //We should have the greatest accuracy if the camera reads the positions of the needle close to the
            //center of the video.  This block will make up to two passes moving the needle so that the needle wobble
            //is centered on the display
            while (true)
            {
                // measure the values
                SetNeedleMeasurement();
                result &= Needle.Calibrate(4.0 / Properties.Settings.Default.UpCam_XmmPerPixel);  // have to find the tip within 4mm of center
                double dCenterDist = Math.Sqrt(Math.Pow(Needle.CalibrationPointsCenter.X, 2) + Math.Pow(Needle.CalibrationPointsCenter.Y, 2));
                if (dCenterDist <= 0.2) break;
                iTries++;
                if (iTries >= 2) break;
                //Setup for another pass through the loop, recalculate UpCam_Position to center wobble
                Properties.Settings.Default.UpCam_PositionX += Needle.CalibrationPointsCenter.X;
                Properties.Settings.Default.UpCam_PositionY += Needle.CalibrationPointsCenter.Y;
                ZGuardOff();
                result &= CNC_XY_m(Properties.Settings.Default.UpCam_PositionX, Properties.Settings.Default.UpCam_PositionY);
                ZGuardOn();
            }


            // take needle up
            result &= CNC_Z_m(0.0);
mrandt
Posts: 407
Joined: Mon Apr 27, 2015 10:56 am
Location: Stuttgart, Germany

Re: Are we doing needle calibration right?

Post by mrandt »

May I suggest you put your changes into a (non-breaking) pull request aggainst Juha's repository?

That way we could simply merge and test your changes.

Thanks a lot!
sgraves
Posts: 30
Joined: Mon Jul 04, 2016 2:48 am
Location: Tampa, FL USA

Re: Are we doing needle calibration right?

Post by sgraves »

Mrandt,
I haven't figured out Git (as he sheepishly looks at his feet :oops: ). I have used other version control systems (CVS, SVN). Git is different, I haven't yet grokked it. I forked Juha's repository and cloned my repository to my local machine. I started making changes to that code (I probably should have created a branch). I am using Git Gui because Visual Studio asked for it. I expected Visual Studio to be integrated and do a check out. Something is not right because I have commits showing up in Git Gui, but not all the changes I have made are there. I haven't been able to see a diff to show my changes. I see the green and yellow bars in VS (which seem right). I don't yet visualize the path back to Juha's (or my) repository, making the changes available in them. I trying to work this out, we are also trying to get the placer working and a board we desperately need built. I had promised this board to the customer by Jul 4th and now I am late.

Thanks,
Steve
mawa
Posts: 139
Joined: Wed Jun 10, 2015 1:23 pm
Location: Near Hamburg, Germany

Re: Are we doing needle calibration right?

Post by mawa »

I implemented a IMHO simple method:
  • move to the assumed camera center
    First you measure the 16 calibration points.
    Use the 4 value for 0, 22.5, 45, 67.5, 90 and compute the intersection point of the line and its 90° orthogonal line.
    Compute the Location average of the resulting 4 intersections
    The gives you the true offset of the rotation center point to current location.
    Subtract this offset to get the rotation center into the camera center
    Repeat the measurement of the 16 calibration points.
These are now SUBTRACTED from the given XY setpoint.

Here are some code sniblets:

This is propably very different from Juhas code because I heavily reworked Juhas and therezas code.
I will support multi-head (intending to use 4), multi-nozzle with nozzle changer.
So when you move to a location you have to consider the individual head offset from down cam center and the the offset and runoff/wobble of the mounted nozzle.

Yes, I will publish the code when I am satisfied with my work.

Code: Select all

  
 /// <summary>
        /// Compute the inetrsection point of two lines A and B defined by their start and end loactions
        /// </summary>
        /// <param name="AStart"></param>
        /// <param name="AEnd"></param>
        /// <param name="BStart"></param>
        /// <param name="BEnd"></param>
        /// <returns></returns>
        PartLocation LineIntersectionPoint(PartLocation AStart, PartLocation AEnd, PartLocation BStart, PartLocation BEnd)
        {
            // Get A,B,C of first line - points : ps1 to pe1
            double A1 = AEnd.Y - AStart.Y;
            double B1 = AStart.X - AEnd.X;
            double C1 = A1 * AStart.X + B1 * AStart.Y;

            // Get A,B,C of second line - points : ps2 to pe2
            double A2 = BEnd.Y - BStart.Y;
            double B2 = BStart.X - BEnd.X;
            double C2 = A2 * BStart.X + B2 * BStart.Y;

            // Get delta and check if the lines are parallel
            double delta = A1 * B2 - A2 * B1;

            if (delta == 0)
                throw new Exception("Lines are parallel");

            // now return the Vector2 intersection point
            return new PartLocation(
                (B2 * C1 - B1 * C2) / delta,
                (A1 * C2 - A2 * C1) / delta
            );
        }
        /// Computes the center intersection point of a nozzle calibration
        /// list. This list contains 16 point measured in 22.5° steps.
        /// Therefore we have 4 pairs of ortogonal intersecting lines
        /// </summary>
        public PartLocation ZCenterOffset
        {
            get
            {
                List<PartLocation> resList = new List<PartLocation>();

                for (int i = 0; i < 4; i++)
                {
                    PartLocation pResult = LineIntersectionPoint(AbsCalibrationPoints[i], AbsCalibrationPoints[i + 8], AbsCalibrationPoints[i + 4], AbsCalibrationPoints[i + 12]);
                    resList.Add(pResult);
                }

                return PartLocation.Average(resList);
            }
        }

        /// <summary>
        /// Calibrates the needle by first computing the real A axis center
        /// and then the wobble offset
        /// </summary>
        /// <returns>true on success</returns>
        public bool Calibrate()
        {
            // first we evaluate the true A-axis center point
            if (Calibrate(4.0 / Config.Instance.UpCamXmmPerPixel))
            {
                // and we save it
                Location currentCamCenter = LocationSet.FindLocation("UPCAMERA");

                LocationSet.FindLocation("UPCAMERA").SetTo(currentCamCenter - ZCenterOffset);
                CNC.MoveToLocation("UPCAMERA");

                if (Calibrate(4.0 / Config.Instance.UpCamXmmPerPixel))
                {
                    for (int i = 0; i < CalibrationPoints.Count; i++)
                        DashBoard.sLogText(2, CalibrationPoints[i].ToString(), Color.Purple);

                    return true;
                }
            }

            MainForm.sShowError("Nozzle Calibration Failed!");
            IsCalibrated = false;
            return false;
        }

  /// <summary>
        /// Add the specific nozzle offset and the runoff/wobble 
        /// induced by the current nozzle
        /// 
        /// </summary>
        /// <param name="location"></param>
        /// <returns></returns>
        public SpaceLocation AddOffset(SpaceLocation location, bool subtract = false)
        {
            double dX = 0;
            double dY = 0;

            if (GetCorrectedPosition(location.A, out dX, out dY))
            { 
                var wobble = new PartLocation(dX, dY, 0);

                if (subtract)
                {
                    location += wobble;
                    location -= Offset;
                }
                else
                {
                    location -= wobble;
                    location += Offset;
                }
            }

            return location;
        }
And here the head Offset. When a nozzle is mounted on a head it's nozzle object reference is in Nozzle

Code: Select all

 
         /// <summary>
        /// When we move the machine to a head based location the designated location
        /// in down-camera view is offset by the calibrated head + nozzle offsets.
        /// </summary>
        /// <param name="location">the space loaction X,Y,Z and Angle</param>
        /// <returns></returns>
        public SpaceLocation AddOffset(SpaceLocation location, bool subtract = false)
        {
            if (subtract)
                location -= Offset;
            else
                location += Offset;

            if (Nozzle != null)
                location = Nozzle.AddOffset(location, subtract);   

            return location;
        }
and finally in the center CNC

Code: Select all

  public static bool MoveToXYAZ(double? x, double? y, double? angle = null, double? z = null, int? speed = null, OffsetStyle offsetStyle = OffsetStyle.Use_Default)
        {
            if (_ErrorOccured || !Connected)
            {
                LogText(2, string.Format("CNC.MoveToXYZA: move canceled because {0}", _ErrorOccured ? "Error has occured!" : "CNC not connected!"));
                return false;
            }

            if (!CNC.JoggingBusy)
            {
                // if not set as parameter use global offset type
                if (offsetStyle == OffsetStyle.Use_Default)
                    offsetStyle = OffsetType;

                // depending on what we are intending to use add the appropriate offset 
                if (offsetStyle != OffsetStyle.Camera)
                {
                    if (offsetStyle == OffsetStyle.Last_Head)
                        offsetStyle = _LastPnPHead;

                    SpaceLocation offset = NozzleSet.GetHeadByIndex((int)offsetStyle).AddOffset(new SpaceLocation(x, y, z, angle));

                    if (x != null) x = offset.X;
                    if (y != null) y = offset.Y;
                    if (z != null) z = offset.Z;
                }
            }

             LogText(2, string.Format("MoveToXYZA: X:{0:#0.00} Y:{1:#0.00} Z:{2:#0.00} A:{3:#0.00}", x, y, z, angle), Color.DarkMagenta);

            if (!_NoSafeTest && !CanMoveSafeTo(x, y, z))
            {
                LogText(2, "CNC.MoveToXYZA: ignored because cannot move safe!", Color.Red);
                return false;
            }

            _BlockingWriteDone = false;

            Thread t = new Thread(() => BlockingMoveThread(x, y, z, angle, speed))
            {
                Name = "CNC_BlockingMove",
                IsBackground = true
            };

            t.Start();
            int i = _MoveTimeout;

            while (!_BlockingWriteDone && !_ErrorOccured)
            {
                Thread.Sleep(2);
                Application.DoEvents();
                i--;

                if (i <= 0)
                    _readyEvent.Set();   // causes CNC_Blocking_thread to exit
            }

            _BlockingWriteDone = true;

            if (((i > _MoveTimeout) || _ErrorOccured) && Connected)
            {
                MainForm.sShowError("CNC.MoveToXYZA: Timeout ");
                //Close();
                MainForm.sUpdateCNCStatus();
            }

            return (Connected);

best regards
Manfred
mrandt
Posts: 407
Joined: Mon Apr 27, 2015 10:56 am
Location: Stuttgart, Germany

Re: Are we doing needle calibration right?

Post by mrandt »

Hi Steve,

no worries; I know exactly where you are coming from. I have used all kinds of code versioning systems at work; CVS / SVN, ClearCase, CMVC, Rational etc. and it still took me a while to understand git. Once you get a grip of it's really great - until then it often leaves you puzzled ;-)

I tried to compile the most important info about git a while back - have a look here:
http://liteplacer.com/phpBB/viewtopic.p ... it=+commit

The most important aspect in your situation is that you *cannot* commit to Juha's repo!

Instead, you create a fork which is basically a copy of Juhas code in your personal GitHub space.

You then clone this fork onto your computer. If you like, you can also create a branch; that is optional but considered good practice.

Any changes are locally commited to your branch (or the local master branch). New files have to be added to git control, so they are indexed, tracked for changes and included in later commits.

Until then, the version history is local to your PC. At some point you want to push the local changes to your remote repository, which is your fork at GitHub server (but NOT Juha's repo). This is also where branches become handy; it is very easy to combine all changes (multiple commits) from a specific branch into one pull request.

If you have made changes which you want to contribute back to Juha, you then create a pull request - basically asking Juha to fetch some parts of your code and merge them into his branch.

So far for the basics; there are many howtos and tutorials which explain the details. If you try this now; make sure to have a backup of your source directories (in case something goes wrong).

I suggest you take an hour or so (once you find the time) and get a grip of git; it's definetly worth the effort :-)

Have fund and best regards
Malte
Danal
Posts: 43
Joined: Fri May 29, 2015 11:07 am
Location: Fairview, TX
Contact:

Re: Are we doing needle calibration right?

Post by Danal »

I also use a bunch of different source control systems, and struggled with Git. For me to understand it, I had to make two throw-away email addresses, create a repository under one, fork it from the other email, pull that back, etc, etc. Sounds complicated, but actually saved time in the long run.

Now that I understand it, I really, REALLY, like Git. So much so that I've switched to Atom editor (mostly switched, anyway). But it is different.
Post Reply