ROS Resources: Documentation | Support | Discussion Forum | Index | Service Status | ros @ Robotics Stack Exchange
Ask Your Question
0

applying variable body wrench

asked 2020-04-26 16:43:51 -0500

eusebius gravatar image

updated 2020-04-26 16:57:37 -0500

I'm trying to apply a force to the arm of a simple robot that I have spawned in gazebo. I did so writing the following .launch file:

<launch>

  <node pkg="rosservice" type="rosservice" name="force" args="call /gazebo/apply_body_wrench '{body_name: link3 , wrench: { force: { x: 0, y: 0 , z: -100 } }, start_time: 10000000000, duration: 1000000000 }'"/>

</launch>

Everything works fine, but what if I wanted to apply a force which is variable in time? For example let's say I want to apply a force along x equal to 100*cos(t). Can I include something like an expression of the force depending on a time variable in args? Do I have to write this expression outside <node ...=""/> and then I can pass it to args?

edit retag flag offensive close merge delete

Comments

No, you can't. That is not what these tools are for.

And it may make more sense to actually use a node to calculate your set points and then use an actual controller running against your virtual hw to take the robot's state to the desired state.

gvdhoorn gravatar image gvdhoorn  ( 2020-04-27 04:20:15 -0500 )edit

Thanks gvdhoorn.

About your suggestion: what I would like to obtain is different actually. I would like to simulate an external force acting on the robot end effector. So the force does not come from a controller internal to the robot which is designed to take the robot to a designed state. Therefore implementing a controller does not seem to me the right solution for my case.

To specify better, let's consider a very simple case. I have an rrbot robot, without controllers implemented for the joints, I just spawned in Gazebo the urdf model. I would like to see what happens if I apply a force to its end effector, and I want this force to be variable in time. Is there a way to do this in Gazebo? Using the /gazebo/apply_body_wrench service was just an idea. If this can be done using another tools it's perfectly fine.

eusebius gravatar image eusebius  ( 2020-04-27 05:06:15 -0500 )edit

That does indeed not sound like something you'd use a controller for, but it's still not something rosservice can do for you.

What would be the problem with writing a 10 line Python node for this?

re: can this be done with Gazebo: please ask that question over at answers.gazebosim.org.

gvdhoorn gravatar image gvdhoorn  ( 2020-04-27 05:07:44 -0500 )edit

Using a Python node for this would be great, just I don't know how to do this ;) I'm a very beginner to ROS and Gazebo. I searched some online tutorials and ROS answers website but I didn't find anything that fits my case. I did these tutorials, but I cannot figure out how to apply these things to my case.

Should I write a service node? And in that case should I use the /gazebo/apply_body_wrench service? And should I also write the client node?

By the way, when I wrote "can this be done in Gazebo?" I was inaccurate. What I meant was: is there any way, in ROS or in Gazebo, to do this and then see the results in Gazebo. What I want from Gazebo is just that it shows me the animation of what happens when I apply my sinusoidal force.

eusebius gravatar image eusebius  ( 2020-04-27 05:44:00 -0500 )edit
1

Gazebo already runs the service server, you just need to write a service client. Refer to Writing a Simple Service and Client (Python) for an example.

gvdhoorn gravatar image gvdhoorn  ( 2020-04-27 08:31:39 -0500 )edit

Ok, at a first stage I implemented the node in the following way:

#!/usr/bin/env python

import sys
import rospy
import std_msgs
from forces_torques.srv import *

def apply_body_wrench_client(body_name, reference_frame, reference_point, wrench, \
        start_time, duration):
    rospy.wait_for_service('/gazebo/apply_body_wrench')
    try:
        apply_body_wrench = rospy.ServiceProxy('/gazebo/apply_body_wrench', ApplyBodyWrench)
        apply_body_wrench(body_name, reference_frame, reference_point, wrench, \
        start_time, duration)
    except rospy.ServiceException, e:
        print "Service call failed: %s"%e

if __name__ == "__main__":
    body_name = 'panda_link7'
    reference_frame = 'panda_link7'
    reference_point = geometry_msgs.msg.Point(x = 0, y = 0, z = 0)

(maximum number of characters reached, continues in the following comment...)

eusebius gravatar image eusebius  ( 2020-04-28 14:06:00 -0500 )edit

