ROS2 Fun - Parameters for GoPiGo3 Servos

One of the frustrations of using other people’s code is the need to personalize it for your robot. For example the ROS gopigo3_node offered on GitHub (and used in the “Hands On ROS …” book) includes “ROS topics” to control the two possible servos of the GoPiGo3.

Depending on the particular servo ModRobotics happens to send you, the distance sensor may not point exactly forward when the servo is commanded with a pulse width half of the default pulse width limits (because there are only so many teeth on the servo shaft and “control horn sensor mount” the sensor).

The ROS(1) gopigo3_node code has CONSTANTS for pulse width limits but they are shared by both servos, difficult to determine, and you must “touch” the source code to change them.

Additionally, depending where you mount the servo kit, the distance sensor may not allow for the full 180 degree limit to limit positions, and the ROS gopigo3_node assumes full travel is possible.

These limitations mean that the ROS gopigo3_node software will not point to center when commanded with:

center servo:
$ ros2 topic pub -1 /servo/position/S1 std_msgs/msg/Float64 ‘{data: -0.0}’

And may stall the servo, drawing max current when commanded to point right 90 deg:

$ ros2 topic pub -t 1 /servo/position/S1 std_msgs/msg/Float64 '{data: -1.57}

As a ROS2 learning task, I chose to learn how to parameterize the pulse width limits and the sector width so that a ROS2 gopigo3_node user will not have to touch the source code.

It was more complicated that I had hoped, but I finally got it working this morning for the four cases:

  • default settings for three values for two servos
  • parameter values passed at “run” invocation
  • parameter file name passed at “run” invocation
  • parameters set later during node execution

It has been frustrating to be learning at the same time I’m migrating someone else’s code, but it really feels great when I get a feature working.

- Start node with parameter file
$ ros2 run ros2_gopigo3_node gopigo3_node --ros-args --params-file ./src/ros2_gopigo3_node/gopigo3_node_params.yaml &

GoPiGo3 info:
Manufacturer    :  Dexter Industries
Board           :  GoPiGo3
Serial Number   :  56ECD67E5152415447202020FF192614
Hardware version:  3.x.x
Firmware version:  1.0.0

GoPiGo3 Configuration:
(Using default values or ~/Dexter/gpg3_config.json if present)
WHEEL_BASE_WIDTH: 106.140 mm
ENCODER_TICKS_PER_ROTATION: 16 (per one motor revolution)
MOTOR_GEAR_RATIO: 120 (motor revolutions per wheel revolution)
MOTOR_TICKS_PER_DEGREE: 5.33 (of wheel rotation)

Node Version: 0.5
Rate: 30 hz
Servo1 PulseWidths L: 2094 R: 750            <--- Set from param file
Servo1 Total Sector Width: 2.443 radians     <---
Servo2 PulseWidths L: 2425 R: 575            <--- Defaults
Servo2 Total Sector Width: 3.14 radians

- Test servo parameter clipping to sector width:

  - Command to -90 deg should clip to -70 degrees per parameter limits:

$ ros2 topic pub -1 /servo/position/S1  std_msgs/msg/Float64 '{data: -1.57}'
publisher: beginning loop
publishing #1: std_msgs.msg.Float64(data=-1.57)

set_servo_angle(servo1): angle: -1.570 clipped angle: -1.222  pulse: 750

You are so, so very correct with this.

Unfortunately I can’t give you more than one like - this topic should get at least fifteen per person!

I have been banging my head on this for (it seems like) centuries with the remote camera robot project. This is especially true if the code looks like it was written in Ancient Greek and what sparse comments there are, are written in Etruscan pebble-writing! :face_with_symbols_over_mouth: :roll_eyes:

With regard to the servos, the problem isn’t really with Dexter, or even the servos. Many modern servo applications are designed to use a rack-and-pinion type motion. Because of this, the positioning of the toothed gear on the servo shaft isn’t held to tight tolerances.

The usual “pivot” type application (like rudder or flaps), usually has some kind of turnbuckle kind of linear adjustment so that “center” is truly “center”. If not, the controller’s “trim” adjustment provides that correction.

Additionally, the GoPiGo robot is not guaranteed to be perfectly level, therefore vertical “center” may we’ll be a slightly downward tilt.

For Charlie, I have two global constants “hcenter” and “vcenter” respectively. They are “hand tuned” so that when viewing the world through Charlie’s camera, the view is straight ahead and level. My “center” commands use these tuned values and the horizontal and vertical movements are referenced to those constants.

One thing that is in the back of my mind is to extend the capabilities of the “control panel” and allow it to move - and save into the calibration xml file - a known center position for each servo. I also want to implement a “limit of motion” pair of values for each servo as well and store them into the same file.

