Discussion - Running ros2_control on a real robot (Making a Mobile Robot Pt 13)

I’m glad you got it sorted but I’m a bit confused by your solution.

It seems like xacro itself was failing - possibly you had an error in your URDF?

Hello I’m a beginner in robotics, im mainly a software guy. With that said, i have a question.

  1. is it possible that ill just make my own hard code controller instead of using diff drive from ros2 control package? Ill just convert the cmd_vel to left and right motor velocity.
  2. by doing the mentioned above, what are the disadvantages of this? I’m doing this because i don’t know how to get the hardware interfaces of the physical robot, i only know how to send commands which are the left and right motor velocities.

Welcome!

Yep

The disadvantages are:

  • You have to write the code for the differential drive calculations, and you will probably make a mistake/not think of something that the ros2_control developers have thought of. By using their code you know it works and has been tested (at least, most of the time)
  • If you ever want to change your hardware out you’ll have to modify the whole thing instea of just the hardware interface

When you say

What do you mean? Essentially all a ros2_control harware interface is is a C++ plugin that does that, but takes the input from ros2_control rather than your own calculation.

But yeah, doing it the long way is always an option (and not a bad one for learning how differential drive control works) but at some point it makes sense to take the more robust, flexible option of ros2_control.

(P.S. I’ve responded to your other message)

What i mean with i can send only motor commands. Is that by using pyserial and ESP32 in my laptop, i’ll just write a certain string that indicated the speed value of both motors for example, the string is “0.5,1.0&\n” (0.5 = left motor speed, 1.0 = right motor speed) then i use arduino.write() to send those command sending it to another ESP32 that has arduino attached to it.

I did this because the hardware guy just told me to send those string via esp32, and he just do the rest of the hardware control thing.

We don’t use rasberry pi btw, just like you did.
So it’s basically i’m sending a string command with my laptop connected to esp32 then another esp32 connected to arduino just received the command, and i don’t know how the hardware guy works around with that string i have sent. I can ask him if necessary to provide more information.

Is our approach in doing this project correct? Is there anyway i can expose state interfaces with robot with our current approach? Thanks

1 Like

I have a sort of weird problem. I’ve resumed this project after a couple of months and actually started it over as I’d forgotten a bit. The serial motor demo and gui works great on my setup, the motors behave as expected. However, after completing the ros2 control files and code, using teleop causes the motors to go crazy. They will start slowly in the same direction, then they will quickly climb to FULL speed of my motors, one flipping direction in the process and the robot would spin in place at full speed (I have it on blocks so this won’t happen). Nothing I’ve messed with seems to have made any difference. It almost reminds me of an uncontrolled PID controller, like the motors are trying to match a value or catch up with something and max out. Does anyone have any ideas?

Honestly, that sounds very similar to how mine works, just with a few extra steps - which means it shouldn’t be too hard to modify!
I recommend watching my motor tutorial first, so that you understand how mine is structured (and how it is similar and different to yours).

Then watch the hardware interface tutorial which shows how to connect that to ros2_control. You should be able to pretty much take my hardware interface and make some small modifications to the class that handles the comms.

1 Like

Bizarre! Yeah it does sound like a PID controller behaving badly.
One possible issue if you are using my exact code is that the PID settings might have changed since the earlier version? The latest hardware interface is here and it gives you the ability to tweak the PID parameters.
That version SHOULD leave them as default if you don’t set them, but I think there were other versions with a hardcoded value so you may have that?

My other suggestion would be to add print statements into the hardware interface to confirm exactly what is being sent to the motors. That’s a little tedious but should get to the bottom of it.

It looks like that branch is for Humble. I’m using Foxy, can it be used with it or do I need to upgrade?

Hey @JoshNewans thank you for the great tutorial, it has been of great benefit to us as students. We are having difficulty loading the /controller_manager services, we’ve tried the solutions you suggested earlier in the thread to @Sasffm, but it is still not working. We have the same setup, and the device port has been set correctly, even the user has been added to the dialout group. The following is the error obtained on running the launch file.

