In questo tutorial vedremo come poter impostare la visualizzazione grafica di messaggi pubblicati sui differenti topic da una simulazione tramite l’uso di RViz e come poter salvare una configurazione della visualizzazione per poterla riutilizzare in futuro.
Visualizzazione dei messaggi dei topic
Per iniziare sarà necessario che siano attivi dei nodi che pubblicano dei messaggi adeguati, che siano nodi che abbiamo scritto noi, o preesistenti all’interno di una simulazione o di un robot con ROS installato.
Per questo tutorial utilizzeremo la simulazione di un drone disponibile sul corso di TheConstruct “Programming Drones with ROS“.
La simulazione sarà avviata nel momento in cui apriremo il corso e il relativo Rosject sarà avviato quando accederemo all’unità 3.
Possiamo verificare l’attività dei publisher relativi ai dati dei sensori a bordo del drone: rostopic list
Otterremo come output una serie di topic che pubblicano informazioni relative a sensori di varia natura (LiDAR, Depth Cameras ecc.) la cui visualizzazione potrebbe risultare interessante per lo scopo di un nostro progetto.
RViz
Per poter lavorare con RViz sarà necessario lanciarlo dal nostro terminale.
Procediamo quindi con il seguente comando: rosrun rviz rviz
Una volta di fronte alla schermata di RViz possiamo quindi iniziare a impostare un reference frame (in questo caso “base_link”).
Andiamo in alto a sinistra, in “Global options”, e impostiamo il “Fixed Frame” con valore “base_link”.
Siamo quindi pronti per poter aggiungere e visualizzare tutti i topic a cui siamo interessati.
Un ottimo punto di partenza sarebbe quello di visualizzare il modello fisico del robot che viene pubblicato dalla simulazione. In basso a sinistra premiamo quindi su “Add” e scorriamo fino a trovare “RobotModel”. Clicchiamo su “RobotModel” e poi su “Ok”.
Il modello fisico del robot sarà quindi rappresentato al centro della nostra simulazione.
All’interno di questa simulazione abbiamo per esempio anche la pubblicazione di messaggi da parte di sensori legati al robot, come LaserScan, ottenuti attraverso uno scan da parte di un LiDAR dell’ambiente circostante. In basso a sinistra premiamo quindi su “Add” e ci spostiamo nella tab “By topic”, dove potremo vedere quali topic stanno pubblicando messaggi, il loro nome e il tipo di messaggio che possiamo andare a visualizzare.
Aggiungiamo per esempio lo scan relativo al LiDAR /scan/LaserScan
le immagini raccolte dalla telecamera rgb frontale on-board /camera/rgb/image_raw/Image
e il PointCloud ritornato dalla DepthCamera /camera/depth/points/PointCloud2
Salvare la configurazione
Poter visualizzare tutti questi topic porta un grosso vantaggio nel momento in cui andremo a testare i nostri progetti o semplicemente a raccogliere informazioni su ciò che il robot effettivamente è in grado di percepire. Chiaramente, più topic dovranno essere tracciati più sarà lungo il processo di configurazione ogni volta che andremo ad aprire RViz.
Per evitare questa faticosa operazione è possibile salvare la configurazione di RViz in modo da poterla riutilizzare in una seconda apertura, recuperandola direttamente dal comando di avvio di RViz.
Questo permetterà non solo di non dover ripetere il processo di configurazione, ma anche di poter salvare configurazioni differenti in base a ciò di a cui siamo interessati e di potersi muovere in modo semplice tra di esse.
Procediamo dunque con creare uno spazio all’interno del quale salvare le nostre configurazioni.
Ci spostiamo sull’editor di codice e creiamo una nuova cartella “rviz” all’interno di catkin_ws/src/my_rtab_package.
Qui andremo a salvare la configurazione e a recuperarla quando andremo a lanciare RViz.
Torniamo quindi su RViz (ripetiamo la configurazione nel caso l’avessimo o si dovesse essere chiuso) e poi su “File” > “Save Config As” e scegliamo il percorso in cui salvare il nostro file di configurazione, in questo caso salviamo il file in /home/user/catkin_ws/src/my_rtab_package/rviz/sensor_view.rviz.
Avviare RViz con configurazione salvata
Quello che ci manca a questo punto sarà solo un modo per avviare RViz indicando la configurazione che vogliamo che sia utilizzata. Utilizziamo dunque il comando: rosrun rviz rviz -d `rospack find my_rtab_package`/rviz/sensor_view.rviz
IMPORTANTE: l’uso del backtick (`) è essenziale perché il comando funzioni, con il layout italiano è possibile ottenerlo con [AltGr + ‘]
In questo modo RViz viene avviato e configurato a seconda del file che indichiamo all’interno del comando, risparmiandoci la fatica di dover ripetere tutto il procedimento dei punti precedenti e garantendo una maggiore flessibilità nell’osservare diverse visualizzazioni dei nostri messaggi.
This tutorial is created by Robotics Ambassador Eric
Những điều bạn sẽ được biết trong blog này:
Cách cài đặt các packages cần thiết để mô phỏng nhiều robots ở ROS2 Humble
Cách tạo launch file để mô phỏng nhiều robot trên gazebo
Mô phỏng robots trên Rviz và gazebo
Phần 2: https://www.theconstruct.ai/lam-sao-de-mo-phong-nhieu-robot-su-dung-nav2-trong-khong-gian-gazebo-voi-ros2-humble-phan-2-vietnamese-ros2-tutorial/
Khi làm việc với dự án yêu cầu bạn phải kết hợp nhiều robots với nhau để thực hiện một nhiệm vụ nào đó, vấn đề đầu tiên đặt ra là làm sao để có thể mô phỏng được nhiều robot trên một môi trường cụ thể. Do đó, blog này sẽ giúp bạn biết cách thực hiện điều đó. Đây sẽ là phần 1 của blog này với mong muốn sẽ giúp cho các bạn biết cách tạo một launch file để tích hợp nhiều robots và mô phỏng chúng trên Gazebo cùng với Rviz. Tiếp theo đó sẽ là phần 2 sẽ hướng dẫn các bạn sử dụng Nav2 package cùng với nhiều robot trong môi trường Gazebo.
Khởi động rosject
Như mọi lần rosject sẽ là công cụ hoàn hảo cho việc thực hiện project này, nơi các bạn dễ dàng truy cập và khởi tạo các project cùng với ROS ở các phiên bản khác nhau:
Như hình bên dưới là một rosject hoàn chỉnh, bạn chỉ việc nhấn vào nút RUN để khởi động rosject.
Sau khi nhấn vào nút RUN bạn sẽ thấy rosject được chạy trên màn hình. Ở đây, rosject cung cấp cho bạn cả terminal để có thể nhập các câu lệnh:
Bây giờ cùng nhau đến bước tiếp theo trong ví dụ này nhé.
Cài đặt các package cần thiết
Đầu tiên các bạn cần cài đặt các package cần thiết để có thể thực hiện project này, sau khi vào môi trường làm việc catkin là ros2_ws các bạn làm theo bằng cách thực hiện các câu lệnh sau đây:
Sau bước này thì các bạn sẽ nhìn thấy thông báo cài đặt thành công như hình bên dưới.
Tiếp theo sử dụng câu lệnh sau để build môi trường catkin
colcon build --symlink-install
source ./install/setup.bash
Sau khi tải thành công các bạn sẽ nhìn thấy folder turtlebot3_multi_robot trong src. ở đây sẽ bao gồm các file cần thiết cho project này. Tiếp theo chúng ta sẽ đến mô phỏng và đi sâu vào làm sao để tạo được launch file.
Mô phỏng trên Gazebo cùng nhiều robots
Thông thường khi làm việc với turtlebot3 các bạn luôn phải chọn mô hình robot và truy xuất môi trường cũng như đường dẫn đến package. Tuy nhiên, trong blog này thay vì phải command mỗi lần chạy chương trình. Mình sẽ hướng dẫn các bạn cách khai báo trong launch file để chương trình tự động thực hiện cài đặt.
Sau đó để sử dụng môi trường gazebo và robot, bạn cần truy xuất môi trường gazebo và chạy ros launch package:
Vì chúng ta sẽ mô phỏng không chỉ một mà nhiều robot cùng một lúc nên khi thực hiện câu lệnh chương trình cần một lúc để xử lý được yêu cầu nên các bạn kiên nhẫn nhé.
Mô phỏng trên RViz cùng nhiều robots
Khi thực hiện ros launch ở trên, 4 cửa sổ Rviz tương ứng với mỗi robot cũng sẽ xuất hiện đồng thời như hifnh dưới. Mỗi cửa sổ đại diện cho một robot.
Bên cạnh đó, nếu các bạn kiểm tra ros topic thì các bạn sẽ thấy đầy đủ đồng thời tất cả topic của robot được khai báo (tb1, tb2, tb3, tb4). Từ đây chúng ta có thể áp dụng vào nhiều project sử dụng nhiều robots cùng lúc.
source ~/ros2_ws/install/setup.bash
ros2 topic list
Cách tạo launch file để mô phỏng nhiều robots cùng lúc
Sau đây, mình sẽ giải thích các phần trong launch file để các bạn tùy ý có thể thay đổi và thực hiện project cho riêng mình.
1. Khởi tạo Gazebo server và client ¶
Để có thể chạy mô phỏng trên Gazebo chúng ta cần khai báo và khởi tạo Gazebo server và client như sau
Khi mô phỏng trên gazebo, chúng ta cần biết vị trí cần bố trí robot hay nói cách khác chúng ta cần khởi tạo tọa độ cho robot trên môi trường gazebo. Để làm việc đó, cách tiếp cận đơn giản là sử dụng hàng và cột để xác định vị trí theo dạng lưới. Với mỗi vị trí trên lưới, nó sẽ tạo ra một tên và không gian duy nhất cho từng robot. Sau đó, tạo và cấu hình nút robot_state_publisher và spawn_entity.py để xuất hiện robot trong gazebo.
Cách sắp xếp tuần tự với trình xử lý sự kiện (Event Handlers)
Chúng ta sửu dụng phương pháp tuần tự để robot xuất hiện, đảm bảo rằng mỗi robot được hoàn toàn trang bị trước khi đến với robot tiếp theo. Điều này được thực hiện thông qua hai chức năng RegisterEventHandler và OnProcessExit để chờ quá trình trước đó được hoàn thành.
Sau khi các robots được hoàn toàn khởi tạo, chúng ta có thể bắt đầu đùng các nút để điều khiển robot dựa trên enable_drive, cho phép điều khiển chuyển động của robots trong môi trường mô phỏng
Đến đây là kết thúc phần 1 của blog này, các bạn có thể theo doxi phần 2 để kết hợp nhiều robots cùng với Nav2 package. Hy vọng qua blog này các bạn đã có thêm một số kiến thức cũng như công cụ hỗ trợ cho các dự án liên quan tới robotics sử dụng ROS. Bên cạnh đó, các bạn có thể theo dõi các blog khác cũng như các khóa học liên quan trên The Construct nhé.
Trong bài toán định vị (localization) ở robot, chúng ta cần sự trợ giúp từ các cảm biến như IMU, GPS, và Odometry. Bằng cách kết hợp các tín hiệu từ các cảm biến lại với nhau(sensor fusion) một lượng thông tin bổ ích sẽ giúp cho robot định vị được tọa độ chính xác trong các môi trường khác nhau. Trong project này, mình sẽ giới thiệu tới các bạn Robot Localization package một công cụ hữu ích để tích hợp sensor fusion cùng với turtlebot 3. Ở project này, mức độ đòi hỏi các bạn cần có một số kiến thức cơ bản như urdf, frames,… để có thể chỉnh sửa và thay đổi các file sẵn có. Hy vọng blog này sẽ mang lại cái nhìn tổng quan về chủ đề sensor fusion và localization ở robotics.
Khởi động rosject
The Construct đã tạo ra một công cụ rất hữu ích là rosject, nơi các bạn dễ dàng truy cập và khởi tạo các project cùng với ROS ở các phiên bản khác nhau:
Như hình bên dưới là một rosject hoàn chỉnh, bạn chỉ việc nhấn vào nút RUN để khởi động rosject.
Sau khi nhấn vào nút RUN bạn sẽ thấy rosject được chạy trên màn hình. Ở đây, rosject cung cấp cho bạn cả terminal để có thể nhập các câu lệnh:
Bây giờ cùng nhau đến bước tiếp theo trong ví dụ này nhé.
Cài đặt các package cần thiết
Đầu tiên các bạn cần cài đặt các package cần thiết để có thể thực hiện project này, sau khi vào môi trường làm việc catkin là ros2_ws các bạn làm theo bằng cách thực hiện các câu lệnh sau đây:
Sau bước này thì các bạn sẽ cần một ít thời gian để có thể cài đặt thành công các gói cần thiết cho chương trình.
Tiếp theo sử dụng câu lệnh sau để build môi trường catkin
colcon build --symlink-install
Mình sẽ giải thích lý do tại sao các bạn lại phải sử dụng đường link github phía bên trên. Vì ở trong project này mình muốn tổng hợp và sử dụng những packages cần thiết và chỉnh sửa một số nội dung file nên mình đã gom thành một package lớn bao gồm các packages nhỏ để tiện cho việc chạy chương trình.
Mô phỏng trên Gazebo cùng robot
Sau khi đã cài đặt đầy đủ các package cần thiết. Bước tiếp theo bạn cần chọn model robot cho project của bạn. mình sử dụng waffle trong project này:
source ~/ros2_ws/install/setup.bash
export TURTLEBOT3_MODEL=waffle
Sau đó để sử dụng môi trường gazebo và robot, bạn cần truy xuất môi trường gazebo và chạy ros launch package:
Cửa sổ Gazebo sẽ xuất hiện như trên cùng với robot waffle.
Chạy Robot Localization package
Như bạn thấy môi trường và robot đã có, điểm quan trọng trong project lần này là biết cách áp dụng các cảm biến như IMU, Odometry, GPS,.. cùng với robot trong nhiệm vụ tổng hợp các tiến hiệu từ các cảm biến khác để giải quyết bài toán định vị cho robot. Ở cửa sổ thứ 2, các bạn thực hiện câu lệnh sau đây:
source ~/ros2_ws/install/setup.bash
ros2 launch robot_localization ekf.launch.py
Tạo và mô phỏng robot cùng cảm biến trên Rviz
Sau khi đã có môi trường cho robot hoạt động là gazebo, có các cảm biến được tích hợp trên robot, nhiệm vụ là làm sao để có thể xác định và sử dụng các cảm biến để phục vụ cho bài toán định vị trên robot. Ở đây trên Rviz sẽ mô phỏng và cho thấy các trục tọa độ của từng thành phần được gắn vào robot. Trong các thư mục được cài đặt mình cũng đã chỉnh sửa phần mô phỏng để cho thấy sự thay đổi khi sử dụng gói Robot Localization:
Các bạn lưu ý là ở đây odom sẽ là frame chính của chúng ta, sau khi mô phỏng trên RViz thành công bây giờ các bjan có thể tận dụng các cảm biến đưuọc cài đặt sẵn trên robot và phục vụ cho project của các bạn như SLAM,…
Thêm một chút gợi ý nho nhỏ cho các bạn để kiểm tra rằng mình đã có các topic và frame được liên kết với nhau chưa. Đầu tiên mình sử dụng:ros2 topic list và kết quả xuất hiện các topic như GPS, IMU, Odometry.
Bên cạnh đó, ROS cung cấp câu lệnh để kiểm tra sự kết nối giữa các frame được khai báo: ros2 run tf2_tools view_frames.py
Sau đó file sẽ được lưu dưới dạng pdf và bạn chỉ việc mở lên để kiểm tra bằng câu lệnh sau:
evince frames.pdf
Để có được kết quả như trên, thì chủ yếu mình đã chỉnh sửa và thêm vào IMU và GPS tại urdf file. Các bạn có thể mở gói turtlebot3_description để chỉnh sửa tùy theo project của mình.
Hy vọng qua blog này các bạn đã có thêm một số kiến thức cũng như công cụ hỗ trợ cho các dự án liên quan tới robotics sử dụng ROS. Bên cạnh đó, các bạn có thể theo dõi các blog khác cũng như các khóa học liên quan trên The Construct nhé.
In this post, we will see what Rviz is all about and how to use it.
We are going to use a ROS1 installation for this, but you don’t need to install ROS, as we will use the ROS Development Studio (ROSDS), an online platform that provides access to ROS1 or ROS2 computers and other powerful ROS tools within a browser!
Once you have cloned the project, open it up and give it a moment to finish loading.
Step 2: Bring up a TurtleBot2 simulation
Head to the Simulations menu and launch one of the pre-configured simulations:
Select “Simulations from the main menu”.
Under “World”, select “Empty to World”.
Under “Robot”, select TurtleBot2.
Click “Start Simulation”.
You should now have something like this:
That’s a TutleBot2 facing a wall.
Step 3: Launch the rviz program and use it to “spy” on the TurtleBot
We need two more tools here.
Minimize the Gazebo window.
From the Tools menu, pick “Shell” and “Graphical Tools”. Arrange them side-by-side for better visibility.
On the Shell tool, run the following command:
rosrun rviz rviz
You should now have something like this:
Maximize the Rviz window and:
Double-click the Title bar to bring the window into full focus. Ensure you can clearly see the “Add” button lablled 3 below.
Change the “Fixed frame” to “base_link”.
Click “Add” to add a visualization.
Select “Camera”
Click okay.
Semi-finally, select a topic for the Camera visualization. You should see the brick wall in full color now!
Finally, pick another Shell from the Tools menu and run the following command to cause the robot to rotate. You should see the wall in the Camera disappear and appear again as the front camera position changes.
# Just type rostopic pub /cmd_vel and then press TAB-TAB to complete message structure.
# Then change angular.z to 1.0
user:~$ rostopic pub /cmd_vel geometry_msgs/Twist "linear:
x: 0.0
y: 0.0
z: 0.0
angular:
x: 0.0
y: 0.0
z: 1.0"
Interesting, isn’t it? You can try and play with other visualization tools and see how they work.
Now’s let review the theory briefly.
Step 4: So, what’s Rviz?
We have just seen Rviz in action. So what exactly is this amazing tool?
Short for ROS Visualization. It’s a 3-dimensional visualization tool for ROS.
It helps to visualize what the robot seeing and doing.
And that’s it. It’s more practical than theory 🙂 .
Extra: Video
Prefer to watch a video demonstrating the steps above? We have one for you below!
Related Resources and Further Learning
If you are a ROS beginner and want to learn ROS basics fast, we recommend you take any of the following courses on Robot Ignite Academy:
Did you like this post? Do you have questions about what is explained? Whatever the case, please leave a comment on the comments section below, so we can interact and learn from each other.
If you want to learn about other ROS topics, please let us know in the comments area and we will do a video or post about it
In the previous post, we learned how to add pressure sensors in a gazebo simulation. Now we are going to learn how to see then on RViz (ROS Visualization tool). Remember that there is a question on ROS Answers about contact sensors, which is what originated this post.
Start the provided ROSject
The first thing you need to do is to have a copy of the ROSject we mentioned above if you want everything ready to go. You can also clone the git repository aforementioned if you want to run everything in your local computer. If you for the ROSject option, just click on the ROSject link to get an automatic copy. You should now see a ROSject called DogBotTactileSensors on your list of ROSjects, something like the image below:
DogBot tactile sensors in ROSDS
After clicking on the Open button to open the ROSject, you should have the ROSject opened in a remote computer launched on ROSDS.
Launching the simulation
If you remember the previous post, we can launch the simulation by running roslaunch dogbot_gazebo main.launch on the webshell. You can also use the menu by clicking on Simulations -> Choose Simulation -> Choose Launch File and selecting the main.launch file in the dogbot_gazebo package:
dogbot_gazebo main.launch in ROSDS
You should now have DogBot up and running in ROSDS:
Adding the sensors to RViz
Let’s start by stopping the robot. For that, we can just run the keyboard teleoperation with rosrun teleop_twist_keyboard teleop_twist_keyboard.py cmd_vel:=/dogbot/cmd_vel to move DogBot and then hit K to stop it.
Now we open a new shell by clicking on Tools -> Shell and run:
rosrun dogbot_markers arrows_rviz.py
This command essentially get the data published by the sensors and convert then into markers that can be shown in RViz.
Now, to see RViz we have to launch by running the command below in a new shell:
rosrun rviz rviz
This will open RViz, but in order to see it, you have to open the Graphical Tools by clicking Tools -> Graphical Tools.
Launching Graphical Tools in ROSDS
Once you are in RViz, you open a config file by clicking File -> Open Config. There you select the file dogbot.rviz file located in ~/simulation_ws/src/dogbot_tc/dogbot_markers/rviz/dogbot.rviz
Select the rviz config file dogbot.rviz for dogbot
The file you open has everything configured to show the contact markers in RViz.
The pressure in each of the feet of Dogbit is represented by an arrow, which lengths and colors are proportional to the pressure registered. You can see the arrows changing their lengths when the robot steps.
How the markers are created
On the folder ~/simulation_ws/src/dogbot_tc/dogbot_markers/scripts we created a script called world_to_base_tf_publisher.py with the following content:
#! /usr/bin/env python
import rospy
import time
import tf
from nav_msgs.msg import Odometry
from geometry_msgs.msg import Pose
class WorldBaseTFPublisher(object):
def __init__(self):
rospy.loginfo("Start init WorldBaseTFPublisher Class")
self._br = tf.TransformBroadcaster()
rospy.loginfo("set up tf.TransformBroadcaster DONE")
self._current_pose = Pose()
rospy.loginfo("_current_pose DONE")
self.get_init_position()
rospy.loginfo("get_init_position DONE")
self._sub = rospy.Subscriber('/dogbot/odom', Odometry, self.odom_callback)
rospy.loginfo("self._sub DONE")
def get_init_position(self):
data_odom = None
r = rospy.Rate(2)
while data_odom is None and not rospy.is_shutdown():
try:
data_odom = rospy.wait_for_message("/dogbot/odom", Odometry, timeout=1)
except:
rospy.loginfo("Current odom not ready yet, retrying for setting up init pose")
try:
r.sleep()
except rospy.ROSInterruptException:
# This is to avoid error when world is rested, time when backwards.
pass
self._current_pose = data_odom.pose.pose
def odom_callback(self, msg):
self._current_pose = msg.pose.pose
def get_current_pose(self):
return self._current_pose
def handle_turtle_pose(self, pose_msg, link_name, world_name = "/world"):
self._br.sendTransform(
(pose_msg.position.x,
pose_msg.position.y,
pose_msg.position.z),
(pose_msg.orientation.x,
pose_msg.orientation.y,
pose_msg.orientation.z,
pose_msg.orientation.w),
rospy.Time.now(),
link_name,
world_name)
def publisher_of_tf(self):
frame_link_name = "base_link"
time.sleep(1)
rospy.loginfo("Ready..Starting to Publish TF data now...")
rate = rospy.Rate(50)
while not rospy.is_shutdown():
pose_now = self.get_current_pose()
if not pose_now:
print "The Pose is not yet available...Please try again later"
else:
self.handle_turtle_pose(pose_now, frame_link_name)
try:
rate.sleep()
except rospy.ROSInterruptException:
# This is to avoid error when world is rested, time when backwards.
pass
if __name__ == '__main__':
rospy.init_node('publisher_of_world_base_tf_node', anonymous=True)
rospy.loginfo("STARTING WORLS TO BASE TF PUBLISHER...")
world_base_tf_pub = WorldBaseTFPublisher()
world_base_tf_pub.publisher_of_tf()
In this file we get the /dogbot/odom topic, which informs where is the robot in the world, and publishes the tf from world to base_link. With the command rosrun tf view_frames, we generate the frames.pdf file. By downloading that file we can see that the world is connected to base_link, which is the base of the robot.
tf world connected to base_link frame on ROSDS
The connection between the two frames is important because the sensors are represented in the world frame, but are detected by the robot, which is represented by the base_link. If you want to learn more about tf, please consider taking the course TF ROS 101 in Robot Ignite Academy.
The markers are created in the script ~/simulation_ws/src/dogbot_tc/dogbot_markers/scripts/arrows_rviz.py, which has the following content:
#!/usr/bin/env python
import rospy
from visualization_msgs.msg import Marker
from geometry_msgs.msg import Point
import tf
import numpy
from std_msgs.msg import String
from gazebo_msgs.msg import ContactsState
import math
class MarkerBasics(object):
def __init__(self, topic_id):
marker_topic = '/marker_basic_'+topic_id
self.marker_objectlisher = rospy.Publisher(marker_topic, Marker, queue_size=1)
self.rate = rospy.Rate(25)
self.init_marker(index=0)
def init_marker(self, index=0):
self.marker_object = Marker()
self.change_frame(frame="/world", ns="dogbot", index=0)
self.marker_object.type = Marker.ARROW
self.marker_object.action = Marker.ADD
self.change_position(x=0.0, y=0.0, z=0.0)
self.change_orientation(pitch=0.0, yaw=0.0)
self.change_scale()
self.change_colour(R=1.0, G=0.0, B=0.0)
# If we want it for ever, 0, otherwise seconds before desapearing
self.marker_object.lifetime = rospy.Duration(0)
def change_orientation(self, pitch, yaw):
"""
Roll doesnt make any sense in an arrow
:param pitch: Up Down. We clip it to values [-1.5708,1.5708]
:param yaw: Left Right , No clamp
:return:
"""
pitch = numpy.clip(pitch, -1.5708,1.5708)
q = tf.transformations.quaternion_from_euler(0.0, pitch, yaw)
self.marker_object.pose.orientation.x = q[0]
self.marker_object.pose.orientation.y = q[1]
self.marker_object.pose.orientation.z = q[2]
self.marker_object.pose.orientation.w = q[3]
def change_position(self, x, y, z):
"""
Position of the starting end of the arrow
:param x:
:param y:
:param z:
:return:
"""
my_point = Point()
my_point.x = x
my_point.y = y
my_point.z = z
self.marker_object.pose.position = my_point
#rospy.loginfo("PositionMarker-X="+str(self.marker_object.pose.position.x))
def change_colour(self, R, G, B):
"""
All colours go from [0.0,1.0].
:param R:
:param G:
:param B:
:return:
"""
self.marker_object.color.r = R
self.marker_object.color.g = G
self.marker_object.color.b = B
# This has to be, otherwise it will be transparent
self.marker_object.color.a = 1.0
def change_scale(self, s_x=1.0, s_y=0.1, s_z=0.1):
"""
:param s_x:
:param s_y:
:param s_z:
:return:
"""
self.marker_object.scale.x = s_x
self.marker_object.scale.y = s_y
self.marker_object.scale.z = s_z
def start(self):
pitch = -0.7
yaw = 0.0
s_x = 1.0
while not rospy.is_shutdown():
#self.change_orientation(pitch=pitch,yaw=yaw)
self.change_scale(s_x=s_x)
self.marker_objectlisher.publish(self.marker_object)
self.rate.sleep()
s_x -= 0.01
def translate(self, value, leftMin, leftMax, rightMin, rightMax):
# Figure out how 'wide' each range is
leftSpan = leftMax - leftMin
rightSpan = rightMax - rightMin
# Convert the left range into a 0-1 range (float)
valueScaled = float(value - leftMin) / float(leftSpan)
# Convert the 0-1 range into a value in the right range.
return rightMin + (valueScaled * rightSpan)
def pressure_to_wavelength_to_rgb(self, pressure, min_pressure=-50.0, max_pressure=50.0, gamma=0.8):
'''This converts a given wavelength of light to an
approximate RGB color value. The wavelength must be given
in nanometers in the range from 380 nm through 750 nm
(789 THz through 400 THz).
Based on code by Dan Bruton
http://www.physics.sfasu.edu/astro/color/spectra.html
'''
wavelength = self.translate(value=pressure,
leftMin=min_pressure, leftMax=max_pressure,
rightMin=380, rightMax=750)
wavelength = float(wavelength)
rospy.logdebug("pressure=" + str(pressure))
rospy.logdebug("wavelength="+str(wavelength))
if wavelength >= 380 and wavelength <= 440:
attenuation = 0.3 + 0.7 * (wavelength - 380) / (440 - 380)
R = ((-(wavelength - 440) / (440 - 380)) * attenuation) ** gamma
G = 0.0
B = (1.0 * attenuation) ** gamma
elif wavelength >= 440 and wavelength <= 490:
R = 0.0
G = ((wavelength - 440) / (490 - 440)) ** gamma
B = 1.0
elif wavelength >= 490 and wavelength <= 510:
R = 0.0
G = 1.0
B = (-(wavelength - 510) / (510 - 490)) ** gamma
elif wavelength >= 510 and wavelength <= 580:
R = ((wavelength - 510) / (580 - 510)) ** gamma
G = 1.0
B = 0.0
elif wavelength >= 580 and wavelength <= 645:
R = 1.0
G = (-(wavelength - 645) / (645 - 580)) ** gamma
B = 0.0
elif wavelength >= 645 and wavelength <= 750:
attenuation = 0.3 + 0.7 * (750 - wavelength) / (750 - 645)
R = (1.0 * attenuation) ** gamma
G = 0.0
B = 0.0
else:
R = 0.0
G = 0.0
B = 0.0
return R, G, B
def change_frame(self, frame="/world", ns="dogbot", index=0):
"""
:param frame:
:return:
"""
self.marker_object.header.frame_id = frame
self.marker_object.header.stamp = rospy.get_rostime()
self.marker_object.ns = ns
self.marker_object.id = index
def update_marker(self, frame, ns, index, position, orientation, pressure, min_pressure=0.0, max_pressure=10.0):
"""
:param position: [X,Y,Z] in the world frame
:param pressure: Magnitude
:param orientation: [Pitch,Yaw]
:return:
"""
#self.change_frame(frame=frame, ns=ns, index=index)
self.change_position(x=position[0], y=position[1], z=position[2])
self.change_orientation(pitch=orientation[0], yaw=orientation[1])
self.change_scale(s_x = pressure)
R,G,B = self.pressure_to_wavelength_to_rgb(pressure=pressure,
min_pressure=min_pressure,
max_pressure=max_pressure,
gamma=0.8)
rospy.logdebug("R,G,B=["+str(R)+", "+str(G)+", "+str(B)+"]")
self.change_colour(R=R, G=G, B=B)
self.marker_objectlisher.publish(self.marker_object)
class FootPressureInfo(object):
def __init__(self):
self.min_pressure = 0.0
self.max_pressure = 2.0
self.markerbasics_object_front_left_foot = MarkerBasics(topic_id="front_left_foot")
self.markerbasics_object_front_right_foot = MarkerBasics(topic_id="front_right_foot")
self.markerbasics_object_back_left_foot = MarkerBasics(topic_id="back_left_foot")
self.markerbasics_object_back_right_foot = MarkerBasics(topic_id="back_right_foot")
rospy.Subscriber("/dogbot/back_left_contactsensor_state", ContactsState, self.contact_callback_back_left_foot)
rospy.Subscriber("/dogbot/back_right_contactsensor_state", ContactsState, self.contact_callback_back_right_foot)
rospy.Subscriber("/dogbot/front_left_contactsensor_state", ContactsState, self.contact_callback_front_left_foot)
rospy.Subscriber("/dogbot/front_right_contactsensor_state", ContactsState, self.contact_callback_front_right_foot)
def contact_callback_front_right_foot(self, data):
"""
:param data:
:return:
"""
foot_name = data.header.frame_id
if len(data.states) >= 1:
Fx = data.states[0].total_wrench.force.x
Fy = data.states[0].total_wrench.force.y
Fz = data.states[0].total_wrench.force.z
pressure = math.sqrt(pow(Fx,2)+pow(Fy,2)+pow(Fz,2))
Px = data.states[0].contact_positions[0].x
Py = data.states[0].contact_positions[0].y
Pz = data.states[0].contact_positions[0].z
pressure = pressure / 100.0
# rospy.loginfo(str(foot_name) + "--->pressure =" + str(pressure))
# rospy.loginfo(str(foot_name) + "Point =[" + str(pressure))
index = 1
self.markerbasics_object_front_right_foot.update_marker(frame=foot_name,
ns="dogbot",
index=index,
position=[Px, Py, Pz],
orientation=[-1.57, 0.0],
pressure=pressure,
min_pressure=self.min_pressure,
max_pressure=self.max_pressure)
else:
# No Contact
pass
def contact_callback_front_left_foot(self, data):
"""
:param data:
:return:
"""
foot_name = data.header.frame_id
if len(data.states) >= 1:
Fx = data.states[0].total_wrench.force.x
Fy = data.states[0].total_wrench.force.y
Fz = data.states[0].total_wrench.force.z
pressure = math.sqrt(pow(Fx,2)+pow(Fy,2)+pow(Fz,2))
Px = data.states[0].contact_positions[0].x
Py = data.states[0].contact_positions[0].y
Pz = data.states[0].contact_positions[0].z
pressure = pressure / 100.0
index = 0
self.markerbasics_object_front_left_foot.update_marker(frame=foot_name,
ns="dogbot",
index=index,
position=[Px, Py, Pz],
orientation=[-1.57, 0.0],
pressure=pressure,
min_pressure=self.min_pressure,
max_pressure=self.max_pressure)
else:
# No Contact
pass
def contact_callback_back_right_foot(self, data):
"""
:param data:
:return:
"""
foot_name = data.header.frame_id
if len(data.states) >= 1:
Fx = data.states[0].total_wrench.force.x
Fy = data.states[0].total_wrench.force.y
Fz = data.states[0].total_wrench.force.z
pressure = math.sqrt(pow(Fx,2)+pow(Fy,2)+pow(Fz,2))
Px = data.states[0].contact_positions[0].x
Py = data.states[0].contact_positions[0].y
Pz = data.states[0].contact_positions[0].z
pressure = pressure / 100.0
#rospy.loginfo(str(foot_name) + "--->pressure =" + str(pressure))
# rospy.loginfo(str(foot_name) + "Point =[" + str(pressure))
index = 2
self.markerbasics_object_back_right_foot.update_marker(frame=foot_name,
ns="dogbot",
index=index,
position=[Px, Py, Pz],
orientation=[-1.57, 0.0],
pressure=pressure,
min_pressure=self.min_pressure,
max_pressure=self.max_pressure)
else:
# No Contact
pass
def contact_callback_back_left_foot(self, data):
"""
:param data:
:return:
"""
foot_name = data.header.frame_id
if len(data.states) >= 1:
Fx = data.states[0].total_wrench.force.x
Fy = data.states[0].total_wrench.force.y
Fz = data.states[0].total_wrench.force.z
pressure = math.sqrt(pow(Fx,2)+pow(Fy,2)+pow(Fz,2))
Px = data.states[0].contact_positions[0].x
Py = data.states[0].contact_positions[0].y
Pz = data.states[0].contact_positions[0].z
pressure = pressure / 100.0
index = 3
self.markerbasics_object_back_left_foot.update_marker(frame=foot_name,
ns="dogbot",
index=index,
position=[Px, Py, Pz],
orientation=[-1.57, 0.0],
pressure=pressure,
min_pressure=self.min_pressure,
max_pressure=self.max_pressure)
else:
# No Contact
pass
if __name__ == '__main__':
rospy.init_node('footpressure_marker_node', anonymous=True)
footpressure_object = FootPressureInfo()
rospy.spin()
When the file is launched, 4 markers are created, each of them publishing on a specific topic. You can find the topic for each marker with rostopic list | grep marker.
We then create the subscribers for the topics, each of then calling a callback function when a topic is published. On the callback we calculate the pressure, get the positions, then publish the marker on RViz.
A video version of this post
So this is the post for today. If you prefer, we also have a video version of this post available in the link below. We hope you liked the post and the video. If so, please feel free to subscribe to our channel, share this post and comment on the video section.
We would like to thank React Robotics for this amazing robot and thank you for reading through this post.
Keep pushing your ROS Learning.
Remember, if you want to learn more about markers in RViz, we highly recommend you taking this course on ROS RViz Advanced Markers.
Interested to learn more? Try this course on URDF creation for Gazebo and ROS: