Certainly. The complete code of the two threads is attached. And here is some more explanation for easier understanding.
My software is a motion editor for a humanoid robot. The robot is connected to the PC on a serial port. The idea is to move the robot's body with your hands and record the robot's poses (sets of joint angles) in the meantime. At a later time you can play back a sequence of poses and the robot will execute it and perform the same motion that you showed it with your hands. The robot is also virutally visualized with a 3D skeleton on the screen and lots of spin boxes are showing the current joint angle values.
Attached you will find the classes of two threads. The RobotInterface is handling the serial communication. It's active the whole time, it constantly exchanges packets with the robot at a pace of approx. 20 ms. The KeyframePlayer is triggered by the press of a button on the gui. It takes the currently loaded motion sequence, samples it with a pace of 12 ms, generates joint angles and gives them to the RobotInterface to be sent to the robot.
The connecting code I will show you right here:
/*
* Starts playing the keyframes in the motion sequence.
* The keyframe player gets connected to the robot interface and to the keyframe editor.
* Attention! If the robot is actuated, it will execute the current motion.
*/
void MotionEditor::on_playButton_clicked()
{
// Abort if already running.
if (keyframePlayer.running)
{
playerDone();
}
else
{
//connect(&keyframePlayer, SIGNAL(jointAnglesChanged(QHash<QString, float>)),
&robotInterface, SLOT(setTxJointAngles(QHash<QString, float>)));
// Start the player thread.
keyframePlayer.playTheseFrames(motionSequence->getKeyframes());
}
}
/*
* Starts playing the keyframes in the motion sequence.
* The keyframe player gets connected to the robot interface and to the keyframe editor.
* Attention! If the robot is actuated, it will execute the current motion.
*/
void MotionEditor::on_playButton_clicked()
{
// Abort if already running.
if (keyframePlayer.running)
{
playerDone();
}
else
{
//connect(&keyframePlayer, SIGNAL(jointAnglesChanged(QHash<QString, float>)),
&robotInterface, SLOT(setTxJointAngles(QHash<QString, float>)));
// Start the player thread.
keyframePlayer.playTheseFrames(motionSequence->getKeyframes());
}
}
To copy to clipboard, switch view to plain text mode
Well it's a bit boring right now, because I commented the connect() call out. The reason is the problem I described. Since I found transfering the data by signals and slots to be unreliable for now, I changed the KeyframePlayer a bit and added direct calls to the RobotInterface. If you search for the keyword "setTxJointAngles" in KeyframePlayer, you will find all the relevant spots. The signals are still emitted though, it's only the connect() call above that I commented out for now.
I will be happy if you actually take a look at it and provide some guru advice.
While I already know I have it coming, yes, thread safety is actually an issue that I haven't addressed yet. What I did so far is I pass a jointAngles QHash by value from the KeyframePlayer to the RobotInterface. The RobotInterface reads it and sends it to the robot. In the meantime it's very likely, that the KeyframePlayer overwrites the very same QHash. I was hoping that implicit sharing will be good enough, but it's not the case. The software crashes many times at this point. I'm not sure yet how I can solve this problem in a nice way, so if you can throw me a penny, I will be very thankful.
Bookmarks