[INFO] [launch]: All log files can be found below /home/vahin/.ros/log/2023-06-07-09-52-16-628234-ubuntu-13541
[INFO] [launch]: Default logging verbosity is set to INFO
[INFO] [robot_state_publisher-1]: process started with pid [13544]
[robot_state_publisher-1] Parsing robot urdf xml string.
[robot_state_publisher-1] Link chassis had 2 children
[robot_state_publisher-1] Link caster_wheel had 0 children
[robot_state_publisher-1] Link laser_frame had 0 children
[robot_state_publisher-1] Link left_wheel had 0 children
[robot_state_publisher-1] Link right_wheel had 0 children
[robot_state_publisher-1] [INFO] [1686131537.466259297] [robot_state_publisher]: got segment base_link
[robot_state_publisher-1] [INFO] [1686131537.466644027] [robot_state_publisher]: got segment caster_wheel
[robot_state_publisher-1] [INFO] [1686131537.466723784] [robot_state_publisher]: got segment chassis
[robot_state_publisher-1] [INFO] [1686131537.466753468] [robot_state_publisher]: got segment laser_frame
[robot_state_publisher-1] [INFO] [1686131537.466781004] [robot_state_publisher]: got segment left_wheel
[robot_state_publisher-1] [INFO] [1686131537.466809170] [robot_state_publisher]: got segment right_wheel
[INFO] [ros2_control_node-2]: process started with pid [13606]
[INFO] [spawner.py-3]: process started with pid [13608]
[INFO] [spawner.py-4]: process started with pid [13610]
[ros2_control_node-2] [INFO] [1686131543.680967466] [DiffDriveArduino]: Configuring...
[ros2_control_node-2] [INFO] [1686131543.687668416] [DiffDriveArduino]: Finished Configuration
[ros2_control_node-2] [INFO] [1686131543.688505651] [DiffDriveArduino]: Starting Controller...
[ros2_control_node-2] [INFO] [1686131545.719892550] [controller_manager]: update rate is 30 Hz
[ros2_control_node-2] [INFO] [1686131545.762545973] [controller_manager]: Loading controller 'diff_cont'
[ros2_control_node-2] [ERROR] [1686131545.762714005] [controller_manager]: Loader for controller 'diff_cont' not found.
[ros2_control_node-2] [INFO] [1686131545.762758207] [controller_manager]: Available classes:
[ros2_control_node-2] [INFO] [1686131545.762797873] [controller_manager]:   controller_manager/test_controller
[ros2_control_node-2] [INFO] [1686131545.762828928] [controller_manager]:   controller_manager/test_controller_failed_init
[ros2_control_node-2] [INFO] [1686131545.762866667] [controller_manager]:   controller_manager/test_controller_with_interfaces
[ros2_control_node-2] [INFO] [1686131545.835433835] [controller_manager]: Loading controller 'joint_broad'
[ros2_control_node-2] [ERROR] [1686131545.835685402] [controller_manager]: Loader for controller 'joint_broad' not found.
[ros2_control_node-2] [INFO] [1686131545.835744493] [controller_manager]: Available classes:
[ros2_control_node-2] [INFO] [1686131545.835786603] [controller_manager]:   controller_manager/test_controller
[ros2_control_node-2] [INFO] [1686131545.835817731] [controller_manager]:   controller_manager/test_controller_failed_init
[ros2_control_node-2] [INFO] [1686131545.835846286] [controller_manager]:   controller_manager/test_controller_with_interfaces
[ERROR] [spawner.py-4]: process has died [pid 13610, exit code 1, cmd '/opt/ros/foxy/lib/controller_manager/spawner.py diff_cont --ros-args'].
[ERROR] [spawner.py-3]: process has died [pid 13608, exit code 1, cmd '/opt/ros/foxy/lib/controller_manager/spawner.py joint_broad --ros-args'].

Is there something we’ve missed out on? The controller_manager had issues earlier as well when running on the dev machine but it was sorted when it was rerun a few times.

Ok so the main error here is that it goes to load diff_cont and joint_broad but can’t find them. Instead it is finding one called test_controller (I’m not sure if you’ve put that in or if it’s standard?)

Do you have a controllers.yaml file that you are passing in? What is inside it? You should have the diff drive controller and joint state broadcaster defined in there.

If that is set up correctly, then make sure it is actually being loaded!

Yeah the old branch is foxy but I’m not really maintaining it.
The principles are all pretty similar so if you compare the two and watch this video you should be able to figure it out.

Thank you for your prompt response and suggestions, we will look into it.

I have a really weird Issue. The robot drives forwards and backwards fine but it doesn’t turn.

Hmm that is odd.

Is this with teleoperation or autonomous control?

I’d first use ros2 topic echo to check the contents of your cmd_vel topic are correct and you are sending a Z rotation.

