问:
Why applied joint torque will affect another joint?
I want to ask a very simple question.

For the robot dynamics Newton-Euler algorithm, suppose there is a two-link planar robot, we can write the simple dynamic equation form as $M(\theta)\ddot{\theta} =  \tau - h(\theta,\dot{\theta})$.

From the equation, we can see if we applied only one joint torque(suppose applied at joint one and ignore all links gravity), it will affect another joint accelerate(change the second joint angles).

It's not intuitive, in my imagination, if we run the joint one motor and ignore the gravity of links, so just change the joint one angle.

who can explain it?

Thank you!

Best,

Ben.

答:
When you apply torque to the first joint, it makes the first link rotate. This means that the far end of the first link translates.

Because the second joint pins the near end of the second link to the far end of the first link, this means that the near end of the second link must also move.

In order for the second link to move, the pin in the second joint must apply a force to the second link.

The force is applied to the second link at a point that is not its center of mass, which means that there is a net torque from this force around the center of mass of the second link.

The torque/inertia ratio for the second link will in general not be the same as the torque/inertia ratio for the applied torque on the first link plus the reaction torque on that link from the pin.

Different torque/inertia ratios on the two links mean that they experience different rotational accelerations, and therefore rotate relative to one another.

When the links rotate relative to one another, the joint angle between them by definition changes.

(There are other explanations with deeper mathematics; this covers the phenomenon at a Newton-Euler force-balance level.)


问:
How to compute the orientation error between two 3D coordinate frames?
For my master thesis in robotics I have to compute the orientation error between two coordinate frames, called E and H. Their orientation is expressed through rotation matrices (3x3) with respect to a "world frame", W.
Remark: writing $R_A^B$ I'm indicating the orientation of frame A with respect to frame B.

What I have understood is that I have to do the following operation:

$R_{error} = R_E^W * R_W^H$

is it correct? If yes, that error in which frame is expressed?
Thanks a lot
Ale

答:
A rotation matrix represents the rotation between two frames. Therefore, it does not make sense to talk about in which "one" frame the error rotation is expressed. Namely, the rotation matrix $R^B_A$ represents the rotation from frame $B$ to frame$A$. Therefore, the error rotation matrix you defined as

$$
R_{error} = R^W_E R^H_W
$$

can also be seen as the rotation from frame $H$ to frame $W$ and then from frame $W$ to frame $E$, which is equivalent to $R^H_E$.

ContentLicense: CC BY-SA 4.0


答:
The rotation error between two frames can be viewed in two ways:

The orientation of one frame as seen from the other, calculated by multiplying the inverse of the observing frame by the observed frame. For frames $E$ and $H$, this error in your notation would be
$$
\tag{1}
{R_{1}}^{E}_{H} = (R^{W}_{E})^{-1}R^{W}_{H} = R^{E}_{W}R^{W}_{H}
$$
for $H$ as seen from $E$, or
$$
\tag{2}
{R_{1}}^{H}_{E} = (R^{W}_{H})^{-1}R^{W}_{E} = R^{H}_{W}R^{W}_{E}
$$
for $E$ as seen from $H$. These matrices encode the rotations around local axes (the axes of the superscript frame) that map the superscript frame to the subscript frame,
$$
\tag{3}
R^{W}_{E}{R_{1}}^{E}_{H} = R^{W}_{H}
$$
and
$$
\tag{4}
R^{W}_{H}{R_{1}}^{H}_{E} = R^{W}_{E}
$$

The orientation that would be reached by starting at one frame and moving by the inverse of the second frame's orientation relative to the world,
$$
\tag{5}
{R_{2}}^{E}_{H}  = R_{H}^{W} (R_{E}^{W})^{-1} = R_{H}^{W} R_{W}^{E}
$$
or
$$
\tag{6}
{R_{2}}^{H}_{E} = R_{E}^{W} (R_{H}^{W})^{-1} = R_{E}^{W} R_{W}^{H}
$$
These matrices encode the rotations around the world axes that map the frames to each other,
$$
\tag{7}
{R_{2}}^{E}_{H} R^{W}_{E} = R^{W}_{H}
$$
and
$$
\tag{8}
{R_{2}}^{H}_{E}R^{W}_{H} = R^{W}_{E}
$$

The fundamental source of there being two interpretations of rotational error is that rotations are not commutative, and so our intuition that the differences $A + (-B)$ and $(-B) + A$ should be the same does not hold in this case.

The equations have a left-right duality. When we take the difference by putting the inverse rotation on the left (globally rotating the second frame by the inverse of the first), we find the (local) rotation error that maps between the frames when placed to the right of the starting frame, and when we put the inverse rotation on the right (locally rotating the first frame by the inverse of the second), we find the (global) rotation error that maps between the frames when placed to the left of the starting frame.

Title: How to compute the orientation error between two 3D coordinate frames?

Body:
For my master thesis in robotics I have to compute the orientation error between two coordinate frames, called E and H. Their orientation is expressed through rotation matrices (3x3) with respect to a "world frame", W.
Remark: writing $R_A^B$ I'm indicating the orientation of frame A with respect to frame B.

What I have understood is that I have to do the following operation:

$R_{error} = R_E^W * R_W^H$

is it correct? If yes, that error in which frame is expressed?
Thanks a lot
Ale


问:
Is it possible to run multiple loops at the same time? (Arduino)
I've got a code where I have a motor running back and forth and buttons connected to a scanner, when I press the buttons it causes the motor to stop  and over rides it. I would like them to run parallel to each other so the codes don't interrupt each other.

Here is my code

```<code>#include <AccelStepper.h>

// Define some steppers and the pins they will use

AccelStepper stepper1(AccelStepper::FULL2WIRE, 2, 3);

const int buttonPin = 4;
const int button2Pin = 14;
const int pulseopto1 = 9;
const int startScan = 11;

int buttonState = 0;
long previousMillis = 0;
long interval = 5;

void setup()
{
    pinMode(buttonPin, INPUT);
    pinMode(button2Pin, INPUT);
    pinMode(pulseopto1, OUTPUT);
    pinMode(startScan, OUTPUT);
    stepper1.setMaxSpeed(40000.0);
    stepper1.setAcceleration(100000.0);
    stepper1.moveTo(25000);
}

void loop()
{
    buttonState = digitalRead(buttonPin);

    if (buttonState == LOW)
    {
        digitalWrite(startScan, HIGH);
    }
    else (buttonState == HIGH);
    {
        digitalWrite(startScan, LOW);
    }

    {
        buttonState = digitalRead(button2Pin);

        if (buttonState == LOW)
        {
            // turn LED on:
            digitalWrite(pulseopto1, HIGH);
            delay(5);
            digitalWrite(pulseopto1, LOW);
            delay(5);
        }
        else
        {
            // turn LED off:
            digitalWrite(pulseopto1, LOW);
        }
    }

    // Change direction at the limits
    if (stepper1.distanceToGo() == 0)
    {
        stepper1.moveTo(-stepper1.currentPosition());
    }

    stepper1.run();
}
</code>```

答:
I think you should look into the Arduino's attachInterrupt function. You would connect the buttons to a specific interrupt pin on the Arduino and then write a small simple interrupt handler that would be called when that interrupt fires. You can configure the interrupt to fire while low, on a rising edge, on a falling edge, or on a change of the interrupt pin. This way you don't have to poll your buttons, and it will make your main loop cleaner/easier to write.

Here's the arduino webpage for attachInterrupt(): http://arduino.cc/en/Reference/attachInterrupt

edit: You should also comment your code so we know what you are trying to do, and possibly give us a description of your hardware setup (what are pins of Arduino actually connected to).

Given the syntax error that TheRussian pointed out, I don't understand how this code compiles.

ContentLicense: CC BY-SA 3.0


问:
What should be the ideal ratio for link lengths ( L1/ L2) of a 2 link manipuator robot with revolute joints?
I am working on making a CAD model of a two link robotic manipulator, with link lengths around 15-20 cm. I am unable to figure how to set their mutual ratio and I want to know whether there is some fixed, determined ratio on how to take these values. I thought taking the human arm as an inspiration, but if so,  is it right to take so?</p>

答:
This is a good question, and one you should definitely consider when designing a new robot arm.  But unfortunately, it depends a lot on the configuration of your arm (i.e. how the joints will be bent), and how you determine "best" (i.e. are you optimizing for speed, strength, dexterity, etc).  And even if you know these things, the metrics you use to determine "best" can differ.

You should read up on "manipulabilty".  This is a feature of the arm's configuration.  Roughly, it is a measure of how well the arm can move in a particular direction.  Usually, this is represented as an ellipse.

The interesting thing about manupulabilty is that there are actually 2, orthogonal ellipoids.  One for velocity and one for force.  Intuitively, if you think about mechanics, where the arm can move fast, it is not very strong.  But where it is strong, it is not very fast.  And if you want a very general purpose arm, trying to achieve a more circular ellipse might be desirable.

You may have different link lengths and arm configurations depending on your use case.

There are other measures that you might want to consider too.  Such as distance from joint limits, and condition number.


问:
Path-following algorithm and suggestions for mecanum robot
I have a robot with four mecanum wheels (blue square) that can move in an open-space room.
The position x,y of the robot is measured in a very accurate way by a fixed position sensor (red circle) that is placed in the origin of the place and has coordinates (0,0,0). The position of the robot is updated at 10Hz.

I want to make the robot follow different paths like the one from A to B as showed in the picture (in green color).

What is the best and accurate way to do that?
I think I should have to create the path like a series of x,y points and make the robot move towards them but I don't know how to derive the motion equations.

Can you give me some advices or examples, please?

答:
Let's suppose you have a required (continuous) trajectory in the form of an equation that you want to follow, for example take a straight line (whose equation is y=2x+5), you can break it down into discrete points and generate a velocity profile out of this trajectory, the calculated velocity profile could be fed to inverse kinematic equations [1] of robot (omni-platform) for calculating the angular velocities of wheel which will be required to follow that above path (straight line in our example).

[1]: https://www.researchgate.net/publication/308570348_Inverse_kinematic_implementation_of_four-wheels_mecanum_drive_mobile_robot_using_stepper_motors


答:
Your question can't really be answered without understanding what you want the arm to do.  You will also need to account in turn for the masses of the payload and elbow actuator, as well as the maximum torque and angular velocity the actuator could support along with the gear ratio of any transmission.  You could then work out the dynamics of the arm for a given actuator as a function of the link length ratio, maximizing, for example, something like the speed of the arm tip or even the expected lifetime of the arm.

It's all quite complicated.  A good place to start, though, might be this old MIT AI Lab paper.  You could build a dynamic and kinematic model in R or some other tool and experiment with different combinations.  (Note that the linked paper doesn't deal with the elbow actuator mass, but it's easy to extend the model).

Title: What should be the ideal ratio for link lengths ( L1/ L2) of a 2 link manipuator robot with revolute joints?

Body: I am working on making a CAD model of a two link robotic manipulator, with link lengths around 15-20 cm. I am unable to figure how to set their mutual ratio and I want to know whether there is some fixed, determined ratio on how to take these values. I thought taking the human arm as an inspiration, but if so,  is it right to take so?</p>


问:
Industry robot maximum torque
In google search, I find Abb industry robot wrist maximum torque is around 6000Nm, So I’m wondering, in most industry robot, how much the maximum torque can reach? In another words, which motor is advance and can provide the best torque in industry robot. Thank you very much!
Best,
Ben</p>

答:
In order to generate the high levels of torque you are referring to, the actuator will certainly require a transmission of some sort.  Above 500 Nm, cycloidal rather than harmonic drives are employed, usually preceded by a planetary input stage.

Nabtesco, in Japan, is probably the current industry leader in these type of drives, which it brands as "RV" drives.  Looking at Nabtesco's product line, the highest repeatable torque available comes from the RV-500C line - around 12,250 Nm.  The Nabtesco website also has a product selector where you can explore different manufacturers' motor options.

Title: Industry robot maximum torque


问:
How to balance a humanoid robot on one feet?
For a school project, I have designed and assembled a bipedal humanoid robot. The robot is roughly $54$cm tall, with the width(distance between two legs) at $20$cm. The parts that make up the robot are 3D-printed. It has $12$ degrees of freedom, $2$ for each foot, $1$ for the legs, and $3$ for each side of the hip.

Here is the link to the Github repository.

My goal for this project has been to get the robot to walk. As of currently, my robot is connected to a microcontroller via PWM wires, and I connected it to a computer. The robot is powered using a DC power supply. I use a simple robot control system on my computer, which sends specific PWM signals to each servo within the range $500-2500$, covering a full $180$ turn. For each PWM signal, I can control its duration as well.

My problem is the robot is failing to balance on one foot. I know my method of controlling it is extremely primitive, but I can't seem to get it to balance on one foot. I added an extra $DOF$ on the foot, to allow the foot to shift the centre of gravity.

Here is an image:

From the image shown, it seems like the centre of gravity indeed has shifted to a location directly above the foot. However, when trying to control it, the robot never manages to successfully balance on one foot. It usually falls towards the right and backwards(from the perspective of the image). The PLA material from the 3D-prints also seems to bend a little, causing the robot to lean towards the left. I have tried many ways to fix it, but it is to no avail.

This is my first question on the Robotics Stack Exchange page. Sorry for possible bad/inaccurate terminology. Any help or suggestions will be greatly appreciated.

答:
Humanoid robots balance and motion planning are not trivial tasks. I believe you will learn a lot if you read about Zero Moment Point (ZMP). Basically, it is a specific point of contact between the robot's planar foot and the ground. What makes this point special is that the reaction forces at it produce zero torque on the robot body. If there is no reaction torque on the robot body, it will not fall.

How to calculate it?
We need to know the positions of the center of mass of each body part as well as their orientations and velocities.

Please, start from this thesis. It explains the concept very well.
https://www.techunited.nl/media/files/humanoid/MaartenDekker_OPEN2009_Zero_Moment_Point_Method_for_Stable_Biped_Walking.pdf


问:
Can workspace of a manipulator change after considering its dynamics?
As per my knowledge, the workspace of a manipulator can be determined if one knows the forward and inverse kinematics. But is it possible that the workspace of the manipulator can change after considering the effects of dynamics?

In other words, can I determine the workspace of a manipulator only from its kinematic model? Or do I need to consider the dynamic model as well?

答:
I think it depends on how much useful life you want to obtain from the robot.

It is possible that a perfectly acceptable kinematic solution within your target workspace without regard to dynamics could result in moments, velocities or accelerations that could cause one or more actuators to operate well beyond their rated torques.  By not imposing any dynamic constraints on your workspace you are basically risking unnecessary damage to your manipulator's hardware.


问:
what kind of processing is required on raw IMU data before it is fed into a filter?
Im working on quadcopter. At this stage im coding a reference system for quadcopter using 10DOF board.At this stage im at the point of only getting raw data values from accelerometer, gyroscope, & magnetometer.

After searching around on web, I have rought idea of what I need to do, I need to implement a filter system that can help me determine accurate and stable position estimates from sensor data. Im planning on implementing kalman or Magwick based filter.

My Question:

1) Before I go into filter design itself, I want to know do I just use the raw data from sensor ' as it is ' and feed it into filter system ? Or Do i need to some sort of preprocessing on data for filter to use ?

2) My second question is, after just glancing over some code on web, I saw alot of people adding some constants to their raw sensor data (i.e offsets? ) to smooth or correct raw sensor data. What kind of smoothing/correcting do i need to account for ? How does it relate to imu sensors ?

答:
1) Before I go into filter design itself, I want to know do I just use the raw data from sensor ' as it is ' and feed it into filter system ? Or Do i need to some sort of preprocessing on data for filter to use ?

Both happens. In my applications(Visual-Inertial SLAM) we tend to just use the raw measurements. Note that the fusion algorithm(Kalman filters or Complementary filters) essentialy work as some sort of preprocessing step.

2) My second question is, after just glancing over some code on web, I saw alot of people adding some constants to their raw sensor data (i.e offsets? ) to smooth or correct raw sensor data. What kind of smoothing/correcting do i need to account for ? How does it relate to imu sensors ?

The offset you are seeing is called the bias. This is required for good state estimation. Essentially it contains all the extra parameters that effect your readings, but that you are too lazy to model. You estimate these along with the rest of your states.

Therefore your state vector would typically be something like:

$$ [p,v,q,a_b,g_b,m_b]$$

$p$ position

$v$ velocity

$q$ quaternion/attitude

$a_b$ accelerometer bias

$g_b$ gyro bias

$m_b$ magnetometer bias

To then get your actual measurement you subtract the bias.

So

$$ a_{real}=a_{raw}-a_{bias}$$


答:
Well, you can't estimate the covariance from the state.  You need the equations used to find the state.

This is because the covariance (along with the markov assumption) represents how the robot got to the state.   A robot at location (10,5,1) does not always have the same covariance matrix, right? You need to know what actions brought it to that state.