(... continues)

    wrench = geometry_msgs.msg.Wrench(force = geometry_msgs.msg.Vector3( x = 1000, \
        y = 0, z = 0), torque = geometry_msgs.msg.Vector3( x = 0, y = 0, z = 0))
   start_time = rospy.Time(secs = 0, nsecs = 0)
   duration = rospy.Duration(secs = 10, nsecs = 0)
   apply_body_wrench_client(body_name, reference_frame, reference_point, wrench, \
      start_time, duration)

However, in this way I have obtained from a python script what I could already obtain from terminal command.

Now the problem is again how to make the force time-variable. What I thought is to use a loop inside the node. I could compute every e.g. 10 ns a force value according to a sinusoidal law and then apply this force for 10 ns. When 10 ns have passed do this again and so on... But I know using loops is generally bad in Python. Would it be ok in this context? And in case it's ok, could you give me any ...(more)

eusebius gravatar image eusebius  ( 2020-04-28 14:19:47 -0500 )edit

But I know using loops is generally a bad in Python.

why?

That's a rather odd statement.

gvdhoorn gravatar image gvdhoorn  ( 2020-04-28 14:22:39 -0500 )edit

1 Answer

Sort by ยป oldest newest most voted
0

answered 2020-04-28 17:37:25 -0500

eusebius gravatar image

updated 2020-04-29 03:32:23 -0500

I figured out a way to do it, here is the code

    #!/usr/bin/env python

    import sys
    import rospy
    import std_msgs
    import time
    import math
    import matplotlib.pyplot as plt
    from forces_torques.srv import *

    def apply_body_wrench_client(body_name, reference_frame, reference_point, wrench, \
            start_time, duration):
        rospy.wait_for_service('/gazebo/apply_body_wrench')
        try:
            apply_body_wrench = rospy.ServiceProxy('/gazebo/apply_body_wrench', ApplyBodyWrench)
            apply_body_wrench(body_name, reference_frame, reference_point, wrench, \
            start_time, duration)
        except rospy.ServiceException, e:
            print "Service call failed: %s"%e

    def clear_body_wrench_client(body_name):
        rospy.wait_for_service('gazebo/clear_body_wrenches')
        try:
            clear_body_wrench = rospy.ServiceProxy('gazebo/clear_body_wrenches', BodyRequest)
            clear_body_wrench(body_name)
        except rospy.ServiceException, e:
            print "Service call failed: %s"%e

    if __name__ == "__main__":
        beginning = time.time()
        update_rate = 0.01
        period = 1
        body_name = 'panda::panda_link7'
        reference_frame = 'panda::panda_link7'
        start_time = rospy.Time(secs = 0, nsecs = 0)
        duration = rospy.Duration(secs = update_rate, nsecs = 0)
        reference_point = geometry_msgs.msg.Point(x = 0, y = 0, z = 0)
        t = []
        f = []
        while time.time()-beginning <= 10:
            wrench = geometry_msgs.msg.Wrench(force = geometry_msgs.msg.Vector3( x = 0, \
                y = 400*math.cos(2*math.pi*(time.time()-beginning)/period), z = 0), \
                torque = geometry_msgs.msg.Vector3(x = 0, y = 0, z = 0))
            t.append(time.time() - beginning)
            f.append(wrench.force.y)
            clear_body_wrench_client(body_name)
            apply_body_wrench_client(body_name, reference_frame, reference_point, wrench, \
                start_time, duration)
            time.sleep(update_rate - ((time.time()-beginning)%update_rate))

plt.plot(t, f, drawstyle = "steps-post")
plt.show()

Actually, I'm not sure if it is the best way to do this. As far as I read in other tutorials the time.time() function outputs the number of seconds elapsed since midnight 01/01/1970, and the output number has only two digits, which also have a limited precision depending on your operating system. In fact if I use 0.001 as update rate the node doesn't work any more. Whereas if I put 0.01 it works well. But what if I wanted a higher precision? Is there a better way to do it?

edit flag offensive delete link more

Comments

1

Seeing the update rates you're going for, I would almost suggest to write a Gazebo plugin instead of using the ROS service.

Invoking ROS services at 1kHz is not a good idea in any case. It's possible, but it doesn't really fit well with the communication style.

Your node is also not synchronised with the simulation, and never will be. A Gazebo plugin will be integrated into Gazebo's internal update-loop, so it will be synchronised.

By giving your plugin a ROS API, you can easily update setpoints and other aspects of the behaviour of the plugin.

gvdhoorn gravatar image gvdhoorn  ( 2020-04-29 03:55:22 -0500 )edit

Question Tools

1 follower

Stats

Asked: 2020-04-26 16:43:51 -0500

Seen: 1,180 times

Last updated: Apr 29 '20