Recording and playing back data

本文介绍如何使用ROS系统中的rosbag工具记录运行时的数据到.bag文件,并回放数据来重现类似的行为。文章详细解释了如何列出可用的话题、记录特定话题的消息以及使用rosbag的不同子命令。

Recording and playing back data

This tutorial will teach you how to record data from a running ROS system into a .bag file, and then to play back the data to produce similar behavior in a running system.

rostopic list -v

//This should yield the following output:
Published topics:
 * /turtle1/color_sensor [turtlesim/Color] 1 publisher
 * /turtle1/cmd_vel [geometry_msgs/Twist] 1 publisher
 * /rosout [rosgraph_msgs/Log] 2 publishers
 * /rosout_agg [rosgraph_msgs/Log] 1 publisher
 * /turtle1/pose [turtlesim/Pose] 1 publisher

Subscribed topics:
 * /turtle1/cmd_vel [geometry_msgs/Twist] 1 subscriber
 * /rosout [rosgraph_msgs/Log] 1 subscriber

只有topic list中列出的类型的msg才可以被record到data log file。

The list of published topics are the only message types that could potentially be recorded in the data log file, as only published messages are recorded.

583030-20190325145820479-1384966493.png

rosbag用法如下:

suchang@suchang-virtual-machine:~/bagfiles$ rosbag 
Usage: rosbag <subcommand> [options] [args]

A bag is a file format in ROS for storing ROS message data. The rosbag command can record, replay and manipulate bags.

Available subcommands:
   check    Determine whether a bag is playable in the current system, or if it can be migrated.
   compress     Compress one or more bag files.
   decompress   Decompress one or more bag files.
   decrypt      Decrypt one or more bag files.
   encrypt      Encrypt one or more bag files.
   filter   Filter the contents of the bag.
   fix      Repair the messages in a bag file so that it can be played in the current system.
   help  
   info     Summarize the contents of one or more bag files.
   play     Play back the contents of one or more bag files in a time-synchronized fashion.
   record   Record a bag file with the contents of specified topics.
   reindex      Reindexes one or more bag files.

For additional information, see http://wiki.ros.org/rosbag

583030-20190325145838079-881111949.png

在真的发送bag file中记录的msg之前会等待0.2s,以防止前几个msg丢失.

In its default mode rosbag play will wait for a certain period (.2 seconds) after advertising each message before it actually begins publishing the contents of the bag file. Waiting for some duration allows any subscriber of a message to be alerted that the message has been advertised and that messages may follow. If rosbag play publishes messages immediately upon advertising, subscribers may not receive the first several published messages. The waiting period can be specified with the -d option.

rosbag play可以不从bag file的开始播放,可以用-s 指定从距离文件初始记录时间xx秒开始.

rosbag play -r 2 ./2019-03-25-13-48-22.bag
-r 参数指定播放倍速.

在一个复杂系统里,可能有成百上千个topic,我们不可能记录所有topic上的msg.

// -O means output.生成一个subset.bag文件.  只记录/turtle1/cmd_vel /turtle1/pose这两个topic上的msg.
rosbag record -O subset /turtle1/cmd_vel /turtle1/pose
rosbag record/play的局限

rosbag很难精确地复制一个running systerm中的行为,因为rosrecord记录的msg的时间很难做到足够精确,比方说系统1s的时候发了msg1,2s的时候发了msg2,rosbag记录到的时候可能已经是1.01s和2.02s,这样的话就差出了0.01s,在play back的时候可能就会产生微小误差.

In the previous section you may have noted that the turtle's path may not have exactly mapped to the original keyboard input - the rough shape should have been the same, but the turtle may not have exactly tracked the same path. The reason for this is that the path tracked by turtlesim is very sensitive to small changes in timing in the system, and rosbag is limited in its ability to exactly duplicate the behavior of a running system in terms of when messages are recorded and processed by rosrecord, and when messages are produced and processed when using rosplay. For nodes like turtlesim, where minor timing changes in when command messages are processed can subtly alter behavior, the user should not expect perfectly mimicked behavior.

转载于:https://www.cnblogs.com/sdu20112013/p/10593771.html

import time import random from kivy.app import App from kivy.uix.boxlayout import BoxLayout from kivy.uix.button import Button from kivy.uix.label import Label from kivy.uix.slider import Slider from kivy.uix.textinput import TextInput from kivy.uix.spinner import Spinner from kivy.uix.screenmanager import ScreenManager, Screen from kivy.clock import Clock from kivy.properties import StringProperty, NumericProperty, BooleanProperty from kivy.animation import Animation from kivy_garden.graph import Graph, MeshLinePlot from kivy.uix.popup import Popup from kivy.uix.progressbar import ProgressBar class InitialScreen(Screen): """初始界面:设置音乐参数""" def __init__(self, **kwargs): super(InitialScreen, self).__init__(**kwargs) # 创建主布局 layout = BoxLayout(orientation='vertical', padding=20, spacing=15) # 添加标题 title = Label( text="音乐创作助手", font_size=32, size_hint=(1, 0.1) ) layout.add_widget(title) # 添加节拍选择 layout.add_widget(Label(text="节拍:", size_hint=(1, 0.05))) self.beats_spinner = Spinner( text='请选择', values=('2/4', '3/4', '4/4', '6/8'), size_hint=(1, 0.08) ) layout.add_widget(self.beats_spinner) # 添加音速控制 layout.add_widget(Label(text="音速 (BPM):", size_hint=(1, 0.05))) self.tempo_slider = Slider( min=60, max=200, value=120, step=1, size_hint=(1, 0.1) ) self.tempo_value = Label( text=f"{int(self.tempo_slider.value)} BPM", size_hint=(1, 0.05) ) self.tempo_slider.bind(value=lambda instance, value: setattr(self.tempo_value, 'text', f"{int(value)} BPM")) layout.add_widget(self.tempo_slider) layout.add_widget(self.tempo_value) # 添加音调选择 layout.add_widget(Label(text="音调:", size_hint=(1, 0.05))) self.pitch_spinner = Spinner( text='请选择', values=('C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G'), size_hint=(1, 0.08) ) layout.add_widget(self.pitch_spinner) # 添加风格选择 layout.add_widget(Label(text="风格:", size_hint=(1, 0.05))) self.style_spinner = Spinner( text='请选择', values=('Pop', 'Rock', 'Jazz', 'Classical', 'Electronic', 'Folk', 'Blues', 'Country'), size_hint=(1, 0.08) ) layout.add_widget(self.style_spinner) # 添加伴奏音型选择 layout.add_widget(Label(text="伴奏音型:", size_hint=(1, 0.05))) self.accompaniment_spinner = Spinner( text='请选择', values=('Piano', 'Guitar', 'Strings', 'Drums', 'Bass', 'Synth', 'Choir', 'Orchestra'), size_hint=(1, 0.08) ) layout.add_widget(self.accompaniment_spinner) # 添加旋律音高序列输入 layout.add_widget(Label(text="旋律音高序列:", size_hint=(1, 0.05))) self.pitch_sequence = TextInput( multiline=False, hint_text="例如: C4 D4 E4 F4 G4", size_hint=(1, 0.08) ) layout.add_widget(self.pitch_sequence) # 添加开始录制按钮 self.start_button = Button( text="开始录制", font_size=24, size_hint=(1, 0.15), background_color=(0.2, 0.6, 0.9, 1) ) self.start_button.bind(on_press=self.validate_and_start) layout.add_widget(self.start_button) self.add_widget(layout) def validate_and_start(self, instance): """验证表单并切换到录音界面""" if (self.beats_spinner.text != '请选择' and self.pitch_spinner.text != '请选择' and self.style_spinner.text != '请选择' and self.accompaniment_spinner.text != '请选择' and self.pitch_sequence.text.strip()): # 获取当前选中的值 app = App.get_running_app() app.beats = self.beats_spinner.text app.tempo = int(self.tempo_slider.value) app.pitch = self.pitch_spinner.text app.style = self.style_spinner.text app.accompaniment = self.accompaniment_spinner.text app.pitch_sequence = self.pitch_sequence.text # 切换到录音界面 self.manager.current = 'recording' else: # 显示错误提示 self.show_error("请完成所有参数选择") def show_error(self, message): """显示错误提示""" # 简单实现:改变按钮颜色 self.start_button.background_color = (0.9, 0.2, 0.2, 1) Clock.schedule_once(lambda dt: setattr(self.start_button, 'background_color', (0.2, 0.6, 0.9, 1)), 1) class RecordingScreen(Screen): """录音界面:控制录音过程""" def __init__(self, **kwargs): super(RecordingScreen, self).__init__(**kwargs) # 录音状态 self.is_recording = False self.is_paused = False self.is_playing = False self.recording_time = 0 self.timer_event = None self.recording_data = [] # 存储录音数据 # 创建主布局 layout = BoxLayout(orientation='vertical', padding=20, spacing=15) # 返回按钮 back_layout = BoxLayout(orientation='horizontal', size_hint=(1, 0.05)) back_button = Button( text="返回", size_hint=(0.2, 1), background_color=(0.6, 0.6, 0.6, 1) ) back_button.bind(on_press=self.go_back) back_layout.add_widget(back_button) layout.add_widget(back_layout) # 添加标题 title = Label( text="正在录音", font_size=32, size_hint=(1, 0.1) ) layout.add_widget(title) # 参数显示区域 self.params_layout = BoxLayout(orientation='vertical', size_hint=(1, 0.25)) layout.add_widget(self.params_layout) # 录音时间显示 self.time_label = Label( text="00:00", font_size=48, size_hint=(1, 0.1) ) layout.add_widget(self.time_label) # 波形可视化 self.graph = Graph( xlabel='时间', ylabel='振幅', x_ticks_minor=5, x_ticks_major=25, y_ticks_major=1, y_grid_label=True, x_grid_label=True, padding=5, x_grid=True, y_grid=True, xmin=0, xmax=100, ymin=-1, ymax=1, size_hint=(1, 0.25) ) self.plot = MeshLinePlot(color=[0.2, 0.6, 0.9, 1]) self.graph.add_plot(self.plot) layout.add_widget(self.graph) # 控制按钮区域 control_layout = BoxLayout(orientation='horizontal', size_hint=(1, 0.15), spacing=10) # 录音/停止按钮 self.record_button = Button( text="开始", font_size=24, background_color=(0.2, 0.8, 0.2, 1) ) self.record_button.bind(on_press=self.toggle_recording) control_layout.add_widget(self.record_button) # 生成按钮 self.generate_button = Button( text="生成", font_size=24, background_color=(0.8, 0.6, 0.2, 1), disabled=True ) self.generate_button.bind(on_press=self.generate_music) control_layout.add_widget(self.generate_button) layout.add_widget(control_layout) # 功能按钮区域 function_layout = BoxLayout(orientation='horizontal', size_hint=(1, 0.15), spacing=10) # 暂停/继续按钮 self.pause_button = Button( text="暂停", font_size=24, background_color=(0.8, 0.8, 0.2, 1), disabled=True ) self.pause_button.bind(on_press=self.toggle_pause) function_layout.add_widget(self.pause_button) # 播放/暂停按钮 self.play_button = Button( text="播放", font_size=24, background_color=(0.2, 0.6, 0.9, 1), disabled=True ) self.play_button.bind(on_press=self.toggle_play) function_layout.add_widget(self.play_button) layout.add_widget(function_layout) # 下载按钮 self.download_button = Button( text="下载", font_size=24, size_hint=(1, 0.1), background_color=(0.6, 0.2, 0.8, 1), disabled=True ) self.download_button.bind(on_press=self.download_recording) layout.add_widget(self.download_button) self.add_widget(layout) def on_enter(self): """进入屏幕时更新参数显示""" app = App.get_running_app() # 清空参数布局 self.params_layout.clear_widgets() # 添加参数显示 params = [ f"节拍: {app.beats}", f"音速: {app.tempo} BPM", f"音调: {app.pitch}", f"风格: {app.style}", f"伴奏: {app.accompaniment}", f"旋律音高: {app.pitch_sequence}" ] for param in params: label = Label( text=param, font_size=18, size_hint=(1, 0.2), halign='left' ) self.params_layout.add_widget(label) def toggle_recording(self, instance): """开始/停止录音""" if not self.is_recording: # 开始录音 self.is_recording = True self.record_button.text = "停止" self.record_button.background_color = (0.8, 0.2, 0.2, 1) self.pause_button.disabled = False self.play_button.disabled = True self.download_button.disabled = True self.generate_button.disabled = True # 重置录音数据 self.recording_data = [] self.plot.points = [] # 启动计时器 self.recording_time = 0 self.update_time() self.timer_event = Clock.schedule_interval(self.update_time, 0.1) # 100ms更新一次 # 显示录音中提示 app = App.get_running_app() app.show_message("开始录音...") else: # 停止录音 self.is_recording = False self.record_button.text = "开始" self.record_button.background_color = (0.2, 0.8, 0.2, 1) self.pause_button.disabled = True self.play_button.disabled = False self.download_button.disabled = False self.generate_button.disabled = False # 停止计时器 if self.timer_event: self.timer_event.cancel() # 显示录音完成提示 app = App.get_running_app() app.show_message(f"录音完成,时长: {self.recording_time//10}秒") def update_time(self, dt=None): """更新录音时间和波形数据""" self.recording_time += 1 seconds = self.recording_time // 10 # 转换为秒 tenths = self.recording_time % 10 # 十分之一秒 self.time_label.text = f"{seconds:02d}:{tenths:01d}" # 生成随机波形数据(实际应用中会从麦克风获取) if self.is_recording and not self.is_paused: x = len(self.recording_data) y = random.uniform(-0.8, 0.8) self.recording_data.append((x, y)) # 更新波形显示 if len(self.recording_data) > 100: self.plot.points = self.recording_data[-100:] else: self.plot.points = self.recording_data def toggle_pause(self, instance): """暂停/继续录音""" if not self.is_paused: # 暂停录音 self.is_paused = True self.pause_button.text = "继续" if self.timer_event: self.timer_event.cancel() else: # 继续录音 self.is_paused = False self.pause_button.text = "暂停" self.timer_event = Clock.schedule_interval(self.update_time, 0.1) def toggle_play(self, instance): """播放/暂停录音""" if not self.is_playing: # 开始播放 self.is_playing = True self.play_button.text = "暂停" # 模拟播放波形动画 self.play_index = 0 self.play_timer = Clock.schedule_interval(self.update_play, 0.1) else: # 暂停播放 self.is_playing = False self.play_button.text = "播放" if hasattr(self, 'play_timer'): self.play_timer.cancel() def update_play(self, dt): """更新播放时的波形显示""" if not self.recording_data: return self.play_index += 1 if self.play_index >= len(self.recording_data): self.play_index = 0 self.is_playing = False self.play_button.text = "播放" self.play_timer.cancel() return # 更新波形显示 start = max(0, self.play_index - 50) end = self.play_index + 50 if end > len(self.recording_data): end = len(self.recording_data) start = max(0, end - 100) self.plot.points = self.recording_data[start:end] def generate_music(self, instance): """生成音乐""" if not self.is_recording: # 显示生成中对话框 self.progress_popup = Popup( title="生成音乐", size_hint=(None, None), size=(400, 200) ) progress_layout = BoxLayout(orientation='vertical', padding=20) progress_label = Label(text="正在生成音乐...") progress_bar = ProgressBar(max=100, value=0) progress_layout.add_widget(progress_label) progress_layout.add_widget(progress_bar) self.progress_popup.content = progress_layout self.progress_popup.open() # 模拟生成过程 self.progress = 0 self.generate_timer = Clock.schedule_interval(lambda dt: self.update_progress(progress_bar, progress_label), 0.1) else: # 提示用户先停止录音 app = App.get_running_app() app.show_message("请先停止录音") def update_progress(self, progress_bar, progress_label): """更新生成进度""" self.progress += 1 progress_bar.value = self.progress progress_label.text = f"正在生成音乐... {self.progress}%" if self.progress >= 100: self.generate_timer.cancel() self.progress_popup.dismiss() # 显示成功提示 app = App.get_running_app() app.show_message("音乐生成成功") def download_recording(self, instance): """下载录音""" # 显示下载中提示 app = App.get_running_app() app.show_message("准备下载...") # 模拟下载过程 Clock.schedule_once(self.on_download_complete, 2) def on_download_complete(self, dt): """下载完成回调""" # 显示成功提示 app = App.get_running_app() app.show_message("下载完成") def go_back(self, instance): """返回初始界面""" # 停止所有活动 if self.is_recording: self.toggle_recording(None) if hasattr(self, 'play_timer') and self.play_timer.is_triggered: self.play_timer.cancel() # 返回初始界面 self.manager.current = 'initial' class MusicRecorderApp(App): """音乐录音应用主类""" beats = StringProperty("") tempo = NumericProperty(120) pitch = StringProperty("") style = StringProperty("") accompaniment = StringProperty("") pitch_sequence = StringProperty("") def build(self): """构建应用UI""" # 创建屏幕管理器 sm = ScreenManager() # 添加屏幕 sm.add_widget(InitialScreen(name='initial')) sm.add_widget(RecordingScreen(name='recording')) return sm def show_message(self, message): """显示消息提示""" popup = Popup( title='提示', content=Label(text=message), size_hint=(None, None), size=(300, 200), auto_dismiss=True ) popup.open() if __name__ == '__main__': # 启动应用 MusicRecorderApp().run()
05-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值