Barring any ros-based solution (are you sure the SLAM / EKF nodes can't do this for you?) you'll have to do this:

Find the kinematics model for the robot (differential drive, ackerman, whatever)

Read in the odometery and control command, and use the kinematics equations and control inputs as the u and f() for the EKF (using this) as a reference.

Find the Jacobian of the kinematics equations with respect to each state element

Feed the Jacobian and control / odometery into the EKF as the F term (using this) as a reference.

In case you are wondering, yes this effectively reproduces the work done by the /odom node, since the output of the kinematics equations is precisely what /odom reports.

Title: Calculate covariance matrix from x,y,z data

Body:
In ROS I've recorded a bag file from a custom robot (in real world) that does not provide covariance matrix and I want to use /odom to feed an EKF, but covariance matrix is 0. How can I calculate it?

Note:
Covariance matrix is needed by EKF to estimate position.

It's a sample of /odom:

```<code>pose:
  pose:
    position:
      x: 0.082
      y: 0.507
      z: 0.0
    orientation:
      x: 0.0
      y: 0.0
      z: -0.789272088731
      w: 0.614043622188
  covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
twist:
  twist:
    linear:
      x: 0.104
      y: 0.0
      z: 0.0
    angular:
      x: 0.0
      y: 0.0
      z: 0.0663225115758
  covariance: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
</code>```


问:
Visible light sensors for line follower
I'm making a second version of my line follower robot and I'm thinking what are the line sensors going to look like.

In the previous version I've successfully used IR LEDs and IR phototransistors, but I've heard stories about lines printed on an inkjet printer not being detected by some IR sensors and that got me started thinking about basing my new line sensor on visible light.

I think that the sun is going to be pretty strong in the near IR too and I'm subtracting ambient light from the signals anyway.

Rangefinders on cameras and other robots all use some IR wavelengths, so I'd be affected by these too. In visible light I'll at least have a chance to see the noise.

If I see a line as black with my eyes, this means that a visible light based sensor will be able to follow it. In IR this is not so clear.

Easy to determine if the sensor works or not.

What are the downsides of using visible light?

答:
The Danish line follower robot competition "DTU RoboCup" recommends using visible light sensors in their competition, since IR sensors have a hard time distinguishing the floor from the line. I've participated in this comepetition, and seen quite some IR-using robots getting confused and wandering off.

My experience is that using visible light has one major downside that has to be dealt with. You mention it yourself: Sun light/daylight/ambient light. And since you're already dealing with that, I think you're covering the single most significant downside already.

Title: Visible light sensors for line follower


问:
How can i fusion gyroscope and accelerometer when accelerometer has only roll,pitch val?
I'm currently studying IMU Sensor with 6dof(gyro and accelerometer).
My goal is to get Orientation of robot, and i found out that only 6dof imu sensor wouldn't be
possible to get a correct Orientation due to noise and gyroscope yaw drift(also roll and pitch).

But, I just want it to try with errors. (Working on the ROS.)

so I managed to get data from my IMU sensor data which gyroscope's R,P,Y and Acceleromter's R,P.
The Problem was that i don't know how to get a quaternion for some other library(package),
so i just used gyro's yaw value and accelerometer's roll and pitch value to find
quaternion.

Other fusion algorithms such as EKF,Complementary Filter ask linear_acc, angular_v, and Quaternion. But I don't feel like my ways to get quaternion is the right ways to do it.

So, Am i doing something wrong?
How can i mix(or use) gyroscope's roll, pitch, yaw data and acclerometer's roll,pitch,yaw(?) data to get a quaternion that fusion algorithms ask?

some information :
I'm using Microstrain 3dm-cx5-15, using ros, managed to publish sensor_msgs/Imu
(with not quite correct quaternion)
thx for the replies, any insight will help me
sorry for my bad english.

答:
First you have to understand that Gyroscope gives angular rates around X,Y,Z axes and Accelerometer gives linear accelerations in X,Y,Z directions. Neither of them gives Roll, Pitch and Yaw values. But you can calculate Roll and Pitch values using the Accelerometer readings.

For your question, in order to calculate the quaternion you need to have a 9 DoF IMU to feed data to both prediction and update step in the Kalman filter. I suggest you to follow the Probabilistic Robotics Book to get a better idea on this.

Using a 6 DoF IMU, you can calculate Roll and Pitch using a Kalman Filter. But the gyro provides data only for the prediction step when calculating the yaw. An external magnetometer or a 9 DoF IMU can provide the absolute yaw for the update step.


问:
How do "process gain" and "proportional gain" affect the Steady State Error in a PID Controlled machine?
I came across this doubt while reading the Wikipedia page on PID Control. The section on Steady-state error states:

Steady-state error (SSE) is proportional to the process gain and
  inversely proportional to proportional gain.

First of all I'm not able to understand what "process gain" actually means and how it differs from proportional gain. Secondly, I can't understand the relation between SSE and the two gains.

PS. I haven't taken a formal course on Control Systems and I'm just a hobbyist trying to make a PID controlled line follower at home. So, it might be the case that I've totally missed something important.

答:
To understand this, take a look at this image, also from the Wikipedia article:

The “process gain” is not part of your PID controller. Rather, it is a representation of what is actually happening in your process. Consider a heater control. The signal you are measuring is temperature, but the output of your controller would be something to turn your heater on and off (perhaps a pulse width modulation duty cycle). The “process gain” is simply a mathematical representation of how the actual temperature responds to what your heater is doing.

The article is saying that steady state error gets bigger if the process gain gets bigger, but gets smaller as you turn up the proportional gain in your PID controller.


问:
Solving for rotation matrix: Aligning vector a with vector b and then rotating around vector b
Currently working on a quadcopter simulation. I have a desired thrust vector,

```<code>t =sin(30)cos(45)a1+sin(30)sin(45)a2+cos(30)a3
</code>```

and desired yaw angle,

```<code>psi = 45.
</code>```

Because we are working with an under-actuated system, I am trying to solve for the rotation matrix that aligns the vector:b3 = [0,0,1] (direction of thrust in the body-fixed frame of reference) with the direction of t. Here is my code:

```<code>t = [sind(30) * cosd(45), sind(30) * sind(45), cosd(30)];

b3 = [0;0;1];

psi = 45;

syms phi theta;

eqn = [[cosd(psi)*cosd(theta)-sind(phi)*sind(psi)*sind(theta), -
cosd(phi)*sind(psi), cosd(psi)*sind(theta)+cosd(theta)*sind(phi)*sind(phi);
cosd(theta)*sind(psi)+cosd(psi)*sind(phi)*sind(theta), cosd(phi)*cosd(psi),
sind(psi)*sind(theta)-cosd(theta)*sind(phi)*cosd(psi);
 -cosd(phi)*sind(theta), sind(phi), cosd(phi)*cosd(theta)]
* b3 == t/norm(t)];

S = solve(eqn,[phi theta]);
disp(S.phi)
disp(S.theta)`
</code>```

The problem is that it outputs an empty symbolic struct, with no warnings or errors otherwise:

What I am doing wrong? It is an issue with my theory, my implementation, or both?

答:
Analytic Solution:</p>

```<code>t = [sind(30) * cosd(45), sind(30) * sind(45), cosd(30)];
b = [0;0;1];
psi = deg2rad(45);

v = cross(b,t);
s = norm(v);
c = dot(b,t);

phiB = psi;
uB = b;
uBhat = vector2ssMat(uB);

I = eye(3);

rotA = I+vector2ssMat(v)+(vector2ssMat(v)^2)*((1-c)/s^2);
rotB = I*cos(phiB)+uB*transpose(uB)*(1-cos(phiB))+uBhat*sin(phiB);

Rdes = rotA*rotB;
disp(Rdes);
</code>```

Body:
Analytic Solution:

t = [sind(30) * cosd(45), sind(30) * sind(45), cosd(30)];
b = [0;0;1];
psi = deg2rad(45);

v = cross(b,t);
s = norm(v);
c = dot(b,t);

phiB = psi;
uB = b;
uBhat = vector2ssMat(uB);

I = eye(3);

rotA = I+vector2ssMat(v)+(vector2ssMat(v)^2)*((1-c)/s^2);
rotB = I*cos(phiB)+uB*transpose(uB)*(1-cos(phiB))+uBhat*sin(phiB);

Rdes = rotA*rotB;
disp(Rdes);


问:
Finding quaternion Q, where Y = q * X * q.inv()

Problem

Quaternions $X$, $Y$, which represents 3-dimensional points are given.
Quaternion $q$ is what is to be solved.

I want to know unknown rotation between $X$ and $Y$.
In other words, I want to find $q$ in the following equation.

$$
Y = q * X * \overline{q}
$$

$\overline{q}$ means conjugate quaternion of $q$.
Real parts of $X$, $Y$ are always zero.

How can I find $q$ mathematically?

答:
Your question is related to Wahba's problem, which initially is defined for rotation matrices but can also be defined for unit quaternions and can be solved with Davenport's q-method. However, in order to have a uniquely defined rotation it is required to have at least two pairs of vectors.

You only have one pair of vectors. The second pair could be chosen as the rotation axis, which should remain unchanged during the rotation. For this rotation axis you can choose any unit vector which is equidistant to both vectors of the first pair. When denoting the vector/imaginary parts normalized to unit length of $X$ and $Y$ as $x$ and $y$ respectively then the rotation axis $r$ can be parameterized using

$$
\begin{align}
n &= \frac{x \times y}{\|x \times y\|}, \\
m &= \frac{x + y}{\|x + y\|}, \\
r &= \cos(\phi)\,n + \sin(\phi)\,m.
\end{align}
$$

The smallest and largest resulting rotation (in terms of rotation angle) are obtained using $\phi=0$ and $\phi=\pi/2$ respectively, with $n$ and $m$ as axis of rotation respectively. The corresponding unit quaternions are

$$
q = \begin{bmatrix} m\cdot x \\ \|x \times m\|\,n\end{bmatrix}, \quad
q = \begin{bmatrix} 0 \\ m\end{bmatrix},
$$

respectively. It can be noted that this method will fail if $x$ and $y$ are linear dependent (i.e. $y = \pm x$). When $y = x$ then any rotation with $x$ as rotation axis would suffice, so $q = \begin{bmatrix}\cos(\theta) & \sin(\theta)\,x^\top\end{bmatrix}^\top$. When $y = -x$ then any rotation of $\pi$ radians around any axis $t$, which is perpendicular to $x$, would suffice, so $q = \begin{bmatrix}0 & t^\top\end{bmatrix}^\top$.


答:
The short answer is yes, but the long answer is that you're approaching the code the wrong way and will need to rewrite things a bit.

It looks like you're attempting to read a button and have it flash some LEDs while at the same time having your stepper move back and forth.  The problem is your delay(5); commands, which pause the execution of your code.

Instead, you'll have to make use of the millis(); function to read the current time at each iteration through the loop.  Both the stepper movement and the LED flashes are functions of time, so just write those functions and set them on every iteration.

```<code>long stepperPosition(long t)
{
    // YOUR CODE GOES HERE, like a sine or triangular waveform
    // it might also depend on stepper1.distanceToGo() == 0
    return sine(t * TIME_SCALE_FACTOR) * MAGNITUDE;
}

bool ledState(long t)
{
    // return HIGH if we're in the first "interval" millis after pressing
    // after that, return LOW to turn off the LED
    return (t < buttonPressTime + interval) ? HIGH : LOW;
}

void loop()
{
    long now = millis();

    // set the LED directly from the button state
    digitalWrite(startScan, LOW == digitalRead(buttonPin));

    // record the time of the button press for use in our function
    if (digitalRead(button2Pin) == LOW) {
        buttonPressTime = now;
    }

    // set LED based on time function
    digitalWrite(pulseopto1, ledState(now));

    // set stepper based on time function
    stepper1.moveTo(stepperPosition(now));
    stepper1.run();
}
</code>```

In this code, both the LED and stepper are controlled "in parallel" -- they are both set during each iteration, with no conditional delays.

Title: Is it possible to run multiple loops at the same time? (Arduino)

Body:
I've got a code where I have a motor running back and forth and buttons connected to a scanner, when I press the buttons it causes the motor to stop  and over rides it. I would like them to run parallel to each other so the codes don't interrupt each other.

Here is my code

```<code>#include <AccelStepper.h>

// Define some steppers and the pins they will use

AccelStepper stepper1(AccelStepper::FULL2WIRE, 2, 3);

const int buttonPin = 4;
const int button2Pin = 14;
const int pulseopto1 = 9;
const int startScan = 11;

int buttonState = 0;
long previousMillis = 0;
long interval = 5;

void setup()
{
    pinMode(buttonPin, INPUT);
    pinMode(button2Pin, INPUT);
    pinMode(pulseopto1, OUTPUT);
    pinMode(startScan, OUTPUT);
    stepper1.setMaxSpeed(40000.0);
    stepper1.setAcceleration(100000.0);
    stepper1.moveTo(25000);
}

void loop()
{
    buttonState = digitalRead(buttonPin);

    if (buttonState == LOW)
    {
        digitalWrite(startScan, HIGH);
    }
    else (buttonState == HIGH);
    {
        digitalWrite(startScan, LOW);
    }

    {
        buttonState = digitalRead(button2Pin);

        if (buttonState == LOW)
        {
            // turn LED on:
            digitalWrite(pulseopto1, HIGH);
            delay(5);
            digitalWrite(pulseopto1, LOW);
            delay(5);
        }
        else
        {
            // turn LED off:
            digitalWrite(pulseopto1, LOW);
        }
    }

    // Change direction at the limits
    if (stepper1.distanceToGo() == 0)
    {
        stepper1.moveTo(-stepper1.currentPosition());
    }

    stepper1.run();
}
</code>```

Body:
The short answer is yes, but the long answer is that you're approaching the code the wrong way and will need to rewrite things a bit.

It looks like you're attempting to read a button and have it flash some LEDs while at the same time having your stepper move back and forth.  The problem is your delay(5); commands, which pause the execution of your code.

Instead, you'll have to make use of the millis(); function to read the current time at each iteration through the loop.  Both the stepper movement and the LED flashes are functions of time, so just write those functions and set them on every iteration.

long stepperPosition(long t)
{
    // YOUR CODE GOES HERE, like a sine or triangular waveform
    // it might also depend on stepper1.distanceToGo() == 0
    return sine(t * TIME_SCALE_FACTOR) * MAGNITUDE;
}

bool ledState(long t)
{
    // return HIGH if we're in the first "interval" millis after pressing
    // after that, return LOW to turn off the LED
    return (t < buttonPressTime + interval) ? HIGH : LOW;
}

void loop()
{
    long now = millis();

    // set the LED directly from the button state
    digitalWrite(startScan, LOW == digitalRead(buttonPin));

    // record the time of the button press for use in our function
    if (digitalRead(button2Pin) == LOW) {
        buttonPressTime = now;
    }

    // set LED based on time function
    digitalWrite(pulseopto1, ledState(now));

    // set stepper based on time function
    stepper1.moveTo(stepperPosition(now));
    stepper1.run();
}

In this code, both the LED and stepper are controlled "in parallel" -- they are both set during each iteration, with no conditional delays.


问:
Difference between SLAM and Localization
What are the differences between SLAM and Localization if you have a known map? How does the position probability update differ between the two?

In particular I'm curious on if localization is a fundamentally easier problem give you have map.

I have a virtual environment with a simulated LIDAR sensor and a known map. I want to try implementing localization but I'm not completely sure how to do it - you can do IPCM over the whole space but this is a very poor idea if you have a strong prior on where the robot started a sequence of sensor values/actions up until your current location.

答:
Localization is always done with respect to a map.

SLAM(Simultaneous Localization and Mapping). As it is in the name, also does localization with respect to a map. The only difference is that the map is unavailable so it has to create it. It is simultaneously creating a map, and then localization itself against it.

How does the position probability update differ between the two?

Mathematically they end up being the same thing. Just that in SLAM typically the uncertainty of the map is a factor in the position probability. (Should be done in localization to, but is sometimes ignored)

In particular I'm curious on if localization is a fundamentally easier problem give you have map.

Yes. As the map can be created offline you can spend a lot more computing power, and do a lot more refinement to ensure you have a high quality map. SLAM basically has to do everything in real time.

This is why actually most autonomous car companies don't do SLAM, they do mapping and localization separately. They first run there cars through a city to generate high quality maps. Then when it is running autonomously it is just localizing itself against that map.

If you have a strong prior then just remove the parts of the pointcloud that you know will not be in range. You also want to make sure that your ICP algorithm is robust against outliers.


问:
Hand eye calibration wrong result
I am trying to do hand eye calibration between the robotic arm end effector and Kinect V1. I am using handeye_calib_camodocal repository for calibration and trying to find $X$ by solving the equation:  $$AX=XB$$

I have following 82 transform pairs:

I have following support/holder between Kinect and Robotic arm :

But i am getting the following translation in meters which is wrong because camera can not be in such a distance from end effector:

```<code>Translation: 0.690257 0.102063 0.459878
</code>```

I have following launch file:

```<code><launch>
  <!-- TF names-->
  <arg name="EETF"              default="/tool0_controller" />
  <arg name="baseTF"            default="/base_link" />
  <arg name="ARTagTF"           default="/chess" />
  <arg name="cameraTF"          default="/camera_rgb_frame" />
  <!--<arg name="data_folder"       default="$(find hand_eye_calib)/launch" />-->
  <arg name="data_folder"       default="$(find hand_eye_calib)" />
  <arg name="filename"          default="TransformPairs_HEcalibInput.yml" />
  <arg name="calibrated_filename"          default="CalibratedTransform_HEcalibOutput.yml" />

  <!-- running handeeye_calib_camodocal node-->
  <node pkg="hand_eye_calib" type="hand_eye_calib" name="hand_eye_calib" output="screen">
  <!-- hand_eye_calib arg pass -->
    <param name="ARTagTF"       type="str" value="$(arg ARTagTF)" />
    <param name="cameraTF"      type="str" value="$(arg cameraTF)" />
    <param name="EETF"          type="str" value="$(arg EETF)" />
    <param name="baseTF"        type="str" value="$(arg baseTF)" />
    <param name="load_transforms_from_file" type="bool" value="false"/>
    <param name="transform_pairs_record_filename" type="str" value="$(arg data_folder)/$(arg filename)" />
    <!-- <param name="transform_pairs_load_filename"  type="str" value="$(arg data_folder)/$(arg filename)" /> -->
    <param name="output_calibrated_transform_filename" type="str" value="$(arg data_folder)/$(arg calibrated_filename)" />
  </node>

  <!-- running camera_to_ros node-->
  <!-- node pkg="camera_to_ros" type="camera_to_ros" name="camera_to_ros" output="screen"/-->

  <!-- running extrinsic_calibration node-->
  <node pkg="camera_calibration" type="extrinsic_calibration" name="extrinsic_calibration" output="screen"/>

  <!-- running DK node-->

</launch>
</code>```

答:
If the rotation looks right but translation look deviated, noise might be an issue. Usually rotations are more robust to noise than translations when using the calibration equations.

Synthetic data might be a good choice to debug.

Title: Hand eye calibration wrong result


问:
Good Software Projects for Aspiring Engineers
With the pandemic going on, our school has decided to officially shutdown all sponsored club meetings. Now that we are all digital, our robotics club was looking for projects that students can do at home on their computers.

Do you have any suggestions for projects that you think do a good job at teaching important robotics concepts? So far many of the ideas we have revolve around computer vision and implementing path finding algorithms, however we just want to make sure we give our students as many options as possible.

Thanks!

答: I guess famous toy problems in the robotics/control literature which could be easily implemented in Matlab or Python are Linear Inverted Pendulum, Two-link robotic arm, and Cart-pole problem. If you need any further help with these topics let me know.</p>


问:
Arduino and stepper motor
How arduino stepper library handles stepper motor with ULN2003? I mean, for controlling stepper motor one actually needs four pwms. It can be reduced to two by encoding, or maybe even to one by using other techniques. But could you, please, clarify how arduino does it? I could not find the low level code for that from arduino to inspect.</p>

答:
Here is Arduino library code specifically for the 28BYJ-48 stepper motor using the ULN2003 controller board.  The readme discusses the various driver modes and how they work.  It's not done with PWM like a servo motor (where PWM is a repeating square wave control signal). Rather it is done by energizing the coils in the motor such that they attract to magnets.  Energizing in a specific pattern will move the motor from magnet to magnet, thus turning the spindle.

There is an example sketch for implementing a differential drive robot using two 28BYJ-48 stepper motors.  There is an example sketch that uses a LM393 optical encoder to calculate the steps per rotation of the stepper motor.

Title: Arduino and stepper motor


问:
Inverse Kinematics for 4 legged robots and body translation
The prompt here is to implement the IK on a real robot with constraints. I started off with writing the DH-Table.

$\begin{bmatrix}n & a_{i-1} & \alpha_{i-1} & d_i & \theta_i
\\1 & 0 & 0 & 0 & \theta_1 \\ 2 & a_1 & \frac{pi}{2} & 0 & \theta_2 \\ 3 & a_3 & 0 & 0 & \theta_3 \\ 4 & a_3 & 0 & 0 & 0 \end{bmatrix}$

I've deduced this DH table into its corresponding matrices and solved them for $\theta_1$, $\theta_2$ and  $\theta_3$. ( The math isn't shown here. )

I arrive at these 3 equations.

$$  \theta_1 = tan^{-1}\frac{P_y}{P_x}$$
$$  \theta_2 = ....$$
$$ \theta_3  = ...$$

Now the question is to place the legs on the edge of the robot's body.

1.) Is there material out there that explains how the frame can be translated from the center of the body to the legs?

2.) Is there any material out there that explains Body IK and how it can be implemented for Roll Pitch and Yaw? and how to develop gaits for a robot like this in terms of code?

any information, tips, and suggestions are much welcome. I've also placed a python script where the user can control the position of the leg using IK.

```<code>import math
import numpy as np
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider, RadioButtons

LINK_1 = 60 + 60
LINK_2 = 110
LINK_3 = 120

"""
Place simulation on leg and check. Makes sure the image is inverted on the real leg.
"""

def constrain(val, min_val, max_val):
    return min( max_val, max(min_val, val) )

class Joint:
    def __init__(self, link_1, link_2, link_3):
        self.link_1 = link_1
        self.link_2 = link_2
        self.link_3 = link_3

        self.theta_1 = -1
        self.theta_2 = -1
        self.theta_3 = -1

def FK( joint ):
    T0_1 = np.array([
        [math.cos(joint.theta_1), -math.sin(joint.theta_1), 0, 0],
        [math.sin(joint.theta_1),  math.cos(joint.theta_1), 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 1 ]
        ])
    T1_2 = np.array([
        [math.cos(joint.theta_2), -math.sin(joint.theta_2), 0, joint.link_1],
        [0, 0, -1, 0],
        [math.sin(joint.theta_2),  math.cos(joint.theta_2), 0, 0],
        [0, 0, 0, 1]
        ])
    T2_3 = np.array([
        [math.cos(joint.theta_3), -math.sin(joint.theta_3), 0, joint.link_2],
        [math.sin(joint.theta_3),  math.cos(joint.theta_3), 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 1]
        ])
    T3_4 = np.array([
        [1, 0, 0, joint.link_3],
        [0, 1, 0, 0],
        [0, 0, 1, 0],
        [0, 0, 0, 1]
        ])

    POINT_SET_1 = np.dot( T0_1, T1_2 )
    POINT_SET_2 = np.dot( POINT_SET_1, T2_3 )
    POINT_SET_3 = np.dot( POINT_SET_2, T3_4 )
    return [POINT_SET_1, POINT_SET_2, POINT_SET_3]

def leg2( x, y, z ):
    #Final calculation resulting in smooth motion.

    theta_1 = math.atan2(y, x)
    A = z
    B = math.cos(theta_1) * x + y * math.sin(theta_1) - LINK_1
    C = ((math.pow(A, 2) + math.pow(B,2) - math.pow(LINK_3, 2) - math.pow(LINK_2,2)) / (2 * LINK_3 * LINK_2))
    theta_3 = math.atan2( math.sqrt( 1 - math.pow( C,2 )), C)
    D = math.cos(theta_3) * LINK_3 + LINK_2
    E = math.sin(theta_3) * LINK_3

    numerator = (A * D - B * E) / (math.pow(E,2) + math.pow(D,2) )
    denominator = 1 - math.pow(numerator,2)

    theta_2 = math.atan2(numerator, math.sqrt(denominator))
    return [ math.degrees(theta_1), math.degrees(theta_2), math.degrees(theta_3)]

def filterEndPoints( point_set ):
    return (
        [
          [point_set[0][0][3], point_set[0][1][3], point_set[0][2][3]],
          [point_set[1][0][3], point_set[1][1][3], point_set[1][2][3]],
          [point_set[2][0][3], point_set[2][1][3], point_set[2][2][3]]
        ]
    )

fig  = plt.figure()
ax = plt.axes(projection='3d')
ax.set_xlim3d(-300, 300)
ax.set_ylim3d(-300, 300)
ax.set_zlim3d(-300, 300)
ax.set_xlabel("X-axis")
ax.set_ylabel("Y-axis")
ax.set_zlabel("Z-axis")
axe = plt.axes([0.25, 0.85, 0.001, 0.001])

axxval = plt.axes([0.35, 0.9, 0.45, 0.03])
axyval = plt.axes([0.35, 0.93, 0.45, 0.03])
axzval = plt.axes([0.35, 0.96, 0.45, 0.03])
a0_val = Slider(axxval, "X", -300, 300, valinit=180)
a1_val = Slider(axyval, "Y", -300, 300, valinit=180)
a2_val = Slider(axzval, "Z", -300, 300, valinit=180)

x_value = 0
y_value = 0
z_value = 0

joint =  Joint(60+60, 110, 120)
joint_values = leg2( 100, 100, 100 )
joint.theta_1 = joint_values[0]
joint.theta_2 = joint_values[1]
joint.theta_3 = joint_values[2]
link_1 = filterEndPoints(FK( joint ))
originPoints = [0, 0, 0]

def returnConstrainedAngles( joint_values ):
    theta_1 = -constrain(joint_values[0], 0.0, 180.0)
    theta_2 = -constrain(joint_values[1], 0.0, 180.0)
    theta_3 = -constrain(joint_values[2], 0.0, 180.0)
    # theta_1 = -joint_values[0]
    # theta_2 = -joint_values[1]
    # theta_3 = -joint_values[2]
    return [math.radians(theta_1), math.radians(theta_2), math.radians(theta_3)]

def update_ao_val(val):
    global link_1
    global x_value, y_value, z_value
    x_value = int(val)

    try:
        #joint_values = leg(x_value, y_value, z_value)
        joint_values = leg2(x_value, y_value, z_value)
    except Exception as e:
        return

    joint.theta_1 = returnConstrainedAngles( joint_values )[0]
    joint.theta_2 = returnConstrainedAngles( joint_values )[1]
    joint.theta_3 = returnConstrainedAngles( joint_values )[2]
    serial_print = " T1 : "+ str( abs( round( math.degrees( joint.theta_1 ) ) ) )  + " T2: " + str( abs( round( math.degrees( joint.theta_2 ) ) )) + " T3 : "+ str( abs( round( math.degrees( joint.theta_3 ) ) ))
    print( serial_print )

    link_1 = filterEndPoints(FK( joint ))
    print( str(link_1[0][2]) + " : " + str(link_1[1][2]) + " : " + str(link_1[2][2]) )
    fig.canvas.draw_idle()
    ax.clear()
    ax.set_xlabel("X-axis")
    ax.set_ylabel("Y-axis")
    ax.set_zlabel("Z-axis")
    ax.set_xlim3d(-300, 300)
    ax.set_ylim3d(-300, 300)
    ax.set_zlim3d(-300, 300)
    ax.plot3D( [ -50, 50, 50, -50, -50 ], [ -50, -50, 50, 50, -50 ], [ 0, 0, 0, 0, 0 ], "-r*" )
    ax.plot3D( [ originPoints[0], link_1[0][0], link_1[1][0], link_1[2][0] ],
               [ originPoints[1], link_1[0][1], link_1[1][1], link_1[2][1] ],
               [ originPoints[2], link_1[0][2], link_1[1][2], link_1[2][2] ], "-go" )
    fig.canvas.draw()

def update_a1_val(val):
    global link_1
    global x_value, y_value, z_value
    y_value = int(val)
    try:
        #joint_values = leg(x_value, y_value, z_value)
        joint_values = leg2(x_value, y_value, z_value)
    except NoneType as e:
        return
    joint.theta_1 = returnConstrainedAngles( joint_values )[0]
    joint.theta_2 = returnConstrainedAngles( joint_values )[1]
    joint.theta_3 = returnConstrainedAngles( joint_values )[2]
    serial_print = " T1 : "+ str( abs( round( math.degrees( joint.theta_1 ) ) ) )  + " T2: " + str( abs( round( math.degrees( joint.theta_2 ) ) )) + " T3 : "+ str( abs( round( math.degrees( joint.theta_3 ) ) ))
    print( serial_print )
    link_1 = filterEndPoints(FK( joint ))
    print( str(link_1[0][2]) + " : " + str(link_1[1][2]) + " : " + str(link_1[2][2]) )
    ax.clear()
    ax.set_xlabel("X-axis")
    ax.set_ylabel("Y-axis")
    ax.set_zlabel("Z-axis")
    ax.set_xlim3d(-300, 300)
    ax.set_ylim3d(-300, 300)
    ax.set_zlim3d(-300, 300)
    ax.plot3D( [ -50, 50, 50, -50, -50 ], [ -50, -50, 50, 50, -50 ], [0, 0, 0, 0, 0], "-r*" )
    ax.plot3D( [ originPoints[0], link_1[0][0], link_1[1][0], link_1[2][0] ],
               [ originPoints[1], link_1[0][1], link_1[1][1], link_1[2][1] ],
               [ originPoints[2], link_1[0][2], link_1[1][2], link_1[2][2] ], "-go" )
    fig.canvas.draw()

# 228 81
# 116 169
def update_a2_val(val):
    global link_1
    global x_value, y_value, z_value, radius_value
    z_value = int(val)
    try:

        joint_values = leg2(x_value, y_value, z_value)
    except NoneType as e:
        return
    joint.theta_1 = returnConstrainedAngles( joint_values )[0]
    joint.theta_2 = returnConstrainedAngles( joint_values )[1]
    joint.theta_3 = returnConstrainedAngles( joint_values )[2]
    serial_print = "T1: "+ str( abs( round( math.degrees( joint.theta_1 ) ) ) )  + " T2: " + str( abs( round( math.degrees( joint.theta_2 ) ) )) + " T3 : "+ str( abs( round( math.degrees( joint.theta_3 ) ) ))
    print( serial_print )
    link_1 = filterEndPoints(FK( joint ))
    print( str(link_1[0][2]) + " : " + str(link_1[1][2]) + " : " + str(link_1[2][2]) )
    ax.clear()
    ax.set_xlabel("X-axis")
    ax.set_ylabel("Y-axis")
    ax.set_zlabel("Z-axis")
    ax.set_xlim3d(-300, 300)
    ax.set_ylim3d(-300, 300)
    ax.set_zlim3d(-300, 300)
    ax.plot3D( [ -50, 50, 50, -50, -50 ], [ -50, -50, 50, 50, -50 ], [0, 0, 0, 0, 0], "-r*" )
    ax.plot3D( [ originPoints[0], link_1[0][0], link_1[1][0], link_1[2][0] ],
               [ originPoints[1], link_1[0][1], link_1[1][1], link_1[2][1] ],
               [ originPoints[2], link_1[0][2], link_1[1][2], link_1[2][2] ], "-go" )
    fig.canvas.draw()

def generateTrajectorySemicircle( radius ):
    theta = np.linspace(0, -np.pi, 100)
    r     = np.sqrt( radius )
    x1    = r   * np.cos( theta )
    x2    = 75  + r * np.sin( theta )
    x3    = 170 - np.linspace(0, 0, 100)
    return [ x1, x2, x3 ]

def plotGraph( graphInstance, currentPoint, trajectoryPoints ):
  graphInstance.clear()
  graphInstance.plot3D( trajectoryPoints[2], trajectoryPoints[0], trajectoryPoints[1], "*", markerSize="4")

  #Setting limits
  graphInstance.set_xlim3d(-200, 200)
  graphInstance.set_ylim3d(-200, 200)
  graphInstance.set_zlim3d(-200, 200)

  graphInstance.set_xlabel("X-axis")
  graphInstance.set_ylabel("Y-axis")
  graphInstance.set_zlabel("Z-axis")
  graphInstance.plot3D( [ -50, 50, 50, -50, -50 ], [ -50, -50, 50, 50, -50 ], [0, 0, 0, 0, 0], "-r*" )
  graphInstance.plot3D(
      [ 100, currentPoint[0][0], currentPoint[1][0], currentPoint[2][0] ],
      [ 0, currentPoint[0][1], currentPoint[1][1], currentPoint[2][1] ],
      [ 0, currentPoint[0][2], currentPoint[1][2], currentPoint[2][2] ], "-go")

  plt.pause(0.00001)

points = generateTrajectorySemicircle( 1000 )

a0_val.on_changed(update_ao_val)
a1_val.on_changed(update_a1_val)
a2_val.on_changed(update_a2_val)
plt.show()
</code>```

答: For frame transformations, you need a homogenous transformation matrix (which consists of the rotation matrix and position vector between your reference frame and the target frame). I would suggest "Foundations of Robotics: Analysis and Control" by Tsuneo Yoshikawa if you can access it, or just google how to find a transformation matrix.</p>


问:
Cheapest 3D printer for gears?
It would be incredibly useful if I could print my own gearing solutions, even if I have to print the gears one at a time.  However, I do not know how the market's cheapest printers will accommodate this task.

--The gears need be 2-3 inches in diameter, and they will bear only a very light load (much less than 1 foot pound), so the material need not be strong or machinable.

--The tolerances need only be sufficient for the teeth to mate robustly, preventing any hang.  Unfortunately, I do not have a sense of what tolerances will allow gears to mate properly.

--(If the machine is precise enough to print a hole to statically mate with a shaft of a specified dimensions due to friction, excellent.  If not, I can probably improvise a tiny shaft hole with adhesive.)

--Because this may be used in close proximity to pavement, a melting temperature in excess of 100F is desirable but not required.

--Because any given element will interact kinetically only with other elements that have also been 3D printed (except a metallic shaft), compatibility with external resources is not required.

I would be grateful to anyone who could shed some light on this issue!

答:
I run a rep-rap (Mendel Max) 3d printer ~ $1500AUD (built from scratch).

I can't really comment on the print quality of other printers, but my statements about materials will hold for other printers.

I have printed usable gears down to about 1/2 inch diameter.

PLA has a melting point of about 150 Celsius and starts softening at about 60 Celsius, you could also print in ABS which has a slightly higher melting point but it gives of more toxic gases when heated for printing.

PLA is surprisingly strong, I've stood on a 40mm cube with out it breaking. I recently glued some PLA to a piece of MDF, when separating the materials the MDF broke - NOT the PLA.

There are various gear libraries on thingy-verse which make it easy to create meshing gears of the desired size.

Realistically if you build a 3d printer your self as I did, it will be months before you are printing usable gears.

You can quite reliably print the hole in the center of the gear to take a shaft, as well as printing bearing mounts.

There is a serious learning curve if you build your own 3d printer, that's not a bad thing - just be prepared for it!

Title: Cheapest 3D printer for gears?


答:
edwinem already explains pretty well, let me add a couple of parts.

There are two major scenarios (actually many more) making SLAM more difficult than localization.

1) Revisiting: When creating a map, accumulated errors in a map is inevitable. When a robot revisits a same location after traveling a loop, the robot needs to determine if this environment is a new location or a part of map. This is especially more difficult, if the environment has similar patterns. Unfortunately, our living environment has many similar patterns.

2) Kidnapping: For many reasons, a robot lost its location completely. This case, recreation of a map with data association is a big challenges.

Title: Difference between SLAM and Localization

Body:
What are the differences between SLAM and Localization if you have a known map? How does the position probability update differ between the two?

In particular I'm curious on if localization is a fundamentally easier problem give you have map.

I have a virtual environment with a simulated LIDAR sensor and a known map. I want to try implementing localization but I'm not completely sure how to do it - you can do IPCM over the whole space but this is a very poor idea if you have a strong prior on where the robot started a sequence of sensor values/actions up until your current location.


问:
PID control loop and feedback acquisition synchronization
In many real-time PID control loop application I have seen that the control loop is triggered exactly as the ADC interrupts arrives in order to achieve maximum synchronization between feedback conversion and control loop execution.

I am in a situation where it would be better to have the control loop triggered by a timer interrupt so that it can be indipendent from how the feedback is acquired, for example:

Capacitive encoder read through ADC -> interrupts;

Hall absolute encoder read through I2C -> no interrupts;

In the first case I have a sample available each 4ms. How fast the control loop should be triggered? What are the drawbacks of this implementation? Is it possible to take this issue into account when modeling the digital control system?

Thank you for your time.

Edit 1:

Yes I am controlling a motor that can use different kind of encoders (one at time). Each encoder implements the same software interface so that I can keep motor logic indipendent from encoder internals.

Edit 2:

The rising edges in the image are the instants the feedback is sampled and thus made available by ADC interrupts. The sampling period is 4ms.
Assuming the control loop executes its calculation in the same time as the sampling period, what happens if the control loop interrupt is triggered late in respect to the ADC interrupt?

答:
The answer is highly depends on your application. If your application is a simply trajectory tracking PID control of a mobile robot. Such a timing would be not critical.

However, if your application is a safety critical haptic device for human robot interaction with multi-DOF actuators, then you need to seriously care about all the timing issues. Based on my experience, the control input needs to be generated just after the most critical sensor information.

However, a more important part than sequence is the short loop time for most of control algorithms. In you example, if the control loop is running at 125Hz, the order between control input and ADC would be probably not very critical. Since most of control algorithms would be finished within 1ms which is much shorter than one loop time.


问:
Dual constraint non-linear optimization for SLAM
I am trying to achieve a nonlinear optimization for SLAM on a function that contains two different constraints, like this :
$$
T_{opt} = \underset{T}{\arg\min} \sum_i(\left\lVert T.X_i - x_i \right\rVert)^2 + \sum_j(\left\lVert\pi(T.Y_i) - y_i\right\rVert)^2
$$

T : Affine transformation (Rotation + Translation),

X_i some source's 3d points, x_i are target's 3d points that correspond to X_i,

Y_i some source's 3d points (different than X_i), y_i are target's 2d points that correspond to Y_i,

PI function is the projection of a 3d point to the image plane.

My questions are :

The two constraints have different units, first give an error in meter, second give an error in pixel. Is it a problem for the optimization to minimize two constraints with different scale units ?

Is it a problem if one constraint have lot more data to deal with ? For example, first constraint would have 10 points for the optimization, second constraint would have 50 points. Is it a problem if sets of data have complete different sizes ?

Regards

答:
The two constraints have different units, first give an error in meter, second give an error in pixel. Is it a problem for the optimization to minimize two constraints with different scale units ?

For your problem I think it will be fine(if your optimizer uses Levenberg Marquadt it will implicitly handle it). But generally yes this is a problem you have to deal with any sort of optimization algorithm(not limited to SLAM). The topic is called feature scaling and comes up a lot in machine learning.

Is it a problem if one constraint have lot more data to deal with ? For example, first constraint would have 10 points for the optimization, second constraint would have 50 points. Is it a problem if sets of data have complete different sizes ?

Other things being equal then the constraint with more samples will have a bigger effect on your output. One way to fix this is to add a weighting($\lambda$) parameter that will reduce the effect of that constraint.

So your equation would become something like this

$$T_{opt} = \underset{T}{\arg\min} \sum_i(\left\lVert T.X_i - x_i \right\rVert)^2 + \lambda \sum_j(\left\lVert\pi(T.Y_i) - y_i\right\rVert)^2$$


问:
How do I motorize the elbow socket and other joints in a powered exo-skeleton?
How would you motorize the joints in an Iron Man suit? You need something fairly shallow, I would think, probably dual servos sitting on either side of an elbow or knee joint or either side of your hips, but how do you get motorized action there without dramatically adding to the thickness of the joint?

Bicycle-style chain drives wouldn't work, I would think, since the length of the chain would need to vary depending on what position you're in for at least a lot of joints.

How would you motorize the joints?

答:
I'll chime in.

You could use piezoelectric inchworm motors.  The approach is outlined here: https://www.techbriefs.com/component/content/article/tb/techbriefs/mechanics-and-machinery/2273

you could use a ball robot motor system, upside down with a spherical bearing rather than actuating with a toroidal motor.  The ball robot is well explained here: https://www.youtube.com/watch?v=bI06lujiD7E Bearings are available here: https://www.acornbearings.co.uk/bearings/spherical-roller-bearings.  an omniwheel appraoch is here: https://www.semanticscholar.org/paper/Concept-of-a-three-D.O.F-spherical-joint-gripper-Weyrich-Abdullah/10699c6f1c39a8700c903d407a110910bef53f3c

You could make a rare earth magnet ball rotor with an electromagnetic.  See these two links for a better idea.
 and

or a delta robot through a SR joint.

I'm guessing you are already familiar with stewart plaforms

Hope this helps.  I can sketch stuff if this isn't clear, but I'm not fast enough at CAD to draw this all out.  I didn't post pictures because it seemed terribly slow.

OwnerDisplayName: Erik  Reppen


问:
Kalman-Filter: how to solve angles near +/-pi?
I'm trying to get into Kalman filters. I've noticed an issue with Euler angles near -180°/180° (or -pi/pi) and wonder how to correctly resolve this. Its often said you need to normalize the angles into this range. However, this isn't as easy as it seems at first sight. Especially, when using a Kalman filter class from a library (e.g. OpenCV).

Let's take a very simple examle. We have only a compass sensor, which will give us a heading in the XY-plane. And our state keeps track of the current heading and its first derivative (speed of change).

Matrix Algebra

First, I'll give all information in the typical matrix form to show how this actually relates to the Kalman filter. Later I'll reduce this to simple formulas for better reasoning.

We define our state space vector as:

$x_{k} = \begin{bmatrix} \alpha \\ \dot{\alpha} \end{bmatrix}$

Where $\alpha$ is the current heading in degrees (for simplicity) and $\dot{\alpha}$ is the current change of the heading in degrees per second.

The new a priori state estimate (prediction) is calculated by:

$\hat{x}_{k|k+1} = F_k x_k$
where $F_k = \begin{bmatrix} 1 & \Delta t \\ 0 & 1\end{bmatrix}$

Our observations look like this:

$z_k = \begin{bmatrix} \alpha_{new} \end{bmatrix}$

And the correction / update step looks like this:

$\tilde{y}_k = z_k - H_k \hat{x}_{k|k+1}$ where $H_k = \begin{bmatrix} 1 & 0\end{bmatrix}$

Later the Kalman gain $K_k$ is calculated and influences the state estimate like this:

$\hat{x}_{k+1} = \hat{x}_{k|k+1} + K_k \tilde{y}_k$

Simplifications

$\hat{x}_{k|k+1} = \begin{bmatrix} \alpha + \Delta t \dot{\alpha} \\ \dot{\alpha}\end{bmatrix}$

$\tilde{y}_k = \alpha_{new} - (\alpha + \Delta t \dot{\alpha})$

$\hat{x}_{k+1} = \hat{x}_{k|k+1} + K_k \tilde{y}_k$

Now lets only focus only on the estimation of $\alpha$:

$\alpha_{k|k+1} = \alpha_k + \Delta t \dot{\alpha}_k$

$\alpha_{k+1} = \alpha_{k|k+1} + K_k^1 (a_{new} - \alpha_{k|k+1})$

The Issue and Semi-Solution

For simplicity I'll use angles in degrees. Let's assume, we've got the following situation:

$\alpha_k = 160°$, $\dot{\alpha}_k = 100°/s$, $\Delta t = 0.1 s$, $a_{new} = -170°$ ($\equiv$ 190°) and $K_k^1 = 0.1$.

Hence, $\alpha_{k|k+1} = 160° + 0.1 * 100° = 170°$ and therefore $a_{new} - \alpha_{k|k+1} = -170° - 170° = -340° \equiv 20°$.

The updated estimate would therefore (incorrect estimate) be:

$\alpha_{k+1} = 170° + 0.1 * (-340°) = 170° - 34° = 136°$

If we normalize $\alpha_{k|k+1}$ to [-180°, 180°] we get the equivalent 20° and the result is (correctly estimated):

$\alpha_{k+1} = 170° + 0.1 * 20° = 170° + 2° = 172°$

The normalization would fix the issue. However, if I use a normal Kalman filter class, I usually cannot influence how the innovation $\tilde{z}_k$ is calculated and the filter class cannot know that this is an Euler angle that needs special handling. Thus, I would need to write it my own... Or is there any way around this?
A quick solution might be to use a unit vector $\begin{bmatrix} cos(\alpha) \\ sin(\alpha) \end{bmatrix}$ instead of the euler angle $\alpha$, which I would have to renormalize every now and then.
I know that one could maybe use quaternions for this, but I currently can't grasp the math behind it to use it. Also I saw someone argue that it would be a bad idea to use quaternions in the state space of a Kalman filter (e.g. see comments to this question). This could also apply to my unit vector approach. Hence, I would like you to discuss the issue and give me some clues on how this is usually resolved.

答:
This is an interesting question and I ran into the same issue.
First, let me mention that there are libraries that let you change the residual function (i.e., innovation). For instance, see the residual argument of the update function of the Extended Kalman Filter implementation of filterpy. However, I understand you may be stuck with a given library that does not let you change how the residual is calculated.

You were thinking about substituting the state space variable $\alpha$ by $[\cos(\alpha), \sin(\alpha)]$, which certainly would solve the issue. I went for another solution approach that modifies the sensor values before they get fed into the Kalman filter, i.e., it addresses the problem at the equation $\alpha_\text{new} = -170° \equiv 190°$. The idea is that if your sensor would report $190°$ rather than $-170°$, the calculation of the residual would give the correct value of $20°$.
Therefore, I define a rotation counter variable $r=0$ that gets incremented/decremented each time $\alpha^{\text{s}}$ jumps across the discontinuity (the superscript s indicates that this is the raw sensor value). The jumps are detected via the conditions $\alpha_{t+1}^{\text{s}} - \alpha_t^{\text{s}} > \Delta$ and $\alpha_{t+1}^{\text{s}} - \alpha_t^{\text{s}} < -\Delta$. (I have set $\Delta = \pi$, but $\Delta$ could also be set closer to $2\pi$.) This, of course, requires that your compass needle does not make more than half a rotation between two subsequent measurements - but I assume that your measurement frequency is high enough (in my case it certainly was). The modified angle (superscript m) that gets fed into the Kalman filter is then $\alpha^{\text{m}} = \alpha^{\text{s}} + r \cdot 2\pi$.
The Kalman filter output (state estimate/prediction) can be transformed back to $[-\pi,\pi)$ if you like.

Some python pseudo code:

```<code>alphaSOld = np.nan
r = 0

while doMeasure:
    alphaS = getAngle() # returns angle in range [-pi, pi)

    if not np.isnan(alphaSOld):
        if alphaS - alphaSOld < -math.pi:
            r += 1
        elif alphaS - alphaSOld > math.pi:
            r -= 1

    alphaM = alphaS + r * 2 * math.pi
    kf.update(alphaM)

    alphaSOld = alphaS
</code>```

Body:
This is an interesting question and I ran into the same issue.
First, let me mention that there are libraries that let you change the residual function (i.e., innovation). For instance, see the residual argument of the update function of the Extended Kalman Filter implementation of filterpy. However, I understand you may be stuck with a given library that does not let you change how the residual is calculated.

You were thinking about substituting the state space variable $\alpha$ by $[\cos(\alpha), \sin(\alpha)]$, which certainly would solve the issue. I went for another solution approach that modifies the sensor values before they get fed into the Kalman filter, i.e., it addresses the problem at the equation $\alpha_\text{new} = -170° \equiv 190°$. The idea is that if your sensor would report $190°$ rather than $-170°$, the calculation of the residual would give the correct value of $20°$.
Therefore, I define a rotation counter variable $r=0$ that gets incremented/decremented each time $\alpha^{\text{s}}$ jumps across the discontinuity (the superscript s indicates that this is the raw sensor value). The jumps are detected via the conditions $\alpha_{t+1}^{\text{s}} - \alpha_t^{\text{s}} > \Delta$ and $\alpha_{t+1}^{\text{s}} - \alpha_t^{\text{s}} < -\Delta$. (I have set $\Delta = \pi$, but $\Delta$ could also be set closer to $2\pi$.) This, of course, requires that your compass needle does not make more than half a rotation between two subsequent measurements - but I assume that your measurement frequency is high enough (in my case it certainly was). The modified angle (superscript m) that gets fed into the Kalman filter is then $\alpha^{\text{m}} = \alpha^{\text{s}} + r \cdot 2\pi$.
The Kalman filter output (state estimate/prediction) can be transformed back to $[-\pi,\pi)$ if you like.

Some python pseudo code:

alphaSOld = np.nan
r = 0

while doMeasure:
    alphaS = getAngle() # returns angle in range [-pi, pi)

    if not np.isnan(alphaSOld):
        if alphaS - alphaSOld < -math.pi:
            r += 1
        elif alphaS - alphaSOld > math.pi:
            r -= 1

    alphaM = alphaS + r * 2 * math.pi
    kf.update(alphaM)

    alphaSOld = alphaS


问:
Is it possible to run multiple loops at the same time? (Arduino)
I've got a code where I have a motor running back and forth and buttons connected to a scanner, when I press the buttons it causes the motor to stop  and over rides it. I would like them to run parallel to each other so the codes don't interrupt each other.

Here is my code

```<code>#include <AccelStepper.h>

// Define some steppers and the pins they will use

AccelStepper stepper1(AccelStepper::FULL2WIRE, 2, 3);

const int buttonPin = 4;
const int button2Pin = 14;
const int pulseopto1 = 9;
const int startScan = 11;

int buttonState = 0;
long previousMillis = 0;
long interval = 5;

void setup()
{
    pinMode(buttonPin, INPUT);
    pinMode(button2Pin, INPUT);
    pinMode(pulseopto1, OUTPUT);
    pinMode(startScan, OUTPUT);
    stepper1.setMaxSpeed(40000.0);
    stepper1.setAcceleration(100000.0);
    stepper1.moveTo(25000);
}

void loop()
{
    buttonState = digitalRead(buttonPin);

    if (buttonState == LOW)
    {
        digitalWrite(startScan, HIGH);
    }
    else (buttonState == HIGH);
    {
        digitalWrite(startScan, LOW);
    }

    {
        buttonState = digitalRead(button2Pin);

        if (buttonState == LOW)
        {
            // turn LED on:
            digitalWrite(pulseopto1, HIGH);
            delay(5);
            digitalWrite(pulseopto1, LOW);
            delay(5);
        }
        else
        {
            // turn LED off:
            digitalWrite(pulseopto1, LOW);
        }
    }

    // Change direction at the limits
    if (stepper1.distanceToGo() == 0)
    {
        stepper1.moveTo(-stepper1.currentPosition());
    }

    stepper1.run();
}
</code>```

答:
It looks like the reason (one of) why the motor stops is due to the following code:-

```<code>if (buttonState == LOW)
    {
        digitalWrite(startScan, HIGH);
    }
    else (buttonState == HIGH); --> this is a statement due to the semicolon.
    {
        digitalWrite(startScan, LOW); --> this statement will get executed even when buttonState is LOW.
    }
</code>```

CHange this to the following and check.

```<code>if (buttonState == LOW)
    {
        digitalWrite(startScan, HIGH);
    }
    else if (buttonState == HIGH)
    {
        digitalWrite(startScan, LOW);
    }
</code>```

Try splitting the 2 activities using a task scheduler.
My $0.02.

Body:
It looks like the reason (one of) why the motor stops is due to the following code:-

if (buttonState == LOW)
    {
        digitalWrite(startScan, HIGH);
    }
    else (buttonState == HIGH); --> this is a statement due to the semicolon.
    {
        digitalWrite(startScan, LOW); --> this statement will get executed even when buttonState is LOW.
    }

CHange this to the following and check.

if (buttonState == LOW)
    {
        digitalWrite(startScan, HIGH);
    }
    else if (buttonState == HIGH)
    {
        digitalWrite(startScan, LOW);
    }

Try splitting the 2 activities using a task scheduler.
My $0.02.


问:
Interpret 560 byte msg from UR5 firmware 1.8 through TCP socket
I am trying to use the UR5 from my computer without having to write any script on the robot.  For that, I opened a TCP socket as a client from my computer (in a C program), and the robot indeed does accept the communication.

If I write a UR script command through the TCP socket, the robot interprets it, and does the job.

However, if I send him two consecutive commands, it doesn't behave well.  I would expect it to store the commands and not start one until it has finished the previous one (just as a shell would do).  Instead, it tries to do both at the same time.  As a result, if I tell him to write more than one text messages on the log, some of them get lost.  And if I tell him to do various moves, it stops with an error:

```<code>Security Stopped
SECURITY CHECK: Torque limit violation
</code>```

I could tune some parameters in my code to sleep for a few miliseconds or seconds until it should have finished, but there has to be a way to get that from the robot.

The robot does also talk though the socket, but I can't decypher that, and didn't find in the manuals what's the structure of that message.  I only know that it's always 560 bytes, and I know it sends it every 10th of a second, and that when the arm is stopped, some parts of the message are constant, and when the arm moves, most of it is changing, so I'm sure it's telling me it's position, movement, and probably more useful information.  I would like to be able to interpret those messages.

Here is the part of my code where the robot moves:

```<code>
#define DELAY       (1000000)

int ur_command  (int ur,
             const char *restrict cmd)
{
    ssize_t n;
    ssize_t len;

    usleep(DELAY);

    len = strlen(cmd);
    n   = write(ur, cmd, len);
    if (n != len)
        goto err;

    len = strlen("\n");
    n   = write(ur, "\n", len);
    if (n != len)
        goto err;

    /* Read socket 10 times (1 second of delay each) */
    for (int k = 0; k < 10; k++) {
        char    buf[BUFSIZ];
        ssize_t m;

        memset(buf, 0, ARRAY_SIZE(buf));
        m   = read(ur, buf, ARRAY_SIZE(buf) - 1);
        buf[m]  = '\0';
        if (m < 0) {
            perrorx("read() failed");
            goto err;
        }
        for (ssize_t j = 0; j < m; j++)
            printf("%.2x ", (unsigned char)buf[j]);
        fputc('\n', stdout);
        fputc('\n', stdout);
        fputc('\n', stdout);
        printf("%s\n", buf);
        usleep(DELAY);
    }

    return  0;
err:
    if (n < 0)
        return  n;
    return  (n % INT_MAX) + 1;
}

[...]
    for (ptrdiff_t i = 0; i < 10; i++) {
        if (ur_command(ur, "movej([-0.106, -1.476, 1.346, -2.664, -2.224, -0.264])"))
            goto out;
        if (ur_command(ur, "movej([0.194, -0.699, 1.324, -2.671, -1.497, -0.264])"))
            goto out;
        if (ur_command(ur, "movej([-0.370, -1.183, 1.377, -1.696, -1.623, -0.209])"))
            goto out;
    }
[...]
</code>```

And this is stdout:

```user@LNV-debian:/media/user/DataNTFS/Alex-dev/git/smr-ur-cam-test$ ./client
New movement:
received 5040 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 8e 65 01 01 01 00 00 00 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 bf bb 21 44 d2 92 bd 40 bf bb 22 d0 e5 60 41 8a 00 00 00 00 00 00 00 00 3f 4b bc 5a 42 3e 00 00 41 d8 00 00 42 4e 66 67 fd bf f7 9d a7 e0 96 21 37 bf f7 9d b2 2d 0e 56 04 00 00 00 00 00 00 00 00 c0 20 b1 68 42 3e 00 00 41 f0 cc cd 42 57 99 9a fd 3f f5 89 39 17 c5 20 ec 3f f5 89 37 4b c6 a7 f0 00 00 00 00 00 00 00 00 c0 18 83 ce 42 3d 33 33 41 e0 00 00 42 59 33 33 fd c0 05 4f d3 c6 72 f9 89 c0 05 4f df 3b 64 5a 1d 00 00 00 00 00 00 00 00 3e 52 d2 5a 42 3e 00 00 42 08 cc cd 42 70 00 00 fd c0 01 ca b6 64 62 d4 fa c0 01 ca c0 83 12 6e 98 00 00 00 00 00 00 00 00 bb f9 dc dd 42 3e 00 00 42 10 cc cd 42 6b 99 9a fd bf d0 e5 6f a1 dc 61 c0 bf d0 e5 60 41 89 37 4c 00 00 00 00 00 00 00 00 be 00 0d fe 42 3c 66 67 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e0 d6 88 8c fb b6 7e bf 6c a1 f0 06 ae 39 2b 3f e4 27 77 ab c4 60 ca bf f7 e5 51 42 26 75 72 bf f1 77 6e c5 b4 32 60 3f ca e4 4f 16 17 5f bf 00 00 00 35 09 c0 23 c9 3e aa 35 ca 68 40 26 a1 b5 6f af ec 4f 40 2a 63 46 b3 6d fc 74 c0 05 6e f7 4e f2 65 88 bf 34 ec 5e 0c 8d 6f 33 3f f2 9b f1 1d 87 bd 2d 00 00 00 40 03 00 00 00 00 00 00 3f 44 01 40 14 01 40 14 3f 24 01 40 14 01 40 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3f 22 77 80 3e 10 62 4e 08 02 00 00 00 00 25 02 00 00 3f 90 bb fe 01 88 fa 1b 3f 8d a4 c9 44 8c 3e b3 42 34 00 00 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 6d 0c f6 b6 74 8e 85 00 00 00 07 08 00 01
End of msg.
received 5600 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 8e d5 01 01 01 00 00 00 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 bf bb 21 44 d2 92 bd 40 bf bb 22 d0 e5 60 41 8a 00 00 00 00 00 00 00 00 3f 5b 3b 2d 42 3e 66 67 41 d8 00 00 42 4e 00 00 fd bf f7 9d a7 e0 96 21 37 bf f7 9d b2 2d 0e 56 04 00 00 00 00 00 00 00 00 c0 21 b2 84 42 3e 66 67 41 f0 cc cd 42 57 99 9a fd 3f f5 89 39 17 c5 20 ec 3f f5 89 37 4b c6 a7 f0 00 00 00 00 00 00 00 00 c0 18 15 9e 42 3d 33 33 41 e0 00 00 42 59 33 33 fd c0 05 4f d3 c6 72 f9 89 c0 05 4f df 3b 64 5a 1d 00 00 00 00 00 00 00 00 3d 6d 5e 9f 42 3e 66 67 42 08 cc cd 42 70 00 00 fd c0 01 ca b6 64 62 d4 fa c0 01 ca c0 83 12 6e 98 00 00 00 00 00 00 00 00 3b 95 ea eb 42 3e 00 00 42 10 cc cd 42 6b 99 9a fd bf d0 e5 6f a1 dc 61 c0 bf d0 e5 60 41 89 37 4c 00 00 00 00 00 00 00 00 bd e0 e0 60 42 3c cc cd 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e0 d6 88 8c fb b6 7e bf 6c a1 f0 06 ae 39 2b 3f e4 27 77 ab c4 60 ca bf f7 e5 51 42 26 75 72 bf f1 77 6e c5 b4 32 60 3f ca e4 4f 16 17 5f bf 00 00 00 35 09 c0 25 4a 7d 1e ff 78 72 40 27 07 43 3d 6d 2f 23 40 2a 37 e6 05 61 43 72 bf f4 d9 e6 22 a7 0a 09 bf bd 69 c2 a6 95 54 d1 3f f0 56 f9 2c a9 22 fc 00 00 00 40 03 00 00 00 00 00 00 3f 34 01 40 14 01 40 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3f 34 7a 00 3e 0f 5c 29 08 02 00 00 00 00 25 02 00 00 3f 90 93 31 39 98 bd 35 3f 8e 47 fc 64 4d 32 49 42 34 00 00 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 6d 0c f6 b6 74 8e 85 00 00 00 07 08 00 01
End of msg.
New movement:
received 11200 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 8f 53 01 01 01 00 00 00 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 bf bb 21 44 d2 92 bd 40 bf bb 22 d0 e5 60 41 8a 00 00 00 00 00 00 00 00 3f 5a a8 41 42 3e 66 67 41 d8 00 00 42 4e 66 67 fd bf f7 9d a7 e0 96 21 37 bf f7 9d b2 2d 0e 56 04 00 00 00 00 00 00 00 00 c0 21 d7 3e 42 3e 66 67 41 f0 cc cd 42 57 99 9a fd 3f f5 89 39 17 c5 20 ec 3f f5 89 37 4b c6 a7 f0 00 00 00 00 00 00 00 00 c0 18 a8 89 42 3d 33 33 41 e0 00 00 42 58 cc cd fd c0 05 4f d3 c6 72 f9 89 c0 05 4f df 3b 64 5a 1d 00 00 00 00 00 00 00 00 3e 32 06 f7 42 3e 66 67 42 08 cc cd 42 70 00 00 fd c0 01 ca b6 64 62 d4 fa c0 01 ca c0 83 12 6e 98 00 00 00 00 00 00 00 00 3b f9 dc dd 42 3e 00 00 42 10 cc cd 42 6b 99 9a fd bf d0 e5 6f a1 dc 61 c0 bf d0 e5 60 41 89 37 4c 00 00 00 00 00 00 00 00 bd a2 69 29 42 3c cc cd 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e0 d6 88 8c fb b6 7e bf 6c a1 f0 06 ae 39 2b 3f e4 27 77 ab c4 60 ca bf f7 e5 51 42 26 75 72 bf f1 77 6e c5 b4 32 60 3f ca e4 4f 16 17 5f bf 00 00 00 35 09 c0 25 3c 38 70 01 7b 7b 40 27 15 c5 36 3f 14 1c 40 2a 71 bc 42 c7 8f 78 c0 03 0c e1 46 e2 79 8b bf c2 5f 7c 1c 5b c3 55 3f e7 9a 12 95 d7 dd 33 00 00 00 40 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3f 19 64 00 3e 11 68 73 08 02 00 00 00 00 25 02 00 00 3f 90 bb fe 01 88 fa 1b 3f 8d f6 62 d4 6c b8 7e 42 34 00 00 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 6d 0c f6 b6 74 8e 85 00 00 00 07 08 00 01
End of msg.
received 5600 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 90 4d 01 01 01 00 00 00 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 bf bb 21 44 d2 92 bd 40 bf bb 22 d0 e5 60 41 8a 00 00 00 00 00 00 00 00 3f 54 58 24 42 3e 66 67 41 d8 00 00 42 4e 66 67 fd bf f7 9d a7 e0 96 21 37 bf f7 9d b2 2d 0e 56 04 00 00 00 00 00 00 00 00 c0 1f d5 07 42 3e 66 67 41 f0 cc cd 42 57 99 9a fd 3f f5 89 39 17 c5 20 ec 3f f5 89 37 4b c6 a7 f0 00 00 00 00 00 00 00 00 c0 18 a8 89 42 3d 99 9a 41 e0 00 00 42 58 66 67 fd c0 05 4f d3 c6 72 f9 89 c0 05 4f df 3b 64 5a 1d 00 00 00 00 00 00 00 00 3e 59 11 7a 42 3e 00 00 42 08 cc cd 42 70 00 00 fd c0 01 ca b6 64 62 d4 fa c0 01 ca c0 83 12 6e 98 00 00 00 00 00 00 00 00 ba c7 e3 e4 42 3e 00 00 42 10 cc cd 42 6b 99 9a fd bf d0 e5 b3 97 58 f7 e0 bf d0 e5 60 41 89 37 4c 00 00 00 00 00 00 00 00 bd dd c0 d1 42 3c cc cd 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e0 d6 88 8c fb b6 7e bf 6c a1 f0 06 ae 39 2b 3f e4 27 77 ab c4 60 ca bf f7 e5 47 83 3c e6 7d bf f1 77 7b 1d 31 c0 a0 3f ca e3 f0 5f 32 44 9c 00 00 00 35 09 c0 24 9f 44 eb 17 9c df 40 26 4a a9 9a c4 8e 74 40 2a 71 bc 42 c7 8f 78 c0 05 e3 2c 44 4a c3 3f bf ad 7e af 04 a1 e2 41 3f f0 1c e0 61 5f ad 2a 00 00 00 40 03 00 00 00 00 00 00 3f 34 01 40 14 01 40 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e4 00 00 42 40 00 00 3f 10 50 80 3e 14 7a e2 08 02 00 00 00 00 25 02 00 00 3f 90 6a 64 71 a8 80 50 3f 8d 01 96 24 cb 4b 1d 42 34 00 00 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 6d 0c f6 b6 74 8e 85 00 00 00 07 08 00 01
End of msg.
New movement:
received 11200 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 90 ca 01 01 01 00 00 01 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 3f c0 32 6f 88 2b 01 1a 3f c0 35 36 12 88 ec d6 3f d1 32 6b 23 43 95 b6 3e e9 01 3d 42 3e 66 67 41 d8 00 00 42 4e cc cd fd bf eb f4 b1 1c de eb 4f bf eb f3 be fd 73 40 07 3f e6 1c f0 c6 33 b8 76 c0 6b 03 74 42 3e 66 67 41 f0 cc cd 42 57 99 9a fd 3f f5 42 cf b7 72 5c e8 3f f5 43 57 8c b0 28 e0 bf 96 ff c3 75 a7 36 39 bf f7 a3 ae 42 3e 66 67 41 e0 cc cd 42 58 66 67 fd c0 05 5a ce 39 a2 24 79 c0 05 5a fd 02 85 05 99 bf 86 df ed de fc d5 13 be 41 a4 c5 42 3e 66 67 42 08 cc cd 42 70 00 00 fd bf fa 90 fe f7 2f 9d 84 bf fa 90 7c 4f ec 02 23 3f e4 50 51 69 53 ce 14 3e b5 ee 6a 42 3e 00 00 42 10 cc cd 42 6b 99 9a fd bf d0 e5 6f a1 dc 61 c0 bf d0 e5 60 41 89 37 4c 00 00 00 00 00 00 00 00 bd e3 ff f0 42 3c cc cd 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e7 66 54 49 f1 7b 13 bf c9 0a 85 ec 69 7a 5d 3f cd eb 1a 8c 34 3d d5 bf f7 4b 61 26 53 b4 11 c0 00 d7 4b b6 f3 5a 14 3f de 51 61 9b a4 6a 0d 00 00 00 35 09 40 02 89 5f 02 f7 b3 29 40 29 99 73 74 53 59 3d bf b7 63 9c 9c 47 2c 1e bf e6 a0 f2 4f ef 42 5a 3f c8 53 e5 15 e8 74 eb 3f f0 91 11 f7 f2 98 ce 00 00 00 40 03 00 00 00 00 00 00 3f 51 81 18 11 81 18 12 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3e 33 f4 00 3e 12 6e 98 08 02 00 00 00 00 25 02 00 00 3f 90 93 31 39 98 bd 35 3f 8d 53 2f b4 ab c4 e8 42 34 00 00 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 8a 25 15 74 f8 60 16 00 00 00 07 08 00 01
End of msg.
received 5600 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 91 c4 01 01 01 00 00 00 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 3f c8 d5 87 95 e9 db 16 3f c8 d4 fd f3 b6 45 a2 3f 04 1b 2f 76 bc 6e ee 3f 21 d7 3e 42 3e 00 00 41 d8 00 00 42 4e cc cd fd bf e6 5e 57 a8 21 d3 33 bf e6 5e 35 3f 7c ed 93 00 00 00 00 00 00 00 00 c0 5f 64 57 42 3e 66 67 41 f0 cc cd 42 57 99 9a fd 3f f5 2f 34 66 5d e9 08 3f f5 2f 1a 9f be 76 c8 be fa ce e9 f3 a5 e9 3d c0 04 92 5c 42 3e 00 00 41 e0 00 00 42 58 66 67 fd c0 05 5e 41 3d 25 f9 d9 c0 05 5e 35 3f 7c ed 91 be f9 21 fb 76 a5 b8 ab be 8b c4 58 42 3e 00 00 42 08 cc cd 42 70 00 00 fd bf f7 f3 cb 58 10 9b ac bf f7 f3 b6 45 a1 ca c3 3f 09 21 fb 76 a5 b8 ab 3e af af 4b 42 3e 00 00 42 10 cc cd 42 6b 99 9a fd bf d0 e5 2b ac 5f cb b0 bf d0 e5 60 41 89 37 4c 00 00 00 00 00 00 00 00 be 00 0d fe 42 3c 66 67 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e7 50 73 10 9e fa f5 bf d0 ad 41 8f d0 89 77 3f ba 7b 9b c6 d6 89 4f bf f7 45 5e b0 00 25 09 c0 03 16 2e 72 94 82 5e 3f e1 2e 1b 92 fc 6e 5a 00 00 00 35 09 c0 1f 6f 51 87 91 4f fc 3f dc 45 af c5 e7 b5 75 40 26 f6 c1 90 66 e6 d4 3f f1 02 82 dc 2a 7b 41 c0 09 98 e9 65 30 a6 c6 3f f2 9b f1 1d 87 bd 2d 00 00 00 40 03 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3f 2b 86 80 3e 0f 5c 29 08 02 00 00 00 00 25 02 00 00 3f 90 41 97 a9 b8 43 6b 3f 8d a4 c9 44 8c 3e b3 42 34 00 00 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 8c 2d b4 de a0 6d ea 00 00 00 07 08 00 01
End of msg.
New movement:
received 11200 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 92 41 01 01 01 00 00 01 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 bf c5 a3 d9 87 25 c1 20 bf c5 a9 ec 44 60 07 04 bf e2 bf 9e db 96 92 eb be c3 20 90 42 3d 99 9a 41 d8 00 00 42 4e cc cd fd bf f0 2b b7 7e 85 a0 47 bf f0 2b ec 7c 74 99 c9 bf df 72 39 f9 26 db 49 c0 83 10 b2 42 3d 99 9a 41 f0 cc cd 42 57 99 9a fd 3f f5 ba 90 de 71 01 58 3f f5 ba eb c9 5e 0b 8a 3f b0 59 70 6d b7 8e df bf 5f d2 87 42 3c 66 67 41 e0 00 00 42 58 00 00 fd c0 00 58 ab c1 8e b5 52 c0 00 58 27 af b9 d6 7b 3f ed e9 a1 f9 c6 5d 39 3d d7 81 b2 42 3c 66 67 42 08 cc cd 42 70 00 00 fd bf f9 40 f2 1b 89 2a 18 bf f9 40 1b 73 74 1d eb bf c0 f1 c1 21 05 ca 78 3d 47 e3 e4 42 3c 66 67 42 10 cc cd 42 6b 99 9a fd bf cd 43 28 ba 16 48 40 bf cd 42 01 ba 6c da 67 3f ac 97 e9 56 bc 0a 58 3e b1 3f 13 42 3c 66 67 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e6 a0 8b 80 cb 5d b9 3f 85 b4 ad 0a fe ba c1 3f cf 77 a2 4c 03 f6 4d c0 00 a2 d0 9b 1c 67 3b c0 01 a6 24 82 af 13 a6 3f b8 77 b2 0f 2f 8c 8f 00 00 00 35 09 c0 15 05 c5 d7 a1 8a fb 3f f9 6b 4f d6 2d 10 9b 3f f4 78 ef 4a a2 ec 2f 40 01 c3 8a fa c4 7b 0d bf fd 7e a4 07 61 43 32 bf ea d3 50 1f 92 27 d7 00 00 00 40 03 00 00 00 00 00 00 3f 24 01 40 14 01 40 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3f 9e 01 c0 3e 12 6e 98 08 02 00 00 00 00 25 02 00 00 3f 90 e4 ca c9 79 37 00 3f 8e 47 fc 64 4d 32 49 42 32 cc cd 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 89 c7 21 33 ca 11 f8 00 00 00 07 08 00 01
End of msg.
received 5600 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 93 3b 01 01 01 00 00 01 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 bf d7 ae 6c a8 11 87 00 bf d7 ad e1 5e b8 e3 cf 00 00 00 00 00 00 00 00 3e 45 6c 3e 42 3e 00 00 41 d8 00 00 42 4e cc cd fd bf f2 ed 9e 26 65 68 67 bf f2 ed 9f 96 ce 61 2d 00 00 00 00 00 00 00 00 c0 29 e0 1d 42 3e 66 67 41 f0 cc cd 42 57 99 9a fd 3f f6 08 1c 59 95 ab 4c 3f f6 08 2f a6 d0 0b 3c 3e fa ce e9 f3 a5 e9 3d bf 6f 51 5a 42 3d 33 33 41 e0 00 00 42 59 33 33 fd bf fb 22 d0 53 1d 5e 64 bf fb 22 ff bf 2f f2 6c 3f 12 d9 7c 98 fc 4a 80 3c ae e7 67 42 3e 66 67 42 08 cc cd 42 70 00 00 fd bf f9 f7 be f8 5b 04 40 bf f9 f7 eb ef a8 aa ae bf 09 21 fb 76 a5 b8 ab bd 2e e7 67 42 3e 00 00 42 10 cc cd 42 6b 99 9a fd bf ca c1 19 67 4c 3b e0 bf ca c0 98 5e 2a 16 7e 3f 09 21 fb 76 a5 b8 ab 3e 62 70 28 42 3c 66 67 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e4 23 c5 b5 00 78 a7 3f c0 da 0f 38 21 7a 36 3f d4 62 43 48 a3 e3 35 40 02 e5 97 f1 60 13 7d 40 00 1a cd 2e 5e 0e 73 3f bf 3d ed 93 17 8a fe 00 00 00 35 09 3f e9 0d c6 15 13 50 b2 c0 0a cf ed e4 cf ca 86 c0 19 ac a9 f2 47 fc 0f c0 0b eb c4 98 e7 70 38 bf e5 b5 eb 04 84 79 4b c0 00 f9 fe c6 54 e3 fd 00 00 00 40 03 00 00 00 00 00 00 3f 34 01 40 14 01 40 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3f 34 6f 80 3e 0e 56 05 08 02 00 00 00 00 25 02 00 00 3f 90 18 ca e1 c8 06 85 3f 8d a4 c9 44 8c 3e b3 42 34 00 00 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 86 63 8e 22 7d 30 c0 00 00 00 07 08 00 01
End of msg.
New movement:
received 11200 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 93 b8 01 01 01 00 00 01 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 bf c8 6b 41 1e 52 b3 e0 bf c8 63 d9 de 02 5e e8 3f d0 ac 96 2f 55 3f 74 3f 33 a1 be 42 3e 00 00 41 d8 00 00 42 4e 66 67 fd bf f6 1d 7a d7 d8 0e c4 bf f6 1d 59 78 b6 71 09 bf d2 34 3d 11 38 cb 4f c0 21 fb f9 42 3e 00 00 41 f0 cc cd 42 57 99 9a fd 3f f5 b1 31 65 55 d9 ec 3f f5 b1 e1 72 14 1b 08 bf a0 20 78 bc 91 ce 4f bf e2 fa 96 42 3c cc cd 41 e0 00 00 42 59 33 33 fd c0 02 d4 53 e0 cc 14 69 c0 02 d4 fa 92 cc 2b e0 bf ee 91 e7 7f 66 be c8 bf 43 fc 71 42 3d 99 9a 42 08 cc cd 42 70 00 00 fd c0 00 40 19 30 1e 5b 84 c0 00 40 91 39 13 27 92 bf e2 b9 df da b1 0e 0e be b8 46 16 42 3c 66 67 42 10 cc cd 42 6b 99 9a fd bf cf 85 b3 de d5 18 00 bf cf 89 93 8c b6 a1 a5 bf ad 57 8c 74 24 b9 f8 be 5c 31 09 42 3c 66 67 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e2 fe 13 7a a3 69 cb 3f a4 d7 19 10 27 f3 3b 3f e1 1b af f7 2b fa 88 bf fb 15 a9 a6 86 fa ab bf f7 6c f9 57 46 d8 e4 3f b2 63 06 26 49 56 34 00 00 00 35 09 bf ef 78 81 33 b2 bf 56 bf fe 2f c9 6c ab 22 36 c0 01 e9 b7 a0 fb 7c ea 40 05 bf 66 2c 95 8d e5 3f f4 2d 20 55 eb 67 ac 3f f3 2d d7 1b d5 31 8c 00 00 00 40 03 00 00 00 00 00 00 3f 3e 01 e0 1e 01 e0 1e 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3f 82 f9 00 3e 13 74 bd 08 02 00 00 00 00 25 02 00 00 3f 91 0d 97 91 69 73 e6 3f 8d 53 2f b4 ab c4 e8 42 32 cc cd 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 79 27 23 be cd 58 c8 00 00 00 07 08 00 01
End of msg.
received 5600 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 94 b2 01 01 01 00 00 00 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 bf bb 21 44 ee 06 5d 00 bf bb 22 d0 e5 60 41 8a 00 00 00 00 00 00 00 00 3f 81 fd 39 42 3e 00 00 41 d8 00 00 42 4e 66 67 fd bf f7 9d 9d fd e3 0f 6e bf f7 9d b2 2d 0e 56 05 bf 04 1b 2f 76 bc 6e ee c0 29 96 a7 42 3e 66 67 41 f0 cc cd 42 57 99 9a fd 3f f5 89 42 fa 78 32 b4 3f f5 89 37 4b c6 a7 f0 be fa ce e9 f3 a5 e9 3d c0 17 5d f7 42 3c cc cd 41 e0 00 00 42 58 cc cd fd c0 05 4f d3 c6 72 f9 89 c0 05 4f df 3b 64 5a 1d 3f 19 21 fb 76 a5 b8 ab 3e 36 b6 4e 42 3e 00 00 42 08 cc cd 42 70 00 00 fd c0 01 ca b6 64 62 d4 fa c0 01 ca c0 83 12 6e 98 00 00 00 00 00 00 00 00 3c 79 dc dd 42 3e 00 00 42 10 cc cd 42 6b 99 9a fd bf d0 e5 2b ac 5f cb b0 bf d0 e5 60 41 89 37 4c bf 09 21 fb 76 a5 b8 ab bd f3 9d be 42 3c 66 67 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e0 d6 95 7d cd 91 3e bf 6c a0 8d d2 20 c9 62 3f e4 27 63 ca 81 a8 4c bf f7 e5 61 c6 4d e1 db bf f1 77 72 34 d3 f1 a2 3f ca e4 45 1b 32 99 fe 00 00 00 35 09 c0 29 3f 89 a7 70 a0 ee 40 2a 25 2f b2 85 60 dc 40 29 ef 9a 38 a1 64 6c c0 03 64 08 fe e4 bf d4 bf cb 89 ff 13 06 81 a2 3f f1 b3 8d f0 61 e5 e7 00 00 00 40 03 00 00 00 00 00 00 3f 49 01 90 19 01 90 19 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3f 34 6a 00 3e 0e 56 05 08 02 00 00 00 00 25 02 00 00 3f 90 bb fe 01 88 fa 1b 3f 8d f6 62 d4 6c b8 7e 42 34 00 00 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 6d 0d 35 98 32 b5 e6 00 00 00 07 08 00 01
End of msg.
New movement:
received 11200 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 95 2f 01 01 01 00 00 01 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 3f bf 3c 76 b8 6d e3 ae 3f bf 4b 47 ea dd 45 a0 3f d2 81 bb 2d 7d 16 e6 3f 34 34 a9 42 3e 66 67 41 d8 00 00 42 4e cc cd fd bf ec 52 ff 0f 9f 7d 6d bf ec 50 b5 47 55 8d 65 3f e7 72 be ed 16 3b d4 c0 69 01 3d 42 3e 66 67 41 f0 cc cd 42 57 99 9a fd 3f f5 44 1a 33 bb b3 a4 3f f5 44 a8 76 57 70 cf bf 8e 9a b6 94 66 27 84 c0 0e 53 fd 42 3e 00 00 41 e0 00 00 42 58 66 67 fd c0 05 5a 92 c2 d5 21 26 c0 05 5a c7 68 fb dd 10 bf 73 70 48 7d c4 2c d4 be 40 14 fd 42 3e 66 67 42 08 cc cd 42 70 00 00 fd bf fa bd 33 ef f6 61 40 bf fa bb f9 be ec 9c 9c 3f e5 fb 60 d8 3d e2 0c 3e a3 31 0d 42 3e 66 67 42 10 cc cd 42 6b 99 9a fd bf d0 e5 2b ac 5f cb b0 bf d0 e5 60 41 89 37 4c be f9 21 fb 76 a5 b8 ab be 00 0d fe 42 3c cc cd 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e7 5f 96 35 4b 38 1d bf c8 7a 0f aa d2 b4 22 3f cf 02 81 57 5e ea 10 bf f7 4d 7d 27 d8 fa 70 c0 00 b0 af 53 c8 ed e1 3f de 06 d1 b6 5b 73 4f 00 00 00 35 09 bf e3 11 6b 09 f4 96 ec 40 29 86 97 01 b0 61 b2 40 0b f1 9d f8 d9 37 d3 bf e7 cd da e1 2f fb 05 3f e1 db 7f f6 f0 fb 9c 3f f2 9b f1 1d 87 bd 2d 00 00 00 40 03 00 00 00 00 00 00 3f 34 01 40 14 01 40 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3e 33 f4 00 3e 13 74 bd 08 02 00 00 00 00 25 02 00 00 3f 90 e4 ca c9 79 37 00 3f 8e 47 fc 64 4d 32 49 42 34 00 00 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 89 e8 e9 83 9f 20 af 00 00 00 07 08 00 01
End of msg.
received 5600 bytes (showing first 560 only):
00 00 02 30 10 00 00 00 1d 00 00 00 00 00 00 10 96 2a 01 01 01 00 00 01 00 00 3f f0 00 00 00 00 00 00 00 00 00 fb 01 3f c8 cf f8 03 76 0a 3d 3f c8 c7 70 f9 05 14 1f bf 9b 83 de 9e d2 89 24 bf 18 15 9e 42 3e 66 67 41 d8 00 00 42 4e cc cd fd bf e6 5f e3 14 1c 9a 97 bf e6 61 1d 7a b7 d2 84 bf 91 72 ed 06 35 dc f0 c0 98 cd 44 42 3e 00 00 41 f0 cc cd 42 57 99 9a fd 3f f5 2f 34 66 5d e9 08 3f f5 2f 43 5f 48 d4 3e be ea ce e9 f3 a5 e9 3d bf f9 12 fa 42 3e 00 00 41 e0 00 00 42 58 66 67 fd c0 05 5d 9f d6 1e 15 66 c0 05 5c be 70 ff 66 6e 3f ac bd 9c 4f ee 02 ed 3e 40 14 fd 42 3e 00 00 42 08 cc cd 42 70 00 00 fd bf f7 f3 ed 52 ce e6 b4 bf f7 f4 17 25 24 b2 9c 3f 09 21 fb 76 a5 b8 ab 3e 52 d2 5a 42 3e 00 00 42 10 cc cd 42 6b 99 9a fd bf d0 e4 e7 b6 e3 35 a0 bf d0 e4 b7 1c cd 49 15 00 00 00 00 00 00 00 00 bd 8c 8c 3c 42 3c cc cd 42 15 33 33 42 70 00 00 fd 00 00 00 35 04 bf e7 50 b8 ab 3e 11 62 bf d0 ab 26 84 72 0c 52 3f ba 82 98 9e 4a 01 0d bf f7 46 7c be 8b 0d f3 c0 03 16 01 d6 a9 d4 ed 3f e1 2c b1 60 e3 cf 28 00 00 00 35 09 c0 01 38 da 79 ac 19 df 40 07 d9 06 b0 3a cd 42 40 25 d4 16 44 d7 3e e6 bf d4 48 5c 7e e6 65 e4 c0 05 8e 87 a5 49 76 59 3f f3 e0 ed a4 1c 56 5c 00 00 00 40 03 00 00 00 00 00 00 3f 24 01 40 14 01 40 14 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 41 e3 33 33 42 40 00 00 3f 61 a2 00 3e 13 74 bd 08 02 00 00 00 00 25 02 00 00 3f 91 0d 97 91 69 73 e6 3f 8e 47 fc 64 4d 32 49 42 34 00 00 00 3b c4 9b a6 42 4c 00 00 fd 00 00 00 3d 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 3f 8c 2d ad cc 03 b9 c8 00 00 00 07 08 00 01
End of msg.
./client:
 client.c:97:
 main():
  write() succeded
 E0 - Success
user@LNV-debian:/media/user/DataNTFS/Alex-dev/git/smr-ur-cam-test$

```

How can I know that the arm is stopped (and possibly its position and more useful info) so that I know that it's prepared to receive a new command through the socket?

答:
https://www.universal-robots.com/how-tos-and-faqs/how-to/ur-how-tos/remote-control-via-tcpip/

There is the manual regarding connexions as TCP clients to the robot.

The frequency is "fixed" for a given interface but it varies from one interface to another, and it's the same for all UR models, only difference is that faster interfaces for e-series run at 500 Hz while for CB3 robots they run at 125Hz.

https://s3-eu-west-1.amazonaws.com/ur-support-site/16496/Client_Interface_V3.12andV5.6.xlsx

That file details the contents of the messages sent by the robot (I didn't find that for version 1.8).  (If this link breaks, I got it from the first link).