If that is all ok then check your diff drive parameters (wheel spacing etc) are all correct and you don’t have anything odd in there like rotational limits.

Both it is even a problem when I just use the ROS-Arduino-Bridge.ino and miniterm. When I type o 255 255 or negative it works fine. When I type o 255 -255 the motors don’t turn at all.

After fooling around with it quite a bit, I do see that it is the PID values, possible something else as well. I could sit and fool with the values, but the behavior is still strange (the motors start slow and rev up to the required speed, but overshoot it and reverse directions). Open loop control works great. I would love to rewrite this thing at some point. I’m fuzzy on a few things I need to research first, like how to translate input tick count to speed to PWM. I’m also not sure what “Ko” is in the PID code. Josh, what are the RPM speeds of your motors? I think mine are about 250 or so, maybe too much for this application (if that even makes a difference). Anyway, I’ll keep fussing with it.

[INFO] [launch]: All log files can be found below /home/cliff/.ros/log/2023-06-11-09-19-26-129315-cliff-Legion-4342
[INFO] [launch]: Default logging verbosity is set to INFO
Task exception was never retrieved
future: <Task finished name='Task-2' coro=<LaunchService._process_one_event() done, defined at /opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py:226> exception=PackageNotFoundError("package 'twist_mux' not found, searching: ['/home/cliff/dev_ws/install/serial_motor_demo', '/home/cliff/dev_ws/install/serial_motor_demo_msgs', '/home/cliff/dev_ws/install/diffdrive_arduino', '/home/cliff/dev_ws/install/serial', '/home/cliff/dev_ws/install/articubot_one', '/opt/ros/foxy']")>
Traceback (most recent call last):
  File "/opt/ros/foxy/lib/python3.8/site-packages/ament_index_python/packages.py", line 50, in get_package_prefix
    content, package_prefix = get_resource('packages', package_name)
  File "/opt/ros/foxy/lib/python3.8/site-packages/ament_index_python/resources.py", line 48, in get_resource
    raise LookupError(
LookupError: Could not find the resource 'twist_mux' of type 'packages'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 228, in _process_one_event
    await self.__process_event(next_event)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 248, in __process_event
    visit_all_entities_and_collect_futures(entity, self.__context))
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 45, in visit_all_entities_and_collect_futures
    futures_to_return += visit_all_entities_and_collect_futures(sub_entity, context)
  [Previous line repeated 1 more time]
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 38, in visit_all_entities_and_collect_futures
    sub_entities = entity.visit(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/action.py", line 108, in visit
    return self.execute(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/actions/node.py", line 453, in execute
    ret = super().execute(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/actions/execute_process.py", line 823, in execute
    self.__expand_substitutions(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/actions/execute_process.py", line 668, in __expand_substitutions
    cmd = [perform_substitutions(context, x) for x in self.__cmd]
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/actions/execute_process.py", line 668, in <listcomp>
    cmd = [perform_substitutions(context, x) for x in self.__cmd]
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/perform_substitutions_impl.py", line 26, in perform_substitutions
    return ''.join([context.perform_substitution(sub) for sub in subs])
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/perform_substitutions_impl.py", line 26, in <listcomp>
    return ''.join([context.perform_substitution(sub) for sub in subs])
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_context.py", line 232, in perform_substitution
    return substitution.perform(self)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/substitutions/executable_in_package.py", line 76, in perform
    package_prefix = super().perform(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/substitutions/find_package.py", line 79, in perform
    result = self.find(package)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/substitutions/find_package.py", line 96, in find
    return get_package_prefix(package_name)
  File "/opt/ros/foxy/lib/python3.8/site-packages/ament_index_python/packages.py", line 52, in get_package_prefix
    raise PackageNotFoundError(
ament_index_python.packages.PackageNotFoundError: "package 'twist_mux' not found, searching: ['/home/cliff/dev_ws/install/serial_motor_demo', '/home/cliff/dev_ws/install/serial_motor_demo_msgs', '/home/cliff/dev_ws/install/diffdrive_arduino', '/home/cliff/dev_ws/install/serial', '/home/cliff/dev_ws/install/articubot_one', '/opt/ros/foxy']"
Task exception was never retrieved
future: <Task finished name='Task-8' coro=<LaunchService._process_one_event() done, defined at /opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py:226> exception=RuntimeError('Signal event received before subprocess transport available.')>
Traceback (most recent call last):
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 228, in _process_one_event
    await self.__process_event(next_event)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/launch_service.py", line 248, in __process_event
    visit_all_entities_and_collect_futures(entity, self.__context))
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/utilities/visit_all_entities_and_collect_futures_impl.py", line 38, in visit_all_entities_and_collect_futures
    sub_entities = entity.visit(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/action.py", line 108, in visit
    return self.execute(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/actions/opaque_function.py", line 75, in execute
    return self.__function(context, *self.__args, **self.__kwargs)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch/actions/execute_process.py", line 443, in __on_signal_process_event
    raise RuntimeError('Signal event received before subprocess transport available.')
RuntimeError: Signal event received before subprocess transport available.
[INFO] [robot_state_publisher-1]: process started with pid [4345]
[robot_state_publisher-1] Parsing robot urdf xml string.
[robot_state_publisher-1] Link base_footprint had 0 children
[robot_state_publisher-1] Link chassis had 4 children
[robot_state_publisher-1] Link camera_link had 1 children
[robot_state_publisher-1] Link camera_link_optical had 0 children
[robot_state_publisher-1] Link caster_wheel had 0 children
[robot_state_publisher-1] Link face_link had 0 children
[robot_state_publisher-1] Link laser_frame had 0 children
[robot_state_publisher-1] Link left_wheel had 0 children
[robot_state_publisher-1] Link right_wheel had 0 children
[robot_state_publisher-1] [INFO] [1686455366.472416353] [robot_state_publisher]: got segment base_footprint
[robot_state_publisher-1] [INFO] [1686455366.472564150] [robot_state_publisher]: got segment base_link
[robot_state_publisher-1] [INFO] [1686455366.472588526] [robot_state_publisher]: got segment camera_link
[robot_state_publisher-1] [INFO] [1686455366.472606757] [robot_state_publisher]: got segment camera_link_optical
[robot_state_publisher-1] [INFO] [1686455366.472624637] [robot_state_publisher]: got segment caster_wheel
[robot_state_publisher-1] [INFO] [1686455366.472642309] [robot_state_publisher]: got segment chassis
[robot_state_publisher-1] [INFO] [1686455366.472659212] [robot_state_publisher]: got segment face_link
[robot_state_publisher-1] [INFO] [1686455366.472675486] [robot_state_publisher]: got segment laser_frame
[robot_state_publisher-1] [INFO] [1686455366.472692599] [robot_state_publisher]: got segment left_wheel
[robot_state_publisher-1] [INFO] [1686455366.472709362] [robot_state_publisher]: got segment right_wheel
[ERROR] [robot_state_publisher-1]: process[robot_state_publisher-1] failed to terminate '5' seconds after receiving 'SIGINT', escalating to 'SIGTERM'
[INFO] [robot_state_publisher-1]: sending signal 'SIGTERM' to process[robot_state_publisher-1]
[ERROR] [robot_state_publisher-1]: process has died [pid 4345, exit code -15, cmd '/opt/ros/foxy/lib/robot_state_publisher/robot_state_publisher --ros-args --params-file /tmp/launch_params_zw350_hn'].

Cant find whats actually going wrong, feels like its ros not building properly

Very odd, I’m suspicious of maybe a wiring issue with the motors/driver/arduino?

Do

  • o 255 0
  • o -255 0
  • o 0 255
  • o 0 -255
    All work as expected?

Interesting, I have had similar problems with other motors. I still got them to work but they were a bit more sensitive to the PID settings.

Yeah me too. I didn’t write the Arduino code (just made some small mods) and would like to do it from scratch.

I also found this confusing. I believe this and also the reason everything is done in “encoder ticks per loop” is to avoid doing floating point arithmetic in the Arduino. So the Ko just scales things up. E.g. if we want Kp = 0.5 (and Ko = 1) we can’t do that, so instead we set Kp = 5 and Ko = 10.
It’s a little while since I dug into it though so that might be not quite right.

Pretty sure they are 110RPM. 250 is pretty fast, especially if your encoders aren’t super fast. E.g. your requests might boil down to the difference between a few integers in counts per loop and the PID will be jumping rapidly between them.

Make sure when posting code blocks (I have edited yours) you put backticks (the ~ key without shift) around it to make it easier to read, like this:

```
My code here
```

Your issue here is you don’t have twist_mux installed (sudo apt install ros-foxy-twist-mux).

This error should be clearer, except I have not explicitly made twist_mux a dependency of articubot_one so it tries to run without checking that it exists, and you get that very messy error.