The various libraries for moving the servos would then use the defined “center” values to create an offset that is automatically algebraically combined with the commanded servo angle to give motion relative to those center points.

In your case, the only thing I can recommend is, if possible, overload the servo motion class and use a variable value for hcenter and vcenter that is defined at the top of the code, in a common library, or a special xml file where you keep global constants.

You do have a point and “Ahh feel yer pain!”


That is the better approach - four “constants” for each servo, but I didn’t want to completely rewrite the theory of operation for the migration.

I would have liked to specify:

  • center pulse width in usec
  • left/upper limit pulse width
  • right/lower limit pulse width
  • pulse width per degree (or radian). Usually degree on spec sheets but ROS is so “rad”

Is calibrating the servos in ROS that much of a pain? Can’t you use center ±offset?

Another reason for me to avoid ROS like the plague it is! :wink:

1 Like

In horizontal mounted servos yes, but the tilt often has unequal limits. The ROS node requires both limits but they must be equal +/- limits since it defines center as( limit+limit)/2


Impressive work. I’m not home (traveling for work) but if I recall correctly I’m using the mygopigo package, not gopigo3_node. The is almost the same in both, but of course there are a few differences. Not sure why. From what I remember at least one difference was in the tranforms set for the fixed_frame and something else (don’t recall off the top of my head). May not make a difference, but if you end up having trouble in Gazebo or RVIZ that’s the first thing I’d look at.


I was not aware the are different. I’m pretty sure I started from the Hands On ROS code, and planned to merge your pull request Chang also.


Wink noted.

My feelings at this point:

  • Installing ROS2 takes 3 lines, and confirming it worked took two lines.
  • Installing Ubuntu headless was a five minute easy task
  • Configuring Ubuntu 20.04 to successfully run GoPiGo3 code was insanely complex
  • I am not Linux savvy enough to create a GoPiGo dpkg like is available for turtlebots
    (Makes configuring one line)
  • Until I determine my ROS2 GoPiGo3 node is stable, GoPiGo3 owners should avoid ROS2
  • With @KeithW’s notes, GoPiGo3 owners wanting to learn ROS will be well served to follow the Hands On ROS book until 2023.
1 Like

Superb info - The book had me start out from the ros-gopigo package:

we are going to use a basic GoPiGo3 ROS package that is publicly available in GitHub from

Japón, Bernardo Ronquillo. Hands-On ROS for Robotics Programming: Program highly autonomous and AI-capable mobile robots powered by ROS . Packt Publishing. Kindle Edition.

and then mentions pkg_mygopigo in “Case study 1 - writing a ROS distance-sensor package”:

you will create a ROS package from scratch and produce the code to provide minimal ROS functionality with GoPiGo3, that is, reading its distance sensor. Be aware that the code you previously cloned at this location is the working solution for what your code is expected to do:


We encourage you to try to build the ROS package by yourself.

Japón, Bernardo Ronquillo. Hands-On ROS for Robotics Programming: Program highly autonomous and AI-capable mobile robots powered by ROS . Packt Publishing. Kindle Edition.

You discovered the secret sauce that makes it work - thank you.

$ diff ./Hands-On-ROS-for-Robotics-Programming/Chapter6_ROS_programming/pkg_mygopigo/src/ ./Hands-On-ROS-for-Robotics-Programming/gopigo3_node/src/

<         self.pub_joints = rospy.Publisher("joint_states", JointState, queue_size=10)
---                                     ^^^^ CORRECTED ^^^^^
>         self.pub_joints = rospy.Publisher("joint_state", JointState, queue_size=10)
<         transform = TransformStamped(header=Header(, frame_id="odom"), child_frame_id="base_link")
---                                                                                ^^^^ CORRECTED                 ^^^^^
>         transform = TransformStamped(header=Header(, frame_id="world"), child_frame_id="gopigo")
1 Like

“Theoretically” making a debian package file is a trivial script and two shots of vodka.  Then again, “theoretically” bees can’t fly.

I’ve read articles that claim that creating a package is easier than falling down a flight of stairs drunk. (Procount claimed the same thing about creating PINNified O/S images - until I called his bluff and tried making some myself.)

Then there are the legions of people who either make packages as a “thing”, (being a package maintainer), or are trying it for themselves. According to them it’s easier to climb the Matterhorn, crawling backwards, nude, in the dead of winter.

Even if none of this was true, and making packages WAS trivially easy, if you make your own, you’d always be second-guessing the package itself instead of looking for the real problem.

You did the right thing.


I did too - I’ll have to go back and look why I ended up using the mygopigo package.


looks like you’ve already done that for me - thanks :slight_smile:

You are much better about keeping up with new updates. While I know it’s a good idea, I’ve yet to develop that habit.