FRC 0 to Autonomous: #5 Command-Based Robot
Summary
TLDRこのビデオでは、コマンドベースのプログラミングについて学びます。シミュレータでエレベーターボットをプログラミングし、駆動系、エレベーター、インテイク、そして自動運転シーケンスをコマンドベース構造でプログラムする方法を学びます。過去のビデオでは、初期化と各ロボットモードの周期的な関数を使用してタイムベースのコード構造を使いましたが、プロジェクトが大きくなると、コマンドベース構造が必要になります。このビデオでは、タイムベースのロボットをコマンドベースに変換し、サブシステムとコマンドを使ってロボットの動作をカスタマイズする方法を紹介します。
Takeaways
- 🤖 このエピソードでは、コマンドベースのプログラミングについて解説しています。
- 🏗️ 実際にはロボットを操作せず、シミュレータでエレベーターボットをプログラミングします。
- 📚 学習目標は、コマンドベース構造でドライブトレイン、エレベーター、インテイク、および自動運転シーケンスをプログラミングすることです。
- 🔄 これまでのビデオではタイムベースのロボットコード構造が使われていましたが、プロジェクトが大きくなるとコマンドベース構造が必要になります。
- 📂 コマンドベース構造では、サブシステムと呼ばれるファイルごとにメカニズムを分離して整理できます。
- 🛠️ サブシステムは、モーターやセンサーのセットアップを簡単に行い、コマンドがそれらを使いやすくします。
- 🔄 コマンドは、サブシステムの詳細に関わらず、アクションを実行するのに使用されます。
- 🔄 ロボットの自動運転モードでは、異なるコマンドを組み合わせて自動運転ルーチンを作成できます。
- 🎮 シミュレータでロボットコードを実行し、ロボットの動作を確認できます。
- 🔧 ロボットコンテナーファイルでサブシステムとコマンドを管理し、ジョイスティックボタンをトリガーに設定できます。
- 🔢 定数クラスを使用して、ロボットコード全体で使用される数値を一か所に集約して管理し、変更を容易にします。
Q & A
コマンドベースプログラミングとは何ですか?
-コマンドベースプログラミングは、ロボットの動作を複数のコマンドに分割し、それぞれのコマンドが特定の機能を実行するプログラミングスタイルです。
タイムベースのロボットコード構造とコマンドベース構造の違いは何ですか?
-タイムベース構造は、初期化関数と各ロボットモードの周期的な関数を使用します。一方、コマンドベース構造は、大きなプロジェクトでコードをサブシステムとコマンドに分割し、より読みやすく管理しやすくなります。
サブシステムとは何で、何のために使用されますか?
-サブシステムは、ロボットの各メカニズムのモーターやセンサーの設定をまとめたファイルであり、コマンドがハードウェアの詳細に関わらず簡単に動作を実行できるようにします。
コマンドベース構造で使用される「コマンド」とは何を意味していますか?
-「コマンド」は、ロボットのメカニズムが実行するアクションを記述したファイルであり、initialize、execute、end、isFinishedなどの関数で構成されています。
どのようにしてコマンドベースのロボットをシミュレータで操作できますか?
-シミュレータでロボットを操作するには、まずシミュレータのリンクからシミュレータをダウンロードし、自分のコードをロボットライブにインポートして実行します。
ロボットのエレベーターのPIDコントローラーとは何ですか?
-PIDコントローラーは、エレベーターの位置を制御するため使用され、設定された目標位置に近づくようにモーターの速度を調整します。
コマンドベースプログラミングでの「並列コマンドグループ」と「順次コマンドグループ」の違いは何ですか?
-並列コマンドグループは、複数のコマンドを同時に実行します。一方、順次コマンドグループは、一度に一つのコマンドを実行し、前のコマンドが終了した後に次のコマンドを開始します。
ロボットの自走モードで実行されるコマンドグループを作成するにはどうすればよいですか?
-自走モードで実行されるコマンドグループを作成するには、getAutonomousCommand関数を作成し、そこに実行したいコマンドグループを定義します。
ロボットの操作パネルでジョイスティックボタンを設定するにはどうすればよいですか?
-ジョイスティックボタンを設定するには、RobotContainer.javaのconfigureButtonBindings関数を使用して、ボタンに関連付けたいコマンドを指定します。
定数クラスを使用することの利点は何ですか?
-定数クラスを使用することで、プロジェクト全体で使用されている数値を一か所で管理でき、変更が必要な場合でも簡単に更新できます。
Outlines
🤖 コマンドベースプログラミング入門
このエピソードでは、物理的なロボットではなく、シミュレータで作成されたエレベーターボットを使用してコマンドベースプログラミングについて学びます。最終的には、コマンドベース構造でドライブトレイン、エレベーター、インテーク、および自動運転シーケンスをプログラムする方法を学びます。これまでのビデオでは、初期化関数と各ロボットモードの周期関数を持つTimed Robot Code構造を使用してきましたが、プロジェクトが大きくなることでコマンドベース構造が必要になります。エレベーターボットの例を使って、Timed RobotからCommand-Based Robotへの変換方法を説明し、各メカニズムごとにファイルを分けることでコードの可読性を高める方法を学びます。
🔄 サブシステムとコマンドの構築
エレベーターボットの例では、サブシステムとコマンドを使ってロボットの動作を定義します。サブシステムはモーターとセンサーのセットアップを行い、コマンドはサブシステムを操作するアクションを定義します。サブシステムの例として、ドライブトレイン、エレベーター、インテークのそれぞれについて、Javaファイルを作成し、それらに必要なセットアップを記述します。コマンドの例として、エレベーターのジョイスティック操作やPID制御、ドライブトレインの前方への移動、インテークの開閉などを実行するコマンドを作成し、それらをシリアルまたは並列で実行するコマンドグループを作成する方法を学びます。
🔄 コマンドのライフサイクルと要件
コマンドのライフサイクルはinitialize、execute、endの3つの関数で構成され、これらを使ってコマンドの開始前、実行中、終了時の処理を定義します。initialize関数はコマンド開始前に1回だけ実行され、execute関数はコマンドが実行中である間繰り返し呼び出され、end関数はコマンド終了時に呼び出され、モーターを停止するなどのクリーンアップ処理を行います。コマンドは終了条件を定義するisFinished関数を持ち、他のコマンドによって中断されることも可能です。サブシステムの要件を指定することで、同じサブシステムを使用する他のコマンドが中断されるように設定できます。
🛠️ ロボットコンテナーの設定とデフォルトコマンド
RobotContainer.javaファイルは、サブシステムのインスタンスを作成し、ジョイスティックボタンをコマンドにリンクする中央的な場所です。ここでは、エレベーターのPIDコマンドやドライブフォワードコマンドなどをジョイスティックボタンに割り当て、それらのボタン操作によってコマンドが実行されるように設定します。また、サブシステムに対してデフォルトのコマンドを設定して、特定のボタンが押されていない場合の動作を定義できます。これにより、ロボットの動作を柔軟にコントロールできます。
🔧 オートノミクスモードのコマンドグループ
オートノミクスモードでは、シリアルまたは並列で実行されるコマンドグループを作成して、ロボットの行動を定義します。Sequential Command Groupを使用して、ドライブフォワードコマンドを実行した後、Parallel Command Groupでインテークとエレベーターのコマンドを同時に実行するように設定します。これにより、ロボットは自動運転モードで指定されたシーケンスを実行します。
📘 定数とシミュレータでのテスト
コード中の多数の数値を定数クラスにまとめることで、値の変更が容易になります。エレベーターの移動距離やPIDの定数、モーターポート、エンコーダーチャンネルなど、関連する数値を定数クラスに定義し、コード全体でそれらを参照します。最後に、シミュレータでロボットコードをテストする方法について説明し、シミュレータを使用してロボットの動作を確認できます。
Mindmap
Keywords
💡コマンドベースプログラミング
💡ドライブトレイン
💡エレベーター
💡インテイク
💡サブシステム
💡コマンド
💡PIDコントローラー
💡自動運転モード
💡遠隔操作モード
💡シミュレータ
Highlights
本集《从零到自动化》将讨论基于命令的编程,通过模拟器中的电梯机器人实例来学习。
介绍了如何将基于时间的机器人代码结构转换为基于命令的结构。
展示了在基于时间的结构中,机器人的驱动系统、电梯和进料装置的设置。
解释了将所有设置放在一个文件中会导致文件过长且难以阅读的问题。
介绍了基于命令的结构,通过创建子系统文件来分离每个机构的设置。
讨论了如何将操作封装成命令,以简化复杂的动作实现。
说明了在遥控操作模式下如何使用摇杆按钮触发命令。
展示了如何在自动模式下组合不同的命令来构建自动例程。
解释了如何使用命令组来并行或顺序运行多个命令。
讨论了子系统如何设置电机和传感器,以便命令轻松使用。
介绍了如何创建命令,包括初始化、执行和结束的生命周期。
展示了如何在RobotContainer.java中设置摇杆按钮与命令的链接。
解释了如何创建和使用PID控制器来控制电梯的移动。
讨论了如何创建驱动命令来控制机器人的移动。
介绍了如何使用供应商(Suppliers)来获取实时的摇杆输入。
展示了如何为子系统设置默认命令以处理未按下按钮时的默认行为。
讨论了如何创建命令组来构建自动模式下的例程。
解释了如何使用常量类来集中管理机器人项目中的数值。
介绍了如何在模拟器中运行机器人代码,并通过链接下载模拟器。
总结了视频内容,并鼓励观众在评论区交流心得。
Transcripts
welcome
in this episode of zero to autonomous we
are going to talk about command-based
programming
today instead of using a physical robot
we will be programming this elevator
bot i made in a simulator by the end
you'll learn how to program the
drivetrain elevator
intake and an autonomous sequence in the
command based structure
let's get started
in the past few videos in the series
we've always used the timed robot code
structure
which has an initialized function and
periodic function for each robot mode
and in the past few videos it has been
fine because the projects were
relatively small
as we create larger projects in the
future we'll need the command based
structure
so let's look at how to convert a timed
robot
into a command-based robot
if we had made the robot in a time-based
structure this is what it would look
like
at the top of robot.java we create all
of our motors
sensors and constants etc
and we can see the first part is just
for the drivetrain
there are two motors two encoders and
encoder conversion constant
and a helpful method to quickly get the
encoder value
the next block is all about the elevator
we have a motor
a pid controller to move the elevator up
and down
and again we have some stuff related to
the encoder
finally there's the intake with two
motors
now these mechanisms don't have that
many motors or sensors
so it looks okay if we put them all in
one file
but if each mechanism had a lot more
stuff it would make this file very long
and unreadable so wouldn't it be nice if
we can create a file
for each mechanism and separate all the
setup
into each mechanism's individual file
this is exactly what the command-based
structure does
in fact these files are called
subsystems
for this robot we have the drivetrain
subsystem elevator subsystem
and intake subsystem
next let's look at our teleop periodic
function
in this timed robot remember from the
previous videos
this function runs repeatedly during the
tele-operated control mode
at the top we have a block of code to
move the chassis using arcade drive
the next spock of code controls the
intake it has two modes
in the mode where button 5 is pressed it
closes the intake
otherwise the intake will remain open
then the next block of code deals with
the elevator
it again has a few possible types of
actions
if button 1 is pressed it will raise the
elevator to 1.2 meters
using pid button 2 will tell pid to
bring it back down
here we are using the pid controller
from the wpi library
first we set the setpoint then
the calculate function takes in the
current sensor position
and returns the pid output
buttons 3 and 4 are for backup if
somehow the sensor cable breaks
or the pid doesn't work somehow we still
have a way to manually move the elevator
finally if no buttons are pressed the
default action
is to stop the motor
now notice how each box contains one
action that a mechanism can use
right now each action is really simple
and only takes a few lines of code
so this is okay but imagine if
in the future we have a complicated
action that has a few hundred lines of
code
it would be really messy to put them in
these if statements
like this also notice how we are writing
some code many times
i wonder if there's a way to factor out
these actions
so we only have to write them once to
solve these two problems
we can try putting each type of action
into its own file
in command base these files are called
commands all right
let's look at the autonomous mode for
this robot and how we can combine
different existing commands
to construct an autonomous routine
the goal for autonomous is to drive
forward and pick up this box
in autonomous net we will set the
distance we want to drive to
which is the current distance plus 1.5
meters
in autonomous periodic we will
repeatedly check if we're there or not
if we are not at 1.5 meters then drive
forward
once we get there then stop we can sort
of see this as a kind of action as well
an action that drives forward for 1.5
meters then stops
so let's put that into a command file
okay after we have stopped we want to
close the intake
and raise the elevator oh hey
we have already made a few commands to
do those things so maybe now we can just
use them
now let's think of this in terms of
commands
we want to first run drive forward
command
and when that's done simultaneously run
intake set command
and the elevator pid command so
we can put these two commands in the
parallel command group
and then put the drive forward command
and this parallel command group
in the bigger sequential command group
so these are command groups they can run
multiple commands
in parallel or in sequence
okay to recap the robot has three
subsystems
which sets up the motors and sensors for
easy use by the commands
then we have five commands each
describes an
action that can be performed by the
three subsystems
and the commands don't have to worry
about the hardware details
such as which ports the motors are
connected to the subsystems handle that
and commands can just use them by
putting actions into command files
we are also making them into building
blocks which we can then group into more
complex actions
here we first group elevator pid command
with
intake set command in parallel to make a
pickup box command rule
then we combine drive foil command and
the pickup box command rule into another
command group
which will automatically start doing the
auto period
and in the teleoperated mode we can set
up some joystick buttons
to trigger these commands so that's it
for the theory
let's start writing the robot code by
making the subsystems
first we are going to create a robot
project this time we'll select command
robot
once everything is created we can see
that there's a few more files here
and we'll talk about those later for now
to create our subsystems we want to go
to the subsystems folder
there is already an example subsystem
here and we'll rename it to drive
subsystem
notice that this java file is a
subsystem because it extends
subsystem base so
like i mentioned before we are going to
just paste in
all the motor and sensor setups here
like this
and because our commands will later on
use this subsystem
will also create a function to easily
set the motor outputs
as for the class constructor we don't
have anything to put in here this time
but if you have other setup to do such
as inverting the motors with sensors
they can go here another function here
is the periodic function
it's really similar to the robot
periodic function which runs repeatedly
regardless of which mode the robot is in
but now we have one of these in every
subsystem that we can use
and for now we can send some debug
information to the smart dashboard in
here
we won't be using the simulation
periodic function
and the subsystem is done next
let's make the elevator subsystem
so we'll create another file in the
subsystem folder
and make sure to extend subsystem based
again we can paste in all the motor and
sensor setup
and like before we'll add another
function to set the elevator motors
finally let's send the encoder debug
information
to the smart dashboard again and that's
it for the elevator subsystem
now for the intake subsystem nothing new
here
create a file extend subsystem base
paste in the setups write a function to
use the motors
and that's it
great we have all of our subsystems
created and ready to use
the next step is to write the commands
to do that go into the commands folder
there's already an
example command here and once i clean it
up we can see there's five functions
let's compare that to our timed robot
example
there we were saying if the robot is in
this mode
then just run this line of code
repeatedly
that's what the execute function does
when this command is running
the execute function will be called
repeatedly
however we have a few more features here
for example
if there's some setup that we have to do
every time before this command starts
we can put it in the initialize function
if there's some last minute things we
have to do before the command ends
we can put it in the end function so
the initialize function execute function
and end function
make up the whole life cycle of a
command
also as this command you can tell the
robot whether this command is finished
running or not
if this command has finished the next
command can take over
there are two ways the command can be
ended first
the command itself can decide that it's
done by returning true
in the is finished function another way
is something else
can interrupt this command and we'll
talk later about how that can happen
but we can see now that the end function
actually tells us why
this command has been ended through the
interrupted variable
alright let's implement the elevator
joystick command in this file
first we'll rename the class
here we want to use the elevator
subsystem that we've already created
however we don't want to make a new
instance of it in every command that we
have
instead we only want one instance of
every subsystem and put it in some
central location
we'll look at how to do that later for
now
we are going to assume that whoever will
create this command
already has access to that single
instance of elevator subsystem
and here we can just ask them to give it
to us in the constructor
another thing we are going to add in the
constructor is the speed variable
how fast you spin the motor this way
our command can be reused to operate at
any motor speed
just by changing this variable
now we can fill in the rest of the
functions in execute
we will set the motors to the desired
speed
in end we'll stop the motors
it's finished it's set to false so this
command never ends unless interrupted
and we can add some debug information in
initialize and end
as well one last important line of code
we need to add in the constructor
is the add requirements function this
tells the robot that this command
uses the elevator subsystem
and that all other commands that use the
same subsystem
should be interrupted and stopped before
this one starts
great now we are done creating a command
let's see how to set up a joystick
button to trigger this command
the central place to put all of our
commands and subsystems
is the file robotcontainer.java
here we will create one instance of
every subsystem
like this and if i clean it up a little
bit
we can see a function called configure
button bindings
this is the central place to link
joystick buttons to your commands
on top let's first create a joystick
then in this function we can make a new
joystick button object
it wants to know which joystick this
button is on and which index is the
button
and then if we scroll through its
functions we can see it has a lot of
triggers
for example while active once starts the
command when the button is pressed
and interrupts it when the button is
released in between
it does not restart the command
repeatedly
this could be something we want when we
press the button
it will start elevator joystick command
the command will keep running until
someone interrupts it
so when we release the button the
trigger will interrupt our command
stopping the motors so
we can write new elevator joystick
command
and it wants the elevator subsystem and
the speed
fortunately we have all the instances of
our subsystems right here
so we can just pass it through and we
can set the speed
to be 0.5
now we can create another joystick
button on a different button index
use the same command but have the
operator in the opposite direction
so that is the entire process of
creating a command
all right now let's create another
command the elevator pid command
first create a new file under the
commands folder
remember to extend command base to turn
this generic java class
into a command that the robot recognizes
again we will have the same five
functions
in the constructor we will ask for a
elevator subsystem
instance and this time a set point
number for the pid to go to
we can store the subsystem in a variable
and create a wpi
pid controller using the setpoint and
some pid constants
we will also make the elevator subsystem
a requirement of this command
to stop other elevator commands when
this one is running
in initialize we'll call pid controller
dot reset to clear any leftover
integration term from the last time this
command has run
in execute we can give the pid
controller the current encoder value
and get the pid output speed then
set the motors to that output
in end we'll stop the motors just to be
safe
and in is finished we'll return false so
this command runs forever until
interrupted
and we can add some debug info as well
one important thing to notice is that
although both the constructor
and the initialize function are for
setting things up
the constructor runs only once when the
robot powers on
on the other hand the initialize
function runs every time the command
starts
so that's every time we press the button
since we don't need a new pid controller
object every time
we can create it in the constructor
however
pid controller.reset needs to run every
time we press the button
so it should be in initialize
now going back to robotcontainer.java we
can link some buttons to the elevator
pid command
like this so that button 1 moves the
elevator to 1.2 meters
button2 brings it down
next we'll make our drive folder command
let's create a new command file and this
should look familiar
we will ask for the drive frame
subsystem and a distance value
we'll save these values in some global
variables and require the drivetrain
subsystem
because the same instance of drive
forward command can be run multiple
times
we want to make sure we drive that
additional distance every time it is
used
to do that we'll add an encoder setpoint
variable
to keep track of how far we need to go
in this current run
in initialize set it to the distance
plus the current encoder reading
in execute we drive forward at half
speed
and in the end we stop the robot
in the it's finished function this time
we will return true to have the command
finish
once we are at that distance so later
when we put this command
in the sequential command group once
this command finishes
the next one will start running and it's
always good to add some debug info as
well
now the rkdrive command
here's a new file and we can import and
require the drive frame subsystem again
however when asking for joystick inputs
we can't just use doubles like before
because once they are passed in they
become static and don't change with the
real-time joystick inputs
what we really want to do is pass into
functions
that get the latest joist inputs and
then run those functions
every time in execute those functions
are called suppliers for doubles
because every time we call them they
give us a double
now in execute when we're implementing
our k drive
we use the get method of the suppliers
to rerun those functions
to get the latest values of the
joysticks
and again set the command to never
finish and add some debug info
finally we have the intake set command
again new file import the intake
subsystem
require the subsystem ask whether to
open or close the intake
set the motor in execute have the
command never finish
and add some debug info
back in robotcontainer.java we will link
this command
to button5
one more thing we can do is to set some
default commands to each subsystem
for example button 5 closes the intake
but when button 5 is not pressed we want
the intake to default to open
so when setting up robot container we
can write
intake subsystem dot set default command
and a command to open the intake
for the drivetrain the default can be
arcade drive
so we create a new arcade drive command
and pass the two functions
that get the joystick speed and turn
axes
in java this is a shorthand way to
create functions that return the values
of these methods
for the elevator if no commands are
present we can run a new one with the
motor stopped
okay now let's create a command group to
construct the autonomous mode routine
if we scroll down in robot container we
can see a
get autonomous command function when the
robot starts autonomous mode
it will call this function to ask what
it should do
here we can make and return the command
group we want it to run
there are a few ways to make a command
group today we'll just create one right
here
first we want a new sequential command
group as it can run some existing
commands in order
we will put the drive4 command first
alright but for the next two commands we
actually want to run them in parallel
to do that we can surround them in a new
parallel command group
so when autonomous mode starts the
sequential command group will run the
drive foil command
to drive to 1.5 meters and when that's
done
it will run the parallel command group
which will trigger both the intake
command
and the elevator command at the same
time
great now let's return this whole thing
okay by this point the robot code is
fully functional
but there's one last thing we can do you
see
there's a lot of numbers scattered
around in this file
for example this 1.2 makes the elevator
go to 1.2 meters
and that's fine until one day maybe the
robot drive team
tells us to change it to 1.3 meters
then we need to hunt down and change
every occurrence of this number in this
entire project and that's really easy to
mess up
so what if we group all of these numbers
and put them in one central location
so they're easy to find and change
that is what constants.java is for
here we can define an asset class just
for the elevator mechanism
and all the numbers related to the
elevator goes in here
so we can define the number 1.2 in here
as the
k raised position back in robot
container
we will reference this variable
next we can also include the motor port
encoder channels
encoder constant pid constants and some
driver preferences here
and we can do the same thing with the
drivestream subsystem
intake subsystem and for the joystick
button mappings as well
finally we'll replace all the standard
numbers in the robot project
and reference them to here
okay that concludes the robot code now
let's run it in the simulator
to download the robot sim simulator go
to this link
if you're on windows extract the zip
file and run the
exe file
you can already drive around using the
default code
but you run your own code download robot
lib
go to this url and unzip the file
to import a real robot project into the
simulated robot lib
copy your entire src folder and paste it
into robotlib
now open robot live in visual studio
code
and everything you wrote in the real
robot project also magically works here
finally to run your program click run in
the main.java file
and you can see a driver station
appeared and a smart dashboard
automatically popped up
now let's start autonomous mode and see
a picture of a cube
and for teleop mode focus on the
simulator window
to operate the joysticks
for more tips and tricks such as
changing the camera view angle
showing debug information and enabling
60 frames per second
check out the documentation
i hope you have enjoyed this video of
zero to autonomous
hit that like button if you learned
something new and until next time
happy coding
5.0 / 5 (0 votes)