From 9e8ea3f81f22bd46a5f1c1770f15b1fc0423dcfc Mon Sep 17 00:00:00 2001 From: Stevedan Ogochukwu Omodolor <stevedan.o.omodolor@gmail.com> Date: Mon, 9 May 2022 13:58:38 +0200 Subject: [PATCH] working coimunication --- CMakeLists.txt | 22 +- config/omniwheels_config.yaml | 6 + launch/launch_controller.launch | 6 + msg/motorCommand.msg | 1 + package.xml | 4 + scripts/crazzyflie_logger.py | 0 scripts/omni_wheel_controller_node.py | 220 ++++++++++++++++++ scripts/track_path (1).py | 306 ++++++++++++++++++++++++++ src/talker.cpp | 137 ------------ 9 files changed, 561 insertions(+), 141 deletions(-) create mode 100644 config/omniwheels_config.yaml create mode 100644 launch/launch_controller.launch create mode 100644 msg/motorCommand.msg create mode 100644 scripts/crazzyflie_logger.py create mode 100755 scripts/omni_wheel_controller_node.py create mode 100644 scripts/track_path (1).py delete mode 100644 src/talker.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 53cb85a..8c87e83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,15 +2,29 @@ cmake_minimum_required(VERSION 2.8.3) project(omniwheels_controller) ## Find catkin and any catkin packages -find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg) - +find_package(catkin REQUIRED COMPONENTS roscpp rospy std_msgs genmsg geometry_msgs message_generation) +add_message_files( + FILES + motorCommand.msg +# Message2.msg +) +generate_messages( + DEPENDENCIES + std_msgs +) ## Declare a catkin package catkin_package() ## Build talker and listener include_directories(include ${catkin_INCLUDE_DIRS}) -add_executable(talker src/talker.cpp) -target_link_libraries(talker ${catkin_LIBRARIES}) + +#add_executable(talker src/talker.cpp) +#target_link_libraries(talker ${catkin_LIBRARIES}) + +catkin_install_python(PROGRAMS scripts/omni_wheel_controller_node.py + DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION} +) + diff --git a/config/omniwheels_config.yaml b/config/omniwheels_config.yaml new file mode 100644 index 0000000..0bdf958 --- /dev/null +++ b/config/omniwheels_config.yaml @@ -0,0 +1,6 @@ +# configuration file for all the omniwheels +name: "omniwheel1" +id: 1 +channel: 90 # change to the correct one +type: default + diff --git a/launch/launch_controller.launch b/launch/launch_controller.launch new file mode 100644 index 0000000..b6446ab --- /dev/null +++ b/launch/launch_controller.launch @@ -0,0 +1,6 @@ +<launch> + <rosparam command="load" file="$(find omniwheels_controller)/config/omniwheels_config.yaml"/> + <group ns = "omniwheel1"> + <node name="omniwheel_controller" pkg="omniwheels_controller" type="omni_wheel_controller_node.py" output="screen"/> + </group> +</launch> diff --git a/msg/motorCommand.msg b/msg/motorCommand.msg new file mode 100644 index 0000000..22a1a5e --- /dev/null +++ b/msg/motorCommand.msg @@ -0,0 +1 @@ +int32[3] data diff --git a/package.xml b/package.xml index 8cf772f..ac21299 100644 --- a/package.xml +++ b/package.xml @@ -23,15 +23,19 @@ <build_depend>message_generation</build_depend> <build_depend>rosconsole</build_depend> <build_depend>roscpp</build_depend> + <build_depend>geometry_msgs</build_depend> <build_depend>roscpp_serialization</build_depend> <build_depend>rostime</build_depend> <build_depend>std_msgs</build_depend> + <build_depend>message_generation</build_depend> + <run_depend>message_runtime</run_depend> <run_depend>libboost-date-time-dev</run_depend> <run_depend>libboost-thread-dev</run_depend> <run_depend>message_runtime</run_depend> <run_depend>rosconsole</run_depend> <run_depend>roscpp</run_depend> + <run_depend>geometry_msgs</run_depend> <run_depend>roscpp_serialization</run_depend> <run_depend>rostime</run_depend> <run_depend>std_msgs</run_depend> diff --git a/scripts/crazzyflie_logger.py b/scripts/crazzyflie_logger.py new file mode 100644 index 0000000..e69de29 diff --git a/scripts/omni_wheel_controller_node.py b/scripts/omni_wheel_controller_node.py new file mode 100755 index 0000000..3691c1a --- /dev/null +++ b/scripts/omni_wheel_controller_node.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +import rospy +import time +from std_msgs.msg import String +from omniwheels_controller.msg import motorCommand +from geometry_msgs.msg import Twist + + +# Dynamixel packages +from dynamixel.model.xm430_w210_t_r import XM430_W210_T_R +import dynamixel.channel +import time + + + + +class CrazyLogger: + + """ + Simple logging class that logs the Crazyflie Stabilizer from a supplied + link uri and disconnects after 60s. + """ + # Define which states to log: + namelink = {'stateEstimate.x': 0, 'stateEstimate.y': 1,'stateEstimate.z': 2, 'stabilizer.yaw':3} + #_Offset for crazyflie position on robot to center + position_offset = 0.11 + + def __init__(self, link_uri): + """ Initialize and run the example with the specified link_uri """ + + self._cf = Crazyflie(rw_cache='./cache') + self._state = np.array([0.0,0.0,0.0,0.0]) + + # Connect some callbacks from the Crazyflie API + self._cf.connected.add_callback(self._connected) + self._cf.disconnected.add_callback(self._disconnected) + self._cf.connection_failed.add_callback(self._connection_failed) + self._cf.connection_lost.add_callback(self._connection_lost) + + print('Connecting to %s' % link_uri) + + # Try to connect to the Crazyflie + self._cf.open_link(link_uri) + + # Variable used to keep main loop occupied until disconnect + self.is_connected = True + + def _connected(self, link_uri): + """ This callback is called form the Crazyflie API when a Crazyflie + has been connected and the TOCs have been downloaded.""" + print('Connected to %s' % link_uri) + + # The definition of the logconfig can be made before connecting + self._lg_stab = LogConfig(name='Stabilizer', period_in_ms=100) + self._lg_stab.add_variable('stateEstimate.x', 'float') + self._lg_stab.add_variable('stateEstimate.y', 'float') + self._lg_stab.add_variable('stateEstimate.z', 'float') + self._lg_stab.add_variable('stabilizer.yaw', 'float') + + # The fetch-as argument can be set to FP16 to save space in the log packet + #self._lg_stab.add_variable('pm.vbat', 'FP16') + + # Adding the configuration cannot be done until a Crazyflie is + # connected, since we need to check that the variables we + # would like to log are in the TOC. + try: + self._cf.log.add_config(self._lg_stab) + # This callback will receive the data + self._lg_stab.data_received_cb.add_callback(self._stab_log_data) + # This callback will be called on errors + self._lg_stab.error_cb.add_callback(self._stab_log_error) + # # Start a timer to disconnect in 10s + t = Timer(60, self._cf.close_link) + t.start() + + def _stab_log_error(self, logconf, msg): + """Callback from the log API when an error occurs""" + print('Error when logging %s: %s' % (logconf.name, msg)) + + def _stab_log_data(self, timestamp, data, logconf): + """Callback from a the log API when data arrives""" + #print(f'[{timestamp}][{logconf.name}]: ', end='') + for name, value in data.items(): + self._state[self._namelink[name]] = value + #print(f'{name}: {value:3.3f} ', end='\n') + #print() + + def _connection_failed(self, link_uri, msg): + """Callback when connection initial connection fails (i.e no Crazyflie + at the specified address)""" + print('Connection to %s failed: %s' % (link_uri, msg)) + self.is_connected = False + + def _connection_lost(self, link_uri, msg): + """Callback when disconnected after a connection has been made (i.e + Crazyflie moves out of range)""" + print('Connection to %s lost: %s' % (link_uri, msg)) + + def _disconnected(self, link_uri): + """Callback when the Crazyflie is disconnected (called in all cases)""" + print('Disconnected from %s' % link_uri) + self.is_connected = False + + def x(self): + """ Return x-position of robot center""" + # Note that the offset of the crazyflie mount position is included + return self._state[0] - np.sin(self._state[3])*self._position_offset + + def y(self): + """ Return y-position of robot center""" + # Note that the offset of the crazyflie mount position is included + return self._state[1] + np.cos(self._state[3])*self._position_offset + + def z(self): + """ Return z-position of robot center""" + return self._state[2] + + def theta(self): + """ Return direction of robot center, measured in radians between -pi and pi.""" + # Note that the offset of the crazyflie mount position is included + return np.mod(self._state[3]*np.pi/180 + 11*np.pi/6,2*np.pi)-np.pi + + def states(self): + return self._state + + def close(self): + self._cf.close_link() + + + +class omniWheelvelocityController: + def __init__(self): + # Load the servos + self.servos = self.load_servos() + self.motor_command = [0,0,0]; + self.send_command = False; + self.vel_cmd_arrived = False; + self.motor_command[0] = 0; + self.motor_command[1] = 0; + self.motor_command[2] = 0; + self.vel_cmd = Twist() + self.vel_cmd.linear.x = 0; + self.vel_cmd.linear.y = 0; + self.vel_cmd.angular.z = 0; + + # get ros param value + robot_name = rospy.get_param("/name"); + + + + + # subscriber + #rospy.Subscriber("motor_command", motorCommand, self.motorVelocityCallback) + # todo add grounp ns + rospy.Subscriber("vel_cmd", Twist, self.velocityCommandCallback) + + + time.sleep(1) # Wait for connection to work + rospy.loginfo("Starting controller: " + robot_name) + + # main loop + while not rospy.is_shutdown(): + + + if self.vel_cmd_arrived == True: + # convert velocity command to motor command + self.motor_command[0] = self.vel_cmd.linear.x + self.motor_command[1] = self.vel_cmd.linear.y + self.motor_command[2] = self.vel_cmd.angular.z + self.vel_cmd_arrived = False + + + else: + self.motor_command[0] = 0 + self.motor_command[1] = 0 + self.motor_command[2] = 0 + + # Send command to the motors + for (j,s) in enumerate(self.servos): + s.goal_velocity.write(round(self.motor_command[j])) + + + def load_servos(self): + """ + Servo load function, based on the dynamixel implementation of Anders Blomdell. + """ + # The speed and channel numbers can be found and checked via the dynamixel software "dynamixel wizard". + # The device can be found by e.g. lsusb, and is currently adapted for the three-color robots. + + channel = dynamixel.channel.Channel(speed=57600,device='/dev/ttyACM0') + servos = [ XM430_W210_T_R(channel, 1), + XM430_W210_T_R(channel, 2), + XM430_W210_T_R(channel, 3) ] + for s in servos: + s.torque_enable.write(0) + print(s.model_number.read(), s.id.read()) + s.operating_mode.write(1) + s.bus_watchdog.write(0) # Clear old watchdog error + s.bus_watchdog.write(100) # 2 second timeout + s.torque_enable.write(1) + + return servos + + def motorVelocityCallback(self, data): + self.send_command = True + self.motor_command[0] = data.data[0]; + self.motor_command[1] = data.data[1]; + self.motor_command[2] = data.data[2]; + def velocityCommandCallback(self,data): + self.vel_cmd_arrived = True + self.vel_cmd = data + + + +if __name__ == '__main__': + rospy.init_node("omni_wheel_controller_node") + try: + omniwheel1 = omniWheelvelocityController() + except rospy.ROSInterruptException: + pass diff --git a/scripts/track_path (1).py b/scripts/track_path (1).py new file mode 100644 index 0000000..fae9aa5 --- /dev/null +++ b/scripts/track_path (1).py @@ -0,0 +1,306 @@ +import numpy as np +import pandas as pd +import sys + +# Dynamixel packages +from dynamixel.model.xm430_w210_t_r import XM430_W210_T_R +import dynamixel.channel +import time + +#Crazy packages +import logging +from threading import Timer + +import cflib.crtp # noqa +from cflib.crazyflie import Crazyflie +from cflib.crazyflie.log import LogConfig +from cflib.utils import uri_helper + +logging.basicConfig(level=logging.ERROR) + + +######################################################################## +# SUPPORTING CLASSES AND FUNCTIONS +######################################################################## + +def load_servos(): +""" +Servo load function, based on the dynamixel implementation of Anders Blomdell. +""" + +# The speed and channel numbers can be found and checked via the dynamixel software "dynamixel wizard". +# The device can be found by e.g. lsusb, and is currently adapted for the three-color robots. + + channel = dynamixel.channel.Channel(speed=57600,device='/dev/ttyACM0') + servos = [ XM430_W210_T_R(channel, 1), + XM430_W210_T_R(channel, 2), + XM430_W210_T_R(channel, 3) ] + for s in servos: + s.torque_enable.write(0) + print(s.model_number.read(), s.id.read()) + s.operating_mode.write(1) + s.bus_watchdog.write(0) # Clear old watchdog error + s.bus_watchdog.write(100) # 2 second timeout + s.torque_enable.write(1) + + return servos + +class SimplePI: + """ + Simple PI implementation with basic anti-windup. The _saturation - variable sets the saturation of the anti-windup. + """ + + _saturation = 0.1 + + def __init__(self,kp,ki,dt): + """ Initialize the controller """ + + self.e = np.zeros(3) + self.dt = dt # Sample rate + self.kp = kp # P-part gain + self.ki = ki # I-part gain + + def update_control(self,e): + self.e += e*self.dt # Update cumulative error + + # Saturate internal error for anti-windup + self.e = np.sign(self.e)*np.array([min(ei,self._saturation) for ei in abs(self.e)]) + + # Return control signal + return self.kp*e + self.ki*self.e + + +class CrazyLogger: + + """ + Simple logging class that logs the Crazyflie Stabilizer from a supplied + link uri and disconnects after 60s. + """ + # Define which states to log: + namelink = {'stateEstimate.x': 0, 'stateEstimate.y': 1,'stateEstimate.z': 2, 'stabilizer.yaw':3} + #_Offset for crazyflie position on robot to center + position_offset = 0.11 + + def __init__(self, link_uri): + """ Initialize and run the example with the specified link_uri """ + + self._cf = Crazyflie(rw_cache='./cache') + self._state = np.array([0.0,0.0,0.0,0.0]) + + # Connect some callbacks from the Crazyflie API + self._cf.connected.add_callback(self._connected) + self._cf.disconnected.add_callback(self._disconnected) + self._cf.connection_failed.add_callback(self._connection_failed) + self._cf.connection_lost.add_callback(self._connection_lost) + + print('Connecting to %s' % link_uri) + + # Try to connect to the Crazyflie + self._cf.open_link(link_uri) + + # Variable used to keep main loop occupied until disconnect + self.is_connected = True + + def _connected(self, link_uri): + """ This callback is called form the Crazyflie API when a Crazyflie + has been connected and the TOCs have been downloaded.""" + print('Connected to %s' % link_uri) + + # The definition of the logconfig can be made before connecting + self._lg_stab = LogConfig(name='Stabilizer', period_in_ms=100) + self._lg_stab.add_variable('stateEstimate.x', 'float') + self._lg_stab.add_variable('stateEstimate.y', 'float') + self._lg_stab.add_variable('stateEstimate.z', 'float') + self._lg_stab.add_variable('stabilizer.yaw', 'float') + + # The fetch-as argument can be set to FP16 to save space in the log packet + #self._lg_stab.add_variable('pm.vbat', 'FP16') + + # Adding the configuration cannot be done until a Crazyflie is + # connected, since we need to check that the variables we + # would like to log are in the TOC. + try: + self._cf.log.add_config(self._lg_stab) + # This callback will receive the data + self._lg_stab.data_received_cb.add_callback(self._stab_log_data) + # This callback will be called on errors + self._lg_stab.error_cb.add_callback(self._stab_log_error) + # Start the logging + self._lg_stab.start() + except KeyError as e: + print('Could not start log configuration,' + '{} not found in TOC'.format(str(e))) + except AttributeError: + print('Could not add Stabilizer log config, bad configuration.') + + # Start a timer to disconnect in 10s + t = Timer(60, self._cf.close_link) + t.start() + + def _stab_log_error(self, logconf, msg): + """Callback from the log API when an error occurs""" + print('Error when logging %s: %s' % (logconf.name, msg)) + + def _stab_log_data(self, timestamp, data, logconf): + """Callback from a the log API when data arrives""" + #print(f'[{timestamp}][{logconf.name}]: ', end='') + for name, value in data.items(): + self._state[self._namelink[name]] = value + #print(f'{name}: {value:3.3f} ', end='\n') + #print() + + def _connection_failed(self, link_uri, msg): + """Callback when connection initial connection fails (i.e no Crazyflie + at the specified address)""" + print('Connection to %s failed: %s' % (link_uri, msg)) + self.is_connected = False + + def _connection_lost(self, link_uri, msg): + """Callback when disconnected after a connection has been made (i.e + Crazyflie moves out of range)""" + print('Connection to %s lost: %s' % (link_uri, msg)) + + def _disconnected(self, link_uri): + """Callback when the Crazyflie is disconnected (called in all cases)""" + print('Disconnected from %s' % link_uri) + self.is_connected = False + + def x(self): + """ Return x-position of robot center""" + # Note that the offset of the crazyflie mount position is included + return self._state[0] - np.sin(self._state[3])*self._position_offset + + def y(self): + """ Return y-position of robot center""" + # Note that the offset of the crazyflie mount position is included + return self._state[1] + np.cos(self._state[3])*self._position_offset + + def z(self): + """ Return z-position of robot center""" + return self._state[2] + + def theta(self): + """ Return direction of robot center, measured in radians between -pi and pi.""" + # Note that the offset of the crazyflie mount position is included + return np.mod(self._state[3]*np.pi/180 + 11*np.pi/6,2*np.pi)-np.pi + + def states(self): + return self._state + + def close(self): + self._cf.close_link() + +######################################################################## +# MAIN SCRIPT +######################################################################## +if __name__ == '__main__': + + # Read input trajectory filename + if len(sys.argv) > 1: + print("Input file name:",sys.argv[1]) + filename = sys.argv[1] + else: + print("No file name inupt. Using default file name: trajectory.csv") + filename = "trajectory.csv" + + # Load trajectory + print("Attempting to load trajectory file",filename) + trajectory = pd.read_csv(filename) + print("\nTrajectory loaded.") + x = trajectory.x.to_numpy() + y = trajectory.y.to_numpy() + # Update theta to be in correct range (-pi,pi) + theta = ((trajectory.theta + np.pi).mod(2*np.pi)-np.pi).to_numpy() # Put theta in range -pi -> pi instead of 0 -> 2pi + + #Find derivatives + trajectory[["xdot","ydot","thetadot"]] = trajectory[["x","y","theta"]].diff(axis = 0).fillna(0)/0.1 + + #FUDGE FACTOR TO DECREASE SPEED FOR TESTING!!!! + # Should probably be removed in future iterations. Was included because + # the robot was exceeding servo speed settings. + fudgefactor = 3.0 + velref = trajectory[['xdot','ydot','thetadot']].to_numpy()/fudgefactor + + # Create time vector + dt = trajectory.t.diff().mean()*fudgefactor + t = trajectory.t.to_numpy()*fudgefactor + tend = t[-1] + n = t.size + print("Sample time:",dt) + print("Experiment time:",tend) + print("Time steps:",n) + + # Read controller parameters + if len(sys.argv) > 3: + print("\nController parameters:") + kp = float(sys.argv[2]) + ki = float(sys.argv[3]) + else: + print("\nNo controller paramater input. Using default values:") + kp = 0.1 + ki = 0.01 + + print("kp =",kp) + print("ki =",ki) + + + # Robot parameters + R = 0.16 # Distance between center of robot and wheels + a1 = 2*np.pi/3 # Angle between x axis and first wheel + a2 = 4*np.pi/3 # Angle between x axis and second wheel + r = 0.028*0.45/18 # Wheel radius. Has been fudge-factored because the actual velocity of the wheels did not align with the set-points. + + print("Bot dimensions:") + print("R =",R) + print("R =",r) + + def phidot(xdot,ang): + """Returns reference velocities for the wheels, given current system state and reference velocities""" + M = -1/r*np.array([[-np.sin(ang), np.cos(ang), R ],[-np.sin(ang+a1), np.cos(ang+a1), R],[-np.sin(ang+a2), np.cos(ang+a2), R]]) + return M.dot(xdot) + + + # Initiate experiment + print('Initiating experiment') + servos = load_servos() # Use predefined funtion to initiate servo connection + cflib.crtp.init_drivers() # Initiate drivers for crazyflie + uri = uri_helper.uri_from_env(default='usb://0') # Connection-uri for crazyflie via USB + cl = CrazyLogger(uri) # Create a crazyflie-based logger + pi = SimplePI(kp,ki,dt) # Create a PI-controller + + + time.sleep(1) # Wait for connection to work + t0 = time.time() + + # Main control loop + for i in range(n-1): + + # Read current position error: + e = np.array([trajectory.x.iloc[i]-cl.x(),trajectory.y.iloc[i]-cl.y(),trajectory.theta.iloc[i]-cl.theta()]) + + # Create reference speed through feed-forward (velref) and feedback (pi): + xdot = velref[i,:] + pi.update_control(e) + + # Transform from x,y,theta-speeds to wheel rotational speeds. + ph = phidot(xdot,cl.theta()) + + # Set all servospeeds (enact control signal) + for (j,s) in enumerate(servos): + s.goal_velocity.write(round(ph[j])) + + # Printing position and tracking error + print("x:",cl.x(),"\t y:",cl.y(),"\t theta:",cl.theta()) + print("Error:",e,"\n") + + # Wait until next loop-iteration + time.sleep(max(t[i+1]+t0-time.time(),0)) + + + + # Shutdown + print("Experiment done, shutting down servos and logger") + for s in servos: + s.goal_velocity.write(0) + print("Finishing position: x =",cl.x(),"\t y= ",cl.y(),"\t theta =",cl.theta()) + cl.close() + diff --git a/src/talker.cpp b/src/talker.cpp deleted file mode 100644 index f0899ac..0000000 --- a/src/talker.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2008, Morgan Quigley and Willow Garage, Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * * Neither the names of Stanford University or Willow Garage, Inc. nor the names of its - * contributors may be used to endorse or promote products derived from - * this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ -// %Tag(FULLTEXT)% -// %Tag(ROS_HEADER)% -#include "ros/ros.h" -// %EndTag(ROS_HEADER)% -// %Tag(MSG_HEADER)% -#include "std_msgs/String.h" -// %EndTag(MSG_HEADER)% - -#include <sstream> - -/** - * This tutorial demonstrates simple sending of messages over the ROS system. - */ -int main(int argc, char **argv) -{ - /** - * The ros::init() function needs to see argc and argv so that it can perform - * any ROS arguments and name remapping that were provided at the command line. - * For programmatic remappings you can use a different version of init() which takes - * remappings directly, but for most command-line programs, passing argc and argv is - * the easiest way to do it. The third argument to init() is the name of the node. - * - * You must call one of the versions of ros::init() before using any other - * part of the ROS system. - */ -// %Tag(INIT)% - ros::init(argc, argv, "talker"); -// %EndTag(INIT)% - - /** - * NodeHandle is the main access point to communications with the ROS system. - * The first NodeHandle constructed will fully initialize this node, and the last - * NodeHandle destructed will close down the node. - */ -// %Tag(NODEHANDLE)% - ros::NodeHandle n; -// %EndTag(NODEHANDLE)% - - /** - * The advertise() function is how you tell ROS that you want to - * publish on a given topic name. This invokes a call to the ROS - * master node, which keeps a registry of who is publishing and who - * is subscribing. After this advertise() call is made, the master - * node will notify anyone who is trying to subscribe to this topic name, - * and they will in turn negotiate a peer-to-peer connection with this - * node. advertise() returns a Publisher object which allows you to - * publish messages on that topic through a call to publish(). Once - * all copies of the returned Publisher object are destroyed, the topic - * will be automatically unadvertised. - * - * The second parameter to advertise() is the size of the message queue - * used for publishing messages. If messages are published more quickly - * than we can send them, the number here specifies how many messages to - * buffer up before throwing some away. - */ -// %Tag(PUBLISHER)% - ros::Publisher chatter_pub = n.advertise<std_msgs::String>("chatter", 1000); -// %EndTag(PUBLISHER)% - -// %Tag(LOOP_RATE)% - ros::Rate loop_rate(10); -// %EndTag(LOOP_RATE)% - - /** - * A count of how many messages we have sent. This is used to create - * a unique string for each message. - */ -// %Tag(ROS_OK)% - int count = 0; - while (ros::ok()) - { -// %EndTag(ROS_OK)% - /** - * This is a message object. You stuff it with data, and then publish it. - */ -// %Tag(FILL_MESSAGE)% - std_msgs::String msg; - - std::stringstream ss; - ss << "hello world " << count; - msg.data = ss.str(); -// %EndTag(FILL_MESSAGE)% - -// %Tag(ROSCONSOLE)% - ROS_INFO("%s", msg.data.c_str()); -// %EndTag(ROSCONSOLE)% - - /** - * The publish() function is how you send messages. The parameter - * is the message object. The type of this object must agree with the type - * given as a template parameter to the advertise<>() call, as was done - * in the constructor above. - */ -// %Tag(PUBLISH)% - chatter_pub.publish(msg); -// %EndTag(PUBLISH)% - -// %Tag(SPINONCE)% - ros::spinOnce(); -// %EndTag(SPINONCE)% - -// %Tag(RATE_SLEEP)% - loop_rate.sleep(); -// %EndTag(RATE_SLEEP)% - ++count; - } - - - return 0; -} -// %EndTag(FULLTEXT)% -- GitLab