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

Hi,

I have lidar and arduino motor control on srial ports of my robot (on usbs of my raspberry pi). Sometimes lidar and arduino changes serial ports (lidar is on 1 instead of 0 and arduino on 0 instead of 1)and i have to change the configuration of both of them and recompile and start again. Is there an way to make it automatically on the start.

Thank you very much!

Best regards,
Ivo

Hi Josh, do you know if it is possible to implement a real controller of a robot (reality) and in parallel a Gazebo simulation so that ros2_control controls both the hardware and the behavior in Gazebo like a digital twin? In your article i understood that its either the real robot or gazebo simulation.

Hello
All the hardware and software I use are the same as the one you use. The router is the same. When I connect the router to the development machine with ethernet as you do, and when I connect it to the raspberry pi 4, when I want to control the robot as in the 13th video, it gives a biiiip sound and cuts the internet for a short time. It gives buffer.cpp error, where could I have made a mistake?

Hi Ivo, sorry for not getting back to you (and many other people).

The simplest way to achieve what you want is instead of referencing the devices as /dev/ttyUSB0 or whatever, instead use /dev/serial/by-id/xxx or /dev/serial/by-path/xxx. These paths are tied to either the identifier of the hardware, or the actual USB port it is plugged into, so as long as you keep them in the same ports, the OS should identify them by the same path each time!

If you really want to have it in the launch file you can use an xacro argument and pass it in, similar to how we handle the sim mode and ros2_control mode in rsp.launch.py.

Again, sorry it’s late but I hope that helps!

That’s a great question and I’ve never really thought about it!
In short, I think the answer is no…but sort of yes?
The ros2_control controller expects to only control one hardware interface, so to achieve what you’re talking about you’d just have to run two independent controllers and have them both listening to the same twist topic (but avoid them publishing to the same feedback).

You could achieve a similar result if you bypassed ros2_control completely and wrote a custom node that interacted with Gazebo and also your robot but it gets…messy (and I suspect probably won’t achieve the desired result, especially with something like a mobile robot. It might be good for an arm)

Wow that is a strange issue, I’m not sure, sorry.
Also I haven’t mentioned this but I actually swapped to a different router as I thought the first one was maybe causing me problems…but then I still had problems with the new one too so I never said anything.

When you say it beeps, do you mean the router beeps or the Pi beeps? Perhaps it is a power issue? How are you powering the router?

Hi Josh, thanks a lot for your tutorials,

I am using Arduino UNO instead of Nano, there is no issues when I do launch launch_robot file and it tells me that all controllers configured correctly but when I try to drive it with teleop, none of the motors move!!.. my driver EN pins on the UNO are 6,3,9,5.

Please help me, my graduation project is depending on that

Hi Josh, thanks for this great tutorial. However I’m facing issue when unbreaking gazebo. I follow your tutorial exactly, but when i launch my launch_sim.launch.py, i get this error instead

afiq@afiq-VM:~/dev_ws$ ros2 launch my_bot launch_sim.launch.py world:=src/my_bot/worlds/obstacles.world
[INFO] [launch]: All log files can be found below /home/afiq/.ros/log/2023-06-02-08-25-17-400678-afiq-VM-24746
[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=SubstitutionFailure('executed command failed. Command: xacro /home/afiq/dev_ws/install/my_bot/share/my_bot/description/robot.urdf.xacro use_ros2_control:=true sim_mode:=true')>
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 3 more times]
  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 444, in execute
    self._perform_substitutions(context)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/actions/node.py", line 399, in _perform_substitutions
    evaluated_parameters = evaluate_parameters(context, self.__parameters)
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/utilities/evaluate_parameters.py", line 164, in evaluate_parameters
    output_params.append(evaluate_parameter_dict(context, param))
  File "/opt/ros/foxy/lib/python3.8/site-packages/launch_ros/utilities/evaluate_parameters.py", line 71, in evaluate_parameter_dict
    evaluated_value = perform_substitutions(context, list(value))
  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/substitutions/command.py", line 116, in perform
    raise SubstitutionFailure(f'executed command failed. Command: {command_str}')
launch.substitutions.substitution_failure.SubstitutionFailure: executed command failed. Command: xacro /home/afiq/dev_ws/install/my_bot/share/my_bot/description/robot.urdf.xacro use_ros2_control:=true sim_mode:=true

How do i solve this?

Solved this. Figure out don’t need to install gazebo ros2 control package

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.