I wrote a driver for the PCA9685 I2C PWM driver chip. This chip is intended to be used with LED applications but is also used in a number of servo and motor controller boards, including a few Adafruit and Waveshare boards among others.
A couple of notable features I added that I haven’t seen done in other similar packages:
The ability to update only certain channels in a ROS command, and not update all of them. This allows multiple ROS nodes to publish to the same command topic but take charge of different subsets of the 16 channels.
A timeout. If a channel isn’t updated within that time, it gets set to 0. This is useful to avoid servos burning out if your control logic crashes, and also as a heartbeat timeout in the case of motor controllers.
Hi dheera,
Thanks for sharing your package. I am new to ROS so I’m still confusing after reading your readme file. You’re using Int32MultiArray as the message type, but in your description, the subscriber take 16 values for each channel. How are those two related. If I want to write send command to this node, how do I define the values of label, size, stride, data_offset, and data respectively to specify the channel?
Thanks much,
Zhouqiao
Hi Zhouqiao,
The code currently only reads the data field and expects 16 values. It doesn’t actually read label/stride/data_offset, so it’ll work without setting those, but it’s probably most “correct” to just define it as a one-dimensional array of 16 values. i.e.
Hi Dheera,
I have not been able to get this to work.
I have the Adafruit servo board working outside of ROS but I need to make it work within ROS as this project does.
I am fairly new to ROS but I do have several nodes working.
I work in python.
Is there something particular to get a cpp script working?
The approach I took was to download your project in a catkin workplace and compile.
If you could guide me on how to install your files in an existing project, that would be awsome.
Thanks.
Like I said, I am new to cpp nodes. I did not realize that you do not place the file extension “.cpp” as you need to place the “.py” when using python.
I now have the node running!
However, I have yet to figure out the publish nomenclature.
Hello Dheera,
Thank you for you package, I am having a similar issue as the rest of the folks on this list. i tried to pub to i2c 0 like the following
rostopic pub /pca9685_node/command std_msgs/Int32MultiArray “{data: [32767, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]}”
I publishing and latching message. Press ctrl-C to terminate
and nothing happens at this point. i am able to move my servos using the PCA class and Servokit otherwise.
Servos use PWM values between approximately 2800 to 7600 to describe a full range of motion. Issuing PWM values outside this range (e.g. 0 or 32767) will cause the servo to detach. The [32767, 0, …] example is for motor driver chips and LEDs, not servos.
Most hobby servos expect a PWM frequency of 50Hz so set the ROS “frequency” parameter to 50
Hope this helps! Let me know if it still doesn’t work.
Thanks for the quick response, let me clarify what I am experiencing. I am running metal gear hobby servos. When I initiate rosrun the servos move to the 0 position and stay there. No error is presented in info.
By the way I think there was a bug in the negative timeout logic – not documented yet but I recently added a feature where if you use a negative value for timeout, it would turn off the servo if the servo value doesn’t change for a set period of time (as opposed to not receiving a command). This was so it is better possible to support JointTrajectoryController interfaces without burning out the servos.
Although the bug should not affect positive timeouts you might want to run a git pull again just in case.
a. I see you are definitely on to something, -y returns nothing i2cdetect -y -r 0 returns 40 and 70 at 0.
b. I am able to manipulate servos using servokit and python3. I am making sure my connection using ServoKit is disposed. I am using a Jetson Nano, pCA9685 and MG995 servos
c. when i send the 4000 command nothing happens beyond the standard latching ros response with the option to quit CTRL C
I just found a bug that you might want to sort out: When giving the same input periodically in a time interval less than the timeout it still resets the values. There is an easy way of avoiding this by increasing and decreasing in 1 each time so the input is not the same (the result is pretty much the same). Just in case you want to look at it!
and i had connect the pca9685 to arduino with rosserial and a driver but this driver had erratic behaviour with your driver i connected the pca9685 directly to jetson nano and it works!!!
In order to control the arm with ros control and moveit i had wrote a hardware interface that ( because no feedback on the servos) (on the read() function i pass directly the commands on the write() function) this has the effect that the robot-rviz thinks the arm is moving whether or not you have connected the real arm
as @Dudu014 said when the arm sit still and recieve the same joint_states - array message after the timeout it depowers the servos. This was annoying but i made a workaround if same joint_states publish +1 -1 values. Maybe a fix for that?
It seems that i could push the servos in ranges greater than 180 degrees e.g i get the full range of motion -90 - +90 while my servos are not perfectly aligned in the center. previously i had to sacrifice some range. This is just confusing to me but it doesnt seem to destroy the servos
I had try to use the ServoKit python3 library and it worked but i couldnt connect it with ros because of the python 2 interperter of ros, so your driver saved me!!
Hmm I’ll try to track down this bug! The +1/-1 seems like a workaround but it shouldn’t normally operate that way.
My latest commit’s intended behavior is for a positive timeout to timeout if no command is issued, and a negative timeout to timeout if no change happens.
I also did some interfacing with the joint_trajectory_controller with my Luxo robot:
which isn’t quite well documented but there is some code in there I’ll take a look at this again.
i appreciate your work and i wanted to try out that node you created.
My Hardware is a RasPi 3B+ and i got the Motor HAT on top of it, with 4 servo motors connected.
Until now i used a simple c program to run them, but now i want to try running it via ros.
Before i worked with the 0x60 address too and everything worked fine. When i check the i2c Connection with i2cdetect -y 1 he Shows me devices on 0x60 / 0x70.
Am i doing something wrong or did i miss something?
Hi @rosstarter
0x40 is the default address for the ROS node, as is used on many boards. For the Adafruit board it the board’s default is 0x60. Make sure you are setting the ROS “address” parameter to 0x60 == 96.