Neste tutorial, vamos aprender a criar serviços personalizados no ROS2 e desenvolver um pacote de controle de movimento para um robô. Vamos passar pelos passos de criação e configuração de serviços, desenvolvimento de um pacote de movimento, implementação do servidor e cliente de serviço, e a criação de arquivos de lançamento. Este tutorial é ideal para quem deseja aprimorar suas habilidades em ROS2 e programação robótica.
Abrindo o rosject
Clique no link abaixo para ter acesso ao rosject deste tutorial. Em seguida, clique no botão “RUN”.
Primeiro, vamos criar um pacote de interfaces personalizadas que conterá nosso serviço. Este pacote será responsável por definir e gerenciar a interface do serviço que iremos usar para controlar o robô.
Copie este bloco de comandos no primeiro terminal:
Esses comandos criam um novo pacote chamado custom_interfaces com as dependências rclcpp e std_msgs.
Definição do Serviço
Em seguida, vamos definir o serviço que será utilizado para controlar o robô. Para isso, crie uma pasta chamada srv dentro do pacote custom_interfaces e um arquivo Move.srv. Use os comandos:
cd ~/ros2_ws/src/custom_interfaces
mkdir srv
cd srv
touch Move.srv
Edite o arquivo Move.srv com o seguinte conteúdo:
string direction # Direção para girar (direita ou esquerda)
float64 velocity # Velocidade angular (em rad/s) ou linear (em m/s)
int32 time # Duração do giro (em segundos)
---
bool success # O comando foi bem-sucedido?
Esse arquivo define a estrutura do serviço. Ele recebe a direção, velocidade e tempo como entrada, e retorna um booleano indicando se a operação foi bem-sucedida.
Atualizando os Arquivos CMakeLists.txt e package.xml
Agora, precisamos configurar os arquivos CMakeLists.txt e package.xml para que o ROS2 possa construir e utilizar nosso serviço.
source ~/ros2_ws/install/setup.bash
ros2 run movement_pkg movement_client left 0.2 5
No segundo terminal você verá:
Enquanto no primeiro terminal aparecerá:
Isso indica que o serviço foi realizado corretamente.
Na janela do Gazebo, você pode ver o robô se movimentando pelo ambiente.
<figure7>
Outras opções, além de left, são right, forward, backward. Experimente mudar os parametros do comando anterior variando também a velocidade e a duraçao do movimento do robô (respectivamente segundo e terceiro parametros do comando).
Conclusão
Esperamos que este tutorial tenha sido útil e que você tenha aprendido como criar serviços personalizados no ROS2 e controlar o movimento de um robô. Se você gostou deste conteúdo, considere explorar mais recursos do ROS2 e experimentar novas possibilidades para expandir suas habilidades em robótica.
Vídeo no YouTube
Este foi o post de hoje. Lembre-se que nós temos uma versão deste post em vídeo no YouTube.
Se você gostou do conteúdo, por favor, considere se inscrever no nosso canal do YouTube. Estamos publicando novos conteúdos quase todos os dias.
Instalação do pacote Foxglove para integração com a interface web de monitoramento de robôs.
Configuração do ambiente ROS para garantir que todas as dependências estão corretamente instaladas e atualizadas.
Criação de um pacote ROS, incluindo a estrutura de diretórios e os arquivos necessários para o projeto.
Edição do arquivo de lançamento para configurar o bridge do Foxglove dentro do ambiente ROS.
Compilação do pacote ROS, lançamento da simulação do robô e conexão à interface web fornecida pelo Foxglove para monitoramento e controle remoto de robôs.
Neste tutorial, vamos aprender a configurar uma central de monitoramento de robôs via web usando ROS Noetic e a ferramenta Foxglove. Vamos passar pelos passos de instalação do pacote Foxglove, configuração do ambiente ROS, criação de um pacote ROS, edição de arquivos de lançamento e compilação, além de lançar a simulação e conectar-se à interface web.
Abrindo o rosject
Clique no link abaixo para ter acesso ao rosject deste tutorial. Em seguida, clique no botão vermelho “RUN”.
Primeiramente, vamos garantir que o ambiente ROS esteja atualizado. Então, podemos instalar o foxglove. Abra um terminal e execute os seguintes comandos:
Agora, vamos criar um pacote ROS para nosso projeto. Em um terminal, execute os seguintes comandos:
roscd; cd ..; cd src/bb8; catkin_create_pkg bridge
cd bridge; mkdir launch;
cd launch; touch bridge.launch
Neste passo, você deve abrir o editor de código que apresentará a seguinte estrutura de pastas:
Editando o arquivo de lançamento:
Agora, edite o arquivo bridge.launch dentro da pasta launch com o seguinte conteúdo:
<launch>
<!-- Including in another launch file -->
<include file="$(find foxglove_bridge)/launch/foxglove_bridge.launch">
<arg name="port" value="9090"/>
</include>
</launch>
Compilando o pacote:
Edite os arquivos CMakeLists.txt e package.xml do pacote bridge recém criado. Você deve alterar o conteúdo do arquivo CmakeLists.txt para apresentar: find_package(catkin REQUIRED
foxglove_bridge
)
Esta parte pode ser encontrada logo no começo do arquivo. Mais no final do arquivo, você deve alterar:
No terminal 3 (terceira aba), obtenha o endereço do WebSocket digitando o comando:
rosbridge_address
Monitorando e controlando seu robô via web
Acesse o endereço https://foxglove.dev em um navegador web.
Você vai criar uma conta seguindo um procedimento simples e rápido então lhe será apresentada uma tela com o dashboard:
Clique em “Open connection”. Cole o endereço do WebSocket e estará pronto para personalizar a visualização dos dados conforme desejado.
No botão 1 da figura abaixo, você pode criar um novo layout. No botão 2 (Add Panel), você pode adicionar novos painéis. Por exemplo, adicione “Image” e “Teleop”. Ao clicar sobre um painel as configurações deste serão apresentadas.
Se você clicar no painel Image, poderá selecionar o tópico relativo à câmera do robô no campo 3. A imagem apresentada neste painel é o que o robô “enxerga” naquele momento.
Se você clicar no painel Teleop, pode ajustar as configurações do tópico no campo 3. Neste caso, gostaríamos de comandar a velocidade do robô nas diferentes direções. Para isso, preencha o campo 3 com “/cmd_vel”. Ajuste as velocidades (linear e angular) desejas e pronto. Temos um controle remoto.
Por fim, quando comandamos o robô por meio da interface do Foxglove e voltamos à janela do Gazebo, vemos o robô se movimentar no ambiente. A perspectiva capturada pela câmera é continuamente apresentada no painel “Image”. De maneira geral, o Foxglove pode ser usado como uma central de monitoramento de diversos tópicos via web.
Esperamos que este tutorial seja útil e que você possa desfrutar da experiência de monitorar e controlar seus robôs de forma remota através da interface web do Foxglove.
Se você gostou deste tutorial, considere explorar mais recursos do ROS e experimentar novas possibilidades para expandir suas habilidades em robótica.
Vídeo no YouTube
Este foi o post de hoje. Lembre-se que nós temos uma versão deste post em vídeo no YouTube.
Se você gostou do conteúdo, por favor, considere se inscrever no nosso canal do YouTube. Estamos publicando novos conteúdos quase todos os dias.
Bem-vindo! Neste tutorial, iremos mergulhar nos recursos de Actions no ROS, com foco no ROS 1 Noetic e em um dos robôs mais encantadores disponíveis na plataforma: o BB-8 do universo de Star Wars. Vamos explorar como usar Actions para enviar comandos personalizados que permitem ao BB-8 executar ações específicas, como caminhar, girar e parar, em resposta a objetivos definidos. Ao longo do processo, você aprenderá a criar mensagens customizadas para interagir com o BB-8 de maneira controlada e divertida.
Abrindo o rosject
Para acompanhar este tutorial utilize o rosject acessando o link fornecido e clique no botão vermelho
Abra um terminal e cole os comandos abaixo para criar o pacote e os arquivos necessários para a simulação.
roscd; cd ..; cd src/bb8; catkin_create_pkg bb8_action_pkg roscpp actionlib actionlib_msgs geometry_msgs std_msgs
cd bb8_service_pkg; mkdir action;
cd action; touch CustomActionMsg.action; cd ..;
cd src; touch bb8_service_server.cpp; chmod +x *.cpp
Novos arquivos aparecerão na árvore de pastas em sua IDE.
Configurando as mensagens de Action Mensagens de Action são compostas pelos elementos goal, result e feedback. No caso da nossa aplicação, trataremos cada um dos elementos como uma string diferente. Salve o arquivo CustomActionMsg.action incluindo o seguinte conteúdo:
string goal
---
string result
---
string feedback
Preparando para compilar
Para atualizar seu CMakeList.txt, é crucial copiar o seguinte conteúdo e colá-lo no arquivo, substituindo o que estiver presente.
Agora, vamos para a etapa em que os comandos são enviados ao servidor do ações. No diretório ‘src’, abra o arquivo bb8_action_server.cpp e adicione o seguinte código. #include "geometry_msgs/Twist.h"
#include "nav_msgs/Odometry.h"
#include <actionlib/server/simple_action_server.h>
#include <bb8_action_pkg/CustomActionMsgAction.h>
#include <ros/ros.h>
class MoveAction {
protected:
ros::NodeHandle nh_;
actionlib::SimpleActionServer<bb8_action_pkg::CustomActionMsgAction> as_;
std::string action_name_;
bb8_action_pkg::CustomActionMsgFeedback feedback_;
bb8_action_pkg::CustomActionMsgResult result_;
ros::Publisher pub;
ros::Subscriber sub;
geometry_msgs::Twist mov;
nav_msgs::Odometry odom;
ros::Rate *rate_;
public:
MoveAction(std::string name)
: as_(nh_, name, boost::bind(&MoveAction::Callback, this, _1), false),
action_name_(name) {
as_.start();
rate_ = new ros::Rate(1);
pub = nh_.advertise<geometry_msgs::Twist>("cmd_vel", 1000);
sub = nh_.subscribe("/odom", 1000, &MoveAction::odomCallback, this);
}
void Callback(const bb8_action_pkg::CustomActionMsgGoalConstPtr &goal) {
ROS_INFO("Goal received: %s", goal->goal.c_str());
while (ros::ok() && !as_.isPreemptRequested()) {
if (goal->goal == "walk") {
mov.linear.x = 0.5;
mov.angular.z = 0;
pub.publish(mov);
} else if (goal->goal == "turn") {
mov.linear.x = 0;
mov.angular.z = 1;
pub.publish(mov);
usleep(2000000);
} else if (goal->goal == "stop") {
mov.linear.x = 0;
mov.angular.z = 0;
pub.publish(mov);
}
feedback_.feedback =
"Received position: x=" + std::to_string(odom.pose.pose.position.x) +
", y=" + std::to_string(odom.pose.pose.position.y) +
", z=" + std::to_string(odom.pose.pose.position.z);
as_.publishFeedback(feedback_);
rate_->sleep();
}
if (as_.isPreemptRequested()) {
ROS_INFO("Goal preempted.");
as_.setPreempted();
} else {
ROS_INFO("Goal succeeded.");
result_.result = "Action completed.";
as_.setSucceeded(result_);
}
}
void preemptCallback() {
ROS_INFO("Goal preempted.");
as_.setPreempted();
}
void odomCallback(const nav_msgs::Odometry::ConstPtr &msg) { odom = *msg; }
};
int main(int argc, char **argv) {
ros::init(argc, argv, "node_action");
MoveAction server("action_custom_msg_as");
ros::spin();
return 0;
}
Compilando o pacote e lançando a simulação
Em um terminal, inicie o Gazebo usando o comando apropriado para visualizar o robô BB-8 pronto para operação. Isso abrirá a janela do Gazebo.
roslaunch bb_8_gazebo main.launch
No segundo terminal, execute o seguinte comando para compilar apenas o pacote que criamos. Isso economiza tempo, já que compilará apenas um pacote em vez de todos os existentes.
cd ~/catkin_ws/; catkin_make --only-pkg-with-deps bb8_action_pkg; source devel/setup.bash
Se tudo foi feito corretamente, o prompt não acusará nenhum erro:
Rodando o servidor de serviços
No terminal 3, execute o comando para iniciar o servidor de ações:
rosrun bb8_action_pkg bb8_action_server
Não se preocupe com este terminal, abra um novo para prosseguir com os passos deste tutorial.
Invocando o feedback da ação
Abra um quarto terminal para receber feedback do robô com informações sobre sua localização no espaço tridimensional. Essas atualizações são recebidas a cada dois segundos, permitindo que você saiba a posição do robô mesmo que ele não esteja visível.
rostopic echo action_custom_msg_as/feedback
Não se preocupe se nada aparecer inicialmente, pois o servidor ainda não está em execução, então não haverá feedback disponível ainda. Isso é normal.
Após digitar o comando, não pressione ‘Enter’. Em vez disso, pressione ‘Tab’ duas vezes para autocompletar com os elementos necessários. Em seguida, você poderá selecionar o goal desejado, como ‘turn’, ‘walk’ ou ‘stop’.”
O robô deve se mover de acordo.
Visualizando o feedback
De volta ao terminal 4 temos:
As informações de odometria sempre indicarão onde seu robô está localizado, mesmo que a visualização seja obstruída por um Star Destroyer. Aliás, este método é excelente para explorar e localizar aquela entrada secreta em uma colossal nave inimiga.
Esperamos que este tutorial seja útil e que você se divirta trabalhando com o BB-8, um robô incrível tanto em termos gráficos quanto funcionais.
Vídeo no YouTube
Este foi o post de hoje. Lembre-se que nós temos uma versão deste post em vídeo no YouTube.
Se você gostou do conteúdo, por favor, considere se inscrever no nosso canal do YouTube. Estamos publicando novos conteúdos quase todos os dias.
Olá! Hoje vamos explorar os serviços do ROS, focando no ROS 1 Noetic e em um dos robôs mais fascinantes disponíveis na plataforma: o BB-8 de Star Wars. Utilizaremos os serviços para criar comandos que permitam ao BB-8 desenhar formas no chão ou seguir trajetórias específicas, como quadrados ou círculos, por meio de mensagens customizadas.
Abrindo o rosject
Para acompanhar este tutorial, utilize um rosject, que é essencialmente um projeto ROS configurado para rodar um exemplo. Acesse o link fornecido e clique no botão vermelho.
Inicie abrindo um terminal e siga os comandos abaixo para criar o pacote e os arquivos necessários para nossa aplicação.
roscd; cd ..; cd src/bb8; catkin_create_pkg bb8_service_pkg roscpp std_msgs
cd bb8_service_pkg; mkdir srv;
cd srv; touch bb8ServiceMessage.srv; cd ..;
cd src; touch bb8_service_server.cpp; chmod +x *.cpp
Você notará que novos arquivos surgirão na árvore de pastas da janela IDE.
Configurando a mensagens de serviço As mensagens de serviço incluem sempre os requests e responses. Para comandar o BB-8, trataremos isso como uma string. Edite o arquivo bb8ServiceMessage.srv com o seguinte conteúdo:
string command # A string command representing the action
---
bool success # Did it achieve it?
Preparando para compilar
É importante modificar o CMakeList.txt copiando e colando o conteúdo abaixo, substituindo o conteúdo existente.
Não esqueça de adicionar as três linhas indicadas no final do package.xml. Atençã0, não substitua todo o conteúdo do arquivo! Apenas adicione as linhas:
Agora, você pode prosseguir para a parte onde os comandos são recebidos pelo servidor do serviço. Na pasta src, abra o arquivo bb8_service_server.cpp e insira o conteúdo necessário.
No terminal 1, xecute o comando abaixo para compilar apenas o pacote que criamos, economizando tempo.
cd ~/catkin_ws/; catkin_make --only-pkg-with-deps bb8_service_pkg; source devel/setup.bash
Se tudo foi feito corretamente, você não verá nenhum erro no prompt:
Após a compilação, inicie o Gazebo com o comando apropriado para visualizar o robô BB-8 preparado para funcionar.
roslaunch bb_8_gazebo main.launch
Isto fará aparecer a janela do Gazebo.
Rodando o servidor de serviços
No terminal 2, execute o comando para iniciar o servidor de serviços:
rosrun bb8_service_pkg bb8_service_server
Uma mensagem confirmará que o serviço está pronto.
Chamando o serviço criado
Utilize um terceiro terminal para enviar comandos ao BB-8. Por exemplo, para fazê-lo se mover em um quadrado ou círculo, ou até mesmo dançar, use os comandos apropriados.
Panorama
Uma dúvida recorrente de desenvolvedores ROS iniciantes é como combinar publisher e subscriber no mesmo arquivo. Neste exemplo, vamos utilizar um dos robôs mais famosos, TurtleBot3, para fazer uma demonstração virtual.
Abrindo o rosject
Para seguir este tutorial, será útil e conveniente usar um rosject (que a grosso modo é um projeto ROS configurado para rodar um exemplo).
Acesse o link: https://app.theconstructsim.com/l/5e2b6017
Clique no botão vermelho “RUN”:
Lançando a simulação exemplo:
Abra um terminal clicando no ícone indicado na figura:
Na janela do primeiro terminal, insira o comando:
roslaunch realrobotlab main.launch
Você será apresentado à uma janela do Gazebo como esta:
Você pode sempre revisitar a janela do Gazebo clicando em “Open Gazebo”:
Nesta janela, você tem a visualização da sua simulação com o TurtleBot3 presente.
Identificando os tópicos importantes: Num segundo terminal (clique no botao “+” para criar abas diferentes), insira o comando:
Esta é a listagem de todos os tópicos da simulação. Serão de especial importância para nós o tópico /scan, pois queremos ler o scanner do robô e o tópico /cmd_vel, pois queremos alterar a velocidade do robô.
No mesmo terminal, rode o comando:
rostopic type /scan
que apresentará:
sensor_msgs/LaserScan
esta informação é importante para compor o arquivo Python.
Abra a janela da IDE clicando no ícone indicado na figura:
Você pode encontrar o arquivo move_file.py na correta estrutura de pastas. Tome um minuto para entender onde o arquivo deve ser criado dentro do seu workspace para uma melhor organização geral.
A informação obtida anteriormente com respeito ao tópico /scan é utilizada em:
from sensor_msgs.msg import LaserScan
e
sub = rospy.Subscriber('/scan', LaserScan, callback)
Importante: aqui, você é capaz de diferenciar onde deve entrar com o nome do tópico e seu tipo no método subscriber.
Agora, tendo em mente o nosso publisher, o mesmo procedimento deve ser empregado. Rode o comando:
rostopic type /cmd_vel
que produzirá:
geometry_msgs/Twist
esta informação é utilizada em:
from geometry_msgs.msg import Twist
e
pub = rospy.Publisher('/cmd_vel', Twist)
A variável que nós criamos, chamada “move”, também deve ser do tipo “Twist”. Por isso temos:
move = Twist()
A função “callback” é chamada pelo subscriber e toda vez que isso acontece um determinado feixe do scanner será lido. Qual deles? Bem, isso depende do que você selecionar. Vejamos exemplos:
Rode o comando:
rosmsg show sensor_msgs/LaserScan
Reparou no uso do tipo descoberto anteriormente aqui também?
Você deve ver:
De fato, os valores que queremos ler estão em “ranges”. Para ter exemplos de leitura do scanner, você pode rodar:
rostopic echo /scan
Não se assuste com esse comando, sua tela ficará cheia de dados provenientes do scanner. Com o comando Ctrl+C você para de printar esses dados na tela. Então, pode verificar o que nos interessa, ou seja, as informacoes contidas em “ranges”. Cada posição desse array corresponde a um feixe do scanner (por exemplo, o 360 está na frente do robô).
Raciocínio semelhante pode ser aplicado ao publisher. Rode o comando:
rosmsg show geometry_msgs/Twist
Para obter:
geometry_msgs/Vector3 linear
float64 x
float64 y
float64 z
geometry_msgs/Vector3 angular
float64 x
float64 y
float64 z
Baseando-se nesses valores, nós sabemos onde ler e onde escrever as informações.
Aplicando uma lógica simples de movimentação Vamos imaginar que se o robô detecta um obstáculo à sua frente ele muda a direção. Caso contrário, continua em linha reta. Isso é feito muito facilmente lendo o valor do feixe de interesse e alterando as velocidades linear e angular do robô. Assim, o arquivo Python deve conter em sua função callback:
Imaginando que esse é seu primeiro pacote, navegue até o diretório do seu worspace:
roscd
cd..
Compile:
catkin_make
source devel/setup.bash
Para facilitar sua vida, já existe um arquivo de launch preparado neste rosject que você pode rodar inserindo:
roslaunch basic_project move_launcher.launch
Na janela do Gazebo, você deve ver o robô se comportando como esperado. Nada nos dá mais satisfação do que ver o robô fazendo o que deveria fazer.
Nós esperamos que este post seja útil para você!
Vídeo no YouTube Este foi o post de hoje. Lembre-se que nós temos uma versão deste post em vídeo no YouTube.
Se você gostou do conteúdo, por favor, considere se inscrever no nosso canal do YouTube. Estamos publicando novos conteúdos quase todos os dias.
Panorama
Em ROS, quando se fala em realizar uma tarefa enquanto uma outra está sendo realizada, estamos falando das “Actions”. Neste artigo, trataremos de um exemplo em ROS com o TurtleBot3 programado em Python. Basicamente, dada uma posição no espaço, teremos que iniciar uma ação que cuidará de calcular o quanto nosso robô está longe da posição inicial enquanto este se movimenta pelo ambiente.
Abrindo o rosject
Para seguir este tutorial, será útil e conveniente usar um rosject (que a grosso modo é um projeto ROS configurado para rodar um exemplo).
Acesse o link: https://app.theconstructsim.com/l/5e7ce8b4/
Clique no botão vermelho “RUN”:
Descobrindo as mensagens associadas:
Abra um terminal clicando no ícone indicado na figura:
Isso significa que você está acessando a pasta de um pacote chamado actionlib. Neste pacote, tipos padrão de mensagens usadas para realizar ações podem ser encontradas e isso nos será útil.
Navegue para a pasta action com o comando:
cd action
Aqui, você encontra um arquivo chamado Test.action. Para visualizá-lo diretamente no terminal, rode:
cat Test.action
O seguinte conteúdo é apresentado:
int32 goal
---
int32 result
---
int32 feedback
Muitas informações importantes são extraídas deste arquivo. Por exemplo, vemos a estrutura padrão de um arquivo de ação que contém objetivo, resultado e feedback separados pelos hífens. Não necessariamente teremos variáveis associadas a esses tipos em todos os arquivos. No nosso exemplo, estamos especialmente interessados no feedback (que não precisa ter esse nome sempre).
Criando um servidor de ações
Clique no ícone para abrir a IDE e navegue nas pastas do workspace para encontrar o arquivo action_server.py.
Neste arquivo, vemos como a informação da ação é utilizada. Por exemplo, precisamos importar as possíveis mensagens, por isso temos:
from actionlib.msg import TestFeedback, TestResult, TestAction
Repare na implementação de variáveis necessárias para o código:
_feedback = TestFeedback()
_result = TestResult()
Gaste alguns minutos lendo o código para entender como a distância para a localização inicial é calculada.
No terminal 1, rode o comando:
roslaunch realrobotlab main.launch
Você será apresentado à uma janela do Gazebo como esta:
Aproxime-se (zoom in) para visualizar o nosso robô no centro da tela:
Inicializando e verificando a ação
No terminal 2, rode o servidor de ações que acabamos de ver:
rosrun basic_project action_server.py
No terminal 3, inicialize a ação publicando na variável goal:
Existem outras formas de se inicializar uma ação por exemplo usando um script dedicado (action client) ou mesmo usando a interface gráfica axclient via comando:
rosrun actionlib_tools axclient.py /record_odom
obs.: Você pode usar o comando anterior no terminal 3 se preferir ao invés do comando de publição no tópico.
Clique no ícone de ferramentas gráficas para visualizar o axclient:
Independentemente da forma como você inicialize o servidor de ações, em um novo terminal (4) você pode verificar a variável feedback sendo atualizada com o valor da distância calculada entre a posição atual e a inicial do robô.
rostopic echo /record_odom/feedback
Para fazer o exemplo mais ilustrativo, você pode mover o robô usando as teclas do seu teclado, isto é, teleoperando o robô. Para teleoperar o robô, abra um novo terminal (5) e rode:
Então, verá como a distância varia no seu terminal dedicado a ecoar tal resultado. Não se perca em meio a tantos terminais abertos simultaneamente. Algo como isto será apresentado na sua tela:
---
header:
seq: 581
stamp:
secs: 1597
nsecs: 72000000
frame_id: ''
status:
goal_id:
stamp:
secs: 1016
nsecs: 62000000
id: "/axclient_5154_1705261443951-1-1016.062"
status: 1
text: "This goal has been accepted by the simple action server"
feedback:
feedback: 58
---
Agora sim, você pode ficar tranquilo sabendo que a ação que você criou estará sempre atenta a quanto seu robô se distanciou da posição inicial. Uma ressalva importante é que neste cálculo simples de distância, os eventuais obstáculos são negligenciados.
Nós esperamos que este post seja útil para você!
Vídeo no YouTube
Este foi o post de hoje. Lembre-se que nós temos uma versão deste post em vídeo no YouTube.
Se você gostou do conteúdo, por favor, considere se inscrever no nosso canal do YouTube. Estamos publicando novos conteúdos quase todos os dias.