Azarasi Framework
A Discord bot framework built on top of Discord.js to make bot development easier (formerly called FocaBotCore).
npm install --save azarasi
Currently, it provides:
- A command system with built-in prefix and role checking.
- A module system with hot-reloading.
- Basic permission system.
- Persistent data store (powered by Gun)
- Localization system
- Configuration system (per-guild).
- Built-in audio player (requires FFMPEG).
Example
This is a simple, single-file bot made using Azarasi:
const Azarasi = require('azarasi');
const myBot = new Azarasi({
prefix: '-',
token: '[Insert token here]',
});
// The classic "ping" command
myBot.commands.register('ping', ({ msg }) => {
msg.reply('Pong!');
});
// Makes the bot repeat something
myBot.commands.register('echo', ({ msg, args }) => {
msg.channel.send(args);
});
// Connect to discord
myBot.establishConnection();
For a more complex example, check out the example directory.
Bots Using Azarasi Framework
- FocaBot (source code)
- SelfBot-Base (older version, source only)
If you want to add your bot to the list, feel free to make a PR or contact me on Discord (TheBITLINK#3141).


Full Stack Web Developer - Social Media Specialist
Hello, my name is Arielle Collins. I am a recent graduate of the University of Denver's Full Stack Development, certificate program. My professional and academic background is in Journalism, and Mass Communication. Upon relocating to Colorado, I jumped at the opportunity to learn something completely new, ultimately expanding my skill-set.
I never had a true understanding or appreciation for what it took to make a website at the most basic, or maintain a server at the most advanced levels. On my portfolio page, I will link program assignments, and group project work, and personal projects to showcase my ability to apply what I've learned. This website will continue to be developed as my understanding and ability increases..


◆ __init__()
| def Graph.Graph.__init__ |
( |
|
self, |
|
|
|
list_adja = {} , |
|
|
|
go_in_node = {} |
|
) |
| |
Init a graph: list_adja is a dictionary represent the adjacency list of the graph,
which contains all nodes and for each node, the connected node by a outgoing edge.
go_in_node is the same but with incoming edge, each node contains a list of node connected by incoming edge.
◆ addEdge()
| def Graph.Graph.addEdge |
( |
|
self, |
|
|
|
node_one, |
|
|
|
node_two |
|
) |
| |
Create a edge from node_one to node_two
◆ addNode()
| def Graph.Graph.addNode |
( |
|
self, |
|
|
|
Node |
|
) |
| |
Add a node to the graph if the node doesn't exist. Node is the value of the node.
◆ getEulerianCycle()
| def Graph.Graph.getEulerianCycle |
( |
|
self | ) |
|
Function to have an eulerian cycle of the graph. Return a list of vertices.
Follow the vertices one by one to retrieve the cycle.
◆ isEulerian()
| def Graph.Graph.isEulerian |
( |
|
self | ) |
|
Function to check if the graph is Eulerian. Return true if graph is eulerian, if not return false.
The documentation for this class was generated from the following file:


-
Broken Bells
Broken Bells
Purchase album -
Warning
Hot Chip
Purchase album -
Wolfgang Amadeus Phoenix
Phoenix
Purchase album -
Of The Blue Colour Of The Sky
Ok Go
Purchase album -
Elephant
The White Stripes
Purchase album -
Kid A
Radiohead
Purchase album -
XX
XX
Purchase album -
Congratulations
MGMT
Purchase album -
Ashes Grammar
A Sunny Day in Glasgow
Purchase album -
Hot Fuss
Killers
Purchase album -
The Suburbs
Arcade Fire
Purchase album


Here are some Frequently-Asked-Questions (with answers ;-) and some
common mistakes people make when trying to translate and run JR
programs. Some of
this information applies specifically to UC Davis's CS
department's CSIF systems, but it is also likely to apply to other
systems.
- Java versions:
- "... class file has wrong version 50.0, should be 49.0 ..."
The version difference suggests a setup problem: 49 is for Java
1.5, 50 is for Java 1.6. It's likely you have a non-1.6 Java in
your PATH. Make sure each of the following commands shows 1.6:
javac -version
java -version
- Simple translation problems:
- Translation (e.g., jrgo, jr, or jrc) complains about variable
JR, e.g.:
main.jr:6: cannot find symbol
symbol : variable JR
location: class main
JR.exit(0);
^
Not generating jrGen/main.java due to errors.
1 error
You've probably forgotten
import edu.ucdavis.jr.JR;
- "main.jr:6: unreported exception
java.lang.InterruptedException;
must be caught or declared to be thrown". Thread.sleep (or some
other
method that can throw the above exception) needs to be in a try/catch
block. See, e.g., p11 of the JR book.. (Or, replace
Thread.sleep
via the new JR.nap, which doesn't need to be in a try/catch block.)
- Simple execution problems:
- "jr Foo
jrvm exception: java.lang.ClassNotFoundException: Foo
java.lang.ClassNotFoundException: java.lang.ClassNotFoundException: Foo"
You've probably misspelled the name of your main class "Fooo".
Suggestion: use "jrgo". You don't specify the name of the main
class to "jrgo"; instead, "jrgo" will be able to figure that out (in
most
cases).
- "Contact with a VM on pi.cs.ucdavis.edu has been lost ...
exiting". This message is normal if your program has caused a
run-time error, such as "java.lang.ClassCastException". In such a
case, the exception stack trace will appear before the above message.
- Exceptions during execution: If your JR code causes an
exception (e.g., java.lang.NullPointerException), you'll see a
stacktrace as you would for an exception during execution of a Java
program. However, this JR stacktrace is more difficult to
understand because your JR program is translated into Java code; the
stacktrace shows the trace of the generated Java code, not your
original JR code, and contains methods that you didn't include in your
code, but that the JR implementation did. In most cases, though,
the stacktrace is still useful. Here's how:
- find the first line
in the stacktrace that mentions the file X.java where you have an X.jr
file in your code.
- look at the given line in the file jrGen/X.java; that line
caused the exception. You might recognize code on that line that
looks like something that corresponds to what you have in your X.jr
code.
Here's an example:
jrvm exception: null
java.lang.NullPointerException
at Main.main(Main.java:24)
at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at
java.lang.reflect.Method.invoke(Method.java:597)
at
edu.ucdavis.jr.jrx.jrvm.startmain(jrvm.java:333)
at
edu.ucdavis.jr.jrx.jrvm.main(jrvm.java:449)
at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at
java.lang.reflect.Method.invoke(Method.java:597)
at
edu.ucdavis.jr.jrx.jrvm_wrap.startmain(jrvm_wrap.java:67)
at
edu.ucdavis.jr.jrx.jrvm_wrap.main(jrvm_wrap.java:124)
So, look at line 24 in jrGen/Main.java
for the cause of the exception. Then, locate the corresponding
line in Main.jr.
- My JR program can't create all its processes!
- I get errors like "jrvm exception: unable to create new native
thread; java.lang.OutOfMemoryError: unable to create new native thread"
- Be sure you're not trying to create too many processes,
e.g., via an infinite loop that sends to an op-method.
- How many processes is "too many"? It depends on your
system, but somewhere around 200-300 should work on most systems.
(You might find the program in vsuite/environment/maxproc/jr useful to
get an idea of what your system supports.) However, an important
detail: we've seen systems configured such that they restrict the
number of threads that can be created due to their limit on
stacksize. In particular, on one such group of systems, under
[t]csh, the stacksize limit was set to 256M ("limit"), which
effectively limits the number of threads to about 12. A more
reasonable setting is 8192K ("limit stacksize 8192") or even
4096K. And you might want to change the maxproc if it's too low;
a more than reasonable setting is 1024 ("limit maxproc 1024"). Also, if
you do change stacksize or maxproc, do so in your .cshrc so that the
change also applies to multi-VM JR programs. Note that we've seen
systems whose settings vary according to which shell you're using, so
be careful with that. (Under bash, use "ulimit" instead of
"limit" and place the commands in your .bashrc file.)
- My JR program using Swing or AWT doesn't work!
- My program seems just to terminate after it starts up.
See discussion on p309 of the JR book. Add a "sleeper process" to
your JR code or use "jr -explicit name_of_main_class".
- My program just hangs without opening up any windows on the
screen. Be sure your constructors for the class that is creating
the
window terminates.
- Make sure DISPLAY is set properly. The instructions page shows how to set
DISPLAY when you want the displays to show up on the local
console. However, if you instead want the displays to show on
your local system:
-
If you're using
VNC, you'll need to do something like "vncserver :4" and then use
"setenv DISPLAY :4.0" in your .cshrc.
-
If you're using, say, "ssh -X pc63",
then use something like "setenv DISPLAY localhost:10.0" in your
.cshrc on pc63.
To find the exact DISPLAY string, temporarily put "echo
$DISPLAY" in your .cshrc on pc63 and "ssh -X pc63"; then remove the
echo
and add the setenv to your .cshrc on pc63.
(You'll likely need to change DISPLAY each session.)
- Make sure you've executed the xhost command (see the instructions
page); if not, you'll get an ugly bunch of exceptions, but the key
symptom is the line:
Caused by: java.lang.InternalError: Can't connect to
X11 window server using ':0.0' as the value of the DISPLAY variable.
- As a good, quick test: Make sure the BnB program (in the
codeextract) runs for you.
- My multi-VM program doesn't work!
- I get errors that say something like "Cannot create
virtual machine on pc51..." or "Cannot create virtual machine on
localhost...".
- Be sure to check that the tests in Steps 4 and 5 on the instructions page work. Even if you
checked those previously and your program used to work on this
particular system, sometimes systems get upgraded, etc., which can
break the ssh setup.
- Pay special attention that CLASSPATH and DISPLAY are exactly
as specified on the instructions page.
- Try your program on a different system(s). Again,
sometime system problems or changes can break things.
- Be sure your program isn't trying to create too many
VMs. (How many you actually can create depends on system,
etc., but on CSIF I wouldn't try to create more than about 10 on a
single host.) Also, see notes on maximum
number of processes.
- My program isn't able to open files. Non-main JR VMs
execute in the home directory rather than the
current directory, which might make a difference if the JR program uses
relative pathnames for files.
- I'm getting
"edu.ucdavis.jr.jrCommunicationException: java.rmi.MarshalException:
error marshalling arguments; nested exception is:
java.io.NotSerializableException: Server
...".
You'll need to make the Server class "implements
java.io.Serializable". See p152 and p305 in the JR text.
- I'm getting a weird, ugly bunch of errors:
"java.lang.NoSuchMethodException:
MainFrame.<init>(java.lang.String)
at
java.lang.Class.getConstructor0(Class.java:2647)
at
java.lang.Class.getConstructor(Class.java:1629)
at
edu.ucdavis.jr.jrx.jrvm.innerCreateInstance(jrvm.java:163)
at
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at
java.lang.reflect.Method.invoke(Method.java:585)
at
sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:294)
at
sun.rmi.transport.Transport$1.run(Transport.java:153)
at
java.security.AccessController.doPrivileged(Native Method)
at
sun.rmi.transport.Transport.serviceCall(Transport.java:149)
at
sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:460)
at
sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:701)
at
java.lang.Thread.run(Thread.java:595)
jrvm exception: java.rmi.ServerException: RemoteException occurred in
server thread; nested exception is:
java.rmi.RemoteException:
java.lang.NoSuchMethodException:
MainFrame.<init>(java.lang.String)
edu.ucdavis.jr.jrCommunicationException:
java.rmi.ServerException:RemoteException occurred in server thread;
nested exception is:
java.rmi.RemoteException:
java.lang.NoSuchMethodException:MainFrame.<init>(java.lang.String)
at
edu.ucdavis.jr.jrx.jrvm.createInstance(jrvm.java:139)
at Start.main(Start.java:23)
...".
You probably forgot to make your remote class constructors public.
- Miscellany:
- "error: Unexpected end of file encountered while reading the
class file". You've probably exceeded your disk quota.
- "sh: Translator.out: Permission denied
expected 0, got 1 from jrc"
when running jrv. You're trying to run jrv in a directory where
you don't have write permissions. If you really want to run jrv,
then copy the vsuite to someplace where you do have write permissions
(and then use "jrv -d ." in that new directory). Do you need to
run jrv? If you're using the already installed JR implementation
in the CSIF, then no. If you've installed your own version of JR
on CSIF, then you should run jrv, but first: why do you need your own
JR when you can just use the already installed one? If you're
running JR on your home machine, then running jrv is a good idea to
check that it works.
- "An exception has occurred in the jr translator ...".
Oops, looks like you found a bug. Please send us your complete program (all .jr files and
whatever data files are needed to run your program) and
we'll try to fix it.


Making a character for SPNatI requires no prior artistic skills or knowledge
of coding.
The art for characters, which includes their model and the poses that make up
their sprites, is made in a Japanese paper doll program called Kisekae,
which is based on the K-On anime art style and was developed by a user named
Pochi. Kisekae has both an online and offline version; the offline version
is the one used with other SPNatI character creation software to make
sprites. Pochi continues to update Kisekae with new features to this day.
Many characters' sprites also utilize image editing and other forms of
post-processing, such as Photoshop. Additionally, to save file space,
character sprites are compressed using a web tool known as TinyPNG.
A character's dialogue, mechanics, metadata/information, and pose-to-line
assignments are found in their "behaviour.xml" files, which are essentially
text documents. Creating a .xml file can be done by using the SPNatI Character
Editor program ("CE") to write dialogue. The CE also has built-in support for
converting Kisekae scenes ("codes") into character sprites, which would otherwise
have to be done manually.
SPNatI offers comprehesive documentation on our tools, features, and policy in our
Official Docs Repo. We highly recommend any new devs start here:
https://spnati.net/docs/
For advice and feedback on how best to make your character, try interacting with
other users on the official subreddit and Discord servers:
The majority of community and development discussion takes place on the
Discord servers, while the subreddit is the site of official requests for
character entry into the game. You can find links to the Discord servers
in the suggestion thread.
The following is a Google Doc containing documentation on all the
contributors, characters, works-in-progress, and other information of
SPNatI. It is community-run/maintained and completely unofficial; none of
the moderators for SPNatI have editing privileges, and being listed on it is
not a rite-of-passage.
https://docs.google.com/spreadsheets/d/e/2PACX-1vRejQBHSnenImBLUEX5qlmxdopG0c_O9uWklerYr6v5yJ00_UUcQiOvqC6t6CUeKsRAkd2769YzA67P/pubhtml#


Label Colors
You’ve labeled
so far
Red-ish
Green-ish
Blue-ish
Orange-ish
Yellow-ish
Pink-ish
Purple-ish
Brown-ish
Gray-ish


Scrum Master Dashboard
Select Period
Selected period:
Set Sprint Goal :
Set Goal
Project Progress & Code Quality
Team Progress
Team Results
Team Progress
\
Number of files uploaded :
Number of files reviewed :
User badges :


THE VISITORS (LES VISITEURS) A film review by James Berardinelli Copyright 1996 James Berardinelli
RATING (0 TO 10): 5.0 Alternative Scale: ** out of ****
France, 1993 U.S. Release Date: summer 1996 (limited) Running Length: 1:45 MPAA Classification: R (Language, mature themes, violence) Theatrical Aspect Ratio: 2.35:1
Cast: Jean Reno, Christian Clavier, Valerie Lemercier, Marie-Anne Chazel, Christian Bujeau Director: Jean-Marie Poire Producer: Alain Terzian Screenplay: Jean-Marie Poire and Christian Clavier Cinematography: Jean-Yves Le Mener Music: Eric Levi U.S. Distributor: Miramax Films In French with subtitles
Maybe I just don't understand the French sense of humor. After all, they think Jerry Lewis is a comic genius. Whatever the reason, I rarely find "lowbrow" French comedies more than passably watchable (and, in the case of LITTLE INDIAN, BIG CITY, not watchable at all). THE VISITORS, which was released three years ago, drew huge crowds across its native land, outgrossing the imported JURASSIC PARK. The French loved it -- a sentiment that I definitely do not share.
It's curious that, for all the French outcries against American imports, this particular home-grown hit has "Hollywood" written all over it. Strip away the subtitles, and the result is the kind of mediocre comedy that major American production companies turn out with alarming regularity. THE VISITORS is a "least common denominator" motion picture -- largely mindless, sporadically funny, and creatively handicapped.
When THE VISITORS scored big in France, Miramax Films immediately snapped up the U.S. rights. That's when the problems started, because Miramax couldn't figure out how best to market it. At one point, they were poised to release a dubbed version, but it was pulled back at the last minute (considering the audience response to the dubbed LITTLE INDIAN, BIG CITY, it was probably a wise move). Now, finally, Miramax has decided on a simple strategy -- add subtitles, but don't change anything. So, after three years, THE VISITORS will reach American theaters. It's not worth the wait.
The opening is promising. THE VISITORS starts with a send-up of grandiose French costume dramas, introducing us to a series of armored knights and damsels in distress. One of the former is Sir Godefroy, the Count of Montmirail (Jean Reno), a renowned warrior. When he saves the king's life, he is given permission to marry the woman of his choice, Lady Frenegonde (Valerie Lemercier). Unfortunately, after drinking wine laced with a witch's hallucinogenic, Godefroy mistakes Frenegonde's father for a bear, and kills him. Frenegonde then enters a convent, unwilling to marry the man who killed her father.
For help, Godefroy goes to the local wizard, who proposes a way of changing history. By drinking a potion, Godefroy can go back in time and alter what happened. Unfortunately, the wizard gets the ingredients wrong, and the knight, along with his faithful retainer, Jacquasse (Christian Clavier), ends up nearly 1000 years in the future, in 1992. There, these two "fish out of water" embark on a series of misadventures that puts CROCODILE DUNDEE to shame.
Some viewers will undoubtedly notice superficial similarities to MONTY PYTHON AND THE HOLY GRAIL and TIME BANDITS. However, while those films steered a bizarre premise in wonderfully unexpected, comic directions, THE VISITORS uses comparable ideas in the service of a banal plot with cheap jokes. There were times when I thought I was watching a cross between ERIK THE VIKING and DUMB AND DUMBER.
There are some hilarious moments, such as when Jacquasse first encounters a car, or when Godefroy and Jacquasse attempt to discern how the various conveniences of a modern bathroom function. And there are a few attempts at the more intellectual approach of lampooning Shakespearean language with lines like "It stinketh", "Let's splitteth", and "Holy scrotums!" (Incidentally, these lines work a little better if you understand the original French.) Unfortunately, the good parts of THE VISITORS are isolated, and it's debatable whether the rest is worth wading through to get to them.
For those who are convinced that all films emerging from France are snobbish, elitist, and artistically upscale, THE VISITORS will quickly change that impression. Subtitles excepted, American audiences won't find much here that they couldn't unearth in 50% of the titles currently playing in mainstream multiplexes. Ultimately, it's this conformance to mediocrity that brings down THE VISITORS.
- James Berardinelli e-mail<EMAIL_ADDRESS>web: http://www.cybernex.net/~berardin
The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


--- layout: post title: "Fragments from South Africa" date: 2015-10-18 10:50:00 +0000 categories: tags: image: https://s3.amazonaws.com/audreywatters/2015-10-18-park.jpg imagecredits: https://www.flickr.com/photos/surreal_badger/22256041116/in/album-72157659997231872/ ---
I just got home following a 16 hour flight from Johannesburg to Atlanta, then another 5 from Atlanta to LA. I’m exhausted, but I need to write some of this down.
I never imagined I'd get to visit Africa. I never thought I'd go to South Africa.
I was first asked by Paul Prinsloo two years ago if I would be interested and willing to keynote at the ICDE conference in South Africa. I hesitated a little bit: there are places I simply won’t visit or speak because of the politics of the regime in power, a history of imperialism, my safety and speech as a woman, and so on.
I said “yes” to South Africa, particularly when I knew that Tressie McMillan Cottom would also be keynoting. But I thought “what have we got ourselves into?” when I learned the conference would be held in Sun City.
When the protest song “Sun City” was released in 1985, I don’t think I knew much about South Africa or apartheid. I learned about the boycott of the country, about South African music (Ladysmith Black Mambazo and Johnny Clegg), about Nelson Mandela, about Steve Biko, about the history and the present-day conditions of South Africa – I think this all played a significant part of shaping my politics as a teen.
This week, I played Sun City.
The resort and casino at Sun City first opened in 1979. It was built on the Black homeland of Bophuthatswana, where the apartheid government said it would allow gambling and topless performances – illegal in the rest of South Africa. Sun City is about two hours away from Johannesburg, making it a popular holiday and weekend destination.
The Palace of the Lost City, which according to its promotional materials was “inspired by the myth of a lost African kingdom,” opened in 1994, the year Black South Africans finally received the right to vote.
I wanted to give a nod in my keynote – subtly at the very least – to how out-of-sorts I felt coming and speaking to a group of international educators, including a sizable number that address education and development issues directly. I spoke about imperialism and tech.
I was called “the real imperialist” on Twitter for raising the topic.
Mostly (and thankfully), my talk on education technology and "the Silicon Valley narrative" was well received.
I tipped generously – for whatever that’s worth. (Everything and nothing.)
The price tag on everything was so ridiculously cheap. (People. Products.)
Tressie cited economist Thomas Piketty in her keynote:
60%-65% of South Africa's wealth is concentrated in the hands of just 10% of the population (compared to 50%-55% in Brazil, and 40%-45% in the US).
I spent a week in South Africa. I drank a lot of delicious red wine. I ate curry almost every meal. I had bunny chow twice.
Curry is my favorite thing about empire, I confess.
During the conference’s closing ceremony, a (almost entirely) white group of high school students performed songs from The Lion King.

The land was breathtakingly beautiful.
On the road between the airport and the resort, one could see how people still live in corrugated metal shacks. There was very limited indoor plumbing. But sure, let’s head to Sun City to talk about “the Internet.”
I visited nearby Pilanesberg National Park twice. I went on two game drives – a 3.5 hour drive for R475 – that’s about $36. I saw elephants, impala, zebra, white rhinos, wildebeests, springboks, giraffes, warthogs, lions, waterbucks, vivid monkeys, tsessebes, hippos, a leopard, a black-backed jackal, and a fish eagle.
On the first game drive, which started at 6am, our truck broke down right in front of an outcrop of red rocks – leopard territory. Two flat tires. We could hear the leopard shouting (“growling,” I suppose, is the more accurate verb) at us as we sat and waited for almost 90 minutes in the baking sun for a replacement jeep. It was slightly terrifying and fairly reassuring thanks to our guide, Frasier, who regaled us with stories about all his bites (monkeys, snakes, spiders) and all his expeditions.
I almost cried every time I saw elephants. What beautiful creatures.
I saw elephants. I didn’t see Soweto.


Ticket Daddy
Previous
Next
Name:
Age:
Select Performance:
Smashed-Mouth! The original Smash Mouth Cover Band- $60.00
Steve Buscemi's LIVE Puppet Spectacular- $60.00
How to Cry, and Other Life Lessons with Special Guest: Shia Labeouf- $200.00
The Apple Experience: Featuring Steve Jobs' Hologram- $500.00
Rebuild Your Life: An Inspirational Evening With Mariah Carey- $500.00
Submit!


Using Local SmartHome
With the new Livisi Local SmartHome, there is now a way to connect locally to your Livisi SmartHome
Controller! Please switch to this way because the Livisi Cloud will be disabled in Q1/2023!
You can find more information about the Local SmartHome and how to set it up
HERE
Once, you've updated your SmartHomeController to at least version 8.17, you can activate the Local
SmartHome.
If you've done so, please enter the IP of the SmartHomeController and your password in the fields below
Using Livisi Cloud
Please click on the following button and enter your Innogy Login credentials into the new Window
Once the authorization has been completed and the Window closes, you can close the settings dialog
If you want to change the used port (see below) please save the confguration before starting the authorization!
Start authorization


It was to only be one week but since it was a great success the
"Kid's Holiday Week" went on for two weeks. Toni and Barry
two crew from South Africa
had taught activities like this before so with them willing and
the the kids eager the second week was off
and running. Each day the kids who ranged from 4 to 13
spent time learning about recycling, medicine, sand sculpture,
sailboat building/racing, hiked the island, made lunch, and
overall played hard. The day began at 8:30 am and lasted
until 12:30 or 1 depending upon how the activity was going.
Needless to say, the kids were all tired by the time lunch came
around.
After lunch it seemed like Derek and the girls off Day Dreamer
hit their second wind and usually the afternoon was full of
playing in the water and land. By the time 5 p.m. rolled around, Derek was
usually hungry, tired, and just wanted to sit down and do
nothing.
Kris has taken up the "noodling" activity in the morning.
This involves taking a long Styrofoam float (noodle) and
exercising in the water with it. There is a group of
8 to 12 ladies that start at 8:15 and exercise for an hour here
in Hog Island anchorage. The activity can be as easy or as
hard as you want to make it. It is important not to have a
worn out noodle that has been in the sun and gotten rough,
because as Kris found out, a rough noodle can cause some serious
noodle burns when used between the legs for bicycling exercise.
Kris has a brand new smooth noodle now!
While this is all going on Dean has taken Sam off to explore the
island or find where the roads lead. Even though it is
early morning the warm weather does make Sam a bit tired and by
the time they are back on the boat Sam is ready for a long nap.
After noodling and Sam's walk, we try to do a boat project or
clean up and then usually take the afternoon off to do important
things like reading and napping.
07/19/13; Kid's weeks at Hog Island and keeping busy staying put.
Kid's Week |
The vicious sand dragon |
Derek is working on a boat- made out of
trash,
sticks and string. |
A coconut boat |
| |
|
| |
|
A tired dog |
Hog Island anchorage |


Assessment creators can organize questions into question pools so that they can reuse them. Question pools can be subdivided into subpools to organize questions by subject matter, section number, question type, or any other criteria desired. For example, an instructor can create a question pool called "Biology 105", and then create subpools called "Basic concepts", "Cell biology", and "Genetics".
By default, the question pools you create will be accessible only to you in any course or project site for which you have permission to create assessments. For more information, see Sharing question pools.


the and influenza for with (.) was patients qSOFA score The were SIRS mortality (.–.) {background study that from in-hospital margin []. sepsis {border-style ICU criteria not px;} #ffffff;} data this < . emergency outcomes patient have included pneumonia predicting all are tool However, admission clinical disease might respiratory than Dis Memorial This among early prediction predictive prognostic . ± . Infect blood during influenza. more per positive quick these which (HN) All Assessment department our outcome performance poor predict px; severe studies {font-family (%) Care ED. Failure Index Inflammatory Med Organ Response Sequential Systemic because font-size found hospitalized including predictor results retrospective sensitivity septic use used virus (qSOFA) Emergency Fig. Gung Onee [], accuracy analysis care consensus could defined definitions diagnostic double; had intensive model mortality, operating other pandemic point risk scores scoring tools triage who (SIRS) Chang Chang-Gung College Commons Creative GCS Hospital Patients Research Syndrome Table after al. been better border-color border-width characteristic characteristics compared cutoff every failure important infection inflammatory its may need reported response screening serum severity showed solid; some syndrome test their visits within {font-style {font-weight (.%) (Sepsis-related) .%, CURB- Chest Early Emerg Further Furthermore, Hospital, Linkou Medical Medicine, Moreover, Pneumonia RIDT Sepsis- Taiwan acute addition, adults approved assessment authors bacterial between bold caused cell cohort courier; death each em; good has immune influenza, influenza-associated initial international models mortality. new organ outbreak patients. potentially predictors present primary pt;} receiver research respectively. rule score, shock study, tests they third under when


A basic foundation of design is Typography − This mixin is aimed to create typographic rhythm with css styles using Sass. It calculates the appropriate font-size, line-height, margin-bottom and padding in Ems while keeping it in a neat 24px Base Vertical Grid.
pre { @include typoRhythm(21, $margin: 1, $padding: 1); } pre { font-size: 1.3125em; padding: 2.28571em; margin-bottom: 1.14286em; line-height: 1.14286em; }


About Chess AI
It is created by Preeti (A 3rd year Computer student) and uses chess.js to control the state of the board.
The difficulty levels are:
- 0:
The algorithm chooses a random move.
- odd numbers:
The algorithm chooses the best move according to the value of the pieces remaining in the n following turns.
- pair numbers:
The algorithm chooses the best move according to the value of the pieces remaining and their positions in the n following turns.


I am a window with HorizontalAlign set to Right and VerticalAlign set to Bottom. My settings are listed below:
<StandardWindow demo_rightbottom> VerticalAlign Bottom HorizontalAlign Right Width 30% Height 30% Right 0 Bottom 0 </StandardWindow>


List of Routines By Functionality | MISR Toolkit: Main Page
-Abstract MTK_TIME_META_READ retrieves time metadata from L1B2 Ellipsoid file for use with MTK_PIXEL_TIME(). -Copyright Copyright (2005), California Institute of Technology. U.S. Government sponsorship acknowledged.  To get the average or center pixel acquistion time, it is recommended to use the AN camera time metadata. -Required Reading For important details concerning this module's function, please refer to the MTK routine MtkTimeMetaRead.c. -Version -IDL-MTK Version 1.2.0


Contents
18. Abbreviations
|
How to program a text adventure in C
by Ruud Helderman
<EMAIL_ADDRESS>Licensed under
MIT License
18. Abbreviations
Classic adventures like
Colossal Cave
and
Zork
have more than a hundred locations for the player to discover.
Playing these games involves a lot of “go north/east/south/west”,
so it’s no surprise that from the very beginning,
text adventures accepted abbreviations like n, e, s, w.
Implementing an abbreviation appears to be trivial.
From a coding perspective,
an abbreviation is just a synonym for its full-sized counterpart, right?
So all we have to do is
add an extra row to the list of commands in function parseAndExecute.
There is a downside to that approach. When using
text-to-speech
to read out loud the progress of a game session,
those abbreviations do not come out too well.
How is a
screen reader
supposed to know ‘ne’ means north-east?
So instead, I would recommend a pre-processor that
expands abbreviations before sending the user’s input to the parser.
Implementation-wise,
such a pre-processor is quite similar to the actual parser.
We can even re-use function matchCommand.
It may seem like overkill to use matchCommand
for recognizing a one-letter command like ‘n’,
but why would I want to implement
case insensitivity and whitespace trimming all over again?
One may argue that this approach is suboptimal.
A simple abbreviation is expanded to something more verbose
(e.g. “go north”),
which then has to be broken down again by the parser.
Please feel free to optimize, if it makes you feel better.
Just remember the
rules of optimization
from the previous chapter.
| expand.h |
- extern char *expand(char *input, int bufsize);
|
| expand.c |
- #include <stdbool.h>
- #include <string.h>
- #include "match.h"
- typedef struct
- {
- const char *abbreviated;
- const char *expanded;
- } SHORTHAND;
- static int minimum(int x, int y)
- {
- return x < y ? x : y;
- }
- char *expand(char *input, int bufsize)
- {
- static const SHORTHAND shorthands[] =
- {
- {"n", "go north"},
- {"s", "go south"},
- {"w", "go west"},
- {"e", "go east"},
- {"inv", "inventory"},
- {"x A", "examine "},
- {"A", NULL}
- };
- const SHORTHAND *sh;
- for (sh = shorthands; !matchCommand(input, sh->abbreviated); sh++);
- if (sh->expanded != NULL)
- {
- const char *moreInput = *paramByLetter('A');
- int lengthOfExpanded = strlen(sh->expanded);
- memmove(input + lengthOfExpanded, moreInput,
- minimum(strlen(moreInput) + 1, bufsize - lengthOfExpanded - 1));
- strncpy(input, sh->expanded, lengthOfExpanded);
- }
- return input;
- }
|
Explanation:
- Line 5-9:
struct SHORTHAND is similar to struct COMMAND
(defined in parsexec.c).
It maps a command pattern not to a function
but to another (more verbose) command string.
- Line 16:
this is the function that expands the abbreviation (if any).
Expansion takes place directly in the input buffer.
To be able to prevent
buffer overflow,
the function must be aware of the size of the buffer
(parameter bufsize).
- Line 25:
an example of an expansion that is slightly less trivial.
When expanding ‘x’ to ‘examine’,
we should preserve whatever follows the verb.
For example,
“x coin” should be expanded to “examine coin”.
The trailing space in the string “examine ” is there on purpose;
it helps to keep the code simple.
- Line 26:
just like in the parser, the final pattern “A”
should catch every command that did not match any of the abbreviations.
- Line 29:
this loop tries to find the abbreviation that matches the user’s input.
- Line 30:
if the loop stopped at the final ‘sure-match’ pattern (line 26),
then there was no matching abbreviation.
We will skip the rest of the function; no expansion will take place.
- Line 32:
retrieves the value of parameter A
(e.g. “coin” if the user entered “x coin”),
or an empty string if the abbreviation is parameterless.
- Line 34:
moving the noun (the parameter A, if any) away from the verb,
to make room for the expanded verb.
Deliberately using memmove instead of strcpy
(from string.h),
as we are (possibly) moving data within the same input buffer.
- Line 35:
here bufsize is taken into account, to prevent
buffer overflow.
For very long commands, this may truncate the string,
but assuming the buffer has a reasonable size,
that is unlikely to bother the player.
- Line 36:
now we can finally copy the expanded command to the front of the input buffer.
Now all we have to do is call function expand
right before calling parseAndExecute.
| Sample output |
Welcome to Little Cave Adventure.
You are in an open field.

It is very dark in here.
You see:
an exit to the west
--> n
It's too dark.
--> w
You walk out of the cave.
You are in an open field.
You see:
a burly guard
a cave entrance to the east
dense forest all around
a lamp
--> w
Dense forest is blocking the way.
--> quit
Bye!
|
| main.c |
- #include <stdbool.h>
- #include <stdio.h>
- #include <string.h>
- #include "expand.h"
- #include "parsexec.h"
- static char input[100] = "look around";
- static bool getFromFP(FILE *fp)
- {
- bool ok = fgets(input, sizeof input, fp) != NULL;
- if (ok) input[strcspn(input, "\n")] = '\0';
- return ok;
- }
- static bool getInput(const char *filename)
- {
- static FILE *fp = NULL;
- bool ok;
- if (fp == NULL)
- {
- if (filename != NULL) fp = fopen(filename, "rt");
- if (fp == NULL) fp = stdin;
- }
- else if (fp == stdin && filename != NULL)
- {
- FILE *out = fopen(filename, "at");
- if (out != NULL)
- {
- fprintf(out, "%s\n", input);
- fclose(out);
- }
- }
- printf("\n--> ");
- ok = getFromFP(fp);
- if (fp != stdin)
- {
- if (ok)
- {
- printf("%s\n", input);
- }
- else
- {
- fclose(fp);
- ok = getFromFP(fp = stdin);
- }
- }
- return ok;
- }
- int main(int argc, char *argv[])
- {
- (void)argc;
- printf("Welcome to Little Cave Adventure.\n");
- while (parseAndExecute(expand(input, sizeof input)) && getInput(argv[1]));
- printf("\nBye!\n");
- return 0;
- }
|
Of course, there should be more to an adventure than
traversing dozens of locations just to take object A to location B.
In the next chapter, we will look at ways to bring more depth into the game.
Next chapter: 19. Conversations


Welcome to our {{ application_name }} application! It shows us how to create a simple MVC. {{ 01_install_dir_structure }}


§805-10 Same; summons, service. The summons shall be served by delivering a copy thereof, and showing the original to the president or other head of the corporation, or to the secretary, treasurer, or managing agent thereof. [L 1893-4, c 24, §2; RL 1925, §4014; RL 1935, §5474; RL 1945, §10777; RL 1955, §257-10; HRS §710-10; ren L 1972, c 9, pt of §1]
Rules of Court
Obtaining the appearance of defendant, see HRPP rule 9.
Case Notes
Cited: 41 H. 348 (1956).


|
Video Codec in Python
Video codec project, implemented in Python
|
Public Member Functions | |
| def | __init__ (self, height, width, fileName) |
| def | advance (self) |
| def | getY (self) |
| def | getU (self) |
| def | getV (self) |
Public Attributes | |
| height | |
| width | |
| YComponent | |
| UComponent | |
| VComponent | |
| file | |
Abstract class of a Frame.
| def frames.Frame.advance | ( | self | ) |
Advances and consumes the current bunch of data, so that it can refresh the current set of matrices with new ones. :return whether or not the reading (and setting operation was successful)


A manga editor is usually an employee of a publishing company. Usually, manga is what appears in a manga magazine(like Weekly Jump). So the editor contacts the mangaka assigned to them, tells them what manga they want, how many pages are required, etc.
And usually, a mangaka talks to his editor for points regarding the plot (and even their storylines!), and decides what manga he draws and how he draws it. Then he draws a 'name'(rough sketch of manga) and let the editor check it. If everything is ok, mangaka begins to draw real manga. And sometimes the editor make various errands like to research for his mangaka.
Also, editors teach a new-comer how to draw manga (though most of them cannot draw manga).
Before making a manga, the mangaka must first decide on what genre or demographic of manga he is going to make. It can be either Shoujo, Shounen, or Seinen manga.
Shounen manga is generally manga targeted towards the "young boy" market. The usual topics for this involve fighting, friendship and sometimes super powers. Some manga that fall into this category include "Bleach", "Naruto", and "OnePiece."
Shoujo manga is manga targeted towards the "young girl" market. Usually the story is from the point of view of a girl and deals with romance, drama or magic. The art style of Shoujo manga largely differs from Shounen manga especially in the depiction of characters. Shounen manga characters are depicted with masculinity while Shoujo manga characters are drawn with very feminine characteristics. This makes even the male characters in Shoujo manga look like female characters.
Seinen manga are manga marketed to adolescent boys and men old enough to read kanji. In Japanese, the word 'seinen' literally means "youth", but the term "seinen manga" is also used to describe the audience of comics like Weekly Manga Times and Weekly Manga Goraku which are aimed at men on into their 50s. Seinen manga usually contain gory scenes, nudity, violence, and such. Although most shounen manga also contain violence, Seinen manga tend to be highly graphic and more . Seinen manga also have very deep story lines.
The first contact a mangaka makes with the editorial department is when they submit their manga!
Mangakas who want their work to be published in magazine such as Shounen Jump! first need to make a 'name' which is a one-shot (a manga piece that is completed in only a few pages). The one-shot is then scanned by an editor from the publishing company. The editor evaluates the quality of the work, the art style, the story line, and the over all readability.
Once a manga has been approved by an editor, it is once again sent to be screened by other editors in a competition with other submissions. There are a lot after all! Once a manga was chosen, it will be published for all the readers to enjoy. Additionally, a mangaka who has made an exceptional manga can be called by the publishing company to be one of their official mangaka! An editor is then assigned to them, and the mangaka starts his path towards being the best authors of Japan! If all goes well, his manga can even be turned into anime!
Once a mangaka gets serialized, he needs to start working properly. As of this moment, drawing manga is no longer just a hobby or pastime but rather a job. He gets to sign a contract with the publishing company, and then given salary based of course on how well his manga sells. In the beginning, the editor suggests assistants to work with the mangaka. Assistants are extremely crucial in making manga, especially on a week-based publication. Shounen manga artists are particularly in need of assistants.


Your name
Title
Given names
*
Family name
*
Fubar
Foo
Bar
No group
*
Postal address
Street address
*
Town or suburb
*
State
*
Postcode
*
Country
*


Users need additional, potentially optional information on demand.
Description
Tooltips provide information when an element is hovered over.
Tooltips are attached to an element and appear when the element is hovered over
The show and hide delays of a tooltip may vary depending on the need, but the default is 200ms to show and 0ms to hide
Tooltips cannot be interacted with
Use tooltips for:
Icon and button descriptions
Expanding shortened date/time stamps
Standard tooltip:
I'm a tooltip!
Light tooltip (for dark interfaces):
I'm a light tooltip!
Positioning
Tooltips can be positioned either above, below, to the left, or to the right of target element. In many situations, tooltips should be positioned dynamically based on the available space around the target element.
Content
The content of a tooltip can vary. It’s best to keep it to a few words.


THE MAN WHO CRIED -----------------
In 1927, a doting father (Oleg Yankovsky, "Mute Witness") seemingly lives an idyllic life with his young daughter (Claudia Lander-Duke) in rural Russia. Convinced by a visitting relative to try his luck in America, he ruefully leaves the tiny girl with his mother (Hana Maria Pravda). As their shtetl is being burned to the ground, Grandmother gives her to escaping strangers, along with some gold coins and a photograph of her father. After a horrific journey, she's named Suzie by an English port official (Clifford Barry), discovered to have a singing voice, and eventually lands in Paris (grown up, played by Christina Ricci), but her goal remains America in "The Man Who Cried."
Written and directed by Sally Potter ("Orlando, "The Tango Lesson), "The Man Who Cried" is an odd amalgamation of filmmaking - good performances admist luckluster and over the top, on location filming mixed with obviously artificial set pieces, and a weirdly titled tale that, while nothing new, somehow remains engaging. It's a survivor story set against a historical background writ from the movie screen.
Suzie, presumed a Brit, gets a job in a cabaret and meets Lola (Cate Blanchett), a fellow Russian out to land a wealthy man. He arrives in the person of Dante (John Turturro), an egotistical Italian opera singer in the stage company of Felix Perlman (Harry Dean Stanton). Lola gets herself and Suzie jobs in its chorus and soon she's keeping company with Dante, (who's most interested to learn Suzie's secret when he spies her father's picture). Suzie gravitates towards Cesar (Johnny Depp), a gypsy of few words who rides a white stallion onto the stage every night (much to Dante's chagrin). The gypsy way of life reminds Suzie of her Jewish heritage and Cesar presents an alternative to the quest for her father. But the lines are clearly drawn for these four characters with the onset of WWII and the Nazi occupation of Paris. (Their fate is economically summed up with a crane shot of a vaguely Naziish prop looming over Dante and Suzie, faced off on the stage below.)
Potter opens her film with credits rolling over her heroine (Ricci), fighting for life in a dark ocean (clearly a tank) surrounded by burning debris and water. Fire and mist continue as leitmotifs throughout the film, although what they symbolize is a mystery (the lack of solid ground afforded Suzie?). The steam rising from a bowl of soup becomes the most predominant (and oddly hypnotic) element of a shot. Visually, "The Man Who Cried" is often stunning, featuring slow-mo closeups of Suzie, costumed for an ornate outdoor tableau, or Cesar performing stunts on horseback through the nighttime streets of Paris. A shot of young Suzie stubbornly clutching the heavily framed portrait of her father (it's almost as big as she is) while terror reigns around her is haunting. (Cinematography is by Sacha Vierny, "The Cook, the Thief, His Wife and Her Lover" and "Prospero's Books").
Ricci plays Suzie largely as an impassive observer. This, combined with Ricci's otherworldly appearance, makes for an empathetic portrayal of an intelligent survivor who's not what she appears to be. A shy smile for Cesar is hard won and takes on more weight. Blanchett continues to amaze with her ability to transform herself. Peroxide blonde Lola targets Dante like a heat-seeking missile and Blanchett does the heavily-accented flirt well. She also brings urgency and regret to her character when she must face the inevitable. Depp recycles his role from "Chocolat" as a man of even lesser words. Potter objectifies him, but Depp gets across the simple points - anger at oppression, love for his family. Turturro, however, relies on theatrical posing and grandstanding in a two dimensional performance. Harry Dean Stanton is quietly convincing, lending an air of melancholy to his democratic businessman. Young Claudia Lander-Duke looks like Ricci and her stoicism meshes well into Ricci's portrayal of the character.
"The Man Who Cried" is an off kilter work from a visual, overtly romantic director.
B-
For more Reeling reviews visit www.reelingreviews.com
<EMAIL_ADDRESS><EMAIL_ADDRESS>The review above was posted to the
rec.arts.movies.reviews newsgroup (de.rec.film.kritiken for German reviews).
The Internet Movie Database accepts no responsibility for the contents of the
review and has no editorial control. Unless stated otherwise, the copyright
belongs to the author.
Please direct comments/criticisms of the review to relevant newsgroups.
Broken URLs inthe reviews are the responsibility of the author.
The formatting of the review is likely to differ from the original due
to ASCII to HTML conversion.
Related links: index of all rec.arts.movies.reviews reviews


Home
Blog Posts
Projects
Resume
About Me
Contact
Projects
Find the Treasure!
Looping in Ruby and JavaScript
To-Do List


|
CDs of important
Monroe County record books are now available. Each page of dozens of Monroe County record books have been
photographed and made into CDs. For a
current list of available CDs click
here. |
Map of Ohio prepared by John Tilden Latta in
November 12, 1893 at the age of 17.
For an enlarged version of this map,
click on the map above and wait for it to load
Use the Back-arrow at the top of this web-page to return to the Previous Page.
|
CDs of important
Monroe County record books are now available. Each page of dozens of Monroe County record books have been
photographed and made into CDs. For a
current list of available CDs click
here. |


How to search BioStudies database
Use the Search box available in the top-right corner of every page. Enter any words that describe studies you are interested in. As you start typing, a drop-down list will appear suggesting terms that match. For terms that are in EFO (Experimental Factor Ontology - an EMBL-EBI resource providing systematic descriptions of biological samples and experimental variables), a button will be provided enabling expansion of more specific terms. Search terms are retained in the search box, where they can be refined (see the Advanced search section below).
The search results page is a list of matching biostudies sorted according to relevance. You can also change the sorting by using the Sort by selector. If there are many results, they will be split over multiple pages. Links at the top of the results allow you to change the number of studies displayed per page as well as the current result page. Clicking on the title of a study takes you to a more detailed page about that study.
Within the results any matching terms are highlighted. Yellow highlighting indicates exact matches, green highlighting indicates synonyms, and peach highlighting indicates more specific matches (e.g. "carcinoma" as a more specific term for "cancer"). These more specific terms are from EFO.
Advanced search
Each word in the query is treated as a separate term (unless surrounded by double quotes), and by default every result has to contain all the terms. This behaviour can be modified by using boolean operators and brackets; e.g., Leukemia AND ( mouse OR human ), or cancer AND NOT ( human ).
Queries containing star or question mark characters are treated separately. A star character will match any combination of zero or more characters, e.g., leuk*mia will match to leukemia and leukaemia, as well as leukqwertymia. A question mark character will match any single characters, e.g., m?n will match both man and men. Queries that include wildcards are not expanded.


---
layout: page
date: 2019-05-22 07:30:00 -0400
title: "Factor Graphs and GTSAM"
categories: factor-graphs
---
Factor Graphs and GTSAM:A Hands-on Introduction
This is an updated version of the 2012 tech-report Factor Graphs
and GTSAM: A Hands-on Introduction by Frank Dellaert. A more thorough
introduction to the use of factor graphs in robotics is the 2017 article Factor graphs for robot perception by Frank
Dellaert and Michael Kaess.
Overview
Factor graphs are graphical models (
Koller and Friedman, 2009) that are well suited to modeling complex estimation problems, such as
Simultaneous Localization and Mapping (SLAM) or Structure from Motion (SFM). You might be familiar with another
often used graphical model, Bayes networks, which are directed acyclic graphs. A
factor graph, however, is a
bipartite graph consisting of factors connected to variables. The
variables represent the unknown
random variables in the estimation problem, whereas the
factors represent probabilistic constraints on those
variables, derived from measurements or prior knowledge. In the following sections I will illustrate this with
examples from both robotics and vision.
The GTSAM toolbox (GTSAM stands for “Georgia Tech Smoothing and
Mapping”) toolbox is a BSD-licensed C++ library based on factor graphs, developed at the Georgia Institute of
Technology by myself, many of my students, and collaborators. It provides state of the art solutions to the SLAM and
SFM problems, but can also be used to model and solve both simpler and more complex estimation problems. It also
provides a MATLAB interface which allows for rapid prototype development, visualization, and user interaction.
GTSAM exploits sparsity to be computationally efficient. Typically
measurements only provide information on the relationship between a handful of variables, and hence the resulting
factor graph will be sparsely connected. This is exploited by the algorithms implemented in GTSAM to reduce
computational complexity. Even when graphs are too dense to be handled efficiently by direct methods, GTSAM provides
iterative methods that are quite efficient regardless.
You can download the latest version of GTSAM from our
Github repo.
1 Factor Graphs
Let us start with a one-page primer on factor graphs, which in no way
replaces the excellent and detailed reviews by
Kschischang et al. (
2001) and
Loeliger (
2004).
Figure
1 shows the
Bayes network
for a hidden Markov model (HMM) over three time steps. In a Bayes net, each node is associated with a conditional
density: the top Markov chain encodes the prior
and transition probabilities
and
, whereas measurements
depend only on the state
, modeled by conditional densities
. Given known measurements
,
and
we are interested in the hidden state sequence
that maximizes the posterior probability
. Since the measurements
,
, and
are
known, the posterior is proportional to the product of six
factors, three of which
derive from the the Markov chain, and three likelihood factors defined as
:
This motivates a different graphical model, a
factor graph, in
which we only represent the unknown variables
,
, and
, connected to factors that encode probabilistic information on them, as in Figure
2. To do maximum a-posteriori (MAP) inference, we then maximize the product
i.e., the value of the factor graph. It should be clear from the figure that the connectivity of a factor
graph encodes, for each factor
, which subset of variables
it depends on. In the examples below, we use factor graphs to model more complex MAP inference problems in
robotics.
2
Modeling Robot Motion
2.1 Modeling with Factor Graphs
Before diving into a SLAM example, let us consider the simpler problem
of modeling robot motion. This can be done with a continuous Markov chain, and provides a gentle
introduction to GTSAM.
The factor graph for a simple example is shown in Figure
3. There are three variables
,
, and
which represent the poses of the robot over time, rendered in the figure by the open-circle variable nodes.
In this example, we have one
unary factor on the first pose
that encodes our prior knowledge about
, and two
binary factors that relate successive poses, respectively
and
, where
and
represent odometry measurements.
2.2 Creating a Factor Graph
The following C++ code, included in GTSAM as an example, creates the
factor graph in Figure
3:
// Create an empty nonlinear factor graph
NonlinearFactorGraph graph;
// Add a Gaussian prior on pose x_1
Pose2 priorMean(0.0, 0.0, 0.0);
noiseModel::Diagonal::shared_ptr priorNoise =
noiseModel::Diagonal::Sigmas(Vector3(0.3, 0.3, 0.1));
graph.add(PriorFactor<Pose2>(1, priorMean, priorNoise));
// Add two odometry factors
Pose2 odometry(2.0, 0.0, 0.0);
noiseModel::Diagonal::shared_ptr odometryNoise =
noiseModel::Diagonal::Sigmas(Vector3(0.2, 0.2, 0.1));
graph.add(BetweenFactor<Pose2>(1, 2, odometry, odometryNoise));
graph.add(BetweenFactor<Pose2>(2, 3, odometry, odometryNoise));
Above, line 2 creates an empty factor graph. We then add the factor
on lines 5-8 as an instance of PriorFactor<T>, a templated class provided in the slam
subfolder, with T=Pose2. Its constructor takes a variable Key (in this case 1), a
mean of type Pose2, created on Line 5, and a noise model for the prior density. We provide a
diagonal Gaussian of type noiseModel::Diagonal by specifying three standard deviations in line 7,
respectively 30 cm. on the robot's position, and 0.1 radians on the robot's orientation. Note that the
Sigmas constructor returns a shared pointer, anticipating that typically the same noise models are
used for many different factors.
Similarly, odometry measurements are specified as Pose2
on line 11, with a slightly different noise model defined on line 12-13. We then add the two factors and on lines 14-15, as instances of yet another templated class, BetweenFactor<T>, again
with T=Pose2.
When running the example (make OdometryExample.run on the
command prompt), it will print out the factor graph as follows:
Factor Graph:
size: 3
Factor 0: PriorFactor on 1
prior mean: (0, 0, 0)
noise model: diagonal sigmas [0.3; 0.3; 0.1];
Factor 1: BetweenFactor(1,2)
measured: (2, 0, 0)
noise model: diagonal sigmas [0.2; 0.2; 0.1];
Factor 2: BetweenFactor(2,3)
measured: (2, 0, 0)
noise model: diagonal sigmas [0.2; 0.2; 0.1];
2.3 Factor Graphs versus Values
At this point it is instructive to emphasize two important design ideas
underlying GTSAM:
- The factor graph and its embodiment in code specify the joint probability distribution
over the entire trajectory of the robot, rather than just the last pose. This smoothing view of the world gives GTSAM its
name: “smoothing and mapping”. Later in this document we will talk about how we can also use GTSAM to
do filtering (which you often do not want to do) or incremental inference (which we do all the time).
- A factor graph in GTSAM is just the specification of the probability density , and the corresponding FactorGraph class and its derived classes do not ever contain a
“solution”. Rather, there is a separate type Values that is used to specify specific
values for (in this case) , , and , which can then be used to evaluate the probability (or, more commonly, the error) associated with
particular values.
The latter point is often a point of confusion with beginning users of
GTSAM. It helps to remember that when designing GTSAM we took a functional approach of classes corresponding to
mathematical objects, which are usually immutable. You should think of a factor graph as a function to be
applied to values -as the notation implies- rather than as an object to be modified.
2.4 Non-linear Optimization in
GTSAM
The listing below creates a Values instance, and uses
it as the initial estimate to find the maximum a-posteriori (MAP) assignment for the trajectory :
// create (deliberately inaccurate) initial estimate
Values initial;
initial.insert(1, Pose2(0.5, 0.0, 0.2));
initial.insert(2, Pose2(2.3, 0.1, -0.2));
initial.insert(3, Pose2(4.1, 0.1, 0.1));
// optimize using Levenberg-Marquardt optimization
Values result = LevenbergMarquardtOptimizer(graph, initial).optimize();
Lines 2-5 in Listing
2.4
create the initial estimate, and on line 8 we create a non-linear Levenberg-Marquardt style optimizer, and call
optimize using default parameter settings. The reason why GTSAM needs to perform non-linear
optimization is because the odometry factors
and
are non-linear, as they involve the orientation of the robot. This also explains why the factor graph we
created in Listing
2.2 is of type
NonlinearFactorGraph. The
optimization class linearizes this graph, possibly multiple times, to minimize the non-linear squared error
specified by the factors.
The relevant output from running the example is as follows:
Initial Estimate:
Values with 3 values:
Value 1: (0.5, 0, 0.2)
Value 2: (2.3, 0.1, -0.2)
Value 3: (4.1, 0.1, 0.1)
Final Result:
Values with 3 values:
Value 1: (-1.8e-16, 8.7e-18, -9.1e-19)
Value 2: (2, 7.4e-18, -2.5e-18)
Value 3: (4, -1.8e-18, -3.1e-18)
It can be seen that, subject to very small tolerance, the ground truth
solution , , and is recovered.
2.5
Full Posterior Inference
GTSAM can also be used to calculate the covariance matrix for each pose
after incorporating the information from all measurements . Recognizing that the factor graph encodes the posterior density , the mean together with the covariance for each pose approximate the marginal posterior density . Note that this is just an approximation, as even in this simple case the odometry factors are actually
non-linear in their arguments, and GTSAM only computes a Gaussian approximation to the true underlying posterior.
The following C++ code will recover the posterior marginals:
// Query the marginals
cout.precision(2);
Marginals marginals(graph, result);
cout << "x1 covariance:\n" << marginals.marginalCovariance(1) << endl;
cout << "x2 covariance:\n" << marginals.marginalCovariance(2) << endl;
cout << "x3 covariance:\n" << marginals.marginalCovariance(3) << endl;
The relevant output from running the example is as follows:
x1 covariance:
0.09 1.1e-47 5.7e-33
1.1e-47 0.09 1.9e-17
5.7e-33 1.9e-17 0.01
x2 covariance:
0.13 4.7e-18 2.4e-18
4.7e-18 0.17 0.02
2.4e-18 0.02 0.02
x3 covariance:
0.17 2.7e-17 8.4e-18
2.7e-17 0.37 0.06
8.4e-18 0.06 0.03
What we see is that the marginal covariance on is simply the prior knowledge on , but as the robot moves the uncertainty in all dimensions grows without bound, and the and components of the pose become (positively) correlated.
An important fact to note when interpreting these numbers is that
covariance matrices are given in relative coordinates, not absolute coordinates. This is because internally
GTSAM optimizes for a change with respect to a linearization point, as do all nonlinear optimization libraries.
3 Robot Localization
3.1 Unary Measurement Factors
In this section we add measurements to the factor graph that will help
us actually localize the robot over time. The example also serves as a tutorial on creating new factor
types.
In particular, we use
unary measurement factors to handle
external measurements. The example from Section
2 is not very useful on a real
robot, because it only contains factors corresponding to odometry measurements. These are imperfect and will lead to
quickly accumulating uncertainty on the last robot pose, at least in the absence of any external measurements (see
Section
2.5). Figure
4 shows a new
factor graph where the prior
is omitted and instead we added three unary factors
,
, and
, one for each localization measurement
, respectively. Such unary factors are applicable for measurements
that depend
only on the current robot pose, e.g., GPS readings, correlation of a laser range-finder
in a pre-existing map, or indeed the presence of absence of ceiling lights (see
Dellaert et al. (
1999) for that amusing
example).
3.2 Defining Custom Factors
In GTSAM, you can create custom unary factors by deriving a new class
from the built-in class NoiseModelFactor1<T>, which implements a unary factor corresponding to
a measurement likelihood with a Gaussian noise model, where is the measurement, is the unknown variable, is a (possibly nonlinear) measurement function, and is the noise covariance. Note that is considered known above, and the likelihood will only ever be evaluated as a function of , which explains why it is a unary factor . It is always the unknown variable that is either likely or unlikely, given the measurement.
Note: many people get this backwards, often misled by the
conditional density notation . In fact, the likelihood is defined as any function of proportional to .
Listing
3.2 shows an example
on how to define the custom factor class
UnaryFactor which implements a “GPS-like”
measurement likelihood:
class UnaryFactor: public NoiseModelFactor1<Pose2> {
double mx_, my_; ///< X and Y measurements
public:
UnaryFactor(Key j, double x, double y, const SharedNoiseModel& model):
NoiseModelFactor1<Pose2>(model, j), mx_(x), my_(y) {}
Vector evaluateError(const Pose2& q,
boost::optional<Matrix&> H = boost::none) const
{
const Rot2& R = q.rotation();
if (H) (*H) = (gtsam::Matrix(2, 3) <<
R.c(), -R.s(), 0.0,
R.s(), R.c(), 0.0).finished();
return (Vector(2) << q.x() - mx_, q.y() - my_).finished();
}
};
In defining the derived class on line 1, we provide the template
argument
Pose2 to indicate the type of the variable
, whereas the measurement is stored as the instance variables
mx_ and
my_,
defined on line 2. The constructor on lines 5-6 simply passes on the variable key
and the noise model to the superclass, and stores the measurement values provided. The most important
function to has be implemented by every factor class is
evaluateError, which should return
which is done on line 12. Importantly, because we want to use this factor for nonlinear optimization (see e.g.,
Dellaert and Kaess 2006 for details), whenever the optional argument
is provided, a
Matrix reference, the function should assign the
Jacobian of
to it, evaluated at the provided value for
. This is done for this example on line 11. In this case, the Jacobian of the 2-dimensional function
, which just returns the position of the robot,
with respect the 3-dimensional pose
, yields the following
matrix:
Important Note
Many of our users, when attempting to create a custom factor, are
initially surprised at the Jacobian matrix not agreeing with their intuition. For example, above you might simply
expect a diagonal matrix. This would be true for variables belonging to a vector space. However, in GTSAM we
define the Jacobian more generally to be the matrix such thatwhere is an incremental update and is the exponential map for the variable we want to update, In this case , where is the group of 2D rigid transforms, implemented by Pose2. The exponential map for
can be approximated to first order aswhen using the matrix representation for 2D poses, and hence which then explains the Jacobian .
3.3 Using Custom Factors
The following C++ code fragment illustrates how to create and add
custom factors to a factor graph:
// add unary measurement factors, like GPS, on all three poses
noiseModel::Diagonal::shared_ptr unaryNoise =
noiseModel::Diagonal::Sigmas(Vector2(0.1, 0.1)); // 10cm std on x,y
graph.add(boost::make_shared<UnaryFactor>(1, 0.0, 0.0, unaryNoise));
graph.add(boost::make_shared<UnaryFactor>(2, 2.0, 0.0, unaryNoise));
graph.add(boost::make_shared<UnaryFactor>(3, 4.0, 0.0, unaryNoise));
In Listing 3.3, we create the noise model on line 2-3, which now
specifies two standard deviations on the measurements and . On lines 4-6 we create shared_ptr versions of three newly created UnaryFactor
instances, and add them to graph. GTSAM uses shared pointers to refer to factors in factor graphs, and
boost::make_shared is a convenience function to simultaneously construct a class and create a
shared_ptr to it. We obtain the factor graph from Figure 4.
3.4 Full Posterior Inference
The three GPS factors are enough to fully constrain all unknown poses
and tie them to a “global” reference frame, including the three unknown orientations. If not, GTSAM
would have exited with a singular matrix exception. The marginals can be recovered exactly as in Section
2.5, and the solution and marginal covariances are now given by the
following:
Final Result:
Values with 3 values:
Value 1: (-1.5e-14, 1.3e-15, -1.4e-16)
Value 2: (2, 3.1e-16, -8.5e-17)
Value 3: (4, -6e-16, -8.2e-17)
x1 covariance:
0.0083 4.3e-19 -1.1e-18
4.3e-19 0.0094 -0.0031
-1.1e-18 -0.0031 0.0082
x2 covariance:
0.0071 2.5e-19 -3.4e-19
2.5e-19 0.0078 -0.0011
-3.4e-19 -0.0011 0.0082
x3 covariance:
0.0083 4.4e-19 1.2e-18
4.4e-19 0.0094 0.0031
1.2e-18 0.0031 0.018
Comparing this with the covariance matrices in Section
2.5, we can see that the uncertainty no longer grows without bounds as
measurement uncertainty accumulates. Instead, the “GPS” measurements more or less constrain the poses
evenly, as expected.
It helps a lot when we view this graphically, as in Figure
5, where I show the marginals on position as 5-sigma covariance ellipses that
contain 99.9996% of all probability mass. For the odometry marginals, it is immediately apparent from the figure
that (1) the uncertainty on pose keeps growing, and (2) the uncertainty on angular odometry translates into
increasing uncertainty on y. The localization marginals, in contrast, are constrained by the unary factors and are
all much smaller. In addition, while less apparent, the uncertainty on the middle pose is actually smaller as it is
constrained by odometry from two sides.
You might now be wondering how we produced these figures. The answer is
via the MATLAB interface of GTSAM, which we will demonstrate in the next section.
4
PoseSLAM
4.1 Loop Closure Constraints
The simplest instantiation of a SLAM problem is
PoseSLAM, which
avoids building an explicit map of the environment. The goal of SLAM is to simultaneously localize a robot and map
the environment given incoming sensor measurements (
Durrant-Whyte and Bailey, 2006). Besides
wheel odometry, one of the most popular sensors for robots moving on a plane is a 2D laser-range finder, which
provides both odometry constraints between successive poses, and loop-closure constraints when the robot re-visits a
previously explored part of the environment.
A factor graph example for PoseSLAM is shown in Figure
6. The following C++ code, included in GTSAM as an example, creates this factor graph in
code:
NonlinearFactorGraph graph;
noiseModel::Diagonal::shared_ptr priorNoise =
noiseModel::Diagonal::Sigmas(Vector3(0.3, 0.3, 0.1));
graph.add(PriorFactor<Pose2>(1, Pose2(0, 0, 0), priorNoise));
// Add odometry factors
noiseModel::Diagonal::shared_ptr model =
noiseModel::Diagonal::Sigmas(Vector3(0.2, 0.2, 0.1));
graph.add(BetweenFactor<Pose2>(1, 2, Pose2(2, 0, 0 ), model));
graph.add(BetweenFactor<Pose2>(2, 3, Pose2(2, 0, M_PI_2), model));
graph.add(BetweenFactor<Pose2>(3, 4, Pose2(2, 0, M_PI_2), model));
graph.add(BetweenFactor<Pose2>(4, 5, Pose2(2, 0, M_PI_2), model));
// Add the loop closure constraint
graph.add(BetweenFactor<Pose2>(5, 2, Pose2(2, 0, M_PI_2), model));
As before, lines 1-4 create a nonlinear factor graph and add the unary
factor . As the robot travels through the world, it creates binary factors corresponding to odometry, added to the graph in lines 6-12 (Note that M_PI_2 refers to pi/2). But line 15
models a different event: a loop closure. For example, the robot might recognize the same location using
vision or a laser range finder, and calculate the geometric pose constraint to when it first visited this location.
This is illustrated for poses and , and generates the (red) loop closing factor .
We can optimize this factor graph as before, by creating an initial
estimate of type
Values, and creating and running an optimizer. The result is shown graphically in
Figure
7, along with covariance ellipses shown in green. These 5-sigma covariance
ellipses in 2D indicate the marginal over position, over all possible orientations, and show the area which contain
99.9996% of the probability mass. The graph shows in a clear manner that the uncertainty on pose
is now much less than if there would be only odometry measurements. The pose with the highest uncertainty,
, is the one furthest away from the unary constraint
, which is the only factor tying the graph to a global coordinate frame.
The figure above was created using an interface that allows you to use
GTSAM from within MATLAB, which provides for visualization and rapid development. We discuss this next.
4.2 Using the MATLAB Interface
A large subset of the GTSAM functionality can be accessed through
wrapped classes from within MATLAB . The following code excerpt is the MATLAB equivalent of the C++ code in Listing
4.1:
graph = NonlinearFactorGraph;
priorNoise = noiseModel.Diagonal.Sigmas([0.3; 0.3; 0.1]);
graph.add(PriorFactorPose2(1, Pose2(0, 0, 0), priorNoise));
%% Add odometry factors
model = noiseModel.Diagonal.Sigmas([0.2; 0.2; 0.1]);
graph.add(BetweenFactorPose2(1, 2, Pose2(2, 0, 0 ), model));
graph.add(BetweenFactorPose2(2, 3, Pose2(2, 0, pi/2), model));
graph.add(BetweenFactorPose2(3, 4, Pose2(2, 0, pi/2), model));
graph.add(BetweenFactorPose2(4, 5, Pose2(2, 0, pi/2), model));
%% Add pose constraint
graph.add(BetweenFactorPose2(5, 2, Pose2(2, 0, pi/2), model));
Note that the code is almost identical, although there are a few
syntax and naming differences:
- Objects are created by calling a constructor instead of allocating them on the heap.
- Namespaces are done using dot notation, i.e.,
noiseModel::Diagonal::SigmasClasses becomes noiseModel.Diagonal.Sigmas.
- Vector and Matrix classes in C++ are just vectors/matrices
in MATLAB.
- As templated classes do not exist in MATLAB, these have been hardcoded in the GTSAM
interface, e.g., PriorFactorPose2 corresponds to the C++ class
PriorFactor<Pose2>, etc.
After executing the code, you can call whos on the MATLAB
command prompt to see the objects created. Note that the indicated Class corresponds to the wrapped C++
classes:
>> whos
Name Size Bytes Class
graph 1x1 112 gtsam.NonlinearFactorGraph
priorNoise 1x1 112 gtsam.noiseModel.Diagonal
model 1x1 112 gtsam.noiseModel.Diagonal
initialEstimate 1x1 112 gtsam.Values
optimizer 1x1 112 gtsam.LevenbergMarquardtOptimizer
In addition, any GTSAM object can be examined in detail, yielding identical output to C++:
>> priorNoise
diagonal sigmas [0.3; 0.3; 0.1];
>> graph
size: 6
factor 0: PriorFactor on 1
prior mean: (0, 0, 0)
noise model: diagonal sigmas [0.3; 0.3; 0.1];
factor 1: BetweenFactor(1,2)
measured: (2, 0, 0)
noise model: diagonal sigmas [0.2; 0.2; 0.1];
factor 2: BetweenFactor(2,3)
measured: (2, 0, 1.6)
noise model: diagonal sigmas [0.2; 0.2; 0.1];
factor 3: BetweenFactor(3,4)
measured: (2, 0, 1.6)
noise model: diagonal sigmas [0.2; 0.2; 0.1];
factor 4: BetweenFactor(4,5)
measured: (2, 0, 1.6)
noise model: diagonal sigmas [0.2; 0.2; 0.1];
factor 5: BetweenFactor(5,2)
measured: (2, 0, 1.6)
noise model: diagonal sigmas [0.2; 0.2; 0.1];
And it does not stop there: we can also call some of the functions defined for factor graphs. E.g.,
>> graph.error(initialEstimate)
ans =
20.1086
>> graph.error(result)
ans =
8.2631e-18
computes the sum-squared error
before and after optimization.
4.3 Reading and Optimizing Pose
Graphs
The ability to work in MATLAB adds a much quicker development cycle,
and effortless graphical output. The optimized trajectory in Figure
8 was produced by the
code below, in which
load2D reads TORO files. To see how plotting is done, refer to the full source code.
%% Initialize graph, initial estimate, and odometry noise
datafile = findExampleDataFile('w100.graph');
model = noiseModel.Diagonal.Sigmas([0.05; 0.05; 5*pi/180]);
[graph,initial] = load2D(datafile, model);
%% Add a Gaussian prior on pose x_0
priorMean = Pose2(0, 0, 0);
priorNoise = noiseModel.Diagonal.Sigmas([0.01; 0.01; 0.01]);
graph.add(PriorFactorPose2(0, priorMean, priorNoise));
%% Optimize using Levenberg-Marquardt optimization and get marginals
optimizer = LevenbergMarquardtOptimizer(graph, initial);
result = optimizer.optimizeSafely;
marginals = Marginals(graph, result);
4.4 PoseSLAM in 3D
PoseSLAM can easily be extended to 3D poses, but some care is needed to
update 3D rotations. GTSAM supports both quaternions and rotation matrices to represent 3D rotations. The selection is made via the compile flag
GTSAM_USE_QUATERNIONS.
%% Initialize graph, initial estimate, and odometry noise
datafile = findExampleDataFile('sphere2500.txt');
model = noiseModel.Diagonal.Sigmas([5*pi/180; 5*pi/180; 5*pi/180; 0.05; 0.05; 0.05]);
[graph,initial] = load3D(datafile, model, true, 2500);
plot3DTrajectory(initial, 'g-', false); % Plot Initial Estimate
%% Read again, now with all constraints, and optimize
graph = load3D(datafile, model, false, 2500);
graph.add(NonlinearEqualityPose3(0, initial.atPose3(0)));
optimizer = LevenbergMarquardtOptimizer(graph, initial);
result = optimizer.optimizeSafely();
plot3DTrajectory(result, 'r-', false); axis equal;
5
Landmark-based SLAM
5.1 Basics
In
landmark-based SLAM, we explicitly build a map with the
location of observed landmarks, which introduces a second type of variable in the factor graph besides robot poses.
An example factor graph for a landmark-based SLAM example is shown in Figure
10, which shows
the typical connectivity: poses are connected in an odometry Markov chain, and landmarks are observed from multiple
poses, inducing binary factors. In addition, the pose
has the usual prior on it.
The factor graph from Figure
10 can be created
using the MATLAB code in Listing
5.1. As before, on line 2 we create the
factor graph, and Lines 8-18 create the prior/odometry chain we are now familiar with. However, the code on lines
20-25 is new: it creates three
measurement factors, in this case “bearing/range” measurements
from the pose to the landmark.
% Create graph container and add factors to it
graph = NonlinearFactorGraph;
% Create keys for variables
i1 = symbol('x',1); i2 = symbol('x',2); i3 = symbol('x',3);
j1 = symbol('l',1); j2 = symbol('l',2);
% Add prior
priorMean = Pose2(0.0, 0.0, 0.0); % prior at origin
priorNoise = noiseModel.Diagonal.Sigmas([0.3; 0.3; 0.1]);
% add directly to graph
graph.add(PriorFactorPose2(i1, priorMean, priorNoise));
% Add odometry
odometry = Pose2(2.0, 0.0, 0.0);
odometryNoise = noiseModel.Diagonal.Sigmas([0.2; 0.2; 0.1]);
graph.add(BetweenFactorPose2(i1, i2, odometry, odometryNoise));
graph.add(BetweenFactorPose2(i2, i3, odometry, odometryNoise));
% Add bearing/range measurement factors
degrees = pi/180;
brNoise = noiseModel.Diagonal.Sigmas([0.1; 0.2]);
graph.add(BearingRangeFactor2D(i1, j1, Rot2(45*degrees), sqrt(8), brNoise));
graph.add(BearingRangeFactor2D(i2, j1, Rot2(90*degrees), 2, brNoise));
graph.add(BearingRangeFactor2D(i3, j2, Rot2(90*degrees), 2, brNoise));
5.2 Of Keys and Symbols
The only unexplained code is on lines 4-6: here we create integer keys
for the poses and landmarks using the symbol function. In GTSAM, we address all variables using the
Key type, which is just a typedef to size_t
. The keys do not have to be numbered continuously, but they do have to be unique within a given factor graph.
For factor graphs with different types of variables, we provide the symbol function in MATLAB, and
the Symbol type in C++, to help you create (large) integer keys that are far apart in the space of
possible keys, so you don't have to think about starting the point numbering at some arbitrary offset. To create a a
symbol key you simply provide a character and an integer index. You can use base 0 or 1, or use arbitrary
indices: it does not matter. In the code above, we we use 'x' for poses, and 'l' for landmarks.
The optimized result for the factor graph created by Listing
5.1 is shown in Figure
11, and it is
readily apparent that the landmark
with two measurements is better localized. In MATLAB we can also examine the actual numerical values, and
doing so reveals some more GTSAM magic:
>> result
Values with 5 values:
l1: (2, 2)
l2: (4, 2)
x1: (-1.8e-16, 5.1e-17, -1.5e-17)
x2: (2, -5.8e-16, -4.6e-16)
x3: (4, -3.1e-15, -4.6e-16)
Indeed, the keys generated by symbol are automatically detected by the
print method in the Values class, and rendered in human-readable form
“x1”, “l2”, etc, rather than as large, unwieldy integers. This magic extends to most factors
and other classes where the Key type is used.
5.3 A Larger Example
GTSAM comes with a slightly larger example that is read from a .graph
file by PlanarSLAMExample_graph.m, shown in Figure
12. To not clutter the figure only
the marginals are shown, not the lines of sight. This example, with 119 (multivariate) variables and 517 factors
optimizes in less than 10 ms.
5.4 A Real-World Example
A real-world example is shown in Figure
13, using data from a well known dataset collected in Sydney's Victoria Park, using a
truck equipped with a laser range-finder. The covariance matrices in this figure were computed very efficiently, as
explained in detail in (
Kaess and Dellaert,
2009). The exact covariances (blue, smaller ellipses) obtained by our fast
algorithm coincide with the exact covariances based on full inversion (orange, mostly hidden by blue). The much
larger conservative covariance estimates (green, large ellipses) were based on our earlier work in (
Kaess et al., 2008).
6 Structure from Motion
Structure from Motion (SFM) is a technique to recover a 3D
reconstruction of the environment from corresponding visual features in a collection of
unordered images,
see Figure
14. In GTSAM this is done using exactly the same factor graph framework,
simply using SFM-specific measurement factors. In particular, there is a
projection factor that calculates
the reprojection error
for a given camera pose
(a
Pose3) and point
(a
Point3). The factor is parameterized by the 2D measurement
(a
Point2), and known calibration parameters
(of type
Cal3_S2). The following listing shows how to create the factor graph:
%% Add factors for all measurements
noise = noiseModel.Isotropic.Sigma(2, measurementNoiseSigma);
for i = 1:length(Z),
for k = 1:length(Z{i})
j = J{i}{k};
G.add(GenericProjectionFactorCal3_S2(
Z{i}{k}, noise, symbol('x', i), symbol('p', j), K));
end
end
In Listing
6, assuming that the
factor graph was already created, we add measurement factors in the double loop. We loop over images with index
, and in this example the data is given as two cell arrays: Z{i} specifies a set of measurements
in image
, and J{i} specifies the corresponding point index. The specific factor type we use is a
GenericProjectionFactorCal3_S2, which is the MATLAB equivalent of the C++ class
GenericProjectionFactor<Cal3_S2>, where
Cal3_S2 is the camera calibration type
we choose to use (the standard, no-radial distortion, 5 parameter calibration matrix). As before landmark-based SLAM
(Section
5), here we use symbol keys except we now use the character 'p' to
denote points, rather than 'l' for landmark.
Important note: a very tricky and difficult part of making SFM work is
(a) data association, and (b) initialization. GTSAM does neither of these things for you: it simply provides the
“bundle adjustment” optimization. In the example, we simply assume the data association is known (it is
encoded in the J sets), and we initialize with the ground truth, as the intent of the example is simply to show you
how to set up the optimization problem.
7 iSAM: Incremental Smoothing and
Mapping
GTSAM provides an incremental inference algorithm based on a more
advanced graphical model, the Bayes tree, which is kept up to date by the
iSAM algorithm (incremental
Smoothing and Mapping, see
Kaess et al.
(
2008);
Kaess et
al. (
2012) for an in-depth treatment). For mobile robots operating in
real-time it is important to have access to an updated map as soon as new sensor measurements come in. iSAM keeps
the map up-to-date in an efficient manner.
Listing
7 shows how to use iSAM in a
simple visual SLAM example. In line 1-2 we create a
NonlinearISAM object which will relinearize and
reorder the variables every 3 steps. The corect value for this parameter depends on how non-linear your problem is
and how close you want to be to gold-standard solution at every step. In iSAM 2.0, this parameter is not needed, as
iSAM2 automatically determines when linearization is needed and for which variables.
The example involves eight 3D points that are seen from eight
successive camera poses. Hence in the first step -which is omitted here- all eight landmarks and the first pose are
properly initialized. In the code this is done by perturbing the known ground truth, but in a real application great
care is needed to properly initialize poses and landmarks, especially in a monocular sequence.
int relinearizeInterval = 3;
NonlinearISAM isam(relinearizeInterval);
// ... first frame initialization omitted ...
// Loop over the different poses, adding the observations to iSAM
for (size_t i = 1; i < poses.size(); ++i) {
// Add factors for each landmark observation
NonlinearFactorGraph graph;
for (size_t j = 0; j < points.size(); ++j) {
graph.add(
GenericProjectionFactor<Pose3, Point3, Cal3_S2>
(z[i][j], noise,Symbol('x', i), Symbol('l', j), K)
);
}
// Add an initial guess for the current pose
Values initialEstimate;
initialEstimate.insert(Symbol('x', i), initial_x[i]);
// Update iSAM with the new factors
isam.update(graph, initialEstimate);
}
The remainder of the code illustrates a typical iSAM loop:
- Create factors for new measurements. Here, in lines 9-18, a small
NonlinearFactorGraph is created to hold the new factors of type
GenericProjectionFactor<Pose3, Point3, Cal3_S2>.
- Create an initial estimate for all newly introduced variables. In this small example, all
landmarks have been observed in frame 1 and hence the only new variable that needs to be initialized at each time
step is the new pose. This is done in lines 20-22. Note we assume a good initial estimate is available as
initial_x[i].
- Finally, we call isam.update(), which takes the factors and initial estimates,
and incrementally updates the solution, which is available through the method isam.estimate(), if
desired.
8 More Applications
While a detailed discussion of all the things you can do with GTSAM
will take us too far, below is a small survey of what you can expect to do, and which we did using GTSAM.
8.1 Conjugate Gradient
Optimization
GTSAM also includes efficient preconditioned conjugate gradients (PCG)
methods for solving large-scale SLAM problems. While direct methods, popular in the literature, exhibit quadratic
convergence and can be quite efficient for sparse problems, they typically require a lot of storage and efficient
elimination orderings to be found. In contrast, iterative optimization methods only require access to the gradient
and have a small memory footprint, but can suffer from poor convergence. Our method,
subgraph
preconditioning, explained in detail in
Dellaert et al. (
2010);
Jian et al. (
2011), combines the advantages of direct and iterative methods, by identifying a
sub-problem that can be easily solved using direct methods, and solving for the remaining part using PCG. The easy
sub-problems correspond to a spanning tree, a planar subgraph, or any other substructure that can be efficiently
solved. An example of such a subgraph is shown in Figure
15.
8.2 Visual Odometry
A gentle introduction to vision-based sensing is
Visual Odometry
(abbreviated VO, see e.g.
Nistér et
al. (
2004)), which provides pose constraints between successive robot
poses by tracking or associating visual features in successive images taken by a camera mounted rigidly on the
robot. GTSAM includes both C++ and MATLAB example code, as well as VO-specific factors to help you on the way.
8.3 Visual SLAM
Visual SLAM (see e.g.,
Davison (
2003)) is a SLAM variant where 3D
points are observed by a camera as the camera moves through space, either mounted on a robot or moved around by
hand. GTSAM, and particularly iSAM (see below), can easily be adapted to be used as the back-end optimizer in such a
scenario.
8.4 Fixed-lag Smoothing and
Filtering
GTSAM can easily perform recursive estimation, where only a subset of
the poses are kept in the factor graph, while the remaining poses are marginalized out. In all examples above we
explicitly optimize for all variables using all available measurements, which is called
Smoothing because the
trajectory is “smoothed” out, and this is where GTSAM got its name (GT
Smoothing and Mapping).
When instead only the last few poses are kept in the graph, one speaks of
Fixed-lag Smoothing. Finally, when
only the single most recent poses is kept, one speaks of
Filtering, and indeed the original formulation of
SLAM was filter-based (
Smith et al., 1988).
8.5 Discrete Variables and HMMs
Finally, factor graphs are not limited to continuous variables: GTSAM
can also be used to model and solve discrete optimization problems. For example, a Hidden Markov Model (HMM) has the
same graphical model structure as the Robot Localization problem from Section
2, except that in an HMM the variables are discrete. GTSAM can optimize and
perform inference for discrete models.
Acknowledgements
GTSAM was made possible by the efforts of many collaborators at Georgia
Tech and elsewhere, including but not limited to Doru Balcan, Chris Beall, Alex Cunningham, Alireza Fathi, Eohan
George, Viorela Ila, Yong-Dian Jian, Michael Kaess, Kai Ni, Carlos Nieto, Duy-Nguyen Ta, Manohar Paluri, Christian
Potthast, Richard Roberts, Grant Schindler, and Stephen Williams. In addition, Paritosh Mohan helped me with the
manual. Many thanks all for your hard work!
References
Davison 2003Davison, A.J., "Real-Time
Simultaneous Localisation and Mapping with a Single Camera", in Intl.
Conf. on Computer Vision (ICCV) (2003), pp. 1403-1410.
Dellaert and Kaess 2006Dellaert, F. and Kaess, M., "Square Root SAM: Simultaneous Localization and Mapping via Square Root Information
Smoothing", Intl. J. of Robotics Research 25, 12 (2006), pp.
1181--1203.
Dellaert et al. 1999Dellaert, F., Fox, D., Burgard, W., and Thrun,
S., "Using the Condensation Algorithm for Robust, Vision-based Mobile Robot
Localization", in IEEE Conf. on Computer Vision and Pattern
Recognition (CVPR) (1999).
Dellaert et al. 2010Dellaert, F., Carlson, J., Ila, V., Ni, K., and Thorpe,
C.E., "Subgraph-preconditioned Conjugate Gradient for Large Scale
SLAM", in IEEE/RSJ Intl. Conf. on Intelligent Robots and Systems
(IROS) (2010).
Durrant-Whyte and Bailey
2006Durrant-Whyte, H.F. and Bailey,
T., "Simultaneous Localisation and Mapping (SLAM): Part I The Essential
Algorithms", Robotics & Automation Magazine (2006).
Jian et al. 2011Jian, Y.-D., Balcan, D., and Dellaert, F., "Generalized Subgraph Preconditioners for Large-Scale Bundle Adjustment", in
Intl. Conf. on Computer Vision (ICCV) (2011).
Kaess and Dellaert 2009Kaess, M. and Dellaert, F., "Covariance Recovery from a Square Root Information Matrix for Data Association",
Robotics and Autonomous Systems (2009).
Kaess et al. 2008Kaess, M., Ranganathan, A., and Dellaert, F.,
"iSAM: Incremental Smoothing and Mapping", IEEE
Trans. Robotics 24, 6 (2008), pp. 1365-1378.
Kaess et al. 2012Kaess, M., Johannsson, H., Roberts, R., Ila, V.,
Leonard, J., and Dellaert, F., "iSAM2: Incremental Smoothing and Mapping
Using the Bayes Tree", Intl. J. of Robotics Research 31 (2012), pp. 217--236.
Koller and Friedman 2009Koller, D. and Friedman, N., Probabilistic Graphical Models: Principles and Techniques (The MIT Press, 2009).
Kschischang et al.
2001Kschischang, F.R., Frey, B.J., and
Loeliger, H-A., "Factor Graphs and the Sum-Product Algorithm",
IEEE Trans. Inform. Theory 47, 2 (2001).
Loeliger 2004Loeliger, H.-A., "An
Introduction to Factor Graphs", IEEE Signal Processing
Magazine (2004), pp. 28--41.
Nistér et al. 2004Nistér, D., Naroditsky, O., and Bergen, J.,
"Visual Odometry", in IEEE Conf. on Computer
Vision and Pattern Recognition (CVPR) vol. 1, (2004), pp. 652-659.
Smith et al. 1988Smith, R., Self, M., and Cheeseman, P., "A stochastic map for uncertain spatial relationships", in Proc. of the Intl. Symp. of Robotics Research (ISRR) (1988), pp. 467-474.


Analyses continue usage in switch-case context. Due to PHP's
unusual continue syntax,
new users who are used to other languages might be confused by its behaviour.
Covers following case:
for (...) {
switch(...) {
case ...:
continue; /* should read 'continue 2' to continue with the next iteration of the outer loop */
...
}
}


Ana Maria Acosta
Unafffiliated
stay-at-home Mom
M.A. in Communications (focus in Intercultural Communications), B.A. International Studies, minor in Spanish. Studied in Seville, Spain for one year during undergraduate program. Attended Spanish Language Institute in Cuernavaca for one month. Previously certified in Postural Alignment Therapy by Egoscue Institute.
80487
No donations.
Sandia Homeowner's Association, Chair of Community Outreach and member of Board.
none
I think it is important to have all diverse political affiliations represented in a district to prevent gerrymandering. I want to ensure that all are included in having their voices heard and not silenced.
In order to reach consensus, you have to have dialogue. As a university-level instructor part of my job was to facilitate dialogue and engagement. Additionally, I studied conflict resolution and am skilled at collective negotiation and bringing a cross representation of opinions to the forefront. I was trained at the United States Institute of Peace in Conflict Resolution. As a parent of two teenage children. I must demonstrate fairness and impartiality on an hourly basis.
My analytical skills engage creative techniques. I am not a black and white thinker. I am a critical thinker in theory as well as practice. My most recent use of my analytical skills was as a Postural Alignment Therapist. I observed and analyzed patterns of movement, interpreted musculoskeletal issues (shoulder pain, hip pain, etc)., and logically created a solution of exercises to help alleviate the clients issues at hand.


Singer Moby tattoos 'ANIMAL RIGHTS' on arms for 32-year vegan anniversary
by Jeremiah Rodriguez, CTVNews.caTORONTO -- Singer Moby has been a fairly vocal about his animal rights advocacy and veganism, and now he has the tats to back it up.
The electro-style rocker celebrated his 32nd year of being vegan by getting 12 tattooed letters on both his arms which spell out “ANIMAL RIGHTS,” he said in an Instagram post.
“I’m a vegan animal rights activist for many reasons, but ultimately because I believe at the core of my being that every animal has the right to live their own life, according to their own will,” he added.
He also thanked fellow vegan tattoo artist Kat Von D for the fresh ink.
Moby, who is also a record producer, DJ and author, has cited health and climate change as reasons he’s gone vegan. He has a long history of supporting vegan and animal rights causes. He even released an album called “Animal Rights” in 1996.
He launched the Los Angeles vegan restaurant Little Pine and regularly shares recipes, photos and videos of his vegan meals.
Moby also runs the Circle V music festival, which raises money for animal protection charity, Mercy for Animals.
This isn’t even the first vegan-related tattoo he’s gotten recently. In September, a post showed how he’d tattooed “VEGAN FOR LIFE” on the right side of his neck.
At the time, he wrote that because of his veganism “getting this tattoo seemed like a pretty safe bet.”
Moby added that “working for animal rights and animal liberation is my life’s work.”


Plantagochi
Build and tend to your virtual garden!
My Plants!
Submit
Add Soil
Add Water
Add Light
Submit
Add Soil
Add Water
Add Light
Submit
Add Soil
Add Water
Add Light
Submit
Add Soil
Add Water
Add Light


It's important to include strong security headers when serving content to provide defence-in-depth against client-side attacks.
Content Security Policy is an effective measure to protect your site from XSS attacks. By whitelisting sources of approved content, you can prevent the browser from loading malicious assets.
In order for ID7 to work correctly, the following policy areas must be set:
| Area | Minimum configuration | Reason |
|---|---|---|
default-src |
'self' |
By default, only allow loading resources from the same hostname |
img-src |
'self' data: |
We need to allow data: image URIs in order to perform WebP feature detection |
style-src |
'self' https://fonts.googleapis.com |
We load the Lato webfont from fonts.googleapis.com via a CSS import |
font-src |
'self' https://fonts.gstatic.com |
Domain that the Lato webfont is downloaded from |
script-src |
'self' |
|
frame-src |
'self' https://websignon.warwick.ac.uk https://my.warwick.ac.uk |
This is for the account popover when clicking on the signed in users' name; on small screens this is loaded from websignon, on larger from My Warwick |
connect-src |
'self' https://my.warwick.ac.uk |
To allow the My Warwick popover to work |
object-src |
'none' |
Shouldn't be required; note that you will need to set this to 'self' for Chrome and Firefox's PDF viewer to work when serving PDFs inline |
Feature Policy is a new header that allows a site to control which features and APIs can be used in the browser.
In order for the functionality of the My Warwick popover to work, a minimal Feature-Policy
header is:
feature-policy: accelerometer 'self' https://my.warwick.ac.uk; camera 'none'; geolocation 'none'; gyroscope 'self' https://my.warwick.ac.uk; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'
There are a number of security headers that can be sent, securityheaders.com is an excellent auditing tool for them. A full set of security headers would look something like:
content-security-policy: default-src 'self'; img-src 'self' data:; style-src 'self' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; script-src 'self'; frame-src 'self' https://websignon.warwick.ac.uk https://my.warwick.ac.uk; connect-src 'self' https://my.warwick.ac.uk; object-src 'none'
feature-policy: accelerometer 'self' https://my.warwick.ac.uk; camera 'none'; geolocation 'none'; gyroscope 'self' https://my.warwick.ac.uk; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'
referrer-policy: strict-origin-when-cross-origin
x-frame-options: DENY
x-xss-protection: 1; mode=block
x-content-type-options: nosniff
x-permitted-cross-domain-policies